diff options
Diffstat (limited to 'fs/nfs/nfsroot.c')
-rw-r--r-- | fs/nfs/nfsroot.c | 568 |
1 files changed, 172 insertions, 396 deletions
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index df101d9f546a..903908a20023 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,245 @@ | |||
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 | |||
110 | /* NFS-related data */ | ||
111 | static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ | ||
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 | 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 | ||
171 | /* | 105 | /* |
172 | * Parse option string. | 106 | * When the "nfsrootdebug" kernel command line option is specified, |
107 | * enable debugging messages for NFSROOT. | ||
173 | */ | 108 | */ |
174 | 109 | static int __init nfs_root_debug(char *__unused) | |
175 | static int __init root_nfs_parse(char *name, char *buf) | ||
176 | { | 110 | { |
177 | 111 | 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; | 112 | return 1; |
286 | } | 113 | } |
287 | 114 | ||
115 | __setup("nfsrootdebug", nfs_root_debug); | ||
116 | #endif | ||
117 | |||
288 | /* | 118 | /* |
289 | * Prepare the NFS data structure and parse all options. | 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. | ||
290 | */ | 126 | */ |
291 | static int __init root_nfs_name(char *name) | 127 | static int __init nfs_root_setup(char *line) |
292 | { | 128 | { |
293 | static char buf[NFS_MAXPATHLEN] __initdata; | 129 | ROOT_DEV = Root_NFS; |
294 | char *cp; | 130 | |
295 | 131 | if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { | |
296 | /* Set some default values */ | 132 | strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms)); |
297 | memset(&nfs_data, 0, sizeof(nfs_data)); | 133 | } else { |
298 | nfs_port = -1; | 134 | size_t n = strlen(line) + sizeof(NFS_ROOT) - 1; |
299 | nfs_data.version = NFS_MOUNT_VERSION; | 135 | if (n >= sizeof(nfs_root_parms)) |
300 | nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ | 136 | line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0'; |
301 | nfs_data.rsize = NFS_DEF_FILE_IO_SIZE; | 137 | 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 | } | 138 | } |
320 | sprintf(nfs_export_path, buf, cp); | 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); | ||
321 | 148 | ||
322 | return 1; | 149 | return 1; |
323 | } | 150 | } |
324 | 151 | ||
152 | __setup("nfsroot=", nfs_root_setup); | ||
325 | 153 | ||
326 | /* | 154 | static int __init root_nfs_copy(char *dest, const char *src, |
327 | * Get NFS server address. | 155 | const size_t destlen) |
328 | */ | ||
329 | static int __init root_nfs_addr(void) | ||
330 | { | 156 | { |
331 | if ((servaddr = root_server_addr) == htonl(INADDR_NONE)) { | 157 | if (strlcpy(dest, src, destlen) > destlen) |
332 | printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n"); | ||
333 | return -1; | 158 | return -1; |
334 | } | 159 | return 0; |
160 | } | ||
335 | 161 | ||
336 | snprintf(nfs_data.hostname, sizeof(nfs_data.hostname), | 162 | static int __init root_nfs_cat(char *dest, const char *src, |
337 | "%pI4", &servaddr); | 163 | const size_t destlen) |
164 | { | ||
165 | if (strlcat(dest, src, destlen) > destlen) | ||
166 | return -1; | ||
338 | return 0; | 167 | return 0; |
339 | } | 168 | } |
340 | 169 | ||
341 | /* | 170 | /* |
342 | * Tell the user what's going on. | 171 | * Parse out root export path and mount options from |
172 | * passed-in string @incoming. | ||
173 | * | ||
174 | * Copy the export path into @exppath. | ||
343 | */ | 175 | */ |
344 | #ifdef NFSROOT_DEBUG | 176 | static int __init root_nfs_parse_options(char *incoming, char *exppath, |
345 | static void __init root_nfs_print(void) | 177 | const size_t exppathlen) |
346 | { | 178 | { |
347 | printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", | 179 | 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 | 180 | ||
360 | static int __init root_nfs_init(void) | 181 | /* |
361 | { | 182 | * Set the NFS remote path |
362 | #ifdef NFSROOT_DEBUG | 183 | */ |
363 | nfs_debug |= NFSDBG_ROOT; | 184 | p = strsep(&incoming, ","); |
364 | #endif | 185 | if (*p != '\0' && strcmp(p, "default") != 0) |
186 | if (root_nfs_copy(exppath, p, exppathlen)) | ||
187 | return -1; | ||
365 | 188 | ||
366 | /* | 189 | /* |
367 | * Decode the root directory path name and NFS options from | 190 | * @incoming now points to the rest of the string; if it |
368 | * the kernel command line. This has to go here in order to | 191 | * 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 | */ | 192 | */ |
372 | if (root_nfs_name(nfs_root_name) < 0 || | 193 | if (incoming != NULL && *incoming != '\0') |
373 | root_nfs_addr() < 0) | 194 | if (root_nfs_cat(nfs_root_options, incoming, |
374 | return -1; | 195 | sizeof(nfs_root_options))) |
196 | return -1; | ||
375 | 197 | ||
376 | #ifdef NFSROOT_DEBUG | 198 | /* |
377 | root_nfs_print(); | 199 | * Possibly prepare for more options to be appended |
378 | #endif | 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; | ||
379 | 206 | ||
380 | return 0; | 207 | return 0; |
381 | } | 208 | } |
382 | 209 | ||
383 | |||
384 | /* | 210 | /* |
385 | * Parse NFS server and directory information passed on the kernel | 211 | * Decode the export directory path name and NFS options from |
386 | * command line. | 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. | ||
387 | */ | 217 | */ |
388 | static int __init nfs_root_setup(char *line) | 218 | static int __init root_nfs_data(char *cmdline) |
389 | { | 219 | { |
390 | ROOT_DEV = Root_NFS; | 220 | char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1]; |
391 | if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { | 221 | int len, retval = -1; |
392 | strlcpy(nfs_root_name, line, sizeof(nfs_root_name)); | 222 | char *tmp = NULL; |
393 | } else { | 223 | const size_t tmplen = sizeof(nfs_export_path); |
394 | int n = strlen(line) + sizeof(NFS_ROOT) - 1; | 224 | |
395 | if (n >= sizeof(nfs_root_name)) | 225 | tmp = kzalloc(tmplen, GFP_KERNEL); |
396 | line[sizeof(nfs_root_name) - sizeof(NFS_ROOT) - 2] = '\0'; | 226 | if (tmp == NULL) |
397 | sprintf(nfs_root_name, NFS_ROOT, line); | 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; | ||
398 | } | 235 | } |
399 | root_server_addr = root_nfs_parse_addr(nfs_root_name); | ||
400 | return 1; | ||
401 | } | ||
402 | |||
403 | __setup("nfsroot=", nfs_root_setup); | ||
404 | |||
405 | /*************************************************************************** | ||
406 | 236 | ||
407 | Routines to actually mount the root directory | 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 | } | ||
408 | 242 | ||
409 | ***************************************************************************/ | 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; | ||
410 | 252 | ||
411 | /* | 253 | /* |
412 | * Construct sockaddr_in from address and port number. | 254 | * Set up nfs_root_device. For NFS mounts, this looks like |
413 | */ | 255 | * |
414 | static inline void | 256 | * server:/path |
415 | set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port) | 257 | * |
416 | { | 258 | * At this point, utsname()->nodename contains our local |
417 | sin->sin_family = AF_INET; | 259 | * IP address or hostname, set by ipconfig. If "%s" exists |
418 | sin->sin_addr.s_addr = addr; | 260 | * in tmp, substitute the nodename, then shovel the whole |
419 | sin->sin_port = port; | 261 | * mess into nfs_root_device. |
420 | } | 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; | ||
421 | 271 | ||
422 | /* | 272 | 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 | 273 | ||
429 | printk(KERN_NOTICE "Looking up port of RPC %d/%d on %pI4\n", | 274 | out: |
430 | program, version, &servaddr); | 275 | kfree(tmp); |
431 | set_sockaddr(&sin, servaddr, 0); | 276 | return retval; |
432 | return rpcb_getport_sync(&sin, program, version, proto); | 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; | ||
433 | } | 286 | } |
434 | 287 | ||
435 | 288 | /** | |
436 | /* | 289 | * nfs_root_data - Return prepared 'data' for NFSROOT mount |
437 | * Use portmapper to find mountd and nfsd port numbers if not overriden | 290 | * @root_device: OUT: address of string containing NFSROOT device |
438 | * by the user. Use defaults if portmapper is not available. | 291 | * @root_data: OUT: address of string containing NFSROOT mount options |
439 | * XXX: Is there any nfs server with no portmapper? | 292 | * |
293 | * Returns zero and sets @root_device and @root_data if successful, | ||
294 | * otherwise -1 is returned. | ||
440 | */ | 295 | */ |
441 | static int __init root_nfs_ports(void) | 296 | int __init nfs_root_data(char **root_device, char **root_data) |
442 | { | 297 | { |
443 | int port; | 298 | servaddr = root_server_addr; |
444 | int nfsd_ver, mountd_ver; | 299 | if (servaddr == htonl(INADDR_NONE)) { |
445 | int nfsd_port, mountd_port; | 300 | printk(KERN_ERR "Root-NFS: no NFS server address\n"); |
446 | int proto; | 301 | 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 | } | 302 | } |
472 | 303 | ||
473 | if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) { | 304 | if (root_nfs_data(nfs_root_parms) < 0) |
474 | printk(KERN_ERR "Root-NFS: Unable to get mountd port " | 305 | 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 | 306 | ||
307 | *root_device = nfs_root_device; | ||
308 | *root_data = nfs_root_options; | ||
481 | return 0; | 309 | return 0; |
482 | } | 310 | } |
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 | } | ||