root/fs/nfs/nfsroot.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nfs_root_debug
  2. nfs_root_setup
  3. root_nfs_copy
  4. root_nfs_cat
  5. root_nfs_parse_options
  6. root_nfs_data
  7. nfs_root_data

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *  Copyright (C) 1995, 1996  Gero Kuhlmann <gero@gkminix.han.de>
   4  *
   5  *  Allow an NFS filesystem to be mounted as root. The way this works is:
   6  *     (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
   7  *     (2) Construct the device string and the options string using DHCP
   8  *         option 17 and/or kernel command line options.
   9  *     (3) When mount_root() sets up the root file system, pass these strings
  10  *         to the NFS client's regular mount interface via sys_mount().
  11  *
  12  *
  13  *      Changes:
  14  *
  15  *      Alan Cox        :       Removed get_address name clash with FPU.
  16  *      Alan Cox        :       Reformatted a bit.
  17  *      Gero Kuhlmann   :       Code cleanup
  18  *      Michael Rausch  :       Fixed recognition of an incoming RARP answer.
  19  *      Martin Mares    : (2.0) Auto-configuration via BOOTP supported.
  20  *      Martin Mares    :       Manual selection of interface & BOOTP/RARP.
  21  *      Martin Mares    :       Using network routes instead of host routes,
  22  *                              allowing the default configuration to be used
  23  *                              for normal operation of the host.
  24  *      Martin Mares    :       Randomized timer with exponential backoff
  25  *                              installed to minimize network congestion.
  26  *      Martin Mares    :       Code cleanup.
  27  *      Martin Mares    : (2.1) BOOTP and RARP made configuration options.
  28  *      Martin Mares    :       Server hostname generation fixed.
  29  *      Gerd Knorr      :       Fixed wired inode handling
  30  *      Martin Mares    : (2.2) "0.0.0.0" addresses from command line ignored.
  31  *      Martin Mares    :       RARP replies not tested for server address.
  32  *      Gero Kuhlmann   : (2.3) Some bug fixes and code cleanup again (please
  33  *                              send me your new patches _before_ bothering
  34  *                              Linus so that I don' always have to cleanup
  35  *                              _afterwards_ - thanks)
  36  *      Gero Kuhlmann   :       Last changes of Martin Mares undone.
  37  *      Gero Kuhlmann   :       RARP replies are tested for specified server
  38  *                              again. However, it's now possible to have
  39  *                              different RARP and NFS servers.
  40  *      Gero Kuhlmann   :       "0.0.0.0" addresses from command line are
  41  *                              now mapped to INADDR_NONE.
  42  *      Gero Kuhlmann   :       Fixed a bug which prevented BOOTP path name
  43  *                              from being used (thanks to Leo Spiekman)
  44  *      Andy Walker     :       Allow to specify the NFS server in nfs_root
  45  *                              without giving a path name
  46  *      Swen Thümmler  :       Allow to specify the NFS options in nfs_root
  47  *                              without giving a path name. Fix BOOTP request
  48  *                              for domainname (domainname is NIS domain, not
  49  *                              DNS domain!). Skip dummy devices for BOOTP.
  50  *      Jacek Zapala    :       Fixed a bug which prevented server-ip address
  51  *                              from nfsroot parameter from being used.
  52  *      Olaf Kirch      :       Adapted to new NFS code.
  53  *      Jakub Jelinek   :       Free used code segment.
  54  *      Marko Kohtala   :       Fixed some bugs.
  55  *      Martin Mares    :       Debug message cleanup
  56  *      Martin Mares    :       Changed to use the new generic IP layer autoconfig
  57  *                              code. BOOTP and RARP moved there.
  58  *      Martin Mares    :       Default path now contains host name instead of
  59  *                              host IP address (but host name defaults to IP
  60  *                              address anyway).
  61  *      Martin Mares    :       Use root_server_addr appropriately during setup.
  62  *      Martin Mares    :       Rewrote parameter parsing, now hopefully giving
  63  *                              correct overriding.
  64  *      Trond Myklebust :       Add in preliminary support for NFSv3 and TCP.
  65  *                              Fix bug in root_nfs_addr(). nfs_data.namlen
  66  *                              is NOT for the length of the hostname.
  67  *      Hua Qin         :       Support for mounting root file system via
  68  *                              NFS over TCP.
  69  *      Fabian Frederick:       Option parser rebuilt (using parser lib)
  70  *      Chuck Lever     :       Use super.c's text-based mount option parsing
  71  *      Chuck Lever     :       Add "nfsrootdebug".
  72  */
  73 
  74 #include <linux/types.h>
  75 #include <linux/string.h>
  76 #include <linux/init.h>
  77 #include <linux/nfs.h>
  78 #include <linux/nfs_fs.h>
  79 #include <linux/utsname.h>
  80 #include <linux/root_dev.h>
  81 #include <net/ipconfig.h>
  82 
  83 #include "internal.h"
  84 
  85 #define NFSDBG_FACILITY NFSDBG_ROOT
  86 
  87 /* Default path we try to mount. "%s" gets replaced by our IP address */
  88 #define NFS_ROOT                "/tftpboot/%s"
  89 
  90 /* Default NFSROOT mount options. */
  91 #define NFS_DEF_OPTIONS         "vers=2,udp,rsize=4096,wsize=4096"
  92 
  93 /* Parameters passed from the kernel command line */
  94 static char nfs_root_parms[NFS_MAXPATHLEN + 1] __initdata = "";
  95 
  96 /* Text-based mount options passed to super.c */
  97 static char nfs_root_options[256] __initdata = NFS_DEF_OPTIONS;
  98 
  99 /* Address of NFS server */
 100 static __be32 servaddr __initdata = htonl(INADDR_NONE);
 101 
 102 /* Name of directory to mount */
 103 static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = "";
 104 
 105 /* server:export path string passed to super.c */
 106 static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
 107 
 108 #ifdef NFS_DEBUG
 109 /*
 110  * When the "nfsrootdebug" kernel command line option is specified,
 111  * enable debugging messages for NFSROOT.
 112  */
 113 static int __init nfs_root_debug(char *__unused)
 114 {
 115         nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
 116         return 1;
 117 }
 118 
 119 __setup("nfsrootdebug", nfs_root_debug);
 120 #endif
 121 
 122 /*
 123  *  Parse NFS server and directory information passed on the kernel
 124  *  command line.
 125  *
 126  *  nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
 127  *
 128  *  If there is a "%s" token in the <root-dir> string, it is replaced
 129  *  by the ASCII-representation of the client's IP address.
 130  */
 131 static int __init nfs_root_setup(char *line)
 132 {
 133         ROOT_DEV = Root_NFS;
 134 
 135         if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
 136                 strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms));
 137         } else {
 138                 size_t n = strlen(line) + sizeof(NFS_ROOT) - 1;
 139                 if (n >= sizeof(nfs_root_parms))
 140                         line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0';
 141                 sprintf(nfs_root_parms, NFS_ROOT, line);
 142         }
 143 
 144         /*
 145          * Extract the IP address of the NFS server containing our
 146          * root file system, if one was specified.
 147          *
 148          * Note: root_nfs_parse_addr() removes the server-ip from
 149          *       nfs_root_parms, if it exists.
 150          */
 151         root_server_addr = root_nfs_parse_addr(nfs_root_parms);
 152 
 153         return 1;
 154 }
 155 
 156 __setup("nfsroot=", nfs_root_setup);
 157 
 158 static int __init root_nfs_copy(char *dest, const char *src,
 159                                      const size_t destlen)
 160 {
 161         if (strlcpy(dest, src, destlen) > destlen)
 162                 return -1;
 163         return 0;
 164 }
 165 
 166 static int __init root_nfs_cat(char *dest, const char *src,
 167                                const size_t destlen)
 168 {
 169         size_t len = strlen(dest);
 170 
 171         if (len && dest[len - 1] != ',')
 172                 if (strlcat(dest, ",", destlen) > destlen)
 173                         return -1;
 174 
 175         if (strlcat(dest, src, destlen) > destlen)
 176                 return -1;
 177         return 0;
 178 }
 179 
 180 /*
 181  * Parse out root export path and mount options from
 182  * passed-in string @incoming.
 183  *
 184  * Copy the export path into @exppath.
 185  */
 186 static int __init root_nfs_parse_options(char *incoming, char *exppath,
 187                                          const size_t exppathlen)
 188 {
 189         char *p;
 190 
 191         /*
 192          * Set the NFS remote path
 193          */
 194         p = strsep(&incoming, ",");
 195         if (*p != '\0' && strcmp(p, "default") != 0)
 196                 if (root_nfs_copy(exppath, p, exppathlen))
 197                         return -1;
 198 
 199         /*
 200          * @incoming now points to the rest of the string; if it
 201          * contains something, append it to our root options buffer
 202          */
 203         if (incoming != NULL && *incoming != '\0')
 204                 if (root_nfs_cat(nfs_root_options, incoming,
 205                                                 sizeof(nfs_root_options)))
 206                         return -1;
 207         return 0;
 208 }
 209 
 210 /*
 211  *  Decode the export directory path name and NFS options from
 212  *  the kernel command line.  This has to be done late in order to
 213  *  use a dynamically acquired client IP address for the remote
 214  *  root directory path.
 215  *
 216  *  Returns zero if successful; otherwise -1 is returned.
 217  */
 218 static int __init root_nfs_data(char *cmdline)
 219 {
 220         char mand_options[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
 221         int len, retval = -1;
 222         char *tmp = NULL;
 223         const size_t tmplen = sizeof(nfs_export_path);
 224 
 225         tmp = kzalloc(tmplen, GFP_KERNEL);
 226         if (tmp == NULL)
 227                 goto out_nomem;
 228         strcpy(tmp, NFS_ROOT);
 229 
 230         if (root_server_path[0] != '\0') {
 231                 dprintk("Root-NFS: DHCPv4 option 17: %s\n",
 232                         root_server_path);
 233                 if (root_nfs_parse_options(root_server_path, tmp, tmplen))
 234                         goto out_optionstoolong;
 235         }
 236 
 237         if (cmdline[0] != '\0') {
 238                 dprintk("Root-NFS: nfsroot=%s\n", cmdline);
 239                 if (root_nfs_parse_options(cmdline, tmp, tmplen))
 240                         goto out_optionstoolong;
 241         }
 242 
 243         /*
 244          * Append mandatory options for nfsroot so they override
 245          * what has come before
 246          */
 247         snprintf(mand_options, sizeof(mand_options), "nolock,addr=%pI4",
 248                         &servaddr);
 249         if (root_nfs_cat(nfs_root_options, mand_options,
 250                                                 sizeof(nfs_root_options)))
 251                 goto out_optionstoolong;
 252 
 253         /*
 254          * Set up nfs_root_device.  For NFS mounts, this looks like
 255          *
 256          *      server:/path
 257          *
 258          * At this point, utsname()->nodename contains our local
 259          * IP address or hostname, set by ipconfig.  If "%s" exists
 260          * in tmp, substitute the nodename, then shovel the whole
 261          * mess into nfs_root_device.
 262          */
 263         len = snprintf(nfs_export_path, sizeof(nfs_export_path),
 264                                 tmp, utsname()->nodename);
 265         if (len >= (int)sizeof(nfs_export_path))
 266                 goto out_devnametoolong;
 267         len = snprintf(nfs_root_device, sizeof(nfs_root_device),
 268                                 "%pI4:%s", &servaddr, nfs_export_path);
 269         if (len >= (int)sizeof(nfs_root_device))
 270                 goto out_devnametoolong;
 271 
 272         retval = 0;
 273 
 274 out:
 275         kfree(tmp);
 276         return retval;
 277 out_nomem:
 278         printk(KERN_ERR "Root-NFS: could not allocate memory\n");
 279         goto out;
 280 out_optionstoolong:
 281         printk(KERN_ERR "Root-NFS: mount options string too long\n");
 282         goto out;
 283 out_devnametoolong:
 284         printk(KERN_ERR "Root-NFS: root device name too long.\n");
 285         goto out;
 286 }
 287 
 288 /**
 289  * nfs_root_data - Return prepared 'data' for NFSROOT mount
 290  * @root_device: OUT: address of string containing NFSROOT device
 291  * @root_data: OUT: address of string containing NFSROOT mount options
 292  *
 293  * Returns zero and sets @root_device and @root_data if successful,
 294  * otherwise -1 is returned.
 295  */
 296 int __init nfs_root_data(char **root_device, char **root_data)
 297 {
 298         servaddr = root_server_addr;
 299         if (servaddr == htonl(INADDR_NONE)) {
 300                 printk(KERN_ERR "Root-NFS: no NFS server address\n");
 301                 return -1;
 302         }
 303 
 304         if (root_nfs_data(nfs_root_parms) < 0)
 305                 return -1;
 306 
 307         *root_device = nfs_root_device;
 308         *root_data = nfs_root_options;
 309         return 0;
 310 }

/* [<][>][^][v][top][bottom][index][help] */