patch-1.3.42 linux/fs/nfs/nfsroot.c
Next file: linux/fs/nfs/proc.c
Previous file: linux/fs/nfs/Makefile
Back to the patch index
Back to the overall index
- Lines: 850
- Date:
Thu Nov 16 08:54:24 1995
- Orig file:
v1.3.41/linux/fs/nfs/nfsroot.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.41/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
@@ -0,0 +1,849 @@
+/*
+ * linux/fs/nfs/nfsroot.c
+ *
+ * Copyright (C) 1995 Gero Kuhlmann <gero@gkminix.han.de>
+ *
+ * Allow an NFS filesystem to be mounted as root. The way this works
+ * is to first determine the local IP address via RARP. Then handle
+ * the RPC negotiation with the system which replied to the RARP. The
+ * actual mounting is done later, when init() is running.
+ */
+
+
+/* Define this to allow debugging output */
+#define NFSROOT_DEBUG 1
+
+/* Define the timeout for waiting for a RARP reply */
+#define RARP_TIMEOUT 30 /* 30 seconds */
+#define RARP_RETRIES 5 /* 5 retries */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/param.h>
+#include <linux/utsname.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#ifdef CONFIG_AX25
+#include <net/ax25.h> /* For AX25_P_IP */
+#endif
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <net/route.h>
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+/* #include <netinet/in.h> */
+
+#define IPPORT_RESERVED 1024
+
+/* Range of privileged ports */
+#define STARTPORT 600
+#define ENDPORT (IPPORT_RESERVED - 1)
+#define NPORTS (ENDPORT - STARTPORT + 1)
+
+
+
+struct open_dev
+{
+ struct device *dev;
+ unsigned short old_flags;
+ struct open_dev *next;
+};
+
+static struct open_dev *open_base = NULL;
+static struct device *root_dev = NULL;
+static struct sockaddr_in myaddr; /* My IP address */
+static struct sockaddr_in server; /* Server IP address */
+static struct nfs_mount_data nfs_data; /* NFS mount info */
+static char nfs_path[NFS_MAXPATHLEN]; /* Name of directory to mount */
+static int nfs_port; /* Port to connect to for NFS service */
+
+
+
+/***************************************************************************
+
+ RARP Subroutines
+
+ ***************************************************************************/
+
+extern void arp_send(int type, int ptype, unsigned long target_ip,
+ struct device *dev, unsigned long src_ip,
+ unsigned char *dest_hw, unsigned char *src_hw,
+ unsigned char *target_hw);
+
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+
+
+static struct packet_type rarp_packet_type =
+{
+ 0, /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */
+ NULL, /* Listen to all devices */
+ root_rarp_recv,
+ NULL,
+ NULL
+};
+
+
+/*
+ * For receiving rarp packets a packet type has to be registered. Also
+ * initialize all devices for usage by RARP.
+ */
+static int root_rarp_open(void)
+{
+ struct open_dev *openp;
+ struct device *dev;
+ unsigned short old_flags;
+ int num;
+
+ /* Register the packet type */
+ rarp_packet_type.type=htons(ETH_P_RARP);
+ dev_add_pack(&rarp_packet_type);
+
+ /* Open all devices which allow RARP */
+ for (dev = dev_base, num = 0; dev != NULL; dev = dev->next) {
+ if (dev->type < ARPHRD_SLIP &&
+ dev->family == AF_INET &&
+ !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOARP))) {
+ /* First up the interface */
+ old_flags = dev->flags;
+ dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+ if (!(old_flags & IFF_UP) && dev_open(dev)) {
+ dev->flags = old_flags;
+ continue;
+ }
+ openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
+ GFP_ATOMIC);
+ if (openp == NULL)
+ continue;
+ openp->dev = dev;
+ openp->old_flags = old_flags;
+ openp->next = open_base;
+ open_base = openp;
+ num++;
+ }
+ }
+ return num;
+}
+
+
+/*
+ * Remove the packet type again when all rarp packets have been received
+ * and restore the state of the device. However, keep the root device
+ * open for the upcoming mount.
+ */
+static void root_rarp_close(void)
+{
+ struct open_dev *openp;
+ struct open_dev *nextp;
+
+ /* Deregister the packet type */
+ rarp_packet_type.type=htons(ETH_P_RARP);
+ dev_remove_pack(&rarp_packet_type);
+
+ /* Deactivate all previously opened devices except that one which is
+ * able to connect to a suitable server
+ */
+ openp = open_base;
+ while (openp != NULL) {
+ nextp = openp->next;
+ openp->next = NULL;
+ if (openp->dev != root_dev) {
+ if (!(openp->old_flags & IFF_UP))
+ dev_close(openp->dev);
+ openp->dev->flags = openp->old_flags;
+ }
+ kfree_s(openp, sizeof(struct open_dev));
+ openp = nextp;
+ }
+}
+
+
+/*
+ * Receive RARP packets.
+ */
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct arphdr *rarp = (struct arphdr *)skb->h.raw;
+ unsigned char *rarp_ptr = (unsigned char *)(rarp+1);
+ unsigned long sip, tip;
+ unsigned char *sha, *tha; /* s for "source", t for "target" */
+
+ /* If this test doesn't pass, its not IP, or we should ignore it anyway */
+ if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* If it's not a RARP reply, delete it. */
+ if (rarp->ar_op != htons(ARPOP_RREPLY)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* If it's not ethernet or AX25, delete it. */
+ if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
+#ifdef CONFIG_AX25
+ (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+ rarp->ar_pln != 4) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* Extract variable width fields */
+ sha = rarp_ptr;
+ rarp_ptr += dev->addr_len;
+ memcpy(&sip, rarp_ptr, 4);
+ rarp_ptr += 4;
+ tha = rarp_ptr;
+ rarp_ptr += dev->addr_len;
+ memcpy(&tip, rarp_ptr, 4);
+
+ /* Discard packets which are not meant for us. */
+ if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* The packet is what we were looking for. Setup the global variables. */
+ cli();
+ if (root_dev != NULL) {
+ sti();
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ root_dev = dev;
+ sti();
+
+ myaddr.sin_family = dev->family;
+ myaddr.sin_addr.s_addr = tip;
+ server.sin_family = dev->family;
+ if (!server.sin_addr.s_addr)
+ server.sin_addr.s_addr = sip;
+
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+
+/*
+ * Send RARP request packet over all devices which allow RARP.
+ */
+static void root_rarp_send(void)
+{
+ struct open_dev *openp;
+ struct device *dev;
+
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "NFS: Sending RARP request...\n");
+#endif
+
+ for (openp = open_base; openp != NULL; openp = openp->next) {
+ dev = openp->dev;
+ arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
+ dev->dev_addr, dev->dev_addr);
+ }
+}
+
+
+/*
+ * Determine client and server IP numbers and appropriate device by using
+ * the RARP protocol.
+ */
+static int do_rarp(void)
+{
+ int retries = 0;
+ unsigned long timeout;
+
+ /* Open all devices and setup RARP protocol */
+ if (!root_rarp_open()) {
+ printk(KERN_ERR "NFS: No network device found to send RARP request to\n");
+ return -1;
+ }
+
+ /* Send RARP request and wait, until we get an answer. This loop seems
+ * to be a terrible waste of cpu time, but actually there is no process
+ * running at all, so we don't need to use any scheduler functions.
+ */
+ for (retries = 0; retries < RARP_RETRIES && root_dev == NULL; retries++) {
+ root_rarp_send();
+ timeout = jiffies + (RARP_TIMEOUT * HZ);
+ while (jiffies < timeout && root_dev == NULL)
+ ;;
+ }
+
+ if (root_dev == NULL) {
+ printk(KERN_ERR "NFS: Timed out while waiting for RARP answer\n");
+ return -1;
+ }
+
+ root_rarp_close();
+
+ printk(KERN_NOTICE "NFS: ");
+ printk("Got RARP answer from %s, ", in_ntoa(server.sin_addr.s_addr));
+ printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
+
+ return 0;
+}
+
+
+
+
+/***************************************************************************
+
+ Routines to setup NFS
+
+ ***************************************************************************/
+
+extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask,
+ unsigned long gw, struct device *dev,
+ unsigned short mss, unsigned long window);
+
+
+/* The following integer options are recognized */
+static struct nfs_int_opts
+{
+ char *name;
+ int *val;
+} root_int_opts[] = {
+ { "port", &nfs_port },
+ { "rsize", &nfs_data.rsize },
+ { "wsize", &nfs_data.wsize },
+ { "timeo", &nfs_data.timeo },
+ { "retrans", &nfs_data.retrans },
+ { "acregmin", &nfs_data.acregmin },
+ { "acregmax", &nfs_data.acregmax },
+ { "acdirmin", &nfs_data.acdirmin },
+ { "acdirmax", &nfs_data.acdirmax },
+ { NULL, NULL }};
+
+
+/* And now the flag options */
+static struct nfs_bool_opts
+{
+ char *name;
+ int and_mask;
+ int or_mask;
+} root_bool_opts[] = {
+ { "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
+ { "hard", ~NFS_MOUNT_SOFT, 0 },
+ { "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
+ { "nointr", ~NFS_MOUNT_INTR, 0 },
+ { "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
+ { "noposix", ~NFS_MOUNT_POSIX, 0 },
+ { "cto", ~NFS_MOUNT_NOCTO, 0 },
+ { "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
+ { "ac", ~NFS_MOUNT_NOAC, 0 },
+ { "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
+ { NULL, 0, 0 }};
+
+
+unsigned long get_address (char **str)
+{
+ unsigned long l;
+ unsigned int val;
+ int i;
+
+ l = 0;
+ for (i = 0; i < 4; i++)
+ {
+
+ l <<= 8;
+ if (**str != '\0')
+ {
+
+ val = 0;
+ while (**str != '\0' && **str != '.' && **str != ':')
+ {
+
+ val *= 10;
+ val += **str - '0';
+ (*str)++;
+ }
+
+ l |= val;
+ if (**str != '\0')
+ (*str)++;
+ }
+
+ }
+
+ return(htonl(l));
+}
+
+/*
+ * Prepare the NFS data structure and parse any options
+ */
+static int root_nfs_parse(char *name)
+{
+ char buf[NFS_MAXPATHLEN];
+ char *cp, *options, *val;
+
+ /* get the host ip number */
+ if (*name >= '0' && *name <= '9'){
+ server.sin_addr.s_addr = get_address (&name);
+ }
+ /* Setup the server hostname */
+ cp = in_ntoa(server.sin_addr.s_addr);
+ strncpy(nfs_data.hostname, cp, 255);
+ nfs_data.addr = server;
+
+ /* Set the name of the directory to mount */
+ cp = in_ntoa(myaddr.sin_addr.s_addr);
+ strncpy(buf, name, 255);
+ if ((options = strchr(buf, ',')))
+ *options++ = '\0';
+ if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+ printk(KERN_ERR "NFS: Pathname for remote directory too long\n");
+ return -1;
+ }
+ sprintf(nfs_path, buf, cp);
+
+ /* Set some default values */
+ nfs_port = -1;
+ nfs_data.version = NFS_MOUNT_VERSION;
+ nfs_data.flags = 0;
+ nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ nfs_data.timeo = 7;
+ nfs_data.retrans = 3;
+ nfs_data.acregmin = 3;
+ nfs_data.acregmax = 60;
+ nfs_data.acdirmin = 30;
+ nfs_data.acdirmax = 60;
+
+ /* Process any options */
+ if (options) {
+ cp = strtok(options, ",");
+ while (cp) {
+ if ((val = strchr(cp, '='))) {
+ struct nfs_int_opts *opts = root_int_opts;
+ *val++ = '\0';
+ while (opts->name && strcmp(opts->name, cp))
+ opts++;
+ if (opts->name)
+ *(opts->val) = (int) simple_strtoul(val, NULL, 10);
+ } else {
+ struct nfs_bool_opts *opts = root_bool_opts;
+ while (opts->name && strcmp(opts->name, cp))
+ opts++;
+ if (opts->name) {
+ nfs_data.flags &= opts->and_mask;
+ nfs_data.flags |= opts->or_mask;
+ }
+ }
+ cp = strtok(NULL, ",");
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Tell the user what's going on.
+ */
+static void root_nfs_print(void)
+{
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "NFS: Mounting %s on server %s as root\n",
+ nfs_path, nfs_data.hostname);
+ printk(KERN_NOTICE "NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+ nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
+ printk(KERN_NOTICE "NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
+ nfs_data.acregmin, nfs_data.acregmax,
+ nfs_data.acdirmin, nfs_data.acdirmax);
+ printk(KERN_NOTICE "NFS: port = %d, flags = %08x\n",
+ nfs_port, nfs_data.flags);
+#endif
+}
+
+
+/*
+ * Set the interface address and configure a route to the server.
+ */
+static void root_nfs_setup(void)
+{
+ struct rtentry server_route;
+ struct sockaddr_in *sin;
+
+ /* Setup the device correctly */
+ root_dev->family = myaddr.sin_family;
+ root_dev->pa_addr = myaddr.sin_addr.s_addr;
+ root_dev->pa_mask = ip_get_mask(myaddr.sin_addr.s_addr);
+ root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
+ root_dev->pa_dstaddr = 0;
+
+ sin=(struct sockaddr_in *)&server_route.rt_dst;
+ *sin=server;
+ sin=(struct sockaddr_in *)&server_route.rt_genmask;
+ sin->sin_family=AF_INET;
+ sin->sin_addr.s_addr= root_dev->pa_mask;
+ server_route.rt_dev[0]=0;
+ server_route.rt_flags=RTF_HOST|RTF_UP;
+
+ /* Now add a route to the server */
+ if(ip_rt_new(&server_route)==-1)
+ printk("Unable to add NFS server route.\n");
+}
+
+
+/*
+ * Get the necessary IP addresses and prepare for mounting the required
+ * NFS filesystem.
+ */
+int nfs_root_init(char *nfsname)
+{
+ /* Initialize network device and get local and server IP address */
+ if (do_rarp() < 0)
+ return -1;
+
+ /* Initialize the global variables necessary for NFS. The server
+ * directory is actually mounted after init() has been started.
+ */
+ if (root_nfs_parse(nfsname) < 0)
+ return -1;
+ root_nfs_print();
+ root_nfs_setup();
+ return 0;
+}
+
+
+
+
+/***************************************************************************
+
+ Routines to actually mount the root directory
+
+ ***************************************************************************/
+
+static struct file nfs_file; /* File descriptor containing socket */
+static struct inode nfs_inode; /* Inode containing socket */
+static int *rpc_packet = NULL; /* RPC packet */
+
+extern asmlinkage int sys_socketcall(int call, unsigned long *args);
+extern struct socket *socki_lookup(struct inode *inode);
+
+
+/*
+ * Open a UDP socket.
+ */
+static int root_nfs_open(void)
+{
+ struct file *filp;
+ unsigned long opt[] = { AF_INET, SOCK_DGRAM, IPPROTO_UDP };
+
+ /* Open the socket */
+ if ((nfs_data.fd = sys_socketcall(SYS_SOCKET, opt)) < 0) {
+ printk(KERN_ERR "NFS: Cannot open UDP socket\n");
+ return -1;
+ }
+
+ /* Copy the file and inode data area so that we can remove the
+ * file lateron without killing the socket. After all this the
+ * closing routine just needs to remove the file pointer from
+ * the init-task descriptor.
+ */
+ filp = current->files->fd[nfs_data.fd];
+ memcpy(&nfs_file, filp, sizeof(struct file));
+ nfs_file.f_next = nfs_file.f_prev = NULL;
+ current->files->fd[nfs_data.fd] = &nfs_file;
+ filp->f_count = 0; /* Free the file descriptor */
+
+ memcpy(&nfs_inode, nfs_file.f_inode, sizeof(struct inode));
+ nfs_inode.i_hash_next = nfs_inode.i_hash_prev = NULL;
+ nfs_inode.i_next = nfs_inode.i_prev = NULL;
+ clear_inode(nfs_file.f_inode);
+ nfs_file.f_inode = &nfs_inode;
+ nfs_inode.u.socket_i.inode = &nfs_inode;
+ nfs_file.private_data = NULL;
+
+ return 0;
+}
+
+
+/*
+ * Close the UDP file descriptor. The main part of preserving the socket
+ * has already been done after opening it. Now we have to remove the
+ * file descriptor from the init task.
+ */
+static void root_nfs_close(int close_all)
+{
+ /* Remove the file from the list of open files */
+ current->files->fd[nfs_data.fd] = NULL;
+ if (current->files->count > 0)
+ current->files->count--;
+
+ /* Clear memory use by the RPC packet */
+ if (rpc_packet != NULL)
+ kfree_s(rpc_packet, nfs_data.wsize + 1024);
+
+ /* In case of an error we also have to close the socket again (sigh) */
+ if (close_all) {
+ nfs_inode.u.socket_i.inode = NULL; /* The inode is already cleared */
+ if (nfs_file.f_op->release)
+ nfs_file.f_op->release(&nfs_inode, &nfs_file);
+ }
+}
+
+
+/*
+ * Find a suitable listening port and bind to it
+ */
+static int root_nfs_bind(void)
+{
+ int res = -1;
+ short port = STARTPORT;
+ struct sockaddr_in *sin = &myaddr;
+ int i;
+
+ if (nfs_inode.u.socket_i.ops->bind) {
+ for (i = 0; i < NPORTS && res < 0; i++) {
+ sin->sin_port = htons(port++);
+ if (port > ENDPORT) {
+ port = STARTPORT;
+ }
+ res = nfs_inode.u.socket_i.ops->bind(&nfs_inode.u.socket_i,
+ (struct sockaddr *) sin, sizeof(struct sockaddr_in));
+ }
+ }
+ if (res < 0) {
+ printk(KERN_ERR "NFS: Cannot find a suitable listening port\n");
+ root_nfs_close(1);
+ return -1;
+ }
+
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "NFS: Binding to listening port %d\n", port);
+#endif
+ return 0;
+}
+
+
+/*
+ * Send an RPC request and wait for the answer
+ */
+static int *root_nfs_call(int *end)
+{
+ struct file *filp;
+ struct socket *sock;
+ int dummylen;
+ static struct nfs_server s =
+ { &nfs_file, /* struct file * */
+ 0, /* struct rsock * */
+ {
+ 0, "",
+ }, /* toaddr */
+ 0, /* lock */
+ NULL, /* wait queue */
+ NFS_MOUNT_SOFT, /* flags */
+ 0, 0, /* rsize, wsize */
+ 0, /* timeo */
+ 0, /* retrans */
+ 3*HZ, 60*HZ, 30*HZ, 60*HZ, "\0" };
+
+ filp = &nfs_file;
+ sock = &((filp->f_inode)->u.socket_i);
+
+ /* extract the other end of the socket into server->toaddr */
+ sock->ops->getname(sock, &(s.toaddr), &dummylen, 1) ;
+ ((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_addr.s_addr = server.sin_addr.s_addr;
+
+ s.rsock = rpc_makesock(filp);
+ s.flags = nfs_data.flags;
+ s.rsize = nfs_data.rsize;
+ s.wsize = nfs_data.wsize;
+ s.timeo = nfs_data.timeo * HZ / 10;
+ s.retrans = nfs_data.retrans;
+ strcpy(s.hostname, nfs_data.hostname);
+
+ /* First connect the UDP socket to a server port, then send the packet
+ * out, and finally check wether the answer is OK.
+ */
+ if (nfs_inode.u.socket_i.ops->connect &&
+ nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i,
+ (struct sockaddr *) &server, sizeof(struct sockaddr_in),
+ nfs_file.f_flags) < 0)
+ return NULL;
+ if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
+ return NULL;
+ return rpc_verify(rpc_packet);
+}
+
+
+/*
+ * Create an RPC packet header
+ */
+static int *root_nfs_header(int proc, int program, int version)
+{
+ int groups[] = { 0, NOGROUP };
+
+ if (rpc_packet == NULL) {
+ if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
+ printk(KERN_ERR "NFS: Cannot allocate UDP buffer\n");
+ return NULL;
+ }
+ }
+ strcpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr));
+ return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
+}
+
+
+/*
+ * Query server portmapper for the port of a daemon program
+ */
+static int root_nfs_get_port(int program, int version)
+{
+ int *p;
+
+ /* Prepare header for portmap request */
+ server.sin_port = htons(NFS_PMAP_PORT);
+ p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
+ if (!p)
+ return -1;
+
+ /* Set arguments for portmapper */
+ *p++ = htonl(program);
+ *p++ = htonl(version);
+ *p++ = htonl(IPPROTO_UDP);
+ *p++ = 0;
+
+ /* Send request to server portmapper */
+ if ((p = root_nfs_call(p)) == NULL)
+ return -1;
+
+ return ntohl(*p);
+}
+
+
+/*
+ * Get portnumbers for mountd and nfsd from server
+ */
+static int root_nfs_ports(void)
+{
+ int port;
+
+ if (nfs_port < 0) {
+ if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
+ printk(KERN_ERR "NFS: Unable to get nfsd port number from server, using default\n");
+ port = NFS_NFS_PORT;
+ }
+ nfs_port = port;
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "NFS: Portmapper on server returned %d as nfsd port\n", port);
+#endif
+ }
+
+ if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
+ printk(KERN_ERR "NFS: Unable to get mountd port number from server, using default\n");
+ port = NFS_MOUNT_PORT;
+ }
+ server.sin_port = htons(port);
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "NFS: Portmapper on server returned %d as mountd port\n", port);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Get a file handle from the server for the directory which is to be mounted
+ */
+static int root_nfs_get_handle(void)
+{
+ int len, status, *p;
+
+ /* Prepare header for mountd request */
+ p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
+ if (!p) {
+ root_nfs_close(1);
+ return -1;
+ }
+
+ /* Set arguments for mountd */
+ len = strlen(nfs_path);
+ *p++ = htonl(len);
+ memcpy(p, nfs_path, len);
+ len = (len + 3) >> 2;
+ p[len] = 0;
+ p += len;
+
+ /* Send request to server portmapper */
+ if ((p = root_nfs_call(p)) == NULL) {
+ root_nfs_close(1);
+ return -1;
+ }
+
+ status = ntohl(*p++);
+ if (status == 0) {
+ nfs_data.root = *((struct nfs_fh *) p);
+ } else {
+ printk(KERN_ERR "NFS: Server returned error %d while mounting %s\n",
+ status, nfs_path);
+ root_nfs_close(1);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Now actually mount the given directory
+ */
+static int root_nfs_do_mount(struct super_block *sb)
+{
+ /* First connect to the nfsd port on the server */
+ server.sin_port = htons(nfs_port);
+ nfs_data.addr = server;
+ if (nfs_inode.u.socket_i.ops->connect &&
+ nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i,
+ (struct sockaddr *) &server, sizeof(struct sockaddr_in),
+ nfs_file.f_flags) < 0) {
+ root_nfs_close(1);
+ return -1;
+ }
+
+ /* Now (finally ;-)) read the super block for mounting */
+ if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
+ root_nfs_close(1);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Get the NFS port numbers and file handle, and then read the super-
+ * block for mounting.
+ */
+int nfs_root_mount(struct super_block *sb)
+{
+ if (root_nfs_open() < 0)
+ return -1;
+ if (root_nfs_bind() < 0)
+ return -1;
+ if (root_nfs_ports() < 0)
+ return -1;
+ if (root_nfs_get_handle() < 0)
+ return -1;
+ if (root_nfs_do_mount(sb) < 0)
+ return -1;
+ root_nfs_close(0);
+ return 0;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this