aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfsroot.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/nfsroot.c')
-rw-r--r--fs/nfs/nfsroot.c182
1 files changed, 176 insertions, 6 deletions
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index 169b67907a65..cb4a6bdca871 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -3,9 +3,10 @@
3 * 3 *
4 * Allow an NFS filesystem to be mounted as root. The way this works is: 4 * Allow an NFS filesystem to be mounted as root. The way this works is:
5 * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. 5 * (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
6 * (2) Handle RPC negotiation with the system which replied to RARP or 6 * (2) Construct the device string and the options string using DHCP
7 * was reported as a boot server by BOOTP or manually. 7 * option 17 and/or kernel command line options.
8 * (3) The actual mounting is done later, when init() is running. 8 * (3) When mount_root() sets up the root file system, pass these strings
9 * to the NFS client's regular mount interface via sys_mount().
9 * 10 *
10 * 11 *
11 * Changes: 12 * Changes:
@@ -65,7 +66,8 @@
65 * Hua Qin : Support for mounting root file system via 66 * Hua Qin : Support for mounting root file system via
66 * NFS over TCP. 67 * NFS over TCP.
67 * Fabian Frederick: Option parser rebuilt (using parser lib) 68 * Fabian Frederick: Option parser rebuilt (using parser lib)
68*/ 69 * Chuck Lever : Use super.c's text-based mount option parsing
70 */
69 71
70#include <linux/types.h> 72#include <linux/types.h>
71#include <linux/string.h> 73#include <linux/string.h>
@@ -101,11 +103,17 @@
101/* Parameters passed from the kernel command line */ 103/* Parameters passed from the kernel command line */
102static char nfs_root_parms[256] __initdata = ""; 104static char nfs_root_parms[256] __initdata = "";
103 105
106/* Text-based mount options passed to super.c */
107static char nfs_root_options[256] __initdata = "";
108
104/* Address of NFS server */ 109/* Address of NFS server */
105static __be32 servaddr __initdata = 0; 110static __be32 servaddr __initdata = 0;
106 111
107/* Name of directory to mount */ 112/* Name of directory to mount */
108static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; 113static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = "";
114
115/* server:export path string passed to super.c */
116static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
109 117
110/* NFS-related data */ 118/* NFS-related data */
111static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ 119static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
@@ -537,7 +545,7 @@ out:
537 * Get the NFS port numbers and file handle, and return the prepared 'data' 545 * Get the NFS port numbers and file handle, and return the prepared 'data'
538 * argument for mount() if everything went OK. Return NULL otherwise. 546 * argument for mount() if everything went OK. Return NULL otherwise.
539 */ 547 */
540void * __init nfs_root_data(void) 548void * __init old_nfs_root_data(void)
541{ 549{
542 if (root_nfs_init() < 0 550 if (root_nfs_init() < 0
543 || root_nfs_ports() < 0 551 || root_nfs_ports() < 0
@@ -546,3 +554,165 @@ void * __init nfs_root_data(void)
546 set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, htons(nfs_port)); 554 set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, htons(nfs_port));
547 return (void*)&nfs_data; 555 return (void*)&nfs_data;
548} 556}
557
558static int __init root_nfs_copy(char *dest, const char *src,
559 const size_t destlen)
560{
561 if (strlcpy(dest, src, destlen) > destlen)
562 return -1;
563 return 0;
564}
565
566static int __init root_nfs_cat(char *dest, const char *src,
567 const size_t destlen)
568{
569 if (strlcat(dest, src, destlen) > destlen)
570 return -1;
571 return 0;
572}
573
574/*
575 * Parse out root export path and mount options from
576 * passed-in string @incoming.
577 *
578 * Copy the export path into @exppath.
579 */
580static int __init root_nfs_parse_options(char *incoming, char *exppath,
581 const size_t exppathlen)
582{
583 char *p;
584
585 /*
586 * Set the NFS remote path
587 */
588 p = strsep(&incoming, ",");
589 if (*p != '\0' && strcmp(p, "default") != 0)
590 if (root_nfs_copy(exppath, p, exppathlen))
591 return -1;
592
593 /*
594 * @incoming now points to the rest of the string; if it
595 * contains something, append it to our root options buffer
596 */
597 if (incoming != NULL && *incoming != '\0')
598 if (root_nfs_cat(nfs_root_options, incoming,
599 sizeof(nfs_root_options)))
600 return -1;
601
602 /*
603 * Possibly prepare for more options to be appended
604 */
605 if (nfs_root_options[0] != '\0' &&
606 nfs_root_options[strlen(nfs_root_options)] != ',')
607 if (root_nfs_cat(nfs_root_options, ",",
608 sizeof(nfs_root_options)))
609 return -1;
610
611 return 0;
612}
613
614/*
615 * Decode the export directory path name and NFS options from
616 * the kernel command line. This has to be done late in order to
617 * use a dynamically acquired client IP address for the remote
618 * root directory path.
619 *
620 * Returns zero if successful; otherwise -1 is returned.
621 */
622static int __init root_nfs_data(char *cmdline)
623{
624 char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
625 int len, retval = -1;
626 char *tmp = NULL;
627 const size_t tmplen = sizeof(nfs_export_path);
628
629 tmp = kzalloc(tmplen, GFP_KERNEL);
630 if (tmp == NULL)
631 goto out_nomem;
632 strcpy(tmp, NFS_ROOT);
633
634 if (root_server_path[0] != '\0') {
635 dprintk("Root-NFS: DHCPv4 option 17: %s\n",
636 root_server_path);
637 if (root_nfs_parse_options(root_server_path, tmp, tmplen))
638 goto out_optionstoolong;
639 }
640
641 if (cmdline[0] != '\0') {
642 dprintk("Root-NFS: nfsroot=%s\n", cmdline);
643 if (root_nfs_parse_options(cmdline, tmp, tmplen))
644 goto out_optionstoolong;
645 }
646
647 /*
648 * Append mandatory options for nfsroot so they override
649 * what has come before
650 */
651 snprintf(addr_option, sizeof(addr_option), "nolock,addr=%pI4",
652 &servaddr);
653 if (root_nfs_cat(nfs_root_options, addr_option,
654 sizeof(nfs_root_options)))
655 goto out_optionstoolong;
656
657 /*
658 * Set up nfs_root_device. For NFS mounts, this looks like
659 *
660 * server:/path
661 *
662 * At this point, utsname()->nodename contains our local
663 * IP address or hostname, set by ipconfig. If "%s" exists
664 * in tmp, substitute the nodename, then shovel the whole
665 * mess into nfs_root_device.
666 */
667 len = snprintf(nfs_export_path, sizeof(nfs_export_path),
668 tmp, utsname()->nodename);
669 if (len > (int)sizeof(nfs_export_path))
670 goto out_devnametoolong;
671 len = snprintf(nfs_root_device, sizeof(nfs_root_device),
672 "%pI4:%s", &servaddr, nfs_export_path);
673 if (len > (int)sizeof(nfs_root_device))
674 goto out_devnametoolong;
675
676 retval = 0;
677
678out:
679 kfree(tmp);
680 return retval;
681out_nomem:
682 printk(KERN_ERR "Root-NFS: could not allocate memory\n");
683 goto out;
684out_optionstoolong:
685 printk(KERN_ERR "Root-NFS: mount options string too long\n");
686 goto out;
687out_devnametoolong:
688 printk(KERN_ERR "Root-NFS: root device name too long.\n");
689 goto out;
690}
691
692/**
693 * nfs_root_data - Return prepared 'data' for NFSROOT mount
694 * @root_device: OUT: address of string containing NFSROOT device
695 * @root_data: OUT: address of string containing NFSROOT mount options
696 *
697 * Returns zero and sets @root_device and @root_data if successful,
698 * otherwise -1 is returned.
699 */
700int __init nfs_root_data(char **root_device, char **root_data)
701{
702#ifdef NFSROOT_DEBUG
703 nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
704#endif /* NFSROOT_DEBUG */
705
706 servaddr = root_server_addr;
707 if (servaddr == htonl(INADDR_NONE)) {
708 printk(KERN_ERR "Root-NFS: no NFS server address\n");
709 return -1;
710 }
711
712 if (root_nfs_data(nfs_root_parms) < 0)
713 return -1;
714
715 *root_device = nfs_root_device;
716 *root_data = nfs_root_options;
717 return 0;
718}