2 #include <sys/socket.h>
3 #include <sys/utsname.h>
5 #include <netinet/in.h>
12 /* Default path we try to mount. "%s" gets replaced by our IP address */
13 #define NFS_ROOT "/tftpboot/%s"
14 #define NFS_DEF_FILE_IO_BUFFER_SIZE 4096
15 #define NFS_MAXPATHLEN 1024
16 #define NFS_MNT_PROGRAM 100005
17 #define NFS_MNT_PORT 627
18 #define NFS_PROGRAM 100003
20 #define NFS2_VERSION 2
21 #define NFS3_VERSION 3
22 #define NFS_MNT_PROGRAM 100005
23 #define NFS_MNT_VERSION 1
24 #define NFS_MNT3_VERSION 3
26 #define MOUNTPROC3_MNT 1
27 #define RPC_PMAP_PROGRAM 100000
28 #define RPC_PMAP_VERSION 2
29 #define RPC_PMAP_PORT 111
31 #define NFS2_FHSIZE 32
32 #define NFS3_FHSIZE 64
41 enum rpc_auth_flavor {
54 #define NFS_MAXFHSIZE 64
57 unsigned char data[NFS_MAXFHSIZE];
61 char data[NFS2_FHSIZE];
64 #define NFS_MOUNT_VERSION 4
66 struct nfs_mount_data {
69 struct nfs2_fh old_root;
79 struct sockaddr_in addr;
86 #define NFS_MOUNT_SOFT 0x0001 /* 1 */
87 #define NFS_MOUNT_INTR 0x0002 /* 1 */
88 #define NFS_MOUNT_SECURE 0x0004 /* 1 */
89 #define NFS_MOUNT_POSIX 0x0008 /* 1 */
90 #define NFS_MOUNT_NOCTO 0x0010 /* 1 */
91 #define NFS_MOUNT_NOAC 0x0020 /* 1 */
92 #define NFS_MOUNT_TCP 0x0040 /* 2 */
93 #define NFS_MOUNT_VER3 0x0080 /* 3 */
94 #define NFS_MOUNT_KERBEROS 0x0100 /* 3 */
95 #define NFS_MOUNT_NONLM 0x0200 /* 3 */
96 #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */
97 #define NFS_MOUNT_FLAGMASK 0xFFFF
99 static char nfs_root_name[256];
100 static u_int32_t root_server_addr;
101 static char root_server_path[256];
103 /* Address of NFS server */
104 static u_int32_t servaddr;
106 /* Name of directory to mount */
107 static char nfs_path[NFS_MAXPATHLEN];
109 /* NFS-related data */
110 static struct nfs_mount_data nfs_data = {
111 .version = NFS_MOUNT_VERSION,
112 .flags = NFS_MOUNT_NONLM, /* No lockd in nfs root yet */
113 .rsize = NFS_DEF_FILE_IO_BUFFER_SIZE,
114 .wsize = NFS_DEF_FILE_IO_BUFFER_SIZE,
123 static int nfs_port = -1;
124 static int mount_port;
126 /***************************************************************************
130 ***************************************************************************/
133 * The following integer options are recognized
135 static struct nfs_int_opts {
138 } root_int_opts[] = {
139 { "port", &nfs_port },
140 { "rsize", &nfs_data.rsize },
141 { "wsize", &nfs_data.wsize },
142 { "timeo", &nfs_data.timeo },
143 { "retrans", &nfs_data.retrans },
144 { "acregmin", &nfs_data.acregmin },
145 { "acregmax", &nfs_data.acregmax },
146 { "acdirmin", &nfs_data.acdirmin },
147 { "acdirmax", &nfs_data.acdirmax },
152 * And now the flag options
154 static struct nfs_bool_opts {
158 } root_bool_opts[] = {
159 { "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
160 { "hard", ~NFS_MOUNT_SOFT, 0 },
161 { "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
162 { "nointr", ~NFS_MOUNT_INTR, 0 },
163 { "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
164 { "noposix", ~NFS_MOUNT_POSIX, 0 },
165 { "cto", ~NFS_MOUNT_NOCTO, 0 },
166 { "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
167 { "ac", ~NFS_MOUNT_NOAC, 0 },
168 { "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
169 { "lock", ~NFS_MOUNT_NONLM, 0 },
170 { "nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM },
172 { "v2", ~NFS_MOUNT_VER3, 0 },
173 { "v3", ~NFS_MOUNT_VER3, NFS_MOUNT_VER3 },
175 { "udp", ~NFS_MOUNT_TCP, 0 },
176 { "tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP },
177 { "broken_suid",~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID },
181 * Parse option string.
183 static void root_nfs_parse(char *name, char *buf)
185 char *options, *val, *cp;
187 if ((options = strchr(name, ','))) {
189 cp = strtok(options, ",");
191 if ((val = strchr(cp, '='))) {
192 struct nfs_int_opts *opts = root_int_opts;
194 while (opts->name && strcmp(opts->name, cp))
197 *(opts->val) = (int) strtoul(val, NULL, 10);
199 struct nfs_bool_opts *opts = root_bool_opts;
200 while (opts->name && strcmp(opts->name, cp))
203 nfs_data.flags &= opts->and_mask;
204 nfs_data.flags |= opts->or_mask;
207 cp = strtok(NULL, ",");
210 if (name[0] && strcmp(name, "default")) {
211 strncpy(buf, name, NFS_MAXPATHLEN-1);
212 buf[NFS_MAXPATHLEN-1] = 0;
217 * Prepare the NFS data structure and parse all options.
219 static int root_nfs_name(char *name)
221 char buf[NFS_MAXPATHLEN];
222 struct utsname uname_buf;
224 /* Set some default values */
225 strcpy(buf, NFS_ROOT);
227 /* Process options received from the remote server */
228 root_nfs_parse(root_server_path, buf);
230 /* Override them by options set on kernel command-line */
231 root_nfs_parse(name, buf);
234 if (strlen(buf) + strlen(uname_buf.nodename) > NFS_MAXPATHLEN) {
235 printf("nfsroot: Pathname for remote directory too long.\n");
238 sprintf(nfs_path, buf, uname_buf.nodename);
243 /***************************************************************************
245 Routines to actually mount the root directory
247 ***************************************************************************/
250 * Construct sockaddr_in from address and port number.
253 set_sockaddr(struct sockaddr_in *sin, u_int32_t addr, u_int16_t port)
255 memset(sin, 0, sizeof(*sin));
256 sin->sin_family = AF_INET;
257 sin->sin_addr.s_addr = addr;
258 sin->sin_port = port;
262 * Extremely crude RPC-over-UDP call. We get an already encoded request
263 * to pass, we do that and put the reply into buffer. That (and callers
264 * below - getport, getfh2 and getfh3) should be replaced with proper
265 * librpc use. Now, if we only had one that wasn't bloated as a dead
266 * gnu that had lied for a while under the sun...
269 static u_int32_t XID;
271 static void timeout(int n)
276 static int do_call(struct sockaddr_in *sin, u_int32_t msg[], u_int32_t rmsg[],
277 u_int32_t len, u_int32_t rlen)
279 struct sockaddr_in from;
280 int slen = sizeof(struct sockaddr_in);
281 struct timeval tv = {1, 0};
285 signal(SIGALRM, timeout);
286 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
289 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&tv, sizeof(tv));
291 if (sendto(fd, msg, len, 0, (struct sockaddr *)sin, slen)!=(int)len)
293 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv, sizeof(tv));
300 n = recvfrom(fd, rmsg, rlen, 0, (struct sockaddr*)&from, &slen);
303 } while (memcmp(&from, sin, sizeof(from)) || rmsg[0] != msg[0]);
305 if (n < 6*4 || n % 4 || ntohl(rmsg[1]) != 1 || rmsg[2] ||
306 rmsg[3] || rmsg[4] || rmsg[5])
312 Esend: printf("rpc: write failed\n");
314 Erecv: printf("rpc: read failed\n");
316 Einval: printf("rpc: invalid response\n");
318 Esocket:printf("rpc: can't create socket\n");
330 static void do_header(u_int32_t msg[], u_int32_t prog, u_int32_t vers, u_int32_t proc)
333 msg[1] = htonl(RPC_CALL);
334 msg[2] = htonl(RPC_VERSION);
335 msg[3] = htonl(prog);
336 msg[4] = htonl(vers);
337 msg[5] = htonl(proc);
338 msg[6] = htonl(RPC_AUTH_NULL);
340 msg[8] = htonl(RPC_AUTH_NULL);
344 static int getport(u_int32_t prog, u_int32_t vers, u_int32_t prot)
346 struct sockaddr_in sin;
350 set_sockaddr(&sin, servaddr, htons(RPC_PMAP_PORT));
351 do_header(msg, RPC_PMAP_PROGRAM, RPC_PMAP_VERSION, PMAP_GETPORT);
352 msg[10] = htonl(prog);
353 msg[11] = htonl(vers);
354 msg[12] = htonl(prot);
356 n = do_call(&sin, msg, rmsg, 14, 7);
360 return ntohl(rmsg[6]);
363 static int getfh2(void)
365 struct sockaddr_in sin;
366 unsigned msg[10+1+256/4];
367 unsigned rmsg[6 + 1 + NFS2_FHSIZE/4];
369 int len = strlen(nfs_path);
370 set_sockaddr(&sin, servaddr, mount_port);
373 printf("nfsroot: pathname is too long");
376 memset(msg, 0, sizeof(msg));
377 do_header(msg, NFS_MNT_PROGRAM, NFS_MNT_VERSION, MNTPROC_MNT);
378 msg[10] = htonl(len);
379 strcpy((char*)&msg[11], nfs_path);
380 n = do_call(&sin, msg, rmsg, 11 + (len + 3)/4, 7 + NFS2_FHSIZE/4);
383 if (n != NFS2_FHSIZE/4 + 1)
386 printf("nfsroot: mountd returned an error (%d)",htonl(rmsg[6]));
389 nfs_data.root.size = NFS2_FHSIZE;
390 memcpy(nfs_data.root.data, &rmsg[7], NFS2_FHSIZE);
393 printf("nfsroot: bad fhandle size");
397 static int getfh3(void)
399 struct sockaddr_in sin;
400 unsigned msg[10+1+256/4];
401 unsigned rmsg[6 + 1 + 1 + NFS3_FHSIZE/4];
403 int len = strlen(nfs_path);
405 set_sockaddr(&sin, servaddr, mount_port);
408 printf("nfsroot: pathname is too long");
411 memset(msg, 0, sizeof(msg));
412 do_header(msg, NFS_MNT_PROGRAM, NFS_MNT3_VERSION, MOUNTPROC3_MNT);
413 msg[10] = htonl(len);
414 strcpy((char*)&msg[11], nfs_path);
415 n = do_call(&sin, msg, rmsg, 11 + (len + 3)/4, 8 + NFS3_FHSIZE/4);
421 printf("nfsroot: mountd returned an error (%d)",htonl(rmsg[6]));
424 size = ntohl(rmsg[7]);
425 if (size > NFS3_FHSIZE || n != 2 + size/4)
427 nfs_data.root.size = size;
428 memcpy(nfs_data.root.data, &rmsg[8], size);
431 printf("nfsroot: bad fhandle size");
436 * Use portmapper to find mountd and nfsd port numbers if not overriden
437 * by the user. Use defaults if portmapper is not available.
438 * XXX: Is there any nfs server with no portmapper?
440 static int root_nfs_ports(void)
443 int nfsd_ver, mountd_ver;
446 if (nfs_data.flags & NFS_MOUNT_VER3) {
447 nfsd_ver = NFS3_VERSION;
448 mountd_ver = NFS_MNT3_VERSION;
450 nfsd_ver = NFS2_VERSION;
451 mountd_ver = NFS_MNT_VERSION;
454 proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
457 if ((port = getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
458 printf("nfsroot: Unable to get nfsd port "
459 "number from server, using default\n");
462 nfs_port = htons(port);
463 printf("nfsroot: Portmapper on server returned %d "
464 "as nfsd port\n", port);
467 if ((port = getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
468 printf("nfsroot: Unable to get mountd port "
469 "number from server, using default\n");
472 mount_port = htons(port);
473 printf("nfsroot: mountd port is %d\n", port);
484 /* FIX: use getopt() instead of this */
486 s = getenv("root_server_addr");
488 root_server_addr = strtoul(s, NULL, 10);
489 s = getenv("root_server_path");
491 strncpy(root_server_path, s, 255);
492 s = getenv("nfs_root_name");
494 strncpy(nfs_root_name, s, 255);
497 * Decode the root directory path name and NFS options from
498 * the kernel command line. This has to go here in order to
499 * be able to use the client IP address for the remote root
500 * directory (necessary for pure RARP booting).
502 if (root_nfs_name(nfs_root_name) < 0)
504 if ((servaddr = root_server_addr) == INADDR_NONE) {
505 printf("nfsroot: No NFS server available, giving up.\n");
509 p = (char *) &servaddr;
510 sprintf(nfs_data.hostname, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
513 printf("nfsroot: Mounting %s on server %s as root\n",
514 nfs_path, nfs_data.hostname);
515 printf("nfsroot: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
516 nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
517 printf("nfsroot: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
518 nfs_data.acregmin, nfs_data.acregmax,
519 nfs_data.acdirmin, nfs_data.acdirmax);
520 printf("nfsroot: nfsd port = %d, mountd port = %d, flags = %08x\n",
521 nfs_port, mount_port, nfs_data.flags);
524 gettimeofday(&tv, NULL);
525 XID = (tv.tv_sec << 15) ^ tv.tv_usec;
527 if (root_nfs_ports() < 0)
529 if (nfs_data.flags & NFS_MOUNT_VER3) {
536 set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port);
537 return mount("/dev/root", "/mnt", "nfs", 0, &nfs_data) == 0;