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