7 /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
8 * large portions are copied verbatim) as distributed in OSKit 0.97. A few
9 * changes were necessary to adapt the code to Etherboot and to fix several
10 * inconsistencies. Also the RPC message preparation is done "by hand" to
11 * avoid adding netsprintf() which I find hard to understand and use. */
13 /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
14 * it loads the kernel image off the boot server (ARP_SERVER) and does not
15 * access the client root disk (root-path in dhcpd.conf), which would use
16 * ARP_ROOTSERVER. The root disk is something the operating system we are
17 * about to load needs to use. This is different from the OSKit 0.97 logic. */
19 /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
20 * If a symlink is encountered, it is followed as far as possible (recursion
21 * possible, maximum 16 steps). There is no clearing of ".."'s inside the
22 * path, so please DON'T DO THAT. thx. */
24 #define START_OPORT 700 /* mountd usually insists on secure ports */
25 #define OPORT_SWEEP 200 /* make sure we don't leave secure range */
27 static int oport = START_OPORT;
28 static struct sockaddr_in mount_server;
29 static struct sockaddr_in nfs_server;
30 static unsigned long rpc_id;
32 /**************************************************************************
33 RPC_INIT - set up the ID counter to something fairly random
34 **************************************************************************/
35 static void rpc_init(void)
40 rpc_id = t ^ (t << 8) ^ (t << 16);
43 /**************************************************************************
44 RPC_PRINTERROR - Print a low level RPC error message
45 **************************************************************************/
46 static void rpc_printerror(struct rpc_t *rpc)
48 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
49 rpc->u.reply.astatus) {
50 /* rpc_printerror() is called for any RPC related error,
51 * suppress output if no low level RPC error happened. */
52 DBG("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
53 ntohl(rpc->u.reply.verifier),
54 ntohl(rpc->u.reply.astatus));
58 /**************************************************************************
59 AWAIT_RPC - Wait for an rpc packet
60 **************************************************************************/
61 static int await_rpc(int ival, void *ptr,
62 unsigned short ptype __unused, struct iphdr *ip,
63 struct udphdr *udp, struct tcphdr *tcp __unused)
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(struct sockaddr_in *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 if ( ! addr->sin_port ) {
107 addr->sin_port = SUNRPC_PORT;
109 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
111 udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port,
112 (char *)p - (char *)&buf, &buf);
113 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
114 if (await_reply(await_rpc, sport, &id, timeout)) {
115 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
116 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
117 rpc->u.reply.astatus) {
121 return ntohl(rpc->u.reply.data[0]);
128 /**************************************************************************
129 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
130 **************************************************************************/
131 static long *rpc_add_credentials(long *p)
135 /* Here's the executive summary on authentication requirements of the
136 * various NFS server implementations: Linux accepts both AUTH_NONE
137 * and AUTH_UNIX authentication (also accepts an empty hostname field
138 * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
139 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
140 * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
141 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
144 hl = (hostnamelen + 3) & ~3;
146 /* Provide an AUTH_UNIX credential. */
147 *p++ = htonl(1); /* AUTH_UNIX */
148 *p++ = htonl(hl+20); /* auth length */
149 *p++ = htonl(0); /* stamp */
150 *p++ = htonl(hostnamelen); /* hostname string */
151 if (hostnamelen & 3) {
152 *(p + hostnamelen / 4) = 0; /* add zero padding */
154 memcpy(p, hostname, hostnamelen);
158 *p++ = 0; /* auxiliary gid list */
160 /* Provide an AUTH_NONE verifier. */
161 *p++ = 0; /* AUTH_NONE */
162 *p++ = 0; /* auth length */
167 /**************************************************************************
168 NFS_PRINTERROR - Print a NFS error message
169 **************************************************************************/
170 static void nfs_printerror(int err)
174 printf("Not owner\n");
177 printf("No such file or directory\n");
180 printf("Permission denied\n");
183 printf("Directory given where filename expected\n");
186 printf("Invalid filehandle\n");
187 break; // INVAL is not defined in NFSv2, some NFS-servers
188 // seem to use it in answers to v2 nevertheless.
190 printf("low-level RPC failure (parameter decoding problem?)\n");
193 printf("low-level RPC failure (authentication problem?)\n");
196 printf("Unknown NFS error %d\n", -err);
200 /**************************************************************************
201 NFS_MOUNT - Mount an NFS Filesystem
202 **************************************************************************/
203 static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport)
205 struct rpc_t buf, *rpc;
209 int pathlen = strlen(path);
212 buf.u.call.id = htonl(id);
213 buf.u.call.type = htonl(MSG_CALL);
214 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
215 buf.u.call.prog = htonl(PROG_MOUNT);
216 buf.u.call.vers = htonl(1); /* mountd is version 1 */
217 buf.u.call.proc = htonl(MOUNT_ADDENTRY);
218 p = rpc_add_credentials((long *)buf.u.call.data);
219 *p++ = htonl(pathlen);
221 *(p + pathlen / 4) = 0; /* add zero padding */
223 memcpy(p, path, pathlen);
224 p += (pathlen + 3) / 4;
225 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
227 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
228 (char *)p - (char *)&buf, &buf);
229 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
230 if (await_reply(await_rpc, sport, &id, timeout)) {
231 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
232 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
233 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
235 if (rpc->u.reply.rstatus) {
236 /* RPC failed, no verifier, data[0] */
239 if (rpc->u.reply.astatus) {
240 /* RPC couldn't decode parameters */
243 return -ntohl(rpc->u.reply.data[0]);
245 memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
253 /**************************************************************************
254 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
255 **************************************************************************/
256 static void nfs_umountall(struct sockaddr_in *server)
258 struct rpc_t buf, *rpc;
264 buf.u.call.id = htonl(id);
265 buf.u.call.type = htonl(MSG_CALL);
266 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
267 buf.u.call.prog = htonl(PROG_MOUNT);
268 buf.u.call.vers = htonl(1); /* mountd is version 1 */
269 buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
270 p = rpc_add_credentials((long *)buf.u.call.data);
271 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
272 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
273 udp_transmit(server->sin_addr.s_addr, oport, server->sin_port,
274 (char *)p - (char *)&buf, &buf);
275 if (await_reply(await_rpc, oport, &id, timeout)) {
276 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
277 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
278 rpc->u.reply.astatus) {
286 /**************************************************************************
287 NFS_RESET - Reset the NFS subsystem
288 **************************************************************************/
289 static void nfs_reset ( void ) {
290 /* If we have a mount server, call nfs_umountall() */
291 if ( mount_server.sin_addr.s_addr ) {
292 nfs_umountall ( &mount_server );
294 /* Zero the data structures */
295 memset ( &mount_server, 0, sizeof ( mount_server ) );
296 memset ( &nfs_server, 0, sizeof ( nfs_server ) );
299 /***************************************************************************
300 * NFS_READLINK (AH 2003-07-14)
301 * This procedure is called when read of the first block fails -
302 * this probably happens when it's a directory or a symlink
303 * In case of successful readlink(), the dirname is manipulated,
304 * so that inside the nfs() function a recursion can be done.
305 **************************************************************************/
306 static int nfs_readlink(struct sockaddr_in *server, char *fh __unused,
307 char *path, char *nfh, int sport)
309 struct rpc_t buf, *rpc;
313 int pathlen = strlen(path);
316 buf.u.call.id = htonl(id);
317 buf.u.call.type = htonl(MSG_CALL);
318 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
319 buf.u.call.prog = htonl(PROG_NFS);
320 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
321 buf.u.call.proc = htonl(NFS_READLINK);
322 p = rpc_add_credentials((long *)buf.u.call.data);
323 memcpy(p, nfh, NFS_FHSIZE);
324 p += (NFS_FHSIZE / 4);
325 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
326 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
327 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
328 (char *)p - (char *)&buf, &buf);
329 if (await_reply(await_rpc, sport, &id, timeout)) {
330 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
331 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
332 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
334 if (rpc->u.reply.rstatus) {
335 /* RPC failed, no verifier, data[0] */
338 if (rpc->u.reply.astatus) {
339 /* RPC couldn't decode parameters */
342 return -ntohl(rpc->u.reply.data[0]);
345 // If it's a relative link, append everything to dirname, filename TOO!
346 retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
347 if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
348 path[pathlen++] = '/';
349 while ( ( retries + pathlen ) > 298 ) {
353 memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
354 } else { retries = 0; }
355 path[pathlen + retries] = 0;
357 // Else make it the only path.
358 if ( retries > 298 ) { retries = 298; }
359 memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
368 /**************************************************************************
369 NFS_LOOKUP - Lookup Pathname
370 **************************************************************************/
371 static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh,
374 struct rpc_t buf, *rpc;
378 int pathlen = strlen(path);
381 buf.u.call.id = htonl(id);
382 buf.u.call.type = htonl(MSG_CALL);
383 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
384 buf.u.call.prog = htonl(PROG_NFS);
385 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
386 buf.u.call.proc = htonl(NFS_LOOKUP);
387 p = rpc_add_credentials((long *)buf.u.call.data);
388 memcpy(p, fh, NFS_FHSIZE);
389 p += (NFS_FHSIZE / 4);
390 *p++ = htonl(pathlen);
392 *(p + pathlen / 4) = 0; /* add zero padding */
394 memcpy(p, path, pathlen);
395 p += (pathlen + 3) / 4;
396 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
397 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
398 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
399 (char *)p - (char *)&buf, &buf);
400 if (await_reply(await_rpc, sport, &id, timeout)) {
401 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
402 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
403 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
405 if (rpc->u.reply.rstatus) {
406 /* RPC failed, no verifier, data[0] */
409 if (rpc->u.reply.astatus) {
410 /* RPC couldn't decode parameters */
413 return -ntohl(rpc->u.reply.data[0]);
415 memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
423 /**************************************************************************
424 NFS_READ - Read File on NFS Server
425 **************************************************************************/
426 static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len,
429 struct rpc_t buf, *rpc;
436 * Try to implement something similar to a window protocol in
437 * terms of response to losses. On successful receive, increment
438 * the number of tokens by 1 (cap at 256). On failure, halve it.
439 * When the number of tokens is >= 2, use a very short timeout.
443 buf.u.call.id = htonl(id);
444 buf.u.call.type = htonl(MSG_CALL);
445 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
446 buf.u.call.prog = htonl(PROG_NFS);
447 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
448 buf.u.call.proc = htonl(NFS_READ);
449 p = rpc_add_credentials((long *)buf.u.call.data);
450 memcpy(p, fh, NFS_FHSIZE);
452 *p++ = htonl(offset);
454 *p++ = 0; /* unused parameter */
455 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
456 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
458 timeout = TICKS_PER_SEC/2;
460 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
461 (char *)p - (char *)&buf, &buf);
462 if (await_reply(await_rpc, sport, &id, timeout)) {
465 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
466 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
467 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
469 if (rpc->u.reply.rstatus) {
470 /* RPC failed, no verifier, data[0] */
473 if (rpc->u.reply.astatus) {
474 /* RPC couldn't decode parameters */
477 return -ntohl(rpc->u.reply.data[0]);
487 /**************************************************************************
488 NFS - Download extended BOOTP data, or kernel image from NFS server
489 **************************************************************************/
490 static int nfs ( char *url __unused,
491 struct sockaddr_in *server,
493 int ( * process ) ( unsigned char *data,
494 unsigned int blocknum,
495 unsigned int len, int eof ) ) {
496 static int recursion = 0;
498 int err, namelen = strlen(name);
499 char dirname[300], *fname;
500 char dirfh[NFS_FHSIZE]; /* file handle of directory */
501 char filefh[NFS_FHSIZE]; /* file handle of kernel image */
503 int rlen, size, offs, len;
507 if (oport > START_OPORT+OPORT_SWEEP) {
511 mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
512 mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
513 if ( ! mount_server.sin_port ) {
514 DBG ( "Cannot get mount port from %!:%d\n",
515 server->sin_addr.s_addr, server->sin_port );
518 nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
519 if ( ! mount_server.sin_port ) {
520 DBG ( "Cannot get nfs port from %!:%d\n",
521 server->sin_addr.s_addr, server->sin_port );
525 if ( name != dirname ) {
526 memcpy(dirname, name, namelen + 1);
530 if ( recursion > NFS_MAXLINKDEPTH ) {
531 DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
536 fname = dirname + (namelen - 1);
537 while (fname >= dirname) {
545 if (fname < dirname) {
546 DBG("can't parse file name %s\n", name);
550 err = nfs_mount(&mount_server, dirname, dirfh, sport);
552 DBG("mounting %s: ", dirname);
554 /* just to be sure... */
559 err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport);
561 DBG("looking up %s: ", fname);
568 block = 1; /* blocks are numbered starting from 1 */
569 size = -1; /* will be set properly with the first reply */
570 len = NFS_READ_SIZE; /* first request is always full size */
572 err = nfs_read(&nfs_server, filefh, offs, len, sport);
573 if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
574 // An error occured. NFS servers tend to sending
575 // errors 21 / 22 when symlink instead of real file
576 // is requested. So check if it's a symlink!
577 block = nfs_readlink(&nfs_server, dirfh, dirname,
580 printf("\nLoading symlink:%s ..",dirname);
588 printf("reading at offset %d: ", offs);
594 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
596 /* size must be found out early to allow EOF detection */
598 size = ntohl(rpc->u.reply.data[6]);
600 rlen = ntohl(rpc->u.reply.data[18]);
602 rlen = len; /* shouldn't happen... */
605 err = process((char *)&rpc->u.reply.data[19], block, rlen,
606 (offs+rlen == size));
614 /* last request is done with matching requested read size */
615 if (size-offs < NFS_READ_SIZE) {
619 /* len == 0 means that all the file has been read */
623 INIT_FN ( INIT_RPC, rpc_init, nfs_reset, nfs_reset );
625 static struct protocol nfs_protocol __protocol = {