OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [nfs/] [nfsroot.c] - Diff between revs 1765 and 1782

Only display areas with differences | Details | Blame | View Log

Rev 1765 Rev 1782
/*
/*
 *  linux/fs/nfs/nfsroot.c -- version 2.3
 *  linux/fs/nfs/nfsroot.c -- version 2.3
 *
 *
 *  Copyright (C) 1995, 1996  Gero Kuhlmann <gero@gkminix.han.de>
 *  Copyright (C) 1995, 1996  Gero Kuhlmann <gero@gkminix.han.de>
 *
 *
 *  For parts of this file:
 *  For parts of this file:
 *  Copyright (C) 1996  Martin Mares <mj@k332.feld.cvut.cz>
 *  Copyright (C) 1996  Martin Mares <mj@k332.feld.cvut.cz>
 *
 *
 *  Allow an NFS filesystem to be mounted as root. The way this works is:
 *  Allow an NFS filesystem to be mounted as root. The way this works is:
 *     (1) Determine the local IP address via RARP or BOOTP or from the
 *     (1) Determine the local IP address via RARP or BOOTP or from the
 *         kernel command line.
 *         kernel command line.
 *     (2) Handle RPC negotiation with the system which replied to RARP or
 *     (2) Handle RPC negotiation with the system which replied to RARP or
 *         was reported as a boot server by BOOTP or manually.
 *         was reported as a boot server by BOOTP or manually.
 *     (3) The actual mounting is done later, when init() is running.
 *     (3) The actual mounting is done later, when init() is running.
 *
 *
 *
 *
 *      Changes:
 *      Changes:
 *
 *
 *      R. Drahtmueller :       Set IFF_MULTICAST in dev->flags if applicable.
 *      R. Drahtmueller :       Set IFF_MULTICAST in dev->flags if applicable.
 *      Alan Cox        :       Removed get_address name clash with FPU.
 *      Alan Cox        :       Removed get_address name clash with FPU.
 *      Alan Cox        :       Reformatted a bit.
 *      Alan Cox        :       Reformatted a bit.
 *      Gero Kuhlmann   :       Code cleanup
 *      Gero Kuhlmann   :       Code cleanup
 *      Michael Rausch  :       Fixed recognition of an incoming RARP answer.
 *      Michael Rausch  :       Fixed recognition of an incoming RARP answer.
 *      Martin Mares    : (2.0) Auto-configuration via BOOTP supported.
 *      Martin Mares    : (2.0) Auto-configuration via BOOTP supported.
 *      Martin Mares    :       Manual selection of interface & BOOTP/RARP.
 *      Martin Mares    :       Manual selection of interface & BOOTP/RARP.
 *      Martin Mares    :       Using network routes instead of host routes,
 *      Martin Mares    :       Using network routes instead of host routes,
 *                              allowing the default configuration to be used
 *                              allowing the default configuration to be used
 *                              for normal operation of the host.
 *                              for normal operation of the host.
 *      Martin Mares    :       Randomized timer with exponential backoff
 *      Martin Mares    :       Randomized timer with exponential backoff
 *                              installed to minimize network congestion.
 *                              installed to minimize network congestion.
 *      Martin Mares    :       Code cleanup.
 *      Martin Mares    :       Code cleanup.
 *      Martin Mares    : (2.1) BOOTP and RARP made configuration options.
 *      Martin Mares    : (2.1) BOOTP and RARP made configuration options.
 *      Martin Mares    :       Server hostname generation fixed.
 *      Martin Mares    :       Server hostname generation fixed.
 *      Gerd Knorr      :       Fixed wired inode handling
 *      Gerd Knorr      :       Fixed wired inode handling
 *      Martin Mares    : (2.2) "0.0.0.0" addresses from command line ignored.
 *      Martin Mares    : (2.2) "0.0.0.0" addresses from command line ignored.
 *      Martin Mares    :       RARP replies not tested for server address.
 *      Martin Mares    :       RARP replies not tested for server address.
 *      Gero Kuhlmann   : (2.3) Some bug fixes and code cleanup again (please
 *      Gero Kuhlmann   : (2.3) Some bug fixes and code cleanup again (please
 *                              send me your new patches _before_ bothering
 *                              send me your new patches _before_ bothering
 *                              Linus so that I don' always have to cleanup
 *                              Linus so that I don' always have to cleanup
 *                              _afterwards_ - thanks)
 *                              _afterwards_ - thanks)
 *      Gero Kuhlmann   :       Last changes of Martin Mares undone.
 *      Gero Kuhlmann   :       Last changes of Martin Mares undone.
 *      Gero Kuhlmann   :       RARP replies are tested for specified server
 *      Gero Kuhlmann   :       RARP replies are tested for specified server
 *                              again. However, it's now possible to have
 *                              again. However, it's now possible to have
 *                              different RARP and NFS servers.
 *                              different RARP and NFS servers.
 *      Gero Kuhlmann   :       "0.0.0.0" addresses from command line are
 *      Gero Kuhlmann   :       "0.0.0.0" addresses from command line are
 *                              now mapped to INADDR_NONE.
 *                              now mapped to INADDR_NONE.
 *      Gero Kuhlmann   :       Fixed a bug which prevented BOOTP path name
 *      Gero Kuhlmann   :       Fixed a bug which prevented BOOTP path name
 *                              from being used (thanks to Leo Spiekman)
 *                              from being used (thanks to Leo Spiekman)
 *      Andy Walker     :       Allow to specify the NFS server in nfs_root
 *      Andy Walker     :       Allow to specify the NFS server in nfs_root
 *                              without giving a path name
 *                              without giving a path name
 *      Swen Th=FCmmler :       Allow to specify the NFS options in nfs_root
 *      Swen Th=FCmmler :       Allow to specify the NFS options in nfs_root
 *                              without giving a path name. Fix BOOTP request
 *                              without giving a path name. Fix BOOTP request
 *                              for domainname (domainname is NIS domain, not
 *                              for domainname (domainname is NIS domain, not
 *                              DNS domain!). Skip dummy devices for BOOTP.
 *                              DNS domain!). Skip dummy devices for BOOTP.
 *      Jacek Zapala    :       Fixed a bug which prevented server-ip address
 *      Jacek Zapala    :       Fixed a bug which prevented server-ip address
 *                              from nfsroot parameter from being used.
 *                              from nfsroot parameter from being used.
 *
 *
 */
 */
 
 
 
 
/* Define this to allow debugging output */
/* Define this to allow debugging output */
#undef NFSROOT_DEBUG
#undef NFSROOT_DEBUG
#undef NFSROOT_BOOTP_DEBUG
#undef NFSROOT_BOOTP_DEBUG
 
 
 
 
#include <linux/config.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/random.h>
#include <linux/random.h>
#include <linux/fcntl.h>
#include <linux/fcntl.h>
 
 
#include <asm/param.h>
#include <asm/param.h>
#include <linux/utsname.h>
#include <linux/utsname.h>
#include <linux/in.h>
#include <linux/in.h>
#include <linux/if.h>
#include <linux/if.h>
#include <linux/inet.h>
#include <linux/inet.h>
#include <linux/net.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_arp.h>
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#include <net/ax25.h>   /* For AX25_P_IP */
#include <net/ax25.h>   /* For AX25_P_IP */
#endif
#endif
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/socket.h>
#include <linux/route.h>
#include <linux/route.h>
#include <linux/nfs.h>
#include <linux/nfs.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs_mount.h>
#include <linux/in.h>
#include <linux/in.h>
#include <net/route.h>
#include <net/route.h>
#include <net/sock.h>
#include <net/sock.h>
 
 
#include <asm/segment.h>
#include <asm/segment.h>
 
 
/* Range of privileged ports */
/* Range of privileged ports */
#define STARTPORT       600
#define STARTPORT       600
#define ENDPORT         1023
#define ENDPORT         1023
#define NPORTS          (ENDPORT - STARTPORT + 1)
#define NPORTS          (ENDPORT - STARTPORT + 1)
 
 
 
 
/* Define the timeout for waiting for a RARP/BOOTP reply */
/* Define the timeout for waiting for a RARP/BOOTP reply */
#define CONF_BASE_TIMEOUT       (HZ*5)  /* Initial timeout: 5 seconds */
#define CONF_BASE_TIMEOUT       (HZ*5)  /* Initial timeout: 5 seconds */
#define CONF_RETRIES            10      /* 10 retries */
#define CONF_RETRIES            10      /* 10 retries */
#define CONF_TIMEOUT_RANDOM     (HZ)    /* Maximum amount of randomization */
#define CONF_TIMEOUT_RANDOM     (HZ)    /* Maximum amount of randomization */
#define CONF_TIMEOUT_MULT       *5/4    /* Speed of timeout growth */
#define CONF_TIMEOUT_MULT       *5/4    /* Speed of timeout growth */
#define CONF_TIMEOUT_MAX        (HZ*30) /* Maximum allowed timeout */
#define CONF_TIMEOUT_MAX        (HZ*30) /* Maximum allowed timeout */
 
 
 
 
/* List of open devices */
/* List of open devices */
struct open_dev {
struct open_dev {
        struct device *dev;
        struct device *dev;
        unsigned short old_flags;
        unsigned short old_flags;
        struct open_dev *next;
        struct open_dev *next;
};
};
 
 
static struct open_dev *open_base = NULL;
static struct open_dev *open_base = NULL;
 
 
 
 
/* IP configuration */
/* IP configuration */
static struct device *root_dev = NULL;  /* Device selected for booting */
static struct device *root_dev = NULL;  /* Device selected for booting */
static char user_dev_name[IFNAMSIZ];    /* Name of user-selected boot device */
static char user_dev_name[IFNAMSIZ];    /* Name of user-selected boot device */
static struct sockaddr_in myaddr;       /* My IP address */
static struct sockaddr_in myaddr;       /* My IP address */
static struct sockaddr_in server;       /* Server IP address */
static struct sockaddr_in server;       /* Server IP address */
static struct sockaddr_in gateway;      /* Gateway IP address */
static struct sockaddr_in gateway;      /* Gateway IP address */
static struct sockaddr_in netmask;      /* Netmask for local subnet */
static struct sockaddr_in netmask;      /* Netmask for local subnet */
 
 
 
 
/* BOOTP/RARP variables */
/* BOOTP/RARP variables */
static int bootp_flag;                  /* User said: Use BOOTP! */
static int bootp_flag;                  /* User said: Use BOOTP! */
static int rarp_flag;                   /* User said: Use RARP! */
static int rarp_flag;                   /* User said: Use RARP! */
static int bootp_dev_count = 0;          /* Number of devices allowing BOOTP */
static int bootp_dev_count = 0;          /* Number of devices allowing BOOTP */
static int rarp_dev_count = 0;           /* Number of devices allowing RARP */
static int rarp_dev_count = 0;           /* Number of devices allowing RARP */
static struct sockaddr_in rarp_serv;    /* IP address of RARP server */
static struct sockaddr_in rarp_serv;    /* IP address of RARP server */
 
 
#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
#define CONFIG_RNFS_DYNAMIC             /* Enable dynamic IP config */
#define CONFIG_RNFS_DYNAMIC             /* Enable dynamic IP config */
static volatile int pkt_arrived;        /* BOOTP/RARP packet detected */
static volatile int pkt_arrived;        /* BOOTP/RARP packet detected */
 
 
#define ARRIVED_BOOTP   1
#define ARRIVED_BOOTP   1
#define ARRIVED_RARP    2
#define ARRIVED_RARP    2
#endif
#endif
 
 
 
 
/* NFS-related data */
/* NFS-related data */
static struct nfs_mount_data nfs_data;          /* NFS mount info */
static struct nfs_mount_data nfs_data;          /* NFS mount info */
static char nfs_path[NFS_MAXPATHLEN] = "";      /* Name of directory to mount */
static char nfs_path[NFS_MAXPATHLEN] = "";      /* Name of directory to mount */
static int nfs_port;                            /* Port to connect to for NFS */
static int nfs_port;                            /* Port to connect to for NFS */
 
 
 
 
/* Yes, we use sys_socket, but there's no include file for it */
/* Yes, we use sys_socket, but there's no include file for it */
extern asmlinkage int sys_socket(int family, int type, int protocol);
extern asmlinkage int sys_socket(int family, int type, int protocol);
 
 
 
 
 
 
/***************************************************************************
/***************************************************************************
 
 
                        Device Handling Subroutines
                        Device Handling Subroutines
 
 
 ***************************************************************************/
 ***************************************************************************/
 
 
