1 #ifdef DOWNLOAD_PROTO_NFS
6 /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
7 * large portions are copied verbatim) as distributed in OSKit 0.97. A few
8 * changes were necessary to adapt the code to Etherboot and to fix several
9 * inconsistencies. Also the RPC message preparation is done "by hand" to
10 * avoid adding netsprintf() which I find hard to understand and use. */
12 /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
13 * it loads the kernel image off the boot server (ARP_SERVER) and does not
14 * access the client root disk (root-path in dhcpd.conf), which would use
15 * ARP_ROOTSERVER. The root disk is something the operating system we are
16 * about to load needs to use. This is different from the OSKit 0.97 logic. */
18 /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
19 * If a symlink is encountered, it is followed as far as possible (recursion
20 * possible, maximum 16 steps). There is no clearing of ".."'s inside the
21 * path, so please DON'T DO THAT. thx. */
23 #define START_OPORT 700 /* mountd usually insists on secure ports */
24 #define OPORT_SWEEP 200 /* make sure we don't leave secure range */
26 static int oport = START_OPORT;
27 static int mount_port = -1;
28 static int nfs_port = -1;
29 static int fs_mounted = 0;
30 static unsigned long rpc_id;
32 /**************************************************************************
33 RPC_INIT - set up the ID counter to something fairly random
34 **************************************************************************/
40 rpc_id = t ^ (t << 8) ^ (t << 16);
44 /**************************************************************************
45 RPC_PRINTERROR - Print a low level RPC error message
46 **************************************************************************/
47 static void rpc_printerror(struct rpc_t *rpc)
49 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
50 rpc->u.reply.astatus) {
51 /* rpc_printerror() is called for any RPC related error,
52 * suppress output if no low level RPC error happened. */
53 printf("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
54 ntohl(rpc->u.reply.verifier),
55 ntohl(rpc->u.reply.astatus));
59 /**************************************************************************
60 AWAIT_RPC - Wait for an rpc packet
61 **************************************************************************/
62 static int await_rpc(int ival, void *ptr,
63 unsigned short ptype, struct iphdr *ip, struct udphdr *udp)
68 if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
70 if (ntohs(udp->dest) != ival)
72 if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
74 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
75 if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
77 if (MSG_REPLY != ntohl(rpc->u.reply.type))
82 /**************************************************************************
83 RPC_LOOKUP - Lookup RPC Port numbers
84 **************************************************************************/
85 static int rpc_lookup(int addr, int prog, int ver, int sport)
87 struct rpc_t buf, *rpc;
93 buf.u.call.id = htonl(id);
94 buf.u.call.type = htonl(MSG_CALL);
95 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
96 buf.u.call.prog = htonl(PROG_PORTMAP);
97 buf.u.call.vers = htonl(2); /* portmapper is version 2 */
98 buf.u.call.proc = htonl(PORTMAP_GETPORT);
99 p = (long *)buf.u.call.data;
100 *p++ = 0; *p++ = 0; /* auth credential */
101 *p++ = 0; *p++ = 0; /* auth verifier */
104 *p++ = htonl(IP_UDP);
106 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
108 udp_transmit(arptable[addr].ipaddr.s_addr, sport, SUNRPC_PORT,
109 (char *)p - (char *)&buf, &buf);
110 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
111 if (await_reply(await_rpc, sport, &id, timeout)) {
112 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
113 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
114 rpc->u.reply.astatus) {
118 return ntohl(rpc->u.reply.data[0]);
125 /**************************************************************************
126 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
127 **************************************************************************/
128 static long *rpc_add_credentials(long *p)
132 /* Here's the executive summary on authentication requirements of the
133 * various NFS server implementations: Linux accepts both AUTH_NONE
134 * and AUTH_UNIX authentication (also accepts an empty hostname field
135 * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
136 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
137 * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
138 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
141 hl = (hostnamelen + 3) & ~3;
143 /* Provide an AUTH_UNIX credential. */
144 *p++ = htonl(1); /* AUTH_UNIX */
145 *p++ = htonl(hl+20); /* auth length */
146 *p++ = htonl(0); /* stamp */
147 *p++ = htonl(hostnamelen); /* hostname string */
148 if (hostnamelen & 3) {
149 *(p + hostnamelen / 4) = 0; /* add zero padding */
151 memcpy(p, hostname, hostnamelen);
155 *p++ = 0; /* auxiliary gid list */
157 /* Provide an AUTH_NONE verifier. */
158 *p++ = 0; /* AUTH_NONE */
159 *p++ = 0; /* auth length */
164 /**************************************************************************
165 NFS_PRINTERROR - Print a NFS error message
166 **************************************************************************/
167 static void nfs_printerror(int err)
171 printf("Not owner\n");
174 printf("No such file or directory\n");
177 printf("Permission denied\n");
180 printf("Directory given where filename expected\n");
183 printf("Invalid filehandle\n");
184 break; // INVAL is not defined in NFSv2, some NFS-servers
185 // seem to use it in answers to v2 nevertheless.
187 printf("low-level RPC failure (parameter decoding problem?)\n");
190 printf("low-level RPC failure (authentication problem?)\n");
193 printf("Unknown NFS error %d\n", -err);
197 /**************************************************************************
198 NFS_MOUNT - Mount an NFS Filesystem
199 **************************************************************************/
200 static int nfs_mount(int server, int port, char *path, char *fh, int sport)
202 struct rpc_t buf, *rpc;
206 int pathlen = strlen(path);
209 buf.u.call.id = htonl(id);
210 buf.u.call.type = htonl(MSG_CALL);
211 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
212 buf.u.call.prog = htonl(PROG_MOUNT);
213 buf.u.call.vers = htonl(1); /* mountd is version 1 */
214 buf.u.call.proc = htonl(MOUNT_ADDENTRY);
215 p = rpc_add_credentials((long *)buf.u.call.data);
216 *p++ = htonl(pathlen);
218 *(p + pathlen / 4) = 0; /* add zero padding */
220 memcpy(p, path, pathlen);
221 p += (pathlen + 3) / 4;
222 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
224 udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
225 (char *)p - (char *)&buf, &buf);
226 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
227 if (await_reply(await_rpc, sport, &id, timeout)) {
228 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
229 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
230 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
232 if (rpc->u.reply.rstatus) {
233 /* RPC failed, no verifier, data[0] */
236 if (rpc->u.reply.astatus) {
237 /* RPC couldn't decode parameters */
240 return -ntohl(rpc->u.reply.data[0]);
243 memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
251 /**************************************************************************
252 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
253 **************************************************************************/
254 void nfs_umountall(int server)
256 struct rpc_t buf, *rpc;
261 if (!arptable[server].ipaddr.s_addr) {
262 /* Haven't sent a single UDP packet to this server */
265 if ((mount_port == -1) || (!fs_mounted)) {
266 /* Nothing mounted, nothing to umount */
270 buf.u.call.id = htonl(id);
271 buf.u.call.type = htonl(MSG_CALL);
272 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
273 buf.u.call.prog = htonl(PROG_MOUNT);
274 buf.u.call.vers = htonl(1); /* mountd is version 1 */
275 buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
276 p = rpc_add_credentials((long *)buf.u.call.data);
277 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
278 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
279 udp_transmit(arptable[server].ipaddr.s_addr, oport, mount_port,
280 (char *)p - (char *)&buf, &buf);
281 if (await_reply(await_rpc, oport, &id, timeout)) {
282 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
283 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
284 rpc->u.reply.astatus) {
292 /***************************************************************************
293 * NFS_READLINK (AH 2003-07-14)
294 * This procedure is called when read of the first block fails -
295 * this probably happens when it's a directory or a symlink
296 * In case of successful readlink(), the dirname is manipulated,
297 * so that inside the nfs() function a recursion can be done.
298 **************************************************************************/
299 static int nfs_readlink(int server, int port, char *fh, char *path, char *nfh,
302 struct rpc_t buf, *rpc;
306 int pathlen = strlen(path);
309 buf.u.call.id = htonl(id);
310 buf.u.call.type = htonl(MSG_CALL);
311 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
312 buf.u.call.prog = htonl(PROG_NFS);
313 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
314 buf.u.call.proc = htonl(NFS_READLINK);
315 p = rpc_add_credentials((long *)buf.u.call.data);
316 memcpy(p, nfh, NFS_FHSIZE);
317 p += (NFS_FHSIZE / 4);
318 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
319 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
320 udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
321 (char *)p - (char *)&buf, &buf);
322 if (await_reply(await_rpc, sport, &id, timeout)) {
323 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
324 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
325 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
327 if (rpc->u.reply.rstatus) {
328 /* RPC failed, no verifier, data[0] */
331 if (rpc->u.reply.astatus) {
332 /* RPC couldn't decode parameters */
335 return -ntohl(rpc->u.reply.data[0]);
338 // If it's a relative link, append everything to dirname, filename TOO!
339 retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
340 if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
341 path[pathlen++] = '/';
342 while ( ( retries + pathlen ) > 298 ) {
346 memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
347 } else { retries = 0; }
348 path[pathlen + retries] = 0;
350 // Else make it the only path.
351 if ( retries > 298 ) { retries = 298; }
352 memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
361 /**************************************************************************
362 NFS_LOOKUP - Lookup Pathname
363 **************************************************************************/
364 static int nfs_lookup(int server, int port, char *fh, char *path, char *nfh,
367 struct rpc_t buf, *rpc;
371 int pathlen = strlen(path);
374 buf.u.call.id = htonl(id);
375 buf.u.call.type = htonl(MSG_CALL);
376 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
377 buf.u.call.prog = htonl(PROG_NFS);
378 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
379 buf.u.call.proc = htonl(NFS_LOOKUP);
380 p = rpc_add_credentials((long *)buf.u.call.data);
381 memcpy(p, fh, NFS_FHSIZE);
382 p += (NFS_FHSIZE / 4);
383 *p++ = htonl(pathlen);
385 *(p + pathlen / 4) = 0; /* add zero padding */
387 memcpy(p, path, pathlen);
388 p += (pathlen + 3) / 4;
389 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
390 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
391 udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
392 (char *)p - (char *)&buf, &buf);
393 if (await_reply(await_rpc, sport, &id, timeout)) {
394 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
395 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
396 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
398 if (rpc->u.reply.rstatus) {
399 /* RPC failed, no verifier, data[0] */
402 if (rpc->u.reply.astatus) {
403 /* RPC couldn't decode parameters */
406 return -ntohl(rpc->u.reply.data[0]);
408 memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
416 /**************************************************************************
417 NFS_READ - Read File on NFS Server
418 **************************************************************************/
419 static int nfs_read(int server, int port, char *fh, int offset, int len,
422 struct rpc_t buf, *rpc;
429 * Try to implement something similar to a window protocol in
430 * terms of response to losses. On successful receive, increment
431 * the number of tokens by 1 (cap at 256). On failure, halve it.
432 * When the number of tokens is >= 2, use a very short timeout.
436 buf.u.call.id = htonl(id);
437 buf.u.call.type = htonl(MSG_CALL);
438 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
439 buf.u.call.prog = htonl(PROG_NFS);
440 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
441 buf.u.call.proc = htonl(NFS_READ);
442 p = rpc_add_credentials((long *)buf.u.call.data);
443 memcpy(p, fh, NFS_FHSIZE);
445 *p++ = htonl(offset);
447 *p++ = 0; /* unused parameter */
448 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
449 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
451 timeout = TICKS_PER_SEC/2;
453 udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
454 (char *)p - (char *)&buf, &buf);
455 if (await_reply(await_rpc, sport, &id, timeout)) {
458 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
459 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
460 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
462 if (rpc->u.reply.rstatus) {
463 /* RPC failed, no verifier, data[0] */
466 if (rpc->u.reply.astatus) {
467 /* RPC couldn't decode parameters */
470 return -ntohl(rpc->u.reply.data[0]);
480 /**************************************************************************
481 NFS - Download extended BOOTP data, or kernel image from NFS server
482 **************************************************************************/
483 int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
485 static int recursion = 0;
487 int err, namelen = strlen(name);
488 char dirname[300], *fname;
489 char dirfh[NFS_FHSIZE]; /* file handle of directory */
490 char filefh[NFS_FHSIZE]; /* file handle of kernel image */
492 int rlen, size, offs, len;
498 if (oport > START_OPORT+OPORT_SWEEP) {
501 if ( name != dirname ) {
502 memcpy(dirname, name, namelen + 1);
506 if ( recursion > NFS_MAXLINKDEPTH ) {
507 printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
511 fname = dirname + (namelen - 1);
512 while (fname >= dirname) {
520 if (fname < dirname) {
521 printf("can't parse file name %s\n", name);
525 if (mount_port == -1) {
526 mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
528 if (nfs_port == -1) {
529 nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
531 if (nfs_port == -1 || mount_port == -1) {
532 printf("can't get nfs/mount ports from portmapper\n");
537 err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
539 printf("mounting %s: ", dirname);
541 /* just to be sure... */
542 nfs_umountall(ARP_SERVER);
546 err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
548 printf("looking up %s: ", fname);
550 nfs_umountall(ARP_SERVER);
555 block = 1; /* blocks are numbered starting from 1 */
556 size = -1; /* will be set properly with the first reply */
557 len = NFS_READ_SIZE; /* first request is always full size */
559 err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
560 if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
561 // An error occured. NFS servers tend to sending
562 // errors 21 / 22 when symlink instead of real file
563 // is requested. So check if it's a symlink!
564 block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
567 printf("\nLoading symlink:%s ..",dirname);
571 nfs_umountall(ARP_SERVER);
575 printf("reading at offset %d: ", offs);
577 nfs_umountall(ARP_SERVER);
581 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
583 /* size must be found out early to allow EOF detection */
585 size = ntohl(rpc->u.reply.data[6]);
587 rlen = ntohl(rpc->u.reply.data[18]);
589 rlen = len; /* shouldn't happen... */
592 err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
593 (offs+rlen == size));
595 nfs_umountall(ARP_SERVER);
601 /* last request is done with matching requested read size */
602 if (size-offs < NFS_READ_SIZE) {
606 /* len == 0 means that all the file has been read */
610 #endif /* DOWNLOAD_PROTO_NFS */