Blame view

fs/nfs/nfsroot.c 9.71 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
   *  Copyright (C) 1995, 1996  Gero Kuhlmann <gero@gkminix.han.de>
   *
   *  Allow an NFS filesystem to be mounted as root. The way this works is:
   *     (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
56463e50d   Chuck Lever   NFS: Use super.c ...
7
8
9
10
   *     (2) Construct the device string and the options string using DHCP
   *         option 17 and/or kernel command line options.
   *     (3) When mount_root() sets up the root file system, pass these strings
   *         to the NFS client's regular mount interface via sys_mount().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
   *
   *
   *	Changes:
   *
   *	Alan Cox	:	Removed get_address name clash with FPU.
   *	Alan Cox	:	Reformatted a bit.
   *	Gero Kuhlmann	:	Code cleanup
   *	Michael Rausch  :	Fixed recognition of an incoming RARP answer.
   *	Martin Mares	: (2.0)	Auto-configuration via BOOTP supported.
   *	Martin Mares	:	Manual selection of interface & BOOTP/RARP.
   *	Martin Mares	:	Using network routes instead of host routes,
   *				allowing the default configuration to be used
   *				for normal operation of the host.
   *	Martin Mares	:	Randomized timer with exponential backoff
   *				installed to minimize network congestion.
   *	Martin Mares	:	Code cleanup.
   *	Martin Mares	: (2.1)	BOOTP and RARP made configuration options.
   *	Martin Mares	:	Server hostname generation fixed.
   *	Gerd Knorr	:	Fixed wired inode handling
   *	Martin Mares	: (2.2)	"0.0.0.0" addresses from command line ignored.
   *	Martin Mares	:	RARP replies not tested for server address.
   *	Gero Kuhlmann	: (2.3) Some bug fixes and code cleanup again (please
   *				send me your new patches _before_ bothering
   *				Linus so that I don' always have to cleanup
   *				_afterwards_ - thanks)
   *	Gero Kuhlmann	:	Last changes of Martin Mares undone.
   *	Gero Kuhlmann	: 	RARP replies are tested for specified server
   *				again. However, it's now possible to have
   *				different RARP and NFS servers.
   *	Gero Kuhlmann	:	"0.0.0.0" addresses from command line are
   *				now mapped to INADDR_NONE.
   *	Gero Kuhlmann	:	Fixed a bug which prevented BOOTP path name
   *				from being used (thanks to Leo Spiekman)
   *	Andy Walker	:	Allow to specify the NFS server in nfs_root
   *				without giving a path name
96de0e252   Jan Engelhardt   Convert files to ...
46
   *	Swen Thümmler	:	Allow to specify the NFS options in nfs_root
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
   *				without giving a path name. Fix BOOTP request
   *				for domainname (domainname is NIS domain, not
   *				DNS domain!). Skip dummy devices for BOOTP.
   *	Jacek Zapala	:	Fixed a bug which prevented server-ip address
   *				from nfsroot parameter from being used.
   *	Olaf Kirch	:	Adapted to new NFS code.
   *	Jakub Jelinek	:	Free used code segment.
   *	Marko Kohtala	:	Fixed some bugs.
   *	Martin Mares	:	Debug message cleanup
   *	Martin Mares	:	Changed to use the new generic IP layer autoconfig
   *				code. BOOTP and RARP moved there.
   *	Martin Mares	:	Default path now contains host name instead of
   *				host IP address (but host name defaults to IP
   *				address anyway).
   *	Martin Mares	:	Use root_server_addr appropriately during setup.
   *	Martin Mares	:	Rewrote parameter parsing, now hopefully giving
   *				correct overriding.
   *	Trond Myklebust :	Add in preliminary support for NFSv3 and TCP.
   *				Fix bug in root_nfs_addr(). nfs_data.namlen
   *				is NOT for the length of the hostname.
   *	Hua Qin		:	Support for mounting root file system via
   *				NFS over TCP.
   *	Fabian Frederick:	Option parser rebuilt (using parser lib)
56463e50d   Chuck Lever   NFS: Use super.c ...
70
   *	Chuck Lever	:	Use super.c's text-based mount option parsing
306a07536   Chuck Lever   NFS: Allow NFSROO...
71
   *	Chuck Lever	:	Add "nfsrootdebug".
56463e50d   Chuck Lever   NFS: Use super.c ...
72
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  #include <linux/types.h>
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  #include <linux/nfs.h>
  #include <linux/nfs_fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  #include <linux/utsname.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  #include <linux/root_dev.h>
  #include <net/ipconfig.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82

146ec944b   Chuck Lever   NFS: Move declara...
83
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
  #define NFSDBG_FACILITY NFSDBG_ROOT
  
  /* Default path we try to mount. "%s" gets replaced by our IP address */
  #define NFS_ROOT		"/tftpboot/%s"
53d473758   Chuck Lever   NFS: NFSROOT shou...
88
  /* Default NFSROOT mount options. */
3fc2bfa36   Helge Deller   nfsroot: Default ...
89
  #if defined(CONFIG_NFS_V2)
89c8023fd   Liwei Song   nfsroot: set tcp ...
90
  #define NFS_DEF_OPTIONS		"vers=2,tcp,rsize=4096,wsize=4096"
3fc2bfa36   Helge Deller   nfsroot: Default ...
91
92
93
94
95
  #elif defined(CONFIG_NFS_V3)
  #define NFS_DEF_OPTIONS		"vers=3,tcp,rsize=4096,wsize=4096"
  #else
  #define NFS_DEF_OPTIONS		"vers=4,tcp,rsize=4096,wsize=4096"
  #endif
53d473758   Chuck Lever   NFS: NFSROOT shou...
96

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  /* Parameters passed from the kernel command line */
c64661935   Li RongQing   nfsroot: make nfs...
98
  static char nfs_root_parms[NFS_MAXPATHLEN + 1] __initdata = "";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99

56463e50d   Chuck Lever   NFS: Use super.c ...
100
  /* Text-based mount options passed to super.c */
53d473758   Chuck Lever   NFS: NFSROOT shou...
101
  static char nfs_root_options[256] __initdata = NFS_DEF_OPTIONS;
56463e50d   Chuck Lever   NFS: Use super.c ...
102

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
  /* Address of NFS server */
8d2321037   Chuck Lever   NFS: Clean up nfs...
104
  static __be32 servaddr __initdata = htonl(INADDR_NONE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
  
  /* Name of directory to mount */
56463e50d   Chuck Lever   NFS: Use super.c ...
107
108
109
110
  static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = "";
  
  /* server:export path string passed to super.c */
  static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111

e27d359e9   Trond Myklebust   SUNRPC/NFS: Add K...
112
  #ifdef NFS_DEBUG
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  /*
306a07536   Chuck Lever   NFS: Allow NFSROO...
114
115
116
117
118
119
120
121
122
123
   * When the "nfsrootdebug" kernel command line option is specified,
   * enable debugging messages for NFSROOT.
   */
  static int __init nfs_root_debug(char *__unused)
  {
  	nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
  	return 1;
  }
  
  __setup("nfsrootdebug", nfs_root_debug);
036a10759   Trond Myklebust   NFS: Fix a compil...
124
  #endif
306a07536   Chuck Lever   NFS: Allow NFSROO...
125
126
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
   *  Parse NFS server and directory information passed on the kernel
   *  command line.
60ac03685   Chuck Lever   NFS: Clean up NFS...
129
130
131
132
133
   *
   *  nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
   *
   *  If there is a "%s" token in the <root-dir> string, it is replaced
   *  by the ASCII-representation of the client's IP address.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
   */
  static int __init nfs_root_setup(char *line)
  {
  	ROOT_DEV = Root_NFS;
60ac03685   Chuck Lever   NFS: Clean up NFS...
138

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  	if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
60ac03685   Chuck Lever   NFS: Clean up NFS...
140
  		strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  	} else {
60ac03685   Chuck Lever   NFS: Clean up NFS...
142
143
144
145
  		size_t n = strlen(line) + sizeof(NFS_ROOT) - 1;
  		if (n >= sizeof(nfs_root_parms))
  			line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0';
  		sprintf(nfs_root_parms, NFS_ROOT, line);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  	}
60ac03685   Chuck Lever   NFS: Clean up NFS...
147
148
149
150
151
152
153
154
155
  
  	/*
  	 * Extract the IP address of the NFS server containing our
  	 * root file system, if one was specified.
  	 *
  	 * Note: root_nfs_parse_addr() removes the server-ip from
  	 *	 nfs_root_parms, if it exists.
  	 */
  	root_server_addr = root_nfs_parse_addr(nfs_root_parms);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
  	return 1;
  }
  
  __setup("nfsroot=", nfs_root_setup);
56463e50d   Chuck Lever   NFS: Use super.c ...
160
161
162
163
164
165
166
167
168
  static int __init root_nfs_copy(char *dest, const char *src,
  				     const size_t destlen)
  {
  	if (strlcpy(dest, src, destlen) > destlen)
  		return -1;
  	return 0;
  }
  
  static int __init root_nfs_cat(char *dest, const char *src,
53d473758   Chuck Lever   NFS: NFSROOT shou...
169
  			       const size_t destlen)
56463e50d   Chuck Lever   NFS: Use super.c ...
170
  {
53d473758   Chuck Lever   NFS: NFSROOT shou...
171
172
173
174
175
  	size_t len = strlen(dest);
  
  	if (len && dest[len - 1] != ',')
  		if (strlcat(dest, ",", destlen) > destlen)
  			return -1;
56463e50d   Chuck Lever   NFS: Use super.c ...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  	if (strlcat(dest, src, destlen) > destlen)
  		return -1;
  	return 0;
  }
  
  /*
   * Parse out root export path and mount options from
   * passed-in string @incoming.
   *
   * Copy the export path into @exppath.
   */
  static int __init root_nfs_parse_options(char *incoming, char *exppath,
  					 const size_t exppathlen)
  {
  	char *p;
  
  	/*
  	 * Set the NFS remote path
  	 */
  	p = strsep(&incoming, ",");
  	if (*p != '\0' && strcmp(p, "default") != 0)
  		if (root_nfs_copy(exppath, p, exppathlen))
  			return -1;
  
  	/*
  	 * @incoming now points to the rest of the string; if it
  	 * contains something, append it to our root options buffer
  	 */
  	if (incoming != NULL && *incoming != '\0')
  		if (root_nfs_cat(nfs_root_options, incoming,
  						sizeof(nfs_root_options)))
  			return -1;
56463e50d   Chuck Lever   NFS: Use super.c ...
208
209
210
211
212
213
214
215
216
217
218
219
220
  	return 0;
  }
  
  /*
   *  Decode the export directory path name and NFS options from
   *  the kernel command line.  This has to be done late in order to
   *  use a dynamically acquired client IP address for the remote
   *  root directory path.
   *
   *  Returns zero if successful; otherwise -1 is returned.
   */
  static int __init root_nfs_data(char *cmdline)
  {
53d473758   Chuck Lever   NFS: NFSROOT shou...
221
  	char mand_options[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
56463e50d   Chuck Lever   NFS: Use super.c ...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  	int len, retval = -1;
  	char *tmp = NULL;
  	const size_t tmplen = sizeof(nfs_export_path);
  
  	tmp = kzalloc(tmplen, GFP_KERNEL);
  	if (tmp == NULL)
  		goto out_nomem;
  	strcpy(tmp, NFS_ROOT);
  
  	if (root_server_path[0] != '\0') {
  		dprintk("Root-NFS: DHCPv4 option 17: %s
  ",
  			root_server_path);
  		if (root_nfs_parse_options(root_server_path, tmp, tmplen))
  			goto out_optionstoolong;
  	}
  
  	if (cmdline[0] != '\0') {
  		dprintk("Root-NFS: nfsroot=%s
  ", cmdline);
  		if (root_nfs_parse_options(cmdline, tmp, tmplen))
  			goto out_optionstoolong;
  	}
  
  	/*
  	 * Append mandatory options for nfsroot so they override
  	 * what has come before
  	 */
53d473758   Chuck Lever   NFS: NFSROOT shou...
250
  	snprintf(mand_options, sizeof(mand_options), "nolock,addr=%pI4",
56463e50d   Chuck Lever   NFS: Use super.c ...
251
  			&servaddr);
53d473758   Chuck Lever   NFS: NFSROOT shou...
252
  	if (root_nfs_cat(nfs_root_options, mand_options,
56463e50d   Chuck Lever   NFS: Use super.c ...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  						sizeof(nfs_root_options)))
  		goto out_optionstoolong;
  
  	/*
  	 * Set up nfs_root_device.  For NFS mounts, this looks like
  	 *
  	 *	server:/path
  	 *
  	 * At this point, utsname()->nodename contains our local
  	 * IP address or hostname, set by ipconfig.  If "%s" exists
  	 * in tmp, substitute the nodename, then shovel the whole
  	 * mess into nfs_root_device.
  	 */
  	len = snprintf(nfs_export_path, sizeof(nfs_export_path),
  				tmp, utsname()->nodename);
c7c545d4a   Dan Carpenter   NFS: a couple off...
268
  	if (len >= (int)sizeof(nfs_export_path))
56463e50d   Chuck Lever   NFS: Use super.c ...
269
270
271
  		goto out_devnametoolong;
  	len = snprintf(nfs_root_device, sizeof(nfs_root_device),
  				"%pI4:%s", &servaddr, nfs_export_path);
c7c545d4a   Dan Carpenter   NFS: a couple off...
272
  	if (len >= (int)sizeof(nfs_root_device))
56463e50d   Chuck Lever   NFS: Use super.c ...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  		goto out_devnametoolong;
  
  	retval = 0;
  
  out:
  	kfree(tmp);
  	return retval;
  out_nomem:
  	printk(KERN_ERR "Root-NFS: could not allocate memory
  ");
  	goto out;
  out_optionstoolong:
  	printk(KERN_ERR "Root-NFS: mount options string too long
  ");
  	goto out;
  out_devnametoolong:
  	printk(KERN_ERR "Root-NFS: root device name too long.
  ");
  	goto out;
  }
  
  /**
   * nfs_root_data - Return prepared 'data' for NFSROOT mount
   * @root_device: OUT: address of string containing NFSROOT device
   * @root_data: OUT: address of string containing NFSROOT mount options
   *
   * Returns zero and sets @root_device and @root_data if successful,
   * otherwise -1 is returned.
   */
  int __init nfs_root_data(char **root_device, char **root_data)
  {
56463e50d   Chuck Lever   NFS: Use super.c ...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  	servaddr = root_server_addr;
  	if (servaddr == htonl(INADDR_NONE)) {
  		printk(KERN_ERR "Root-NFS: no NFS server address
  ");
  		return -1;
  	}
  
  	if (root_nfs_data(nfs_root_parms) < 0)
  		return -1;
  
  	*root_device = nfs_root_device;
  	*root_data = nfs_root_options;
  	return 0;
  }