Branch data Line data Source code
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 : 0 : static int __init nfs_root_setup(char *line) 132 : : { 133 : 0 : ROOT_DEV = Root_NFS; 134 : : 135 : 0 : if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { 136 : 0 : strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms)); 137 : : } else { 138 : 0 : size_t n = strlen(line) + sizeof(NFS_ROOT) - 1; 139 : 0 : if (n >= sizeof(nfs_root_parms)) 140 : 0 : line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0'; 141 : 0 : 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 : 0 : root_server_addr = root_nfs_parse_addr(nfs_root_parms); 152 : : 153 : 0 : return 1; 154 : : } 155 : : 156 : : __setup("nfsroot=", nfs_root_setup); 157 : : 158 : 0 : static int __init root_nfs_copy(char *dest, const char *src, 159 : : const size_t destlen) 160 : : { 161 : 0 : if (strlcpy(dest, src, destlen) > destlen) 162 : : return -1; 163 : 0 : return 0; 164 : : } 165 : : 166 : 0 : static int __init root_nfs_cat(char *dest, const char *src, 167 : : const size_t destlen) 168 : : { 169 : 0 : size_t len = strlen(dest); 170 : : 171 : 0 : if (len && dest[len - 1] != ',') 172 : 0 : if (strlcat(dest, ",", destlen) > destlen) 173 : : return -1; 174 : : 175 : 0 : if (strlcat(dest, src, destlen) > destlen) 176 : : return -1; 177 : 0 : 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 : 0 : 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 : 0 : p = strsep(&incoming, ","); 195 : 0 : if (*p != '\0' && strcmp(p, "default") != 0) 196 : 0 : 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 : 0 : if (incoming != NULL && *incoming != '\0') 204 : 0 : 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 : 0 : 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 : 0 : tmp = kzalloc(tmplen, GFP_KERNEL); 226 : 0 : if (tmp == NULL) 227 : : goto out_nomem; 228 : 0 : strcpy(tmp, NFS_ROOT); 229 : : 230 : 0 : if (root_server_path[0] != '\0') { 231 : : dprintk("Root-NFS: DHCPv4 option 17: %s\n", 232 : : root_server_path); 233 : 0 : if (root_nfs_parse_options(root_server_path, tmp, tmplen)) 234 : : goto out_optionstoolong; 235 : : } 236 : : 237 : 0 : if (cmdline[0] != '\0') { 238 : : dprintk("Root-NFS: nfsroot=%s\n", cmdline); 239 : 0 : 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 : 0 : snprintf(mand_options, sizeof(mand_options), "nolock,addr=%pI4", 248 : : &servaddr); 249 : 0 : 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 : 0 : len = snprintf(nfs_export_path, sizeof(nfs_export_path), 264 : 0 : tmp, utsname()->nodename); 265 : 0 : if (len >= (int)sizeof(nfs_export_path)) 266 : : goto out_devnametoolong; 267 : 0 : len = snprintf(nfs_root_device, sizeof(nfs_root_device), 268 : : "%pI4:%s", &servaddr, nfs_export_path); 269 : 0 : if (len >= (int)sizeof(nfs_root_device)) 270 : : goto out_devnametoolong; 271 : : 272 : : retval = 0; 273 : : 274 : : out: 275 : 0 : kfree(tmp); 276 : 0 : return retval; 277 : : out_nomem: 278 : 0 : printk(KERN_ERR "Root-NFS: could not allocate memory\n"); 279 : 0 : goto out; 280 : : out_optionstoolong: 281 : 0 : printk(KERN_ERR "Root-NFS: mount options string too long\n"); 282 : 0 : goto out; 283 : : out_devnametoolong: 284 : 0 : printk(KERN_ERR "Root-NFS: root device name too long.\n"); 285 : 0 : 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 : 0 : int __init nfs_root_data(char **root_device, char **root_data) 297 : : { 298 : 0 : servaddr = root_server_addr; 299 : 0 : if (servaddr == htonl(INADDR_NONE)) { 300 : 0 : printk(KERN_ERR "Root-NFS: no NFS server address\n"); 301 : 0 : return -1; 302 : : } 303 : : 304 : 0 : if (root_nfs_data(nfs_root_parms) < 0) 305 : : return -1; 306 : : 307 : 0 : *root_device = nfs_root_device; 308 : 0 : *root_data = nfs_root_options; 309 : 0 : return 0; 310 : : }