/*
/*
 * Setup and initialize all network devices. If there is a user-preferred
 * Setup and initialize all network devices. If there is a user-preferred
 * interface, ignore all other interfaces.
 * interface, ignore all other interfaces.
 */
 */
static int root_dev_open(void)
static int root_dev_open(void)
{
{
        struct open_dev *openp, **last;
        struct open_dev *openp, **last;
        struct device *dev;
        struct device *dev;
        unsigned short old_flags;
        unsigned short old_flags;
 
 
        last = &open_base;
        last = &open_base;
        for (dev = dev_base; dev != NULL; dev = dev->next) {
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if (dev->type < ARPHRD_SLIP &&
                if (dev->type < ARPHRD_SLIP &&
                    dev->family == AF_INET &&
                    dev->family == AF_INET &&
                    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
                    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
                    (0 != strncmp(dev->name, "dummy", 5)) &&
                    (0 != strncmp(dev->name, "dummy", 5)) &&
                    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
                    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
                        /* First up the interface */
                        /* First up the interface */
                        old_flags = dev->flags;
                        old_flags = dev->flags;
#ifdef CONFIG_IP_MULTICAST
#ifdef CONFIG_IP_MULTICAST
                        dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
                        dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
#else
#else
                        dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
                        dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
#endif
#endif
                        if (!(old_flags & IFF_UP) && dev_open(dev)) {
                        if (!(old_flags & IFF_UP) && dev_open(dev)) {
                                dev->flags = old_flags;
                                dev->flags = old_flags;
                                continue;
                                continue;
                        }
                        }
                        openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
                        openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
                                                GFP_ATOMIC);
                                                GFP_ATOMIC);
                        if (openp == NULL)
                        if (openp == NULL)
                                continue;
                                continue;
                        openp->dev = dev;
                        openp->dev = dev;
                        openp->old_flags = old_flags;
                        openp->old_flags = old_flags;
                        *last = openp;
                        *last = openp;
                        last = &openp->next;
                        last = &openp->next;
                        bootp_dev_count++;
                        bootp_dev_count++;
                        if (!(dev->flags & IFF_NOARP))
                        if (!(dev->flags & IFF_NOARP))
                                rarp_dev_count++;
                                rarp_dev_count++;
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
                        printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
                        printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
#endif
#endif
                }
                }
        }
        }
        *last = NULL;
        *last = NULL;
 
 
        if (!bootp_dev_count && !rarp_dev_count) {
        if (!bootp_dev_count && !rarp_dev_count) {
                printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
                printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
                return -1;
                return -1;
        }
        }
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Restore the state of all devices. However, keep the root device open
 *  Restore the state of all devices. However, keep the root device open
 *  for the upcoming mount.
 *  for the upcoming mount.
 */
 */
static void root_dev_close(void)
static void root_dev_close(void)
{
{
        struct open_dev *openp;
        struct open_dev *openp;
        struct open_dev *nextp;
        struct open_dev *nextp;
 
 
        openp = open_base;
        openp = open_base;
        while (openp != NULL) {
        while (openp != NULL) {
                nextp = openp->next;
                nextp = openp->next;
                openp->next = NULL;
                openp->next = NULL;
                if (openp->dev != root_dev) {
                if (openp->dev != root_dev) {
                        if (!(openp->old_flags & IFF_UP))
                        if (!(openp->old_flags & IFF_UP))
                                dev_close(openp->dev);
                                dev_close(openp->dev);
                        openp->dev->flags = openp->old_flags;
                        openp->dev->flags = openp->old_flags;
                }
                }
                kfree_s(openp, sizeof(struct open_dev));
                kfree_s(openp, sizeof(struct open_dev));
                openp = nextp;
                openp = nextp;
        }
        }
}
}
 
 
 
 
 
 
/***************************************************************************
/***************************************************************************
 
 
                              RARP Subroutines
                              RARP Subroutines
 
 
 ***************************************************************************/
 ***************************************************************************/
 
 
#ifdef CONFIG_RNFS_RARP
#ifdef CONFIG_RNFS_RARP
 
 
extern void arp_send(int type, int ptype, unsigned long target_ip,
extern void arp_send(int type, int ptype, unsigned long target_ip,
                     struct device *dev, unsigned long src_ip,
                     struct device *dev, unsigned long src_ip,
                     unsigned char *dest_hw, unsigned char *src_hw,
                     unsigned char *dest_hw, unsigned char *src_hw,
                     unsigned char *target_hw);
                     unsigned char *target_hw);
 
 
static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
                          struct packet_type *pt);
                          struct packet_type *pt);
 
 
 
 
static struct packet_type rarp_packet_type = {
static struct packet_type rarp_packet_type = {
        0,                       /* Should be: __constant_htons(ETH_P_RARP)
        0,                       /* Should be: __constant_htons(ETH_P_RARP)
                                 * - but this _doesn't_ come out constant! */
                                 * - but this _doesn't_ come out constant! */
        NULL,                   /* Listen to all devices */
        NULL,                   /* Listen to all devices */
        root_rarp_recv,
        root_rarp_recv,
        NULL,
        NULL,
        NULL
        NULL
};
};
 
 
 
 
/*
/*
 *  Register the packet type for RARP
 *  Register the packet type for RARP
 */
 */
static void root_rarp_open(void)
static void root_rarp_open(void)
{
{
        rarp_packet_type.type = htons(ETH_P_RARP);
        rarp_packet_type.type = htons(ETH_P_RARP);
        dev_add_pack(&rarp_packet_type);
        dev_add_pack(&rarp_packet_type);
}
}
 
 
 
 
/*
/*
 *  Deregister the RARP packet type
 *  Deregister the RARP packet type
 */
 */
static void root_rarp_close(void)
static void root_rarp_close(void)
{
{
        rarp_packet_type.type = htons(ETH_P_RARP);
        rarp_packet_type.type = htons(ETH_P_RARP);
        dev_remove_pack(&rarp_packet_type);
        dev_remove_pack(&rarp_packet_type);
}
}
 
 
 
 
/*
/*
 *  Receive RARP packets.
 *  Receive RARP packets.
 */
 */
static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
{
        struct arphdr *rarp = (struct arphdr *)skb->h.raw;
        struct arphdr *rarp = (struct arphdr *)skb->h.raw;
        unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
        unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
        unsigned long sip, tip;
        unsigned long sip, tip;
        unsigned char *sha, *tha;               /* s for "source", t for "target" */
        unsigned char *sha, *tha;               /* s for "source", t for "target" */
 
 
        /* If this test doesn't pass, it's not IP, or we should ignore it anyway */
        /* If this test doesn't pass, it's not IP, or we should ignore it anyway */
        if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
        if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
                kfree_skb(skb, FREE_READ);
                kfree_skb(skb, FREE_READ);
                return 0;
                return 0;
        }
        }
 
 
        /* If it's not a RARP reply, delete it. */
        /* If it's not a RARP reply, delete it. */
        if (rarp->ar_op != htons(ARPOP_RREPLY)) {
        if (rarp->ar_op != htons(ARPOP_RREPLY)) {
                kfree_skb(skb, FREE_READ);
                kfree_skb(skb, FREE_READ);
                return 0;
                return 0;
        }
        }
 
 
        /* If it's not ethernet or AX25, delete it. */
        /* If it's not ethernet or AX25, delete it. */
        if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
        if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
           (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
           (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
#endif
#endif
            rarp->ar_pln != 4) {
            rarp->ar_pln != 4) {
                kfree_skb(skb, FREE_READ);
                kfree_skb(skb, FREE_READ);
                return 0;
                return 0;
        }
        }
 
 
        /* Extract variable width fields */
        /* Extract variable width fields */
        sha = rarp_ptr;
        sha = rarp_ptr;
        rarp_ptr += dev->addr_len;
        rarp_ptr += dev->addr_len;
        memcpy(&sip, rarp_ptr, 4);
        memcpy(&sip, rarp_ptr, 4);
        rarp_ptr += 4;
        rarp_ptr += 4;
        tha = rarp_ptr;
        tha = rarp_ptr;
        rarp_ptr += dev->addr_len;
        rarp_ptr += dev->addr_len;
        memcpy(&tip, rarp_ptr, 4);
        memcpy(&tip, rarp_ptr, 4);
 
 
        /* Discard packets which are not meant for us. */
        /* Discard packets which are not meant for us. */
        if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
        if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
                kfree_skb(skb, FREE_READ);
                kfree_skb(skb, FREE_READ);
                return 0;
                return 0;
        }
        }
        /* Discard packets which are not from specified server. */
        /* Discard packets which are not from specified server. */
        if (rarp_flag && !bootp_flag &&
        if (rarp_flag && !bootp_flag &&
            rarp_serv.sin_addr.s_addr != INADDR_NONE &&
            rarp_serv.sin_addr.s_addr != INADDR_NONE &&
            rarp_serv.sin_addr.s_addr != sip) {
            rarp_serv.sin_addr.s_addr != sip) {
                kfree_skb(skb, FREE_READ);
                kfree_skb(skb, FREE_READ);
                return 0;
                return 0;
        }
        }
 
 
        /*
        /*
         * The packet is what we were looking for. Setup the global
         * The packet is what we were looking for. Setup the global
         * variables.
         * variables.
         */
         */
        cli();
        cli();
        if (pkt_arrived) {
        if (pkt_arrived) {
                sti();
                sti();
                kfree_skb(skb, FREE_READ);
                kfree_skb(skb, FREE_READ);
                return 0;
                return 0;
        }
        }
        pkt_arrived = ARRIVED_RARP;
        pkt_arrived = ARRIVED_RARP;
        sti();
        sti();
        root_dev = dev;
        root_dev = dev;
 
 
        if (myaddr.sin_addr.s_addr == INADDR_NONE) {
        if (myaddr.sin_addr.s_addr == INADDR_NONE) {
                myaddr.sin_family = dev->family;
                myaddr.sin_family = dev->family;
                myaddr.sin_addr.s_addr = tip;
                myaddr.sin_addr.s_addr = tip;
        }
        }
        if (server.sin_addr.s_addr == INADDR_NONE) {
        if (server.sin_addr.s_addr == INADDR_NONE) {
                server.sin_family = dev->family;
                server.sin_family = dev->family;
                server.sin_addr.s_addr = sip;
                server.sin_addr.s_addr = sip;
        }
        }
        kfree_skb(skb, FREE_READ);
        kfree_skb(skb, FREE_READ);
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Send RARP request packet over all devices which allow RARP.
 *  Send RARP request packet over all devices which allow RARP.
 */
 */
