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 : 0 : return -1;
163 : : 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 : 0 : 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 : 0 : static int __init root_nfs_parse_options(char *incoming, char *exppath,
187 : : const size_t exppathlen)
188 : : {
189 : 0 : 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 : 0 : 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 : 0 : char mand_options[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
221 : 0 : int len, retval = -1;
222 : 0 : char *tmp = NULL;
223 : 0 : const size_t tmplen = sizeof(nfs_export_path);
224 : :
225 : 0 : tmp = kzalloc(tmplen, GFP_KERNEL);
226 [ # # ]: 0 : if (tmp == NULL)
227 : 0 : goto out_nomem;
228 : 0 : strcpy(tmp, NFS_ROOT);
229 : :
230 [ # # ]: 0 : if (root_server_path[0] != '\0') {
231 : 0 : 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 : 0 : goto out_optionstoolong;
235 : : }
236 : :
237 [ # # ]: 0 : if (cmdline[0] != '\0') {
238 : 0 : dprintk("Root-NFS: nfsroot=%s\n", cmdline);
239 [ # # ]: 0 : if (root_nfs_parse_options(cmdline, tmp, tmplen))
240 : 0 : 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 : 0 : 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 : 0 : 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 : 0 : goto out_devnametoolong;
271 : :
272 : : retval = 0;
273 : :
274 : 0 : 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 : 0 : out_optionstoolong:
281 : 0 : printk(KERN_ERR "Root-NFS: mount options string too long\n");
282 : 0 : goto out;
283 : 0 : 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 : : }
|