diff options
Diffstat (limited to 'fs/nfs/nfsroot.c')
-rw-r--r-- | fs/nfs/nfsroot.c | 182 |
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 */ |
102 | static char nfs_root_parms[256] __initdata = ""; | 104 | static char nfs_root_parms[256] __initdata = ""; |
103 | 105 | ||
106 | /* Text-based mount options passed to super.c */ | ||
107 | static char nfs_root_options[256] __initdata = ""; | ||
108 | |||
104 | /* Address of NFS server */ | 109 | /* Address of NFS server */ |
105 | static __be32 servaddr __initdata = 0; | 110 | static __be32 servaddr __initdata = 0; |
106 | 111 | ||
107 | /* Name of directory to mount */ | 112 | /* Name of directory to mount */ |
108 | static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; | 113 | static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; |
114 | |||
115 | /* server:export path string passed to super.c */ | ||
116 | static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; | ||
109 | 117 | ||
110 | /* NFS-related data */ | 118 | /* NFS-related data */ |
111 | static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ | 119 | static 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 | */ |
540 | void * __init nfs_root_data(void) | 548 | void * __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 | |||
558 | static 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 | |||
566 | static 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 | */ | ||
580 | static 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 | */ | ||
622 | static 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 | |||
678 | out: | ||
679 | kfree(tmp); | ||
680 | return retval; | ||
681 | out_nomem: | ||
682 | printk(KERN_ERR "Root-NFS: could not allocate memory\n"); | ||
683 | goto out; | ||
684 | out_optionstoolong: | ||
685 | printk(KERN_ERR "Root-NFS: mount options string too long\n"); | ||
686 | goto out; | ||
687 | out_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 | */ | ||
700 | int __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 | } | ||