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 /* Parameters passed from the kernel command line */ 90 static char nfs_root_parms[256] __initdata = ""; 91 92 /* Text-based mount options passed to super.c */ 93 static char nfs_root_options[256] __initdata = ""; 94 95 /* Address of NFS server */ 96 static __be32 servaddr __initdata = htonl(INADDR_NONE); 97 98 /* Name of directory to mount */ 99 static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; 100 101 /* server:export path string passed to super.c */ 102 static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; 103 104 #ifdef RPC_DEBUG 105 /* 106 * When the "nfsrootdebug" kernel command line option is specified, 107 * enable debugging messages for NFSROOT. 108 */ 109 static int __init nfs_root_debug(char *__unused) 110 { 111 nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; 112 return 1; 113 } 114 115 __setup("nfsrootdebug", nfs_root_debug); 116 #endif 117 118 /* 119 * Parse NFS server and directory information passed on the kernel 120 * command line. 121 * 122 * nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] 123 * 124 * If there is a "%s" token in the <root-dir> string, it is replaced 125 * by the ASCII-representation of the client's IP address. 126 */ 127 static int __init nfs_root_setup(char *line) 128 { 129 ROOT_DEV = Root_NFS; 130 131 if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { 132 strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms)); 133 } else { 134 size_t n = strlen(line) + sizeof(NFS_ROOT) - 1; 135 if (n >= sizeof(nfs_root_parms)) 136 line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0'; 137 sprintf(nfs_root_parms, NFS_ROOT, line); 138 } 139 140 /* 141 * Extract the IP address of the NFS server containing our 142 * root file system, if one was specified. 143 * 144 * Note: root_nfs_parse_addr() removes the server-ip from 145 * nfs_root_parms, if it exists. 146 */ 147 root_server_addr = root_nfs_parse_addr(nfs_root_parms); 148 149 return 1; 150 } 151 152 __setup("nfsroot=", nfs_root_setup); 153 154 static int __init root_nfs_copy(char *dest, const char *src, 155 const size_t destlen) 156 { 157 if (strlcpy(dest, src, destlen) > destlen) 158 return -1; 159 return 0; 160 } 161 162 static int __init root_nfs_cat(char *dest, const char *src, 163 const size_t destlen) 164 { 165 if (strlcat(dest, src, destlen) > destlen) 166 return -1; 167 return 0; 168 } 169 170 /* 171 * Parse out root export path and mount options from 172 * passed-in string @incoming. 173 * 174 * Copy the export path into @exppath. 175 */ 176 static int __init root_nfs_parse_options(char *incoming, char *exppath, 177 const size_t exppathlen) 178 { 179 char *p; 180 181 /* 182 * Set the NFS remote path 183 */ 184 p = strsep(&incoming, ","); 185 if (*p != '\0' && strcmp(p, "default") != 0) 186 if (root_nfs_copy(exppath, p, exppathlen)) 187 return -1; 188 189 /* 190 * @incoming now points to the rest of the string; if it 191 * contains something, append it to our root options buffer 192 */ 193 if (incoming != NULL && *incoming != '\0') 194 if (root_nfs_cat(nfs_root_options, incoming, 195 sizeof(nfs_root_options))) 196 return -1; 197 198 /* 199 * Possibly prepare for more options to be appended 200 */ 201 if (nfs_root_options[0] != '\0' && 202 nfs_root_options[strlen(nfs_root_options)] != ',') 203 if (root_nfs_cat(nfs_root_options, ",", 204 sizeof(nfs_root_options))) 205 return -1; 206 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 addr_option[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(addr_option, sizeof(addr_option), "nolock,addr=%pI4", 248 &servaddr); 249 if (root_nfs_cat(nfs_root_options, addr_option, 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 } 311