static void root_rarp_send(void)
static void root_rarp_send(void)
{
{
        struct open_dev *openp;
        struct open_dev *openp;
        struct device *dev;
        struct device *dev;
        int num = 0;
        int num = 0;
 
 
        for (openp = open_base; openp != NULL; openp = openp->next) {
        for (openp = open_base; openp != NULL; openp = openp->next) {
                dev = openp->dev;
                dev = openp->dev;
                if (!(dev->flags & IFF_NOARP)) {
                if (!(dev->flags & IFF_NOARP)) {
                        arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
                        arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
                                 dev->dev_addr, dev->dev_addr);
                                 dev->dev_addr, dev->dev_addr);
                        num++;
                        num++;
                }
                }
        }
        }
}
}
#endif
#endif
 
 
 
 
 
 
/***************************************************************************
/***************************************************************************
 
 
                             BOOTP Subroutines
                             BOOTP Subroutines
 
 
 ***************************************************************************/
 ***************************************************************************/
 
 
#ifdef CONFIG_RNFS_BOOTP
#ifdef CONFIG_RNFS_BOOTP
 
 
static struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */
static struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */
 
 
static int bootp_xmit_fd = -1;          /* Socket descriptor for transmit */
static int bootp_xmit_fd = -1;          /* Socket descriptor for transmit */
static struct socket *bootp_xmit_sock;  /* The socket itself */
static struct socket *bootp_xmit_sock;  /* The socket itself */
static int bootp_recv_fd = -1;          /* Socket descriptor for receive */
static int bootp_recv_fd = -1;          /* Socket descriptor for receive */
static struct socket *bootp_recv_sock;  /* The socket itself */
static struct socket *bootp_recv_sock;  /* The socket itself */
 
 
struct bootp_pkt {              /* BOOTP packet format */
struct bootp_pkt {              /* BOOTP packet format */
        u8 op;                  /* 1=request, 2=reply */
        u8 op;                  /* 1=request, 2=reply */
        u8 htype;               /* HW address type */
        u8 htype;               /* HW address type */
        u8 hlen;                /* HW address length */
        u8 hlen;                /* HW address length */
        u8 hops;                /* Used only by gateways */
        u8 hops;                /* Used only by gateways */
        u32 xid;                /* Transaction ID */
        u32 xid;                /* Transaction ID */
        u16 secs;               /* Seconds since we started */
        u16 secs;               /* Seconds since we started */
        u16 flags;              /* Just what is says */
        u16 flags;              /* Just what is says */
        u32 client_ip;          /* Client's IP address if known */
        u32 client_ip;          /* Client's IP address if known */
        u32 your_ip;            /* Assigned IP address */
        u32 your_ip;            /* Assigned IP address */
        u32 server_ip;          /* Server's IP address */
        u32 server_ip;          /* Server's IP address */
        u32 relay_ip;           /* IP address of BOOTP relay */
        u32 relay_ip;           /* IP address of BOOTP relay */
        u8 hw_addr[16];         /* Client's HW address */
        u8 hw_addr[16];         /* Client's HW address */
        u8 serv_name[64];       /* Server host name */
        u8 serv_name[64];       /* Server host name */
        u8 boot_file[128];      /* Name of boot file */
        u8 boot_file[128];      /* Name of boot file */
        u8 vendor_area[128];    /* Area for extensions */
        u8 vendor_area[128];    /* Area for extensions */
};
};
 
 
#define BOOTP_REQUEST 1
#define BOOTP_REQUEST 1
#define BOOTP_REPLY 2
#define BOOTP_REPLY 2
 
 
static struct bootp_pkt *xmit_bootp;    /* Packet being transmitted */
static struct bootp_pkt *xmit_bootp;    /* Packet being transmitted */
static struct bootp_pkt *recv_bootp;    /* Packet being received */
static struct bootp_pkt *recv_bootp;    /* Packet being received */
 
 
static int bootp_have_route = 0; /* BOOTP route installed */
static int bootp_have_route = 0; /* BOOTP route installed */
 
 
 
 
/*
/*
 *  Free BOOTP packet buffers
 *  Free BOOTP packet buffers
 */
 */
static void root_free_bootp(void)
static void root_free_bootp(void)
{
{
        if (xmit_bootp) {
        if (xmit_bootp) {
                kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
                kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
                xmit_bootp = NULL;
                xmit_bootp = NULL;
        }
        }
        if (recv_bootp) {
        if (recv_bootp) {
                kfree_s(recv_bootp, sizeof(struct bootp_pkt));
                kfree_s(recv_bootp, sizeof(struct bootp_pkt));
                recv_bootp = NULL;
                recv_bootp = NULL;
        }
        }
}
}
 
 
 
 
/*
/*
 *  Allocate memory for BOOTP packet buffers
 *  Allocate memory for BOOTP packet buffers
 */
 */
static inline int root_alloc_bootp(void)
static inline int root_alloc_bootp(void)
{
{
        if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
        if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
            !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
            !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
                printk("BOOTP: Out of memory!");
                printk("BOOTP: Out of memory!");
                return -1;
                return -1;
        }
        }
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Create default route for BOOTP sending
 *  Create default route for BOOTP sending
 */
 */
static int root_add_bootp_route(void)
static int root_add_bootp_route(void)
{
{
        struct rtentry route;
        struct rtentry route;
 
 
        memset(&route, 0, sizeof(route));
        memset(&route, 0, sizeof(route));
        route.rt_dev = bootp_dev->name;
        route.rt_dev = bootp_dev->name;
        route.rt_mss = bootp_dev->mtu;
        route.rt_mss = bootp_dev->mtu;
        route.rt_flags = RTF_UP;
        route.rt_flags = RTF_UP;
        ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
        ((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
        ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
        ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
        if (ip_rt_new(&route)) {
        if (ip_rt_new(&route)) {
                printk(KERN_ERR "BOOTP: Adding of route failed!\n");
                printk(KERN_ERR "BOOTP: Adding of route failed!\n");
                return -1;
                return -1;
        }
        }
        bootp_have_route = 1;
        bootp_have_route = 1;
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Delete default route for BOOTP sending
 *  Delete default route for BOOTP sending
 */
 */
static int root_del_bootp_route(void)
static int root_del_bootp_route(void)
{
{
        struct rtentry route;
        struct rtentry route;
 
 
        if (!bootp_have_route)
        if (!bootp_have_route)
                return 0;
                return 0;
        memset(&route, 0, sizeof(route));
        memset(&route, 0, sizeof(route));
        ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
        ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
        if (ip_rt_kill(&route)) {
        if (ip_rt_kill(&route)) {
                printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
                printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
                return -1;
                return -1;
        }
        }
        bootp_have_route = 0;
        bootp_have_route = 0;
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Open UDP socket.
 *  Open UDP socket.
 */
 */
static int root_open_udp_sock(int *fd, struct socket **sock)
static int root_open_udp_sock(int *fd, struct socket **sock)
{
{
        struct file *file;
        struct file *file;
        struct inode *inode;
        struct inode *inode;
 
 
        *fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        *fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (*fd >= 0) {
        if (*fd >= 0) {
                file = current->files->fd[*fd];
                file = current->files->fd[*fd];
                inode = file->f_inode;
                inode = file->f_inode;
                *sock = &inode->u.socket_i;
                *sock = &inode->u.socket_i;
                return 0;
                return 0;
        }
        }
 
 
        printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
        printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
        return -1;
        return -1;
}
}
 
 
 
 
/*
/*
 *  Connect UDP socket.
 *  Connect UDP socket.
 */
 */
static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
{
{
        struct sockaddr_in sa;
        struct sockaddr_in sa;
        int result;
        int result;
 
 
        sa.sin_family = AF_INET;
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = htonl(addr);
        sa.sin_addr.s_addr = htonl(addr);
        sa.sin_port = htons(port);
        sa.sin_port = htons(port);
        result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
        result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
        if (result < 0) {
        if (result < 0) {
                printk(KERN_ERR "BOOTP: connect() failed\n");
                printk(KERN_ERR "BOOTP: connect() failed\n");
                return -1;
                return -1;
        }
        }
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Bind UDP socket.
 *  Bind UDP socket.
 */
 */
static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
{
{
        struct sockaddr_in sa;
        struct sockaddr_in sa;
        int result;
        int result;
 
 
        sa.sin_family = AF_INET;
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = htonl(addr);
        sa.sin_addr.s_addr = htonl(addr);
        sa.sin_port = htons(port);
        sa.sin_port = htons(port);
        result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
        result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
        if (result < 0) {
        if (result < 0) {
                printk(KERN_ERR "BOOTP: bind() failed\n");
                printk(KERN_ERR "BOOTP: bind() failed\n");
                return -1;
                return -1;
        }
        }
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Send UDP packet.
 *  Send UDP packet.
 */
 */
static inline int root_send_udp(struct socket *sock, void *buf, int size)
static inline int root_send_udp(struct socket *sock, void *buf, int size)
{
{
        u32 oldfs;
        u32 oldfs;
        int result;
        int result;
        struct msghdr msg;
        struct msghdr msg;
        struct iovec iov;
        struct iovec iov;
 
 
        oldfs = get_fs();
        oldfs = get_fs();
        set_fs(get_ds());
        set_fs(get_ds());
        iov.iov_base = buf;
        iov.iov_base = buf;
        iov.iov_len = size;
        iov.iov_len = size;
        msg.msg_name = NULL;
        msg.msg_name = NULL;
        msg.msg_iov = &iov;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;
        msg.msg_control = NULL;
        result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
        result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
        set_fs(oldfs);
        set_fs(oldfs);
        return (result != size);
        return (result != size);
}
}
 
 
 
 
/*
/*
 *  Try to receive UDP packet.
 *  Try to receive UDP packet.
 */
 */
static inline int root_recv_udp(struct socket *sock, void *buf, int size)
static inline int root_recv_udp(struct socket *sock, void *buf, int size)
{
{
        u32 oldfs;
        u32 oldfs;
        int result;
        int result;
        struct msghdr msg;
        struct msghdr msg;
        struct iovec iov;
        struct iovec iov;
 
 
        oldfs = get_fs();
        oldfs = get_fs();
        set_fs(get_ds());
        set_fs(get_ds());
        iov.iov_base = buf;
        iov.iov_base = buf;
        iov.iov_len = size;
        iov.iov_len = size;
        msg.msg_name = NULL;
        msg.msg_name = NULL;
        msg.msg_iov = &iov;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;
        msg.msg_control = NULL;
        msg.msg_namelen = 0;
        msg.msg_namelen = 0;
        result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
        result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
        set_fs(oldfs);
        set_fs(oldfs);
        return result;
        return result;
}
}
 
 
 
 
/*
/*
 *  Initialize BOOTP extension fields in the request.
 *  Initialize BOOTP extension fields in the request.
 */
 */
static void root_bootp_init_ext(u8 *e)
static void root_bootp_init_ext(u8 *e)
{
{
        *e++ = 99;              /* RFC1048 Magic Cookie */
        *e++ = 99;              /* RFC1048 Magic Cookie */
        *e++ = 130;
        *e++ = 130;
        *e++ = 83;
        *e++ = 83;
        *e++ = 99;
        *e++ = 99;
        *e++ = 1;               /* Subnet mask request */
        *e++ = 1;               /* Subnet mask request */
        *e++ = 4;
        *e++ = 4;
        e += 4;
        e += 4;
        *e++ = 3;               /* Default gateway request */
        *e++ = 3;               /* Default gateway request */
        *e++ = 4;
        *e++ = 4;
        e += 4;
        e += 4;
        *e++ = 12;              /* Host name request */
        *e++ = 12;              /* Host name request */
        *e++ = 32;
        *e++ = 32;
        e += 32;
        e += 32;
        *e++ = 40;              /* NIS Domain name request */
        *e++ = 40;              /* NIS Domain name request */
        *e++ = 32;
        *e++ = 32;
        e += 32;
        e += 32;
        *e++ = 17;              /* Boot path */
        *e++ = 17;              /* Boot path */
        *e++ = 32;
        *e++ = 32;
        e += 32;
        e += 32;
        *e = 255;               /* End of the list */
        *e = 255;               /* End of the list */
}
}
 
 
 
 
/*
/*
 *  Deinitialize the BOOTP mechanism.
 *  Deinitialize the BOOTP mechanism.
 */
 */
static void root_bootp_close(void)
static void root_bootp_close(void)
{
{
        if (bootp_xmit_fd != -1)
        if (bootp_xmit_fd != -1)
                sys_close(bootp_xmit_fd);
                sys_close(bootp_xmit_fd);
        if (bootp_recv_fd != -1)
        if (bootp_recv_fd != -1)
                sys_close(bootp_recv_fd);
                sys_close(bootp_recv_fd);
        root_del_bootp_route();
        root_del_bootp_route();
        root_free_bootp();
        root_free_bootp();
}
}
 
 
 
 
/*
/*
 *  Initialize the BOOTP mechanism.
 *  Initialize the BOOTP mechanism.
 */
 */
static int root_bootp_open(void)
static int root_bootp_open(void)
{
{
        struct open_dev *openp;
        struct open_dev *openp;
        struct device *dev, *best_dev;
        struct device *dev, *best_dev;
 
 
        /*
        /*
         * Select the best interface for BOOTP. We try to select a first
         * Select the best interface for BOOTP. We try to select a first
         * Ethernet-like interface. It's shame I know no simple way how to send
         * Ethernet-like interface. It's shame I know no simple way how to send
         * BOOTP's to all interfaces, but it doesn't apply to usual diskless
         * BOOTP's to all interfaces, but it doesn't apply to usual diskless
         * stations as they don't have multiple interfaces.
         * stations as they don't have multiple interfaces.
         */
         */
 
 
        best_dev = NULL;
        best_dev = NULL;
        for (openp = open_base; openp != NULL; openp = openp->next) {
        for (openp = open_base; openp != NULL; openp = openp->next) {
                dev = openp->dev;
                dev = openp->dev;
                if (dev->flags & IFF_BROADCAST) {
                if (dev->flags & IFF_BROADCAST) {
                        if (!best_dev ||
                        if (!best_dev ||
                           ((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
                           ((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
                                best_dev = dev;
                                best_dev = dev;
                        }
                        }
                }
                }
 
 
        if (!best_dev) {
        if (!best_dev) {
                printk(KERN_ERR "BOOTP: This cannot happen!\n");
                printk(KERN_ERR "BOOTP: This cannot happen!\n");
                return -1;
                return -1;
        }
        }
        bootp_dev = best_dev;
        bootp_dev = best_dev;
 
 
        /* Allocate memory for BOOTP packets */
        /* Allocate memory for BOOTP packets */
        if (root_alloc_bootp())
        if (root_alloc_bootp())
                return -1;
                return -1;
 
 
        /* Construct BOOTP request */
        /* Construct BOOTP request */
        memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
        memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
        xmit_bootp->op = BOOTP_REQUEST;
        xmit_bootp->op = BOOTP_REQUEST;
        get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
        get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
        xmit_bootp->htype = best_dev->type;
        xmit_bootp->htype = best_dev->type;
        xmit_bootp->hlen = best_dev->addr_len;
        xmit_bootp->hlen = best_dev->addr_len;
        memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
        memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
        root_bootp_init_ext(xmit_bootp->vendor_area);
        root_bootp_init_ext(xmit_bootp->vendor_area);
#ifdef NFSROOT_BOOTP_DEBUG
#ifdef NFSROOT_BOOTP_DEBUG
        {
        {
                int x;
                int x;
                printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
                printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
                        xmit_bootp->xid,
                        xmit_bootp->xid,
                        best_dev->name,
                        best_dev->name,
                        xmit_bootp->htype,
                        xmit_bootp->htype,
                        xmit_bootp->hlen);
                        xmit_bootp->hlen);
                for(x=0; x<xmit_bootp->hlen; x++)
                for(x=0; x<xmit_bootp->hlen; x++)
                        printk("%02x", xmit_bootp->hw_addr[x]);
                        printk("%02x", xmit_bootp->hw_addr[x]);
                printk("\n");
                printk("\n");
        }
        }
#endif
#endif
 
 
        /* Create default route to that interface */
        /* Create default route to that interface */
        if (root_add_bootp_route())
        if (root_add_bootp_route())
                return -1;
                return -1;
 
 
        /* Open the sockets */
        /* Open the sockets */
        if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
        if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
            root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
            root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
                return -1;
                return -1;
 
 
        /* Bind/connect the sockets */
        /* Bind/connect the sockets */
        ((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
        ((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
        ((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
        ((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
        ((struct sock *) bootp_recv_sock->data) -> reuse = 1;
        ((struct sock *) bootp_recv_sock->data) -> reuse = 1;
        if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
        if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
            root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
            root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
            root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
            root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
                return -1;
                return -1;
 
 
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Send BOOTP request.
 *  Send BOOTP request.
 */
 */
static int root_bootp_send(u32 jiffies)
static int root_bootp_send(u32 jiffies)
{
{
        xmit_bootp->secs = htons(jiffies / HZ);
        xmit_bootp->secs = htons(jiffies / HZ);
        return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
        return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
}
}
 
 
 
 
/*
/*
 *  Copy BOOTP-supplied string if not already set.
 *  Copy BOOTP-supplied string if not already set.
 */
 */
static int root_bootp_string(char *dest, char *src, int len, int max)
static int root_bootp_string(char *dest, char *src, int len, int max)
{
{
        if (*dest || !len)
        if (*dest || !len)
                return 0;
                return 0;
        if (len > max-1)
        if (len > max-1)
                len = max-1;
                len = max-1;
        strncpy(dest, src, len);
        strncpy(dest, src, len);
        dest[len] = '\0';
        dest[len] = '\0';
        return 1;
        return 1;
}
}
 
 
 
 
/*
/*
 *  Process BOOTP extension.
 *  Process BOOTP extension.
 */
 */
static void root_do_bootp_ext(u8 *ext)
static void root_do_bootp_ext(u8 *ext)
{
{
#ifdef NFSROOT_BOOTP_DEBUG
#ifdef NFSROOT_BOOTP_DEBUG
        u8 *c;
        u8 *c;
 
 
        printk("BOOTP: Got extension %02x",*ext);
        printk("BOOTP: Got extension %02x",*ext);
        for(c=ext+2; c<ext+2+ext[1]; c++)
        for(c=ext+2; c<ext+2+ext[1]; c++)
                printk(" %02x", *c);
                printk(" %02x", *c);
        printk("\n");
        printk("\n");
#endif
#endif
 
 
        switch (*ext++) {
        switch (*ext++) {
                case 1:         /* Subnet mask */
                case 1:         /* Subnet mask */
                        if (netmask.sin_addr.s_addr == INADDR_NONE)
                        if (netmask.sin_addr.s_addr == INADDR_NONE)
                                memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
                                memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
                        break;
                        break;
                case 3:         /* Default gateway */
                case 3:         /* Default gateway */
                        if (gateway.sin_addr.s_addr == INADDR_NONE)
                        if (gateway.sin_addr.s_addr == INADDR_NONE)
                                memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
                                memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
                        break;
                        break;
                case 12:        /* Host name */
                case 12:        /* Host name */
                        root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
                        root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
                        break;
                        break;
                case 40:        /* NIS Domain name */
                case 40:        /* NIS Domain name */
                        root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
                        root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
                        break;
                        break;
                case 17:        /* Root path */
                case 17:        /* Root path */
                        root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
                        root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
                        break;
                        break;
        }
        }
}
}
 
 
 
 
/*
/*
 *  Receive BOOTP request.
 *  Receive BOOTP request.
 */
 */
static void root_bootp_recv(void)
static void root_bootp_recv(void)
{
{
        int len;
        int len;
        u8 *ext, *end, *opt;
        u8 *ext, *end, *opt;
 
 
        len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
        len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
        if (len < 0)
        if (len < 0)
                return;
                return;
 
 
        /* Check consistency of incoming packet */
        /* Check consistency of incoming packet */
        if (len < 300 ||                        /* See RFC 1542:2.1 */
        if (len < 300 ||                        /* See RFC 1542:2.1 */
            recv_bootp->op != BOOTP_REPLY ||
            recv_bootp->op != BOOTP_REPLY ||
            recv_bootp->htype != xmit_bootp->htype ||
            recv_bootp->htype != xmit_bootp->htype ||
            recv_bootp->hlen != xmit_bootp->hlen ||
            recv_bootp->hlen != xmit_bootp->hlen ||
            recv_bootp->xid != xmit_bootp->xid) {
            recv_bootp->xid != xmit_bootp->xid) {
#ifdef NFSROOT_BOOTP_DEBUG
#ifdef NFSROOT_BOOTP_DEBUG
                printk("?");
                printk("?");
#endif
#endif
                return;
                return;
                }
                }
 
 
        /* Record BOOTP packet arrival in the global variables */
        /* Record BOOTP packet arrival in the global variables */
        cli();
        cli();
        if (pkt_arrived) {
        if (pkt_arrived) {
                sti();
                sti();
                return;
                return;
        }
        }
        pkt_arrived = ARRIVED_BOOTP;
        pkt_arrived = ARRIVED_BOOTP;
        sti();
        sti();
        root_dev = bootp_dev;
        root_dev = bootp_dev;
 
 
        /* Extract basic fields */
        /* Extract basic fields */
        myaddr.sin_addr.s_addr = recv_bootp->your_ip;
        myaddr.sin_addr.s_addr = recv_bootp->your_ip;
        if (server.sin_addr.s_addr==INADDR_NONE)
        if (server.sin_addr.s_addr==INADDR_NONE)
                server.sin_addr.s_addr = recv_bootp->server_ip;
                server.sin_addr.s_addr = recv_bootp->server_ip;
 
 
        /* Parse extensions */
        /* Parse extensions */
        if (recv_bootp->vendor_area[0] == 99 &&  /* Check magic cookie */
        if (recv_bootp->vendor_area[0] == 99 &&  /* Check magic cookie */
            recv_bootp->vendor_area[1] == 130 &&
            recv_bootp->vendor_area[1] == 130 &&
            recv_bootp->vendor_area[2] == 83 &&
            recv_bootp->vendor_area[2] == 83 &&
            recv_bootp->vendor_area[3] == 99) {
            recv_bootp->vendor_area[3] == 99) {
                ext = &recv_bootp->vendor_area[4];
                ext = &recv_bootp->vendor_area[4];
                end = (u8 *) recv_bootp + len;
                end = (u8 *) recv_bootp + len;
                while (ext < end && *ext != 255) {
                while (ext < end && *ext != 255) {
                        if (*ext == 0)           /* Padding */
                        if (*ext == 0)           /* Padding */
                                ext++;
                                ext++;
                        else {
                        else {
                                opt = ext;
                                opt = ext;
                                ext += ext[1] + 2;
                                ext += ext[1] + 2;
                                if (ext <= end)
                                if (ext <= end)
                                        root_do_bootp_ext(opt);
                                        root_do_bootp_ext(opt);
                        }
                        }
                }
                }
        }
        }
}
}
#endif
#endif
 
 
 
 
 
 
/***************************************************************************
/***************************************************************************
 
 
                        Dynamic configuration of IP.
                        Dynamic configuration of IP.
 
 
 ***************************************************************************/
 ***************************************************************************/
 
 
#ifdef CONFIG_RNFS_DYNAMIC
#ifdef CONFIG_RNFS_DYNAMIC
 
 
/*
/*
 *  Determine client and server IP numbers and appropriate device by using
 *  Determine client and server IP numbers and appropriate device by using
 *  the RARP and BOOTP protocols.
 *  the RARP and BOOTP protocols.
 */
 */
static int root_auto_config(void)
static int root_auto_config(void)
{
{
        int retries;
        int retries;
        unsigned long timeout, jiff;
        unsigned long timeout, jiff;
        unsigned long start_jiffies;
        unsigned long start_jiffies;
 
 
        /*
        /*
         * If neither BOOTP nor RARP was selected, return with an error. This
         * If neither BOOTP nor RARP was selected, return with an error. This
         * routine gets only called when some pieces of information are mis-
         * routine gets only called when some pieces of information are mis-
         * sing, and without BOOTP and RARP we are not able to get that in-
         * sing, and without BOOTP and RARP we are not able to get that in-
         * formation.
         * formation.
         */
         */
        if (!bootp_flag && !rarp_flag) {
        if (!bootp_flag && !rarp_flag) {
                printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
                printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
                return -1;
                return -1;
        }
        }
 
 
#ifdef CONFIG_RNFS_BOOTP
#ifdef CONFIG_RNFS_BOOTP
        if (bootp_flag && !bootp_dev_count) {
        if (bootp_flag && !bootp_dev_count) {
                printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
                printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
                bootp_flag = 0;
                bootp_flag = 0;
        }
        }
#else
#else
        bootp_flag = 0;
        bootp_flag = 0;
#endif
#endif
 
 
#ifdef CONFIG_RNFS_RARP
#ifdef CONFIG_RNFS_RARP
        if (rarp_flag && !rarp_dev_count) {
        if (rarp_flag && !rarp_dev_count) {
                printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
                printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
                rarp_flag = 0;
                rarp_flag = 0;
        }
        }
#else
#else
        rarp_flag = 0;
        rarp_flag = 0;
#endif
#endif
 
 
        if (!bootp_flag && !rarp_flag)
        if (!bootp_flag && !rarp_flag)
                /* Error message already printed */
                /* Error message already printed */
                return -1;
                return -1;
 
 
        /*
        /*
         * Setup RARP and BOOTP protocols
         * Setup RARP and BOOTP protocols
         */
         */
#ifdef CONFIG_RNFS_RARP
#ifdef CONFIG_RNFS_RARP
        if (rarp_flag)
        if (rarp_flag)
                root_rarp_open();
                root_rarp_open();
#endif
#endif
#ifdef CONFIG_RNFS_BOOTP
#ifdef CONFIG_RNFS_BOOTP
        if (bootp_flag && root_bootp_open() < 0) {
        if (bootp_flag && root_bootp_open() < 0) {
                root_bootp_close();
                root_bootp_close();
                return -1;
                return -1;
        }
        }
#endif
#endif
 
 
        /*
        /*
         * Send requests and wait, until we get an answer. This loop
         * Send requests and wait, until we get an answer. This loop
         * seems to be a terrible waste of CPU time, but actually there is
         * seems to be a terrible waste of CPU time, but actually there is
         * only one process running at all, so we don't need to use any
         * only one process running at all, so we don't need to use any
         * scheduler functions.
         * scheduler functions.
         * [Actually we could now, but the nothing else running note still
         * [Actually we could now, but the nothing else running note still
         *  applies.. - AC]
         *  applies.. - AC]
         */
         */
        printk(KERN_NOTICE "Sending %s%s%s requests...",
        printk(KERN_NOTICE "Sending %s%s%s requests...",
                bootp_flag ? "BOOTP" : "",
                bootp_flag ? "BOOTP" : "",
                bootp_flag && rarp_flag ? " and " : "",
                bootp_flag && rarp_flag ? " and " : "",
                rarp_flag ? "RARP" : "");
                rarp_flag ? "RARP" : "");
        start_jiffies = jiffies;
        start_jiffies = jiffies;
        retries = CONF_RETRIES;
        retries = CONF_RETRIES;
        get_random_bytes(&timeout, sizeof(timeout));
        get_random_bytes(&timeout, sizeof(timeout));
        timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
        timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
        for(;;) {
        for(;;) {
#ifdef CONFIG_RNFS_BOOTP
#ifdef CONFIG_RNFS_BOOTP
                if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
                if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
                        printk(" BOOTP failed!\n");
                        printk(" BOOTP failed!\n");
                        root_bootp_close();
                        root_bootp_close();
                        bootp_flag = 0;
                        bootp_flag = 0;
                        if (!rarp_flag)
                        if (!rarp_flag)
                                break;
                                break;
                }
                }
#endif
#endif
#ifdef CONFIG_RNFS_RARP
#ifdef CONFIG_RNFS_RARP
                if (rarp_flag)
                if (rarp_flag)
                        root_rarp_send();
                        root_rarp_send();
#endif
#endif
                printk(".");
                printk(".");
                jiff = jiffies + timeout;
                jiff = jiffies + timeout;
                while (jiffies < jiff && !pkt_arrived)
                while (jiffies < jiff && !pkt_arrived)
#ifdef CONFIG_RNFS_BOOTP
#ifdef CONFIG_RNFS_BOOTP
                        root_bootp_recv();
                        root_bootp_recv();
#else
#else
                        ;
                        ;
#endif
#endif
                if (pkt_arrived)
                if (pkt_arrived)
                        break;
                        break;
                if (! --retries) {
                if (! --retries) {
                        printk(" timed out!\n");
                        printk(" timed out!\n");
                        break;
                        break;
                }
                }
                timeout = timeout CONF_TIMEOUT_MULT;
                timeout = timeout CONF_TIMEOUT_MULT;
                if (timeout > CONF_TIMEOUT_MAX)
                if (timeout > CONF_TIMEOUT_MAX)
                        timeout = CONF_TIMEOUT_MAX;
                        timeout = CONF_TIMEOUT_MAX;
        }
        }
 
 
#ifdef CONFIG_RNFS_RARP
#ifdef CONFIG_RNFS_RARP
        if (rarp_flag)
        if (rarp_flag)
                root_rarp_close();
                root_rarp_close();
#endif
#endif
#ifdef CONFIG_RNFS_BOOTP
#ifdef CONFIG_RNFS_BOOTP
        if (bootp_flag)
        if (bootp_flag)
                root_bootp_close();
                root_bootp_close();
#endif
#endif
 
 
        if (!pkt_arrived)
        if (!pkt_arrived)
                return -1;
                return -1;
 
 
        printk(" OK\n");
        printk(" OK\n");
        printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
        printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
                (pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
                (pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
                in_ntoa(server.sin_addr.s_addr));
                in_ntoa(server.sin_addr.s_addr));
        printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
        printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
 
 
        return 0;
        return 0;
}
}
#endif
#endif
 
 
 
 
 
 
/***************************************************************************
/***************************************************************************
 
 
                             Parsing of options
                             Parsing of options
 
 
 ***************************************************************************/
 ***************************************************************************/
 
 
 
 
/*
/*
 *  The following integer options are recognized
 *  The following integer options are recognized
 */
 */
static struct nfs_int_opts {
static struct nfs_int_opts {
        char *name;
        char *name;
        int  *val;
        int  *val;
} root_int_opts[] = {
} root_int_opts[] = {
        { "port",       &nfs_port },
        { "port",       &nfs_port },
        { "rsize",      &nfs_data.rsize },
        { "rsize",      &nfs_data.rsize },
        { "wsize",      &nfs_data.wsize },
        { "wsize",      &nfs_data.wsize },
        { "timeo",      &nfs_data.timeo },
        { "timeo",      &nfs_data.timeo },
        { "retrans",    &nfs_data.retrans },
        { "retrans",    &nfs_data.retrans },
        { "acregmin",   &nfs_data.acregmin },
        { "acregmin",   &nfs_data.acregmin },
        { "acregmax",   &nfs_data.acregmax },
        { "acregmax",   &nfs_data.acregmax },
        { "acdirmin",   &nfs_data.acdirmin },
        { "acdirmin",   &nfs_data.acdirmin },
        { "acdirmax",   &nfs_data.acdirmax },
        { "acdirmax",   &nfs_data.acdirmax },
        { NULL,         NULL }
        { NULL,         NULL }
};
};
 
 
 
 
/*
/*
 *  And now the flag options
 *  And now the flag options
 */
 */
static struct nfs_bool_opts {
static struct nfs_bool_opts {
        char *name;
        char *name;
        int  and_mask;
        int  and_mask;
        int  or_mask;
        int  or_mask;
} root_bool_opts[] = {
} root_bool_opts[] = {
        { "soft",       ~NFS_MOUNT_SOFT,        NFS_MOUNT_SOFT },
        { "soft",       ~NFS_MOUNT_SOFT,        NFS_MOUNT_SOFT },
        { "hard",       ~NFS_MOUNT_SOFT,        0 },
        { "hard",       ~NFS_MOUNT_SOFT,        0 },
        { "intr",       ~NFS_MOUNT_INTR,        NFS_MOUNT_INTR },
        { "intr",       ~NFS_MOUNT_INTR,        NFS_MOUNT_INTR },
        { "nointr",     ~NFS_MOUNT_INTR,        0 },
        { "nointr",     ~NFS_MOUNT_INTR,        0 },
        { "posix",      ~NFS_MOUNT_POSIX,       NFS_MOUNT_POSIX },
        { "posix",      ~NFS_MOUNT_POSIX,       NFS_MOUNT_POSIX },
        { "noposix",    ~NFS_MOUNT_POSIX,       0 },
        { "noposix",    ~NFS_MOUNT_POSIX,       0 },
        { "cto",        ~NFS_MOUNT_NOCTO,       0 },
        { "cto",        ~NFS_MOUNT_NOCTO,       0 },
        { "nocto",      ~NFS_MOUNT_NOCTO,       NFS_MOUNT_NOCTO },
        { "nocto",      ~NFS_MOUNT_NOCTO,       NFS_MOUNT_NOCTO },
        { "ac",         ~NFS_MOUNT_NOAC,        0 },
        { "ac",         ~NFS_MOUNT_NOAC,        0 },
        { "noac",       ~NFS_MOUNT_NOAC,        NFS_MOUNT_NOAC },
        { "noac",       ~NFS_MOUNT_NOAC,        NFS_MOUNT_NOAC },
        { NULL,         0,                       0 }
        { NULL,         0,                       0 }
};
};
 
 
 
 
/*
/*
 *  Prepare the NFS data structure and parse any options. This tries to
 *  Prepare the NFS data structure and parse any options. This tries to
 *  set as many values in the nfs_data structure as known right now.
 *  set as many values in the nfs_data structure as known right now.
 */
 */
static int root_nfs_name(char *name)
static int root_nfs_name(char *name)
{
{
        char buf[NFS_MAXPATHLEN];
        char buf[NFS_MAXPATHLEN];
        char *cp, *cq, *options, *val;
        char *cp, *cq, *options, *val;
        int octets = 0;
        int octets = 0;
 
 
        /* It is possible to override the server IP number here */
        /* It is possible to override the server IP number here */
        cp = cq = name;
        cp = cq = name;
        while (octets < 4) {
        while (octets < 4) {
                while (*cp >= '0' && *cp <= '9')
                while (*cp >= '0' && *cp <= '9')
                        cp++;
                        cp++;
                if (cp == cq || cp - cq > 3)
                if (cp == cq || cp - cq > 3)
                        break;
                        break;
                if (*cp == '.' || octets == 3)
                if (*cp == '.' || octets == 3)
                        octets++;
                        octets++;
                if (octets < 4)
                if (octets < 4)
                        cp++;
                        cp++;
                cq = cp;
                cq = cp;
        }
        }
        if (octets == 4 && (*cp == ':' || *cp == '\0')) {
        if (octets == 4 && (*cp == ':' || *cp == '\0')) {
                if (*cp == ':')
                if (*cp == ':')
                        *cp++ = '\0';
                        *cp++ = '\0';
                server.sin_addr.s_addr = in_aton(name);
                server.sin_addr.s_addr = in_aton(name);
                name = cp;
                name = cp;
        }
        }
 
 
        /* Clear the nfs_data structure and setup the server hostname */
        /* Clear the nfs_data structure and setup the server hostname */
        memset(&nfs_data, 0, sizeof(nfs_data));
        memset(&nfs_data, 0, sizeof(nfs_data));
        strncpy(nfs_data.hostname, in_ntoa(server.sin_addr.s_addr),
        strncpy(nfs_data.hostname, in_ntoa(server.sin_addr.s_addr),
                                                sizeof(nfs_data.hostname)-1);
                                                sizeof(nfs_data.hostname)-1);
 
 
        /* Set the name of the directory to mount */
        /* Set the name of the directory to mount */
        if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
        if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
                strncpy(buf, name, NFS_MAXPATHLEN);
                strncpy(buf, name, NFS_MAXPATHLEN);
        else
        else
                strncpy(buf, nfs_path, NFS_MAXPATHLEN);
                strncpy(buf, nfs_path, NFS_MAXPATHLEN);
        if ((options = strchr(buf, ',')))
        if ((options = strchr(buf, ',')))
                *options++ = '\0';
                *options++ = '\0';
        if (!strcmp(buf, "default"))
        if (!strcmp(buf, "default"))
                strcpy(buf, NFS_ROOT);
                strcpy(buf, NFS_ROOT);
        cp = in_ntoa(myaddr.sin_addr.s_addr);
        cp = in_ntoa(myaddr.sin_addr.s_addr);
        if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
        if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
                printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
                printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
                return -1;
                return -1;
        }
        }
        /* update nfs_path with path from nfsroot=... command line parameter */
        /* update nfs_path with path from nfsroot=... command line parameter */
        if (*buf)
        if (*buf)
                sprintf(nfs_path, buf, cp);
                sprintf(nfs_path, buf, cp);
 
 
        /* Set some default values */
        /* Set some default values */
        nfs_port          = -1;
        nfs_port          = -1;
        nfs_data.version  = NFS_MOUNT_VERSION;
        nfs_data.version  = NFS_MOUNT_VERSION;
        nfs_data.flags    = 0;
        nfs_data.flags    = 0;
        nfs_data.rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
        nfs_data.rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
        nfs_data.wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
        nfs_data.wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
        nfs_data.timeo    = 7;
        nfs_data.timeo    = 7;
        nfs_data.retrans  = 3;
        nfs_data.retrans  = 3;
        nfs_data.acregmin = 3;
        nfs_data.acregmin = 3;
        nfs_data.acregmax = 60;
        nfs_data.acregmax = 60;
        nfs_data.acdirmin = 30;
        nfs_data.acdirmin = 30;
        nfs_data.acdirmax = 60;
        nfs_data.acdirmax = 60;
 
 
        /* Process any options */
        /* Process any options */
        if (options) {
        if (options) {
                cp = strtok(options, ",");
                cp = strtok(options, ",");
                while (cp) {
                while (cp) {
                        if ((val = strchr(cp, '='))) {
                        if ((val = strchr(cp, '='))) {
                                struct nfs_int_opts *opts = root_int_opts;
                                struct nfs_int_opts *opts = root_int_opts;
                                *val++ = '\0';
                                *val++ = '\0';
                                while (opts->name && strcmp(opts->name, cp))
                                while (opts->name && strcmp(opts->name, cp))
                                        opts++;
                                        opts++;
                                if (opts->name)
                                if (opts->name)
                                        *(opts->val) = (int) simple_strtoul(val, NULL, 10);
                                        *(opts->val) = (int) simple_strtoul(val, NULL, 10);
                        } else {
                        } else {
                                struct nfs_bool_opts *opts = root_bool_opts;
                                struct nfs_bool_opts *opts = root_bool_opts;
                                while (opts->name && strcmp(opts->name, cp))
                                while (opts->name && strcmp(opts->name, cp))
                                        opts++;
                                        opts++;
                                if (opts->name) {
                                if (opts->name) {
                                        nfs_data.flags &= opts->and_mask;
                                        nfs_data.flags &= opts->and_mask;
                                        nfs_data.flags |= opts->or_mask;
                                        nfs_data.flags |= opts->or_mask;
                                }
                                }
                        }
                        }
                        cp = strtok(NULL, ",");
                        cp = strtok(NULL, ",");
                }
                }
        }
        }
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Tell the user what's going on.
 *  Tell the user what's going on.
 */
 */
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
static void root_nfs_print(void)
static void root_nfs_print(void)
{
{
#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
 
 
        printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
        printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
                root_dev ? root_dev->name : "none");
                root_dev ? root_dev->name : "none");
        printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
        printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
        printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
        printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
        printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
        printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
        printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
        printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
        printk("host=%s, domain=%s\n",
        printk("host=%s, domain=%s\n",
                system_utsname.nodename[0] ? system_utsname.nodename : "none",
                system_utsname.nodename[0] ? system_utsname.nodename : "none",
                system_utsname.domainname[0] ? system_utsname.domainname : "none");
                system_utsname.domainname[0] ? system_utsname.domainname : "none");
        printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
        printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
                nfs_path, nfs_data.hostname);
                nfs_path, nfs_data.hostname);
        printk(KERN_NOTICE "Root-NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
        printk(KERN_NOTICE "Root-NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
                nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
                nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
        printk(KERN_NOTICE "Root-NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
        printk(KERN_NOTICE "Root-NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
                nfs_data.acregmin, nfs_data.acregmax,
                nfs_data.acregmin, nfs_data.acregmax,
                nfs_data.acdirmin, nfs_data.acdirmax);
                nfs_data.acdirmin, nfs_data.acdirmax);
        printk(KERN_NOTICE "Root-NFS:     port = %d, flags = %08x\n",
        printk(KERN_NOTICE "Root-NFS:     port = %d, flags = %08x\n",
                nfs_port, nfs_data.flags);
                nfs_port, nfs_data.flags);
 
 
#undef IN_NTOA
#undef IN_NTOA
}
}
#endif
#endif
 
 
 
 
/*
/*
 *  Decode any IP configuration options in the "nfsaddrs" kernel command
 *  Decode any IP configuration options in the "nfsaddrs" kernel command
 *  line parameter. It consists of option fields separated by colons in
 *  line parameter. It consists of option fields separated by colons in
 *  the following order:
 *  the following order:
 *
 *
 *  <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
 *  <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
 *
 *
 *  Any of the fields can be empty which means to use a default value:
 *  Any of the fields can be empty which means to use a default value:
 *      <client-ip>     - address given by BOOTP or RARP
 *      <client-ip>     - address given by BOOTP or RARP
 *      <server-ip>     - address of host returning BOOTP or RARP packet
 *      <server-ip>     - address of host returning BOOTP or RARP packet
 *      <gw-ip>         - none, or the address returned by BOOTP
 *      <gw-ip>         - none, or the address returned by BOOTP
 *      <netmask>       - automatically determined from <client-ip>, or the
 *      <netmask>       - automatically determined from <client-ip>, or the
 *                        one returned by BOOTP
 *                        one returned by BOOTP
 *      <host name>     - <client-ip> in ASCII notation, or the name returned
 *      <host name>     - <client-ip> in ASCII notation, or the name returned
 *                        by BOOTP
 *                        by BOOTP
 *      <device>        - use all available devices for RARP and the first
 *      <device>        - use all available devices for RARP and the first
 *                        one for BOOTP
 *                        one for BOOTP
 *      <bootp|rarp>    - use both protocols to determine my own address
 *      <bootp|rarp>    - use both protocols to determine my own address
 */
 */
static void root_nfs_addrs(char *addrs)
static void root_nfs_addrs(char *addrs)
{
{
        char *cp, *ip, *dp;
        char *cp, *ip, *dp;
        int num = 0;
        int num = 0;
 
 
        /* Clear all addresses and strings */
        /* Clear all addresses and strings */
        myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
        myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
            gateway.sin_family = netmask.sin_family = AF_INET;
            gateway.sin_family = netmask.sin_family = AF_INET;
        myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
        myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
            gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
            gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
        system_utsname.nodename[0] = '\0';
        system_utsname.nodename[0] = '\0';
        system_utsname.domainname[0] = '\0';
        system_utsname.domainname[0] = '\0';
        user_dev_name[0] = '\0';
        user_dev_name[0] = '\0';
        bootp_flag = rarp_flag = 1;
        bootp_flag = rarp_flag = 1;
 
 
        /* The following is just a shortcut for automatic IP configuration */
        /* The following is just a shortcut for automatic IP configuration */
        if (!strcmp(addrs, "bootp")) {
        if (!strcmp(addrs, "bootp")) {
                rarp_flag = 0;
                rarp_flag = 0;
                return;
                return;
        } else if (!strcmp(addrs, "rarp")) {
        } else if (!strcmp(addrs, "rarp")) {
                bootp_flag = 0;
                bootp_flag = 0;
                return;
                return;
        } else if (!strcmp(addrs, "both")) {
        } else if (!strcmp(addrs, "both")) {
                return;
                return;
        }
        }
 
 
        /* Parse the whole string */
        /* Parse the whole string */
        ip = addrs;
        ip = addrs;
        while (ip && *ip) {
        while (ip && *ip) {
                if ((cp = strchr(ip, ':')))
                if ((cp = strchr(ip, ':')))
                        *cp++ = '\0';
                        *cp++ = '\0';
                if (strlen(ip) > 0) {
                if (strlen(ip) > 0) {
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
                        printk(KERN_NOTICE "Root-NFS: Config string num %d is \"%s\"\n",
                        printk(KERN_NOTICE "Root-NFS: Config string num %d is \"%s\"\n",
                                                                num, ip);
                                                                num, ip);
#endif
#endif
                        switch (num) {
                        switch (num) {
                        case 0:
                        case 0:
                                if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                        myaddr.sin_addr.s_addr = INADDR_NONE;
                                        myaddr.sin_addr.s_addr = INADDR_NONE;
                                break;
                                break;
                        case 1:
                        case 1:
                                if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                        server.sin_addr.s_addr = INADDR_NONE;
                                        server.sin_addr.s_addr = INADDR_NONE;
                                break;
                                break;
                        case 2:
                        case 2:
                                if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                        gateway.sin_addr.s_addr = INADDR_NONE;
                                        gateway.sin_addr.s_addr = INADDR_NONE;
                                break;
                                break;
                        case 3:
                        case 3:
                                if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
                                        netmask.sin_addr.s_addr = INADDR_NONE;
                                        netmask.sin_addr.s_addr = INADDR_NONE;
                                break;
                                break;
                        case 4:
                        case 4:
                                if ((dp = strchr(ip, '.'))) {
                                if ((dp = strchr(ip, '.'))) {
                                        *dp++ = '\0';
                                        *dp++ = '\0';
                                        strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
                                        strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
                                        system_utsname.domainname[__NEW_UTS_LEN] = '\0';
                                        system_utsname.domainname[__NEW_UTS_LEN] = '\0';
                                }
                                }
                                strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
                                strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
                                system_utsname.nodename[__NEW_UTS_LEN] = '\0';
                                system_utsname.nodename[__NEW_UTS_LEN] = '\0';
                                break;
                                break;
                        case 5:
                        case 5:
                                strncpy(user_dev_name, ip, IFNAMSIZ);
                                strncpy(user_dev_name, ip, IFNAMSIZ);
                                user_dev_name[IFNAMSIZ-1] = '\0';
                                user_dev_name[IFNAMSIZ-1] = '\0';
                                break;
                                break;
                        case 6:
                        case 6:
                                if (!strcmp(ip, "rarp"))
                                if (!strcmp(ip, "rarp"))
                                        bootp_flag = 0;
                                        bootp_flag = 0;
                                else if (!strcmp(ip, "bootp"))
                                else if (!strcmp(ip, "bootp"))
                                        rarp_flag = 0;
                                        rarp_flag = 0;
                                else if (strcmp(ip, "both"))
                                else if (strcmp(ip, "both"))
                                        bootp_flag = rarp_flag = 0;
                                        bootp_flag = rarp_flag = 0;
                                break;
                                break;
                        default:
                        default:
                                break;
                                break;
                        }
                        }
                }
                }
                ip = cp;
                ip = cp;
                num++;
                num++;
        }
        }
        rarp_serv = server;
        rarp_serv = server;
}
}
 
 
 
 
/*
/*
 *  Set the interface address and configure a route to the server.
 *  Set the interface address and configure a route to the server.
 */
 */
static int root_nfs_setup(void)
static int root_nfs_setup(void)
{
{
        struct rtentry route;
        struct rtentry route;
 
 
        /* Set the default system name in case none was previously found */
        /* Set the default system name in case none was previously found */
        if (!system_utsname.nodename[0]) {
        if (!system_utsname.nodename[0]) {
                strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
                strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
                system_utsname.nodename[__NEW_UTS_LEN] = '\0';
                system_utsname.nodename[__NEW_UTS_LEN] = '\0';
        }
        }
 
 
        /* Set the correct netmask */
        /* Set the correct netmask */
        if (netmask.sin_addr.s_addr == INADDR_NONE)
        if (netmask.sin_addr.s_addr == INADDR_NONE)
                netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
                netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
 
 
        /* Setup the device correctly */
        /* Setup the device correctly */
        root_dev->family     = myaddr.sin_family;
        root_dev->family     = myaddr.sin_family;
        root_dev->pa_addr    = myaddr.sin_addr.s_addr;
        root_dev->pa_addr    = myaddr.sin_addr.s_addr;
        root_dev->pa_mask    = netmask.sin_addr.s_addr;
        root_dev->pa_mask    = netmask.sin_addr.s_addr;
        root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
        root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
        root_dev->pa_dstaddr = 0;
        root_dev->pa_dstaddr = 0;
 
 
        /*
        /*
         * Now add a route to the server. If there is no gateway given,
         * Now add a route to the server. If there is no gateway given,
         * the server is on the same subnet, so we establish only a route to
         * the server is on the same subnet, so we establish only a route to
         * the local network. Otherwise we create a route to the gateway (the
         * the local network. Otherwise we create a route to the gateway (the
         * same local network router as in the former case) and then setup a
         * same local network router as in the former case) and then setup a
         * gatewayed default route. Note that this gives sufficient network
         * gatewayed default route. Note that this gives sufficient network
         * setup even for full system operation in all common cases.
         * setup even for full system operation in all common cases.
         */
         */
        memset(&route, 0, sizeof(route));        /* Local subnet route */
        memset(&route, 0, sizeof(route));        /* Local subnet route */
        route.rt_dev = root_dev->name;
        route.rt_dev = root_dev->name;
        route.rt_mss = root_dev->mtu;
        route.rt_mss = root_dev->mtu;
        route.rt_flags = RTF_UP;
        route.rt_flags = RTF_UP;
        *((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
        *((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
        (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
        (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
        *((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
        *((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
        if (ip_rt_new(&route)) {
        if (ip_rt_new(&route)) {
                printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
                printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
                return -1;
                return -1;
        }
        }
 
 
        if (gateway.sin_addr.s_addr != INADDR_NONE) {   /* Default route */
        if (gateway.sin_addr.s_addr != INADDR_NONE) {   /* Default route */
                (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
                (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
                (((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
                (((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
                *((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
                *((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
                route.rt_flags |= RTF_GATEWAY;
                route.rt_flags |= RTF_GATEWAY;
                if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
                if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
                        printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
                        printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
                        return -1;
                        return -1;
                }
                }
                if (ip_rt_new(&route)) {
                if (ip_rt_new(&route)) {
                        printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
                        printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
                        return -1;
                        return -1;
                }
                }
        } else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
        } else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
                printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
                printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
                return -1;
                return -1;
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Get the necessary IP addresses and prepare for mounting the required
 *  Get the necessary IP addresses and prepare for mounting the required
 *  NFS filesystem.
 *  NFS filesystem.
 */
 */
int nfs_root_init(char *nfsname, char *nfsaddrs)
int nfs_root_init(char *nfsname, char *nfsaddrs)
{
{
        /*
        /*
         * Decode IP addresses and other configuration info contained
         * Decode IP addresses and other configuration info contained
         * in the nfsaddrs string (which came from the kernel command
         * in the nfsaddrs string (which came from the kernel command
         * line).
         * line).
         */
         */
        root_nfs_addrs(nfsaddrs);
        root_nfs_addrs(nfsaddrs);
 
 
        /*
        /*
         * Setup all network devices
         * Setup all network devices
         */
         */
        if (root_dev_open() < 0)
        if (root_dev_open() < 0)
                return -1;
                return -1;
 
 
        /*
        /*
         * If the config information is insufficient (e.g., our IP address or
         * If the config information is insufficient (e.g., our IP address or
         * IP address of the boot server is missing or we have multiple network
         * IP address of the boot server is missing or we have multiple network
         * interfaces and no default was set), use BOOTP or RARP to get the
         * interfaces and no default was set), use BOOTP or RARP to get the
         * missing values.
         * missing values.
         *
         *
         * Note that we don't try to set up correct routes for multiple
         * Note that we don't try to set up correct routes for multiple
         * interfaces (could be solved by trying icmp echo requests), because
         * interfaces (could be solved by trying icmp echo requests), because
         * it's only necessary in the rare case of multiple ethernet devices
         * it's only necessary in the rare case of multiple ethernet devices
         * in the (diskless) system and if the server is on another subnet.
         * in the (diskless) system and if the server is on another subnet.
         * If only one interface is installed, the routing is obvious.
         * If only one interface is installed, the routing is obvious.
         */
         */
        if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
        if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
             server.sin_addr.s_addr == INADDR_NONE ||
             server.sin_addr.s_addr == INADDR_NONE ||
             (open_base != NULL && open_base->next != NULL))
             (open_base != NULL && open_base->next != NULL))
#ifdef CONFIG_RNFS_DYNAMIC
#ifdef CONFIG_RNFS_DYNAMIC
                && root_auto_config() < 0
                && root_auto_config() < 0
#endif
#endif
           ) {
           ) {
                root_dev_close();
                root_dev_close();
                return -1;
                return -1;
        }
        }
        if (root_dev == NULL) {
        if (root_dev == NULL) {
                if (open_base != NULL && open_base->next == NULL) {
                if (open_base != NULL && open_base->next == NULL) {
                        root_dev = open_base->dev;
                        root_dev = open_base->dev;
                } else {
                } else {
                        printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
                        printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
                        root_dev_close();
                        root_dev_close();
                        return -1;
                        return -1;
                }
                }
        }
        }
 
 
        /*
        /*
         * Close all network devices except the device which connects to
         * Close all network devices except the device which connects to
         * server
         * server
         */
         */
        root_dev_close();
        root_dev_close();
 
 
        /*
        /*
         * Decode the root directory path name and NFS options from
         * Decode the root directory path name and NFS options from
         * the kernel command line. This has to go here in order to
         * the kernel command line. This has to go here in order to
         * be able to use the client IP address for the remote root
         * be able to use the client IP address for the remote root
         * directory (necessary for pure RARP booting).
         * directory (necessary for pure RARP booting).
         */
         */
        if (root_nfs_name(nfsname) < 0)
        if (root_nfs_name(nfsname) < 0)
                return -1;
                return -1;
 
 
        /*
        /*
         * Setup devices and routes. The server directory is actually
         * Setup devices and routes. The server directory is actually
         * mounted after init() has been started.
         * mounted after init() has been started.
         */
         */
        if (root_nfs_setup() < 0)
        if (root_nfs_setup() < 0)
                return -1;
                return -1;
 
 
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
        root_nfs_print();
        root_nfs_print();
#endif
#endif
 
 
        return 0;
        return 0;
}
}
 
 
 
 
/***************************************************************************
/***************************************************************************
 
 
               Routines to actually mount the root directory
               Routines to actually mount the root directory
 
 
 ***************************************************************************/
 ***************************************************************************/
 
 
static struct file  *nfs_file;          /* File descriptor pointing to inode */
static struct file  *nfs_file;          /* File descriptor pointing to inode */
static struct inode *nfs_sock_inode;    /* Inode containing socket */
static struct inode *nfs_sock_inode;    /* Inode containing socket */
static int *rpc_packet = NULL;          /* RPC packet */
static int *rpc_packet = NULL;          /* RPC packet */
 
 
 
 
/*
/*
 *  Open a UDP socket.
 *  Open a UDP socket.
 */
 */
static int root_nfs_open(void)
static int root_nfs_open(void)
{
{
        /* Open the socket */
        /* Open the socket */
        if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
                printk(KERN_ERR "Root-NFS: Cannot open UDP socket for NFS!\n");
                printk(KERN_ERR "Root-NFS: Cannot open UDP socket for NFS!\n");
                return -1;
                return -1;
        }
        }
        nfs_file = current->files->fd[nfs_data.fd];
        nfs_file = current->files->fd[nfs_data.fd];
        nfs_sock_inode = nfs_file->f_inode;
        nfs_sock_inode = nfs_file->f_inode;
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Close the UDP file descriptor. If nfs_read_super is successful, it
 *  Close the UDP file descriptor. If nfs_read_super is successful, it
 *  increases the reference count, so we can simply close the file, and
 *  increases the reference count, so we can simply close the file, and
 *  the socket keeps open.
 *  the socket keeps open.
 */
 */
static void root_nfs_close(void)
static void root_nfs_close(void)
{
{
        /*
        /*
         * The following close doesn't touch the server structure, which
         * The following close doesn't touch the server structure, which
         * now contains a file pointer pointing into nowhere. The system
         * now contains a file pointer pointing into nowhere. The system
         * _should_ crash as soon as someone tries to select on the root
         * _should_ crash as soon as someone tries to select on the root
         * filesystem. Haven't tried it yet - we can still change it back
         * filesystem. Haven't tried it yet - we can still change it back
         * to the old way of keeping a static copy of all important data
         * to the old way of keeping a static copy of all important data
         * structures, including their pointers. At least this should be
         * structures, including their pointers. At least this should be
         * checked out _carefully_ before going into a public release
         * checked out _carefully_ before going into a public release
         * kernel.  -  GK
         * kernel.  -  GK
         */
         */
        sys_close(nfs_data.fd);
        sys_close(nfs_data.fd);
}
}
 
 
 
 
/*
/*
 *  Find a suitable listening port and bind to it
 *  Find a suitable listening port and bind to it
 */
 */
static int root_nfs_bind(void)
static int root_nfs_bind(void)
{
{
        int res = -1;
        int res = -1;
        short port = STARTPORT;
        short port = STARTPORT;
        struct sockaddr_in *sin = &myaddr;
        struct sockaddr_in *sin = &myaddr;
        int i;
        int i;
 
 
        if (nfs_sock_inode->u.socket_i.ops->bind) {
        if (nfs_sock_inode->u.socket_i.ops->bind) {
                for (i = 0; i < NPORTS && res < 0; i++) {
                for (i = 0; i < NPORTS && res < 0; i++) {
                        sin->sin_port = htons(port++);
                        sin->sin_port = htons(port++);
                        if (port > ENDPORT) {
                        if (port > ENDPORT) {
                                port = STARTPORT;
                                port = STARTPORT;
                        }
                        }
                        res = nfs_sock_inode->u.socket_i.ops->bind(&nfs_sock_inode->u.socket_i,
                        res = nfs_sock_inode->u.socket_i.ops->bind(&nfs_sock_inode->u.socket_i,
                                                (struct sockaddr *)sin,
                                                (struct sockaddr *)sin,
                                                sizeof(struct sockaddr_in));
                                                sizeof(struct sockaddr_in));
                }
                }
        }
        }
        if (res < 0) {
        if (res < 0) {
                printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
                printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
                root_nfs_close();
                root_nfs_close();
                return -1;
                return -1;
        }
        }
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
        printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
        printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
#endif
#endif
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Send an RPC request and wait for the answer
 *  Send an RPC request and wait for the answer
 */
 */
static int *root_nfs_call(int *end)
static int *root_nfs_call(int *end)
{
{
        struct socket *sock;
        struct socket *sock;
        int dummylen;
        int dummylen;
        static struct nfs_server s = {
        static struct nfs_server s = {
                0,                       /* struct file *         */
                0,                       /* struct file *         */
                0,                       /* struct rsock *        */
                0,                       /* struct rsock *        */
                {
                {
                    0, "",
                    0, "",
                },                      /* toaddr                */
                },                      /* toaddr                */
                0,                       /* lock                  */
                0,                       /* lock                  */
                NULL,                   /* wait queue            */
                NULL,                   /* wait queue            */
                NFS_MOUNT_SOFT,         /* flags                 */
                NFS_MOUNT_SOFT,         /* flags                 */
                0, 0,                     /* rsize, wsize          */
                0, 0,                     /* rsize, wsize          */
                0,                       /* timeo                 */
                0,                       /* timeo                 */
                0,                       /* retrans               */
                0,                       /* retrans               */
                3 * HZ, 60 * HZ, 30 * HZ, 60 * HZ, "\0"
                3 * HZ, 60 * HZ, 30 * HZ, 60 * HZ, "\0"
        };
        };
 
 
        s.file = nfs_file;
        s.file = nfs_file;
        sock = &((nfs_file->f_inode)->u.socket_i);
        sock = &((nfs_file->f_inode)->u.socket_i);
 
 
        /* Extract the other end of the socket into s->toaddr */
        /* Extract the other end of the socket into s->toaddr */
        sock->ops->getname(sock, &(s.toaddr), &dummylen, 1);
        sock->ops->getname(sock, &(s.toaddr), &dummylen, 1);
        ((struct sockaddr_in *) &s.toaddr)->sin_port   = server.sin_port;
        ((struct sockaddr_in *) &s.toaddr)->sin_port   = server.sin_port;
        ((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
        ((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
        ((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
        ((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
 
 
        s.rsock = rpc_makesock(nfs_file);
        s.rsock = rpc_makesock(nfs_file);
        s.flags = nfs_data.flags;
        s.flags = nfs_data.flags;
        s.rsize = nfs_data.rsize;
        s.rsize = nfs_data.rsize;
        s.wsize = nfs_data.wsize;
        s.wsize = nfs_data.wsize;
        s.timeo = nfs_data.timeo * HZ / 10;
        s.timeo = nfs_data.timeo * HZ / 10;
        s.retrans = nfs_data.retrans;
        s.retrans = nfs_data.retrans;
        strcpy(s.hostname, nfs_data.hostname);
        strcpy(s.hostname, nfs_data.hostname);
 
 
        /*
        /*
         * First connect the UDP socket to a server port, then send the
         * First connect the UDP socket to a server port, then send the
         * packet out, and finally check whether the answer is OK.
         * packet out, and finally check whether the answer is OK.
         */
         */
        if (nfs_sock_inode->u.socket_i.ops->connect &&
        if (nfs_sock_inode->u.socket_i.ops->connect &&
            nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
            nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
                                                (struct sockaddr *) &server,
                                                (struct sockaddr *) &server,
                                                sizeof(struct sockaddr_in),
                                                sizeof(struct sockaddr_in),
                                                nfs_file->f_flags) < 0)
                                                nfs_file->f_flags) < 0)
                return NULL;
                return NULL;
        if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
        if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
                return NULL;
                return NULL;
        return rpc_verify(rpc_packet);
        return rpc_verify(rpc_packet);
}
}
 
 
 
 
/*
/*
 *  Create an RPC packet header
 *  Create an RPC packet header
 */
 */
static int *root_nfs_header(int proc, int program, int version)
static int *root_nfs_header(int proc, int program, int version)
{
{
        int groups[] = { 0, NOGROUP };
        int groups[] = { 0, NOGROUP };
 
 
        if (rpc_packet == NULL) {
        if (rpc_packet == NULL) {
                if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
                if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
                        printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
                        printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
                        return NULL;
                        return NULL;
                }
                }
        }
        }
        return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
        return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
}
}
 
 
 
 
/*
/*
 *  Query server portmapper for the port of a daemon program
 *  Query server portmapper for the port of a daemon program
 */
 */
static int root_nfs_get_port(int program, int version)
static int root_nfs_get_port(int program, int version)
{
{
        int *p;
        int *p;
 
 
        /* Prepare header for portmap request */
        /* Prepare header for portmap request */
        server.sin_port = htons(NFS_PMAP_PORT);
        server.sin_port = htons(NFS_PMAP_PORT);
        p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
        p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
        if (!p)
        if (!p)
                return -1;
                return -1;
 
 
        /* Set arguments for portmapper */
        /* Set arguments for portmapper */
        *p++ = htonl(program);
        *p++ = htonl(program);
        *p++ = htonl(version);
        *p++ = htonl(version);
        *p++ = htonl(IPPROTO_UDP);
        *p++ = htonl(IPPROTO_UDP);
        *p++ = 0;
        *p++ = 0;
 
 
        /* Send request to server portmapper */
        /* Send request to server portmapper */
        if ((p = root_nfs_call(p)) == NULL)
        if ((p = root_nfs_call(p)) == NULL)
                return -1;
                return -1;
 
 
        return ntohl(*p);
        return ntohl(*p);
}
}
 
 
 
 
/*
/*
 *  Get portnumbers for mountd and nfsd from server
 *  Get portnumbers for mountd and nfsd from server
 */
 */
static int root_nfs_ports(void)
static int root_nfs_ports(void)
{
{
        int port;
        int port;
 
 
        if (nfs_port < 0) {
        if (nfs_port < 0) {
                if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
                if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
                        printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
                        printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
                        port = NFS_NFS_PORT;
                        port = NFS_NFS_PORT;
                }
                }
                nfs_port = port;
                nfs_port = port;
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
                printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
                printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
#endif
#endif
        }
        }
        if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
        if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
                printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
                printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
                port = NFS_MOUNT_PORT;
                port = NFS_MOUNT_PORT;
        }
        }
        server.sin_port = htons(port);
        server.sin_port = htons(port);
#ifdef NFSROOT_DEBUG
#ifdef NFSROOT_DEBUG
        printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
        printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
#endif
#endif
 
 
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Get a file handle from the server for the directory which is to be
 *  Get a file handle from the server for the directory which is to be
 *  mounted
 *  mounted
 */
 */
static int root_nfs_get_handle(void)
static int root_nfs_get_handle(void)
{
{
        int len, status, *p;
        int len, status, *p;
 
 
        /* Prepare header for mountd request */
        /* Prepare header for mountd request */
        p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
        p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
        if (!p) {
        if (!p) {
                root_nfs_close();
                root_nfs_close();
                return -1;
                return -1;
        }
        }
 
 
        /* Set arguments for mountd */
        /* Set arguments for mountd */
        len = strlen(nfs_path);
        len = strlen(nfs_path);
        *p++ = htonl(len);
        *p++ = htonl(len);
        memcpy(p, nfs_path, len);
        memcpy(p, nfs_path, len);
        len = (len + 3) >> 2;
        len = (len + 3) >> 2;
        p[len] = 0;
        p[len] = 0;
        p += len;
        p += len;
 
 
        /* Send request to server mountd */
        /* Send request to server mountd */
        if ((p = root_nfs_call(p)) == NULL) {
        if ((p = root_nfs_call(p)) == NULL) {
                root_nfs_close();
                root_nfs_close();
                return -1;
                return -1;
        }
        }
        status = ntohl(*p++);
        status = ntohl(*p++);
        if (status == 0) {
        if (status == 0) {
                nfs_data.root = *((struct nfs_fh *) p);
                nfs_data.root = *((struct nfs_fh *) p);
                printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
                printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
        } else {
        } else {
                printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
                printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
                        status, nfs_path);
                        status, nfs_path);
                root_nfs_close();
                root_nfs_close();
                return -1;
                return -1;
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Now actually mount the given directory
 *  Now actually mount the given directory
 */
 */
static int root_nfs_do_mount(struct super_block *sb)
static int root_nfs_do_mount(struct super_block *sb)
{
{
        /* First connect to the nfsd port on the server */
        /* First connect to the nfsd port on the server */
        server.sin_port = htons(nfs_port);
        server.sin_port = htons(nfs_port);
        nfs_data.addr = server;
        nfs_data.addr = server;
        if (nfs_sock_inode->u.socket_i.ops->connect &&
        if (nfs_sock_inode->u.socket_i.ops->connect &&
            nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
            nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
                                                (struct sockaddr *) &server,
                                                (struct sockaddr *) &server,
                                                sizeof(struct sockaddr_in),
                                                sizeof(struct sockaddr_in),
                                                nfs_file->f_flags) < 0) {
                                                nfs_file->f_flags) < 0) {
                root_nfs_close();
                root_nfs_close();
                return -1;
                return -1;
        }
        }
 
 
        /* Now (finally ;-)) read the super block for mounting */
        /* Now (finally ;-)) read the super block for mounting */
        if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
        if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
                root_nfs_close();
                root_nfs_close();
                return -1;
                return -1;
        }
        }
        return 0;
        return 0;
}
}
 
 
 
 
/*
/*
 *  Get the NFS port numbers and file handle, and then read the super-
 *  Get the NFS port numbers and file handle, and then read the super-
 *  block for mounting.
 *  block for mounting.
 */
 */
int nfs_root_mount(struct super_block *sb)
int nfs_root_mount(struct super_block *sb)
{
{
        if (root_nfs_open() < 0)
        if (root_nfs_open() < 0)
                return -1;
                return -1;
        if (root_nfs_bind() < 0)
        if (root_nfs_bind() < 0)
                return -1;
                return -1;
        if (root_nfs_ports() < 0)
        if (root_nfs_ports() < 0)
                return -1;
                return -1;
        if (root_nfs_get_handle() < 0)
        if (root_nfs_get_handle() < 0)
                return -1;
                return -1;
        if (root_nfs_do_mount(sb) < 0)
        if (root_nfs_do_mount(sb) < 0)
                return -1;
                return -1;
        root_nfs_close();
        root_nfs_close();
        return 0;
        return 0;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.