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 }