diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-25 16:48:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-25 16:48:29 -0400 |
commit | 74eb94b218d087798a52c0b4f1379b635287a4b8 (patch) | |
tree | 4e467c3014c2b1169f6f71d88cf5d1598f3ce28e /fs/nfs/nfsroot.c | |
parent | 7b6181e06841f5ad15c4ff708b967b4db65a64de (diff) | |
parent | 9a84d38031c258a17bb39beed1e500eadee67407 (diff) |
Merge branch 'nfs-for-2.6.37' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'nfs-for-2.6.37' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (67 commits)
SUNRPC: Cleanup duplicate assignment in rpcauth_refreshcred
nfs: fix unchecked value
Ask for time_delta during fsinfo probe
Revalidate caches on lock
SUNRPC: After calling xprt_release(), we must restart from call_reserve
NFSv4: Fix up the 'dircount' hint in encode_readdir
NFSv4: Clean up nfs4_decode_dirent
NFSv4: nfs4_decode_dirent must clear entry->fattr->valid
NFSv4: Fix a regression in decode_getfattr
NFSv4: Fix up decode_attr_filehandle() to handle the case of empty fh pointer
NFS: Ensure we check all allocation return values in new readdir code
NFS: Readdir plus in v4
NFS: introduce generic decode_getattr function
NFS: check xdr_decode for errors
NFS: nfs_readdir_filler catch all errors
NFS: readdir with vmapped pages
NFS: remove page size checking code
NFS: decode_dirent should use an xdr_stream
SUNRPC: Add a helper function xdr_inline_peek
NFS: remove readdir plus limit
...
Diffstat (limited to 'fs/nfs/nfsroot.c')
-rw-r--r-- | fs/nfs/nfsroot.c | 566 |
1 files changed, 170 insertions, 396 deletions
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index df101d9f546a..460df3652889 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c | |||
@@ -3,9 +3,10 @@ | |||
3 | * | 3 | * |
4 | * Allow an NFS filesystem to be mounted as root. The way this works is: | 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. | 5 | * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. |
6 | * (2) Handle RPC negotiation with the system which replied to RARP or | 6 | * (2) Construct the device string and the options string using DHCP |
7 | * was reported as a boot server by BOOTP or manually. | 7 | * option 17 and/or kernel command line options. |
8 | * (3) The actual mounting is done later, when init() is running. | 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(). | ||
9 | * | 10 | * |
10 | * | 11 | * |
11 | * Changes: | 12 | * Changes: |
@@ -65,470 +66,243 @@ | |||
65 | * Hua Qin : Support for mounting root file system via | 66 | * Hua Qin : Support for mounting root file system via |
66 | * NFS over TCP. | 67 | * NFS over TCP. |
67 | * Fabian Frederick: Option parser rebuilt (using parser lib) | 68 | * Fabian Frederick: Option parser rebuilt (using parser lib) |
68 | */ | 69 | * Chuck Lever : Use super.c's text-based mount option parsing |
70 | * Chuck Lever : Add "nfsrootdebug". | ||
71 | */ | ||
69 | 72 | ||
70 | #include <linux/types.h> | 73 | #include <linux/types.h> |
71 | #include <linux/string.h> | 74 | #include <linux/string.h> |
72 | #include <linux/kernel.h> | ||
73 | #include <linux/time.h> | ||
74 | #include <linux/fs.h> | ||
75 | #include <linux/init.h> | 75 | #include <linux/init.h> |
76 | #include <linux/sunrpc/clnt.h> | ||
77 | #include <linux/sunrpc/xprtsock.h> | ||
78 | #include <linux/nfs.h> | 76 | #include <linux/nfs.h> |
79 | #include <linux/nfs_fs.h> | 77 | #include <linux/nfs_fs.h> |
80 | #include <linux/nfs_mount.h> | ||
81 | #include <linux/in.h> | ||
82 | #include <linux/major.h> | ||
83 | #include <linux/utsname.h> | 78 | #include <linux/utsname.h> |
84 | #include <linux/inet.h> | ||
85 | #include <linux/root_dev.h> | 79 | #include <linux/root_dev.h> |
86 | #include <net/ipconfig.h> | 80 | #include <net/ipconfig.h> |
87 | #include <linux/parser.h> | ||
88 | 81 | ||
89 | #include "internal.h" | 82 | #include "internal.h" |
90 | 83 | ||
91 | /* Define this to allow debugging output */ | ||
92 | #undef NFSROOT_DEBUG | ||
93 | #define NFSDBG_FACILITY NFSDBG_ROOT | 84 | #define NFSDBG_FACILITY NFSDBG_ROOT |
94 | 85 | ||
95 | /* Default port to use if server is not running a portmapper */ | ||
96 | #define NFS_MNT_PORT 627 | ||
97 | |||
98 | /* Default path we try to mount. "%s" gets replaced by our IP address */ | 86 | /* Default path we try to mount. "%s" gets replaced by our IP address */ |
99 | #define NFS_ROOT "/tftpboot/%s" | 87 | #define NFS_ROOT "/tftpboot/%s" |
100 | 88 | ||
101 | /* Parameters passed from the kernel command line */ | 89 | /* Parameters passed from the kernel command line */ |
102 | static char nfs_root_name[256] __initdata = ""; | 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 = ""; | ||
103 | 94 | ||
104 | /* Address of NFS server */ | 95 | /* Address of NFS server */ |
105 | static __be32 servaddr __initdata = 0; | 96 | static __be32 servaddr __initdata = htonl(INADDR_NONE); |
106 | 97 | ||
107 | /* Name of directory to mount */ | 98 | /* Name of directory to mount */ |
108 | static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; | 99 | static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; |
109 | 100 | ||
110 | /* NFS-related data */ | 101 | /* server:export path string passed to super.c */ |
111 | static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ | 102 | static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; |
112 | static int nfs_port __initdata = 0; /* Port to connect to for NFS */ | ||
113 | static int mount_port __initdata = 0; /* Mount daemon port number */ | ||
114 | |||
115 | |||
116 | /*************************************************************************** | ||
117 | |||
118 | Parsing of options | ||
119 | |||
120 | ***************************************************************************/ | ||
121 | |||
122 | enum { | ||
123 | /* Options that take integer arguments */ | ||
124 | Opt_port, Opt_rsize, Opt_wsize, Opt_timeo, Opt_retrans, Opt_acregmin, | ||
125 | Opt_acregmax, Opt_acdirmin, Opt_acdirmax, | ||
126 | /* Options that take no arguments */ | ||
127 | Opt_soft, Opt_hard, Opt_intr, | ||
128 | Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, | ||
129 | Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, | ||
130 | Opt_acl, Opt_noacl, | ||
131 | /* Error token */ | ||
132 | Opt_err | ||
133 | }; | ||
134 | |||
135 | static const match_table_t tokens __initconst = { | ||
136 | {Opt_port, "port=%u"}, | ||
137 | {Opt_rsize, "rsize=%u"}, | ||
138 | {Opt_wsize, "wsize=%u"}, | ||
139 | {Opt_timeo, "timeo=%u"}, | ||
140 | {Opt_retrans, "retrans=%u"}, | ||
141 | {Opt_acregmin, "acregmin=%u"}, | ||
142 | {Opt_acregmax, "acregmax=%u"}, | ||
143 | {Opt_acdirmin, "acdirmin=%u"}, | ||
144 | {Opt_acdirmax, "acdirmax=%u"}, | ||
145 | {Opt_soft, "soft"}, | ||
146 | {Opt_hard, "hard"}, | ||
147 | {Opt_intr, "intr"}, | ||
148 | {Opt_nointr, "nointr"}, | ||
149 | {Opt_posix, "posix"}, | ||
150 | {Opt_noposix, "noposix"}, | ||
151 | {Opt_cto, "cto"}, | ||
152 | {Opt_nocto, "nocto"}, | ||
153 | {Opt_ac, "ac"}, | ||
154 | {Opt_noac, "noac"}, | ||
155 | {Opt_lock, "lock"}, | ||
156 | {Opt_nolock, "nolock"}, | ||
157 | {Opt_v2, "nfsvers=2"}, | ||
158 | {Opt_v2, "v2"}, | ||
159 | {Opt_v3, "nfsvers=3"}, | ||
160 | {Opt_v3, "v3"}, | ||
161 | {Opt_udp, "proto=udp"}, | ||
162 | {Opt_udp, "udp"}, | ||
163 | {Opt_tcp, "proto=tcp"}, | ||
164 | {Opt_tcp, "tcp"}, | ||
165 | {Opt_acl, "acl"}, | ||
166 | {Opt_noacl, "noacl"}, | ||
167 | {Opt_err, NULL} | ||
168 | |||
169 | }; | ||
170 | 103 | ||
171 | /* | 104 | /* |
172 | * Parse option string. | 105 | * When the "nfsrootdebug" kernel command line option is specified, |
106 | * enable debugging messages for NFSROOT. | ||
173 | */ | 107 | */ |
174 | 108 | static int __init nfs_root_debug(char *__unused) | |
175 | static int __init root_nfs_parse(char *name, char *buf) | ||
176 | { | 109 | { |
177 | 110 | nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; | |
178 | char *p; | ||
179 | substring_t args[MAX_OPT_ARGS]; | ||
180 | int option; | ||
181 | |||
182 | if (!name) | ||
183 | return 1; | ||
184 | |||
185 | /* Set the NFS remote path */ | ||
186 | p = strsep(&name, ","); | ||
187 | if (p[0] != '\0' && strcmp(p, "default") != 0) | ||
188 | strlcpy(buf, p, NFS_MAXPATHLEN); | ||
189 | |||
190 | while ((p = strsep (&name, ",")) != NULL) { | ||
191 | int token; | ||
192 | if (!*p) | ||
193 | continue; | ||
194 | token = match_token(p, tokens, args); | ||
195 | |||
196 | /* %u tokens only. Beware if you add new tokens! */ | ||
197 | if (token < Opt_soft && match_int(&args[0], &option)) | ||
198 | return 0; | ||
199 | switch (token) { | ||
200 | case Opt_port: | ||
201 | nfs_port = option; | ||
202 | break; | ||
203 | case Opt_rsize: | ||
204 | nfs_data.rsize = option; | ||
205 | break; | ||
206 | case Opt_wsize: | ||
207 | nfs_data.wsize = option; | ||
208 | break; | ||
209 | case Opt_timeo: | ||
210 | nfs_data.timeo = option; | ||
211 | break; | ||
212 | case Opt_retrans: | ||
213 | nfs_data.retrans = option; | ||
214 | break; | ||
215 | case Opt_acregmin: | ||
216 | nfs_data.acregmin = option; | ||
217 | break; | ||
218 | case Opt_acregmax: | ||
219 | nfs_data.acregmax = option; | ||
220 | break; | ||
221 | case Opt_acdirmin: | ||
222 | nfs_data.acdirmin = option; | ||
223 | break; | ||
224 | case Opt_acdirmax: | ||
225 | nfs_data.acdirmax = option; | ||
226 | break; | ||
227 | case Opt_soft: | ||
228 | nfs_data.flags |= NFS_MOUNT_SOFT; | ||
229 | break; | ||
230 | case Opt_hard: | ||
231 | nfs_data.flags &= ~NFS_MOUNT_SOFT; | ||
232 | break; | ||
233 | case Opt_intr: | ||
234 | case Opt_nointr: | ||
235 | break; | ||
236 | case Opt_posix: | ||
237 | nfs_data.flags |= NFS_MOUNT_POSIX; | ||
238 | break; | ||
239 | case Opt_noposix: | ||
240 | nfs_data.flags &= ~NFS_MOUNT_POSIX; | ||
241 | break; | ||
242 | case Opt_cto: | ||
243 | nfs_data.flags &= ~NFS_MOUNT_NOCTO; | ||
244 | break; | ||
245 | case Opt_nocto: | ||
246 | nfs_data.flags |= NFS_MOUNT_NOCTO; | ||
247 | break; | ||
248 | case Opt_ac: | ||
249 | nfs_data.flags &= ~NFS_MOUNT_NOAC; | ||
250 | break; | ||
251 | case Opt_noac: | ||
252 | nfs_data.flags |= NFS_MOUNT_NOAC; | ||
253 | break; | ||
254 | case Opt_lock: | ||
255 | nfs_data.flags &= ~NFS_MOUNT_NONLM; | ||
256 | break; | ||
257 | case Opt_nolock: | ||
258 | nfs_data.flags |= NFS_MOUNT_NONLM; | ||
259 | break; | ||
260 | case Opt_v2: | ||
261 | nfs_data.flags &= ~NFS_MOUNT_VER3; | ||
262 | break; | ||
263 | case Opt_v3: | ||
264 | nfs_data.flags |= NFS_MOUNT_VER3; | ||
265 | break; | ||
266 | case Opt_udp: | ||
267 | nfs_data.flags &= ~NFS_MOUNT_TCP; | ||
268 | break; | ||
269 | case Opt_tcp: | ||
270 | nfs_data.flags |= NFS_MOUNT_TCP; | ||
271 | break; | ||
272 | case Opt_acl: | ||
273 | nfs_data.flags &= ~NFS_MOUNT_NOACL; | ||
274 | break; | ||
275 | case Opt_noacl: | ||
276 | nfs_data.flags |= NFS_MOUNT_NOACL; | ||
277 | break; | ||
278 | default: | ||
279 | printk(KERN_WARNING "Root-NFS: unknown " | ||
280 | "option: %s\n", p); | ||
281 | return 0; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | return 1; | 111 | return 1; |
286 | } | 112 | } |
287 | 113 | ||
114 | __setup("nfsrootdebug", nfs_root_debug); | ||
115 | |||
288 | /* | 116 | /* |
289 | * Prepare the NFS data structure and parse all options. | 117 | * Parse NFS server and directory information passed on the kernel |
118 | * command line. | ||
119 | * | ||
120 | * nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] | ||
121 | * | ||
122 | * If there is a "%s" token in the <root-dir> string, it is replaced | ||
123 | * by the ASCII-representation of the client's IP address. | ||
290 | */ | 124 | */ |
291 | static int __init root_nfs_name(char *name) | 125 | static int __init nfs_root_setup(char *line) |
292 | { | 126 | { |
293 | static char buf[NFS_MAXPATHLEN] __initdata; | 127 | ROOT_DEV = Root_NFS; |
294 | char *cp; | 128 | |
295 | 129 | if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { | |
296 | /* Set some default values */ | 130 | strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms)); |
297 | memset(&nfs_data, 0, sizeof(nfs_data)); | 131 | } else { |
298 | nfs_port = -1; | 132 | size_t n = strlen(line) + sizeof(NFS_ROOT) - 1; |
299 | nfs_data.version = NFS_MOUNT_VERSION; | 133 | if (n >= sizeof(nfs_root_parms)) |
300 | nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ | 134 | line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0'; |
301 | nfs_data.rsize = NFS_DEF_FILE_IO_SIZE; | 135 | sprintf(nfs_root_parms, NFS_ROOT, line); |
302 | nfs_data.wsize = NFS_DEF_FILE_IO_SIZE; | ||
303 | nfs_data.acregmin = NFS_DEF_ACREGMIN; | ||
304 | nfs_data.acregmax = NFS_DEF_ACREGMAX; | ||
305 | nfs_data.acdirmin = NFS_DEF_ACDIRMIN; | ||
306 | nfs_data.acdirmax = NFS_DEF_ACDIRMAX; | ||
307 | strcpy(buf, NFS_ROOT); | ||
308 | |||
309 | /* Process options received from the remote server */ | ||
310 | root_nfs_parse(root_server_path, buf); | ||
311 | |||
312 | /* Override them by options set on kernel command-line */ | ||
313 | root_nfs_parse(name, buf); | ||
314 | |||
315 | cp = utsname()->nodename; | ||
316 | if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { | ||
317 | printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); | ||
318 | return -1; | ||
319 | } | 136 | } |
320 | sprintf(nfs_export_path, buf, cp); | 137 | |
138 | /* | ||
139 | * Extract the IP address of the NFS server containing our | ||
140 | * root file system, if one was specified. | ||
141 | * | ||
142 | * Note: root_nfs_parse_addr() removes the server-ip from | ||
143 | * nfs_root_parms, if it exists. | ||
144 | */ | ||
145 | root_server_addr = root_nfs_parse_addr(nfs_root_parms); | ||
321 | 146 | ||
322 | return 1; | 147 | return 1; |
323 | } | 148 | } |
324 | 149 | ||
150 | __setup("nfsroot=", nfs_root_setup); | ||
325 | 151 | ||
326 | /* | 152 | static int __init root_nfs_copy(char *dest, const char *src, |
327 | * Get NFS server address. | 153 | const size_t destlen) |
328 | */ | ||
329 | static int __init root_nfs_addr(void) | ||
330 | { | 154 | { |
331 | if ((servaddr = root_server_addr) == htonl(INADDR_NONE)) { | 155 | if (strlcpy(dest, src, destlen) > destlen) |
332 | printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n"); | ||
333 | return -1; | 156 | return -1; |
334 | } | 157 | return 0; |
158 | } | ||
335 | 159 | ||
336 | snprintf(nfs_data.hostname, sizeof(nfs_data.hostname), | 160 | static int __init root_nfs_cat(char *dest, const char *src, |
337 | "%pI4", &servaddr); | 161 | const size_t destlen) |
162 | { | ||
163 | if (strlcat(dest, src, destlen) > destlen) | ||
164 | return -1; | ||
338 | return 0; | 165 | return 0; |
339 | } | 166 | } |
340 | 167 | ||
341 | /* | 168 | /* |
342 | * Tell the user what's going on. | 169 | * Parse out root export path and mount options from |
170 | * passed-in string @incoming. | ||
171 | * | ||
172 | * Copy the export path into @exppath. | ||
343 | */ | 173 | */ |
344 | #ifdef NFSROOT_DEBUG | 174 | static int __init root_nfs_parse_options(char *incoming, char *exppath, |
345 | static void __init root_nfs_print(void) | 175 | const size_t exppathlen) |
346 | { | 176 | { |
347 | printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", | 177 | char *p; |
348 | nfs_export_path, nfs_data.hostname); | ||
349 | printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", | ||
350 | nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans); | ||
351 | printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n", | ||
352 | nfs_data.acregmin, nfs_data.acregmax, | ||
353 | nfs_data.acdirmin, nfs_data.acdirmax); | ||
354 | printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n", | ||
355 | nfs_port, mount_port, nfs_data.flags); | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | 178 | ||
360 | static int __init root_nfs_init(void) | 179 | /* |
361 | { | 180 | * Set the NFS remote path |
362 | #ifdef NFSROOT_DEBUG | 181 | */ |
363 | nfs_debug |= NFSDBG_ROOT; | 182 | p = strsep(&incoming, ","); |
364 | #endif | 183 | if (*p != '\0' && strcmp(p, "default") != 0) |
184 | if (root_nfs_copy(exppath, p, exppathlen)) | ||
185 | return -1; | ||
365 | 186 | ||
366 | /* | 187 | /* |
367 | * Decode the root directory path name and NFS options from | 188 | * @incoming now points to the rest of the string; if it |
368 | * the kernel command line. This has to go here in order to | 189 | * contains something, append it to our root options buffer |
369 | * be able to use the client IP address for the remote root | ||
370 | * directory (necessary for pure RARP booting). | ||
371 | */ | 190 | */ |
372 | if (root_nfs_name(nfs_root_name) < 0 || | 191 | if (incoming != NULL && *incoming != '\0') |
373 | root_nfs_addr() < 0) | 192 | if (root_nfs_cat(nfs_root_options, incoming, |
374 | return -1; | 193 | sizeof(nfs_root_options))) |
194 | return -1; | ||
375 | 195 | ||
376 | #ifdef NFSROOT_DEBUG | 196 | /* |
377 | root_nfs_print(); | 197 | * Possibly prepare for more options to be appended |
378 | #endif | 198 | */ |
199 | if (nfs_root_options[0] != '\0' && | ||
200 | nfs_root_options[strlen(nfs_root_options)] != ',') | ||
201 | if (root_nfs_cat(nfs_root_options, ",", | ||
202 | sizeof(nfs_root_options))) | ||
203 | return -1; | ||
379 | 204 | ||
380 | return 0; | 205 | return 0; |
381 | } | 206 | } |
382 | 207 | ||
383 | |||
384 | /* | 208 | /* |
385 | * Parse NFS server and directory information passed on the kernel | 209 | * Decode the export directory path name and NFS options from |
386 | * command line. | 210 | * the kernel command line. This has to be done late in order to |
211 | * use a dynamically acquired client IP address for the remote | ||
212 | * root directory path. | ||
213 | * | ||
214 | * Returns zero if successful; otherwise -1 is returned. | ||
387 | */ | 215 | */ |
388 | static int __init nfs_root_setup(char *line) | 216 | static int __init root_nfs_data(char *cmdline) |
389 | { | 217 | { |
390 | ROOT_DEV = Root_NFS; | 218 | char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1]; |
391 | if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { | 219 | int len, retval = -1; |
392 | strlcpy(nfs_root_name, line, sizeof(nfs_root_name)); | 220 | char *tmp = NULL; |
393 | } else { | 221 | const size_t tmplen = sizeof(nfs_export_path); |
394 | int n = strlen(line) + sizeof(NFS_ROOT) - 1; | 222 | |
395 | if (n >= sizeof(nfs_root_name)) | 223 | tmp = kzalloc(tmplen, GFP_KERNEL); |
396 | line[sizeof(nfs_root_name) - sizeof(NFS_ROOT) - 2] = '\0'; | 224 | if (tmp == NULL) |
397 | sprintf(nfs_root_name, NFS_ROOT, line); | 225 | goto out_nomem; |
226 | strcpy(tmp, NFS_ROOT); | ||
227 | |||
228 | if (root_server_path[0] != '\0') { | ||
229 | dprintk("Root-NFS: DHCPv4 option 17: %s\n", | ||
230 | root_server_path); | ||
231 | if (root_nfs_parse_options(root_server_path, tmp, tmplen)) | ||
232 | goto out_optionstoolong; | ||
398 | } | 233 | } |
399 | root_server_addr = root_nfs_parse_addr(nfs_root_name); | ||
400 | return 1; | ||
401 | } | ||
402 | 234 | ||
403 | __setup("nfsroot=", nfs_root_setup); | 235 | if (cmdline[0] != '\0') { |
404 | 236 | dprintk("Root-NFS: nfsroot=%s\n", cmdline); | |
405 | /*************************************************************************** | 237 | if (root_nfs_parse_options(cmdline, tmp, tmplen)) |
406 | 238 | goto out_optionstoolong; | |
407 | Routines to actually mount the root directory | 239 | } |
408 | 240 | ||
409 | ***************************************************************************/ | 241 | /* |
242 | * Append mandatory options for nfsroot so they override | ||
243 | * what has come before | ||
244 | */ | ||
245 | snprintf(addr_option, sizeof(addr_option), "nolock,addr=%pI4", | ||
246 | &servaddr); | ||
247 | if (root_nfs_cat(nfs_root_options, addr_option, | ||
248 | sizeof(nfs_root_options))) | ||
249 | goto out_optionstoolong; | ||
410 | 250 | ||
411 | /* | 251 | /* |
412 | * Construct sockaddr_in from address and port number. | 252 | * Set up nfs_root_device. For NFS mounts, this looks like |
413 | */ | 253 | * |
414 | static inline void | 254 | * server:/path |
415 | set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port) | 255 | * |
416 | { | 256 | * At this point, utsname()->nodename contains our local |
417 | sin->sin_family = AF_INET; | 257 | * IP address or hostname, set by ipconfig. If "%s" exists |
418 | sin->sin_addr.s_addr = addr; | 258 | * in tmp, substitute the nodename, then shovel the whole |
419 | sin->sin_port = port; | 259 | * mess into nfs_root_device. |
420 | } | 260 | */ |
261 | len = snprintf(nfs_export_path, sizeof(nfs_export_path), | ||
262 | tmp, utsname()->nodename); | ||
263 | if (len > (int)sizeof(nfs_export_path)) | ||
264 | goto out_devnametoolong; | ||
265 | len = snprintf(nfs_root_device, sizeof(nfs_root_device), | ||
266 | "%pI4:%s", &servaddr, nfs_export_path); | ||
267 | if (len > (int)sizeof(nfs_root_device)) | ||
268 | goto out_devnametoolong; | ||
421 | 269 | ||
422 | /* | 270 | retval = 0; |
423 | * Query server portmapper for the port of a daemon program. | ||
424 | */ | ||
425 | static int __init root_nfs_getport(int program, int version, int proto) | ||
426 | { | ||
427 | struct sockaddr_in sin; | ||
428 | 271 | ||
429 | printk(KERN_NOTICE "Looking up port of RPC %d/%d on %pI4\n", | 272 | out: |
430 | program, version, &servaddr); | 273 | kfree(tmp); |
431 | set_sockaddr(&sin, servaddr, 0); | 274 | return retval; |
432 | return rpcb_getport_sync(&sin, program, version, proto); | 275 | out_nomem: |
276 | printk(KERN_ERR "Root-NFS: could not allocate memory\n"); | ||
277 | goto out; | ||
278 | out_optionstoolong: | ||
279 | printk(KERN_ERR "Root-NFS: mount options string too long\n"); | ||
280 | goto out; | ||
281 | out_devnametoolong: | ||
282 | printk(KERN_ERR "Root-NFS: root device name too long.\n"); | ||
283 | goto out; | ||
433 | } | 284 | } |
434 | 285 | ||
435 | 286 | /** | |
436 | /* | 287 | * nfs_root_data - Return prepared 'data' for NFSROOT mount |
437 | * Use portmapper to find mountd and nfsd port numbers if not overriden | 288 | * @root_device: OUT: address of string containing NFSROOT device |
438 | * by the user. Use defaults if portmapper is not available. | 289 | * @root_data: OUT: address of string containing NFSROOT mount options |
439 | * XXX: Is there any nfs server with no portmapper? | 290 | * |
291 | * Returns zero and sets @root_device and @root_data if successful, | ||
292 | * otherwise -1 is returned. | ||
440 | */ | 293 | */ |
441 | static int __init root_nfs_ports(void) | 294 | int __init nfs_root_data(char **root_device, char **root_data) |
442 | { | 295 | { |
443 | int port; | 296 | servaddr = root_server_addr; |
444 | int nfsd_ver, mountd_ver; | 297 | if (servaddr == htonl(INADDR_NONE)) { |
445 | int nfsd_port, mountd_port; | 298 | printk(KERN_ERR "Root-NFS: no NFS server address\n"); |
446 | int proto; | 299 | return -1; |
447 | |||
448 | if (nfs_data.flags & NFS_MOUNT_VER3) { | ||
449 | nfsd_ver = NFS3_VERSION; | ||
450 | mountd_ver = NFS_MNT3_VERSION; | ||
451 | nfsd_port = NFS_PORT; | ||
452 | mountd_port = NFS_MNT_PORT; | ||
453 | } else { | ||
454 | nfsd_ver = NFS2_VERSION; | ||
455 | mountd_ver = NFS_MNT_VERSION; | ||
456 | nfsd_port = NFS_PORT; | ||
457 | mountd_port = NFS_MNT_PORT; | ||
458 | } | ||
459 | |||
460 | proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; | ||
461 | |||
462 | if (nfs_port < 0) { | ||
463 | if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) { | ||
464 | printk(KERN_ERR "Root-NFS: Unable to get nfsd port " | ||
465 | "number from server, using default\n"); | ||
466 | port = nfsd_port; | ||
467 | } | ||
468 | nfs_port = port; | ||
469 | dprintk("Root-NFS: Portmapper on server returned %d " | ||
470 | "as nfsd port\n", port); | ||
471 | } | 300 | } |
472 | 301 | ||
473 | if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) { | 302 | if (root_nfs_data(nfs_root_parms) < 0) |
474 | printk(KERN_ERR "Root-NFS: Unable to get mountd port " | 303 | return -1; |
475 | "number from server, using default\n"); | ||
476 | port = mountd_port; | ||
477 | } | ||
478 | mount_port = port; | ||
479 | dprintk("Root-NFS: mountd port is %d\n", port); | ||
480 | 304 | ||
305 | *root_device = nfs_root_device; | ||
306 | *root_data = nfs_root_options; | ||
481 | return 0; | 307 | return 0; |
482 | } | 308 | } |
483 | |||
484 | |||
485 | /* | ||
486 | * Get a file handle from the server for the directory which is to be | ||
487 | * mounted. | ||
488 | */ | ||
489 | static int __init root_nfs_get_handle(void) | ||
490 | { | ||
491 | struct sockaddr_in sin; | ||
492 | unsigned int auth_flav_len = 0; | ||
493 | struct nfs_mount_request request = { | ||
494 | .sap = (struct sockaddr *)&sin, | ||
495 | .salen = sizeof(sin), | ||
496 | .dirpath = nfs_export_path, | ||
497 | .version = (nfs_data.flags & NFS_MOUNT_VER3) ? | ||
498 | NFS_MNT3_VERSION : NFS_MNT_VERSION, | ||
499 | .protocol = (nfs_data.flags & NFS_MOUNT_TCP) ? | ||
500 | XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP, | ||
501 | .auth_flav_len = &auth_flav_len, | ||
502 | }; | ||
503 | int status = -ENOMEM; | ||
504 | |||
505 | request.fh = nfs_alloc_fhandle(); | ||
506 | if (!request.fh) | ||
507 | goto out; | ||
508 | set_sockaddr(&sin, servaddr, htons(mount_port)); | ||
509 | status = nfs_mount(&request); | ||
510 | if (status < 0) | ||
511 | printk(KERN_ERR "Root-NFS: Server returned error %d " | ||
512 | "while mounting %s\n", status, nfs_export_path); | ||
513 | else { | ||
514 | nfs_data.root.size = request.fh->size; | ||
515 | memcpy(&nfs_data.root.data, request.fh->data, request.fh->size); | ||
516 | } | ||
517 | nfs_free_fhandle(request.fh); | ||
518 | out: | ||
519 | return status; | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | * Get the NFS port numbers and file handle, and return the prepared 'data' | ||
524 | * argument for mount() if everything went OK. Return NULL otherwise. | ||
525 | */ | ||
526 | void * __init nfs_root_data(void) | ||
527 | { | ||
528 | if (root_nfs_init() < 0 | ||
529 | || root_nfs_ports() < 0 | ||
530 | || root_nfs_get_handle() < 0) | ||
531 | return NULL; | ||
532 | set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, htons(nfs_port)); | ||
533 | return (void*)&nfs_data; | ||
534 | } | ||