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

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