Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  Copyright (C) 1995, 1996  Gero Kuhlmann <gero@gkminix.han.de>
0004  *
0005  *  Allow an NFS filesystem to be mounted as root. The way this works is:
0006  *     (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
0007  *     (2) Construct the device string and the options string using DHCP
0008  *         option 17 and/or kernel command line options.
0009  *     (3) When mount_root() sets up the root file system, pass these strings
0010  *         to the NFS client's regular mount interface via sys_mount().
0011  *
0012  *
0013  *  Changes:
0014  *
0015  *  Alan Cox    :   Removed get_address name clash with FPU.
0016  *  Alan Cox    :   Reformatted a bit.
0017  *  Gero Kuhlmann   :   Code cleanup
0018  *  Michael Rausch  :   Fixed recognition of an incoming RARP answer.
0019  *  Martin Mares    : (2.0) Auto-configuration via BOOTP supported.
0020  *  Martin Mares    :   Manual selection of interface & BOOTP/RARP.
0021  *  Martin Mares    :   Using network routes instead of host routes,
0022  *              allowing the default configuration to be used
0023  *              for normal operation of the host.
0024  *  Martin Mares    :   Randomized timer with exponential backoff
0025  *              installed to minimize network congestion.
0026  *  Martin Mares    :   Code cleanup.
0027  *  Martin Mares    : (2.1) BOOTP and RARP made configuration options.
0028  *  Martin Mares    :   Server hostname generation fixed.
0029  *  Gerd Knorr  :   Fixed wired inode handling
0030  *  Martin Mares    : (2.2) "0.0.0.0" addresses from command line ignored.
0031  *  Martin Mares    :   RARP replies not tested for server address.
0032  *  Gero Kuhlmann   : (2.3) Some bug fixes and code cleanup again (please
0033  *              send me your new patches _before_ bothering
0034  *              Linus so that I don' always have to cleanup
0035  *              _afterwards_ - thanks)
0036  *  Gero Kuhlmann   :   Last changes of Martin Mares undone.
0037  *  Gero Kuhlmann   :   RARP replies are tested for specified server
0038  *              again. However, it's now possible to have
0039  *              different RARP and NFS servers.
0040  *  Gero Kuhlmann   :   "0.0.0.0" addresses from command line are
0041  *              now mapped to INADDR_NONE.
0042  *  Gero Kuhlmann   :   Fixed a bug which prevented BOOTP path name
0043  *              from being used (thanks to Leo Spiekman)
0044  *  Andy Walker :   Allow to specify the NFS server in nfs_root
0045  *              without giving a path name
0046  *  Swen Thümmler  :   Allow to specify the NFS options in nfs_root
0047  *              without giving a path name. Fix BOOTP request
0048  *              for domainname (domainname is NIS domain, not
0049  *              DNS domain!). Skip dummy devices for BOOTP.
0050  *  Jacek Zapala    :   Fixed a bug which prevented server-ip address
0051  *              from nfsroot parameter from being used.
0052  *  Olaf Kirch  :   Adapted to new NFS code.
0053  *  Jakub Jelinek   :   Free used code segment.
0054  *  Marko Kohtala   :   Fixed some bugs.
0055  *  Martin Mares    :   Debug message cleanup
0056  *  Martin Mares    :   Changed to use the new generic IP layer autoconfig
0057  *              code. BOOTP and RARP moved there.
0058  *  Martin Mares    :   Default path now contains host name instead of
0059  *              host IP address (but host name defaults to IP
0060  *              address anyway).
0061  *  Martin Mares    :   Use root_server_addr appropriately during setup.
0062  *  Martin Mares    :   Rewrote parameter parsing, now hopefully giving
0063  *              correct overriding.
0064  *  Trond Myklebust :   Add in preliminary support for NFSv3 and TCP.
0065  *              Fix bug in root_nfs_addr(). nfs_data.namlen
0066  *              is NOT for the length of the hostname.
0067  *  Hua Qin     :   Support for mounting root file system via
0068  *              NFS over TCP.
0069  *  Fabian Frederick:   Option parser rebuilt (using parser lib)
0070  *  Chuck Lever :   Use super.c's text-based mount option parsing
0071  *  Chuck Lever :   Add "nfsrootdebug".
0072  */
0073 
0074 #include <linux/types.h>
0075 #include <linux/string.h>
0076 #include <linux/init.h>
0077 #include <linux/nfs.h>
0078 #include <linux/nfs_fs.h>
0079 #include <linux/utsname.h>
0080 #include <linux/root_dev.h>
0081 #include <net/ipconfig.h>
0082 
0083 #include "internal.h"
0084 
0085 #define NFSDBG_FACILITY NFSDBG_ROOT
0086 
0087 /* Default path we try to mount. "%s" gets replaced by our IP address */
0088 #define NFS_ROOT        "/tftpboot/%s"
0089 
0090 /* Default NFSROOT mount options. */
0091 #if defined(CONFIG_NFS_V2)
0092 #define NFS_DEF_OPTIONS     "vers=2,tcp,rsize=4096,wsize=4096"
0093 #elif defined(CONFIG_NFS_V3)
0094 #define NFS_DEF_OPTIONS     "vers=3,tcp,rsize=4096,wsize=4096"
0095 #else
0096 #define NFS_DEF_OPTIONS     "vers=4,tcp,rsize=4096,wsize=4096"
0097 #endif
0098 
0099 /* Parameters passed from the kernel command line */
0100 static char nfs_root_parms[NFS_MAXPATHLEN + 1] __initdata = "";
0101 
0102 /* Text-based mount options passed to super.c */
0103 static char nfs_root_options[256] __initdata = NFS_DEF_OPTIONS;
0104 
0105 /* Address of NFS server */
0106 static __be32 servaddr __initdata = htonl(INADDR_NONE);
0107 
0108 /* Name of directory to mount */
0109 static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = "";
0110 
0111 /* server:export path string passed to super.c */
0112 static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
0113 
0114 #ifdef NFS_DEBUG
0115 /*
0116  * When the "nfsrootdebug" kernel command line option is specified,
0117  * enable debugging messages for NFSROOT.
0118  */
0119 static int __init nfs_root_debug(char *__unused)
0120 {
0121     nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
0122     return 1;
0123 }
0124 
0125 __setup("nfsrootdebug", nfs_root_debug);
0126 #endif
0127 
0128 /*
0129  *  Parse NFS server and directory information passed on the kernel
0130  *  command line.
0131  *
0132  *  nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
0133  *
0134  *  If there is a "%s" token in the <root-dir> string, it is replaced
0135  *  by the ASCII-representation of the client's IP address.
0136  */
0137 static int __init nfs_root_setup(char *line)
0138 {
0139     ROOT_DEV = Root_NFS;
0140 
0141     if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
0142         strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms));
0143     } else {
0144         size_t n = strlen(line) + sizeof(NFS_ROOT) - 1;
0145         if (n >= sizeof(nfs_root_parms))
0146             line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0';
0147         sprintf(nfs_root_parms, NFS_ROOT, line);
0148     }
0149 
0150     /*
0151      * Extract the IP address of the NFS server containing our
0152      * root file system, if one was specified.
0153      *
0154      * Note: root_nfs_parse_addr() removes the server-ip from
0155      *   nfs_root_parms, if it exists.
0156      */
0157     root_server_addr = root_nfs_parse_addr(nfs_root_parms);
0158 
0159     return 1;
0160 }
0161 
0162 __setup("nfsroot=", nfs_root_setup);
0163 
0164 static int __init root_nfs_copy(char *dest, const char *src,
0165                      const size_t destlen)
0166 {
0167     if (strlcpy(dest, src, destlen) > destlen)
0168         return -1;
0169     return 0;
0170 }
0171 
0172 static int __init root_nfs_cat(char *dest, const char *src,
0173                    const size_t destlen)
0174 {
0175     size_t len = strlen(dest);
0176 
0177     if (len && dest[len - 1] != ',')
0178         if (strlcat(dest, ",", destlen) > destlen)
0179             return -1;
0180 
0181     if (strlcat(dest, src, destlen) > destlen)
0182         return -1;
0183     return 0;
0184 }
0185 
0186 /*
0187  * Parse out root export path and mount options from
0188  * passed-in string @incoming.
0189  *
0190  * Copy the export path into @exppath.
0191  */
0192 static int __init root_nfs_parse_options(char *incoming, char *exppath,
0193                      const size_t exppathlen)
0194 {
0195     char *p;
0196 
0197     /*
0198      * Set the NFS remote path
0199      */
0200     p = strsep(&incoming, ",");
0201     if (*p != '\0' && strcmp(p, "default") != 0)
0202         if (root_nfs_copy(exppath, p, exppathlen))
0203             return -1;
0204 
0205     /*
0206      * @incoming now points to the rest of the string; if it
0207      * contains something, append it to our root options buffer
0208      */
0209     if (incoming != NULL && *incoming != '\0')
0210         if (root_nfs_cat(nfs_root_options, incoming,
0211                         sizeof(nfs_root_options)))
0212             return -1;
0213     return 0;
0214 }
0215 
0216 /*
0217  *  Decode the export directory path name and NFS options from
0218  *  the kernel command line.  This has to be done late in order to
0219  *  use a dynamically acquired client IP address for the remote
0220  *  root directory path.
0221  *
0222  *  Returns zero if successful; otherwise -1 is returned.
0223  */
0224 static int __init root_nfs_data(char *cmdline)
0225 {
0226     char mand_options[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
0227     int len, retval = -1;
0228     char *tmp = NULL;
0229     const size_t tmplen = sizeof(nfs_export_path);
0230 
0231     tmp = kzalloc(tmplen, GFP_KERNEL);
0232     if (tmp == NULL)
0233         goto out_nomem;
0234     strcpy(tmp, NFS_ROOT);
0235 
0236     if (root_server_path[0] != '\0') {
0237         dprintk("Root-NFS: DHCPv4 option 17: %s\n",
0238             root_server_path);
0239         if (root_nfs_parse_options(root_server_path, tmp, tmplen))
0240             goto out_optionstoolong;
0241     }
0242 
0243     if (cmdline[0] != '\0') {
0244         dprintk("Root-NFS: nfsroot=%s\n", cmdline);
0245         if (root_nfs_parse_options(cmdline, tmp, tmplen))
0246             goto out_optionstoolong;
0247     }
0248 
0249     /*
0250      * Append mandatory options for nfsroot so they override
0251      * what has come before
0252      */
0253     snprintf(mand_options, sizeof(mand_options), "nolock,addr=%pI4",
0254             &servaddr);
0255     if (root_nfs_cat(nfs_root_options, mand_options,
0256                         sizeof(nfs_root_options)))
0257         goto out_optionstoolong;
0258 
0259     /*
0260      * Set up nfs_root_device.  For NFS mounts, this looks like
0261      *
0262      *  server:/path
0263      *
0264      * At this point, utsname()->nodename contains our local
0265      * IP address or hostname, set by ipconfig.  If "%s" exists
0266      * in tmp, substitute the nodename, then shovel the whole
0267      * mess into nfs_root_device.
0268      */
0269     len = snprintf(nfs_export_path, sizeof(nfs_export_path),
0270                 tmp, utsname()->nodename);
0271     if (len >= (int)sizeof(nfs_export_path))
0272         goto out_devnametoolong;
0273     len = snprintf(nfs_root_device, sizeof(nfs_root_device),
0274                 "%pI4:%s", &servaddr, nfs_export_path);
0275     if (len >= (int)sizeof(nfs_root_device))
0276         goto out_devnametoolong;
0277 
0278     retval = 0;
0279 
0280 out:
0281     kfree(tmp);
0282     return retval;
0283 out_nomem:
0284     printk(KERN_ERR "Root-NFS: could not allocate memory\n");
0285     goto out;
0286 out_optionstoolong:
0287     printk(KERN_ERR "Root-NFS: mount options string too long\n");
0288     goto out;
0289 out_devnametoolong:
0290     printk(KERN_ERR "Root-NFS: root device name too long.\n");
0291     goto out;
0292 }
0293 
0294 /**
0295  * nfs_root_data - Return prepared 'data' for NFSROOT mount
0296  * @root_device: OUT: address of string containing NFSROOT device
0297  * @root_data: OUT: address of string containing NFSROOT mount options
0298  *
0299  * Returns zero and sets @root_device and @root_data if successful,
0300  * otherwise -1 is returned.
0301  */
0302 int __init nfs_root_data(char **root_device, char **root_data)
0303 {
0304     servaddr = root_server_addr;
0305     if (servaddr == htonl(INADDR_NONE)) {
0306         printk(KERN_ERR "Root-NFS: no NFS server address\n");
0307         return -1;
0308     }
0309 
0310     if (root_nfs_data(nfs_root_parms) < 0)
0311         return -1;
0312 
0313     *root_device = nfs_root_device;
0314     *root_data = nfs_root_options;
0315     return 0;
0316 }