Adding debian version 3.70~pre8+dfsg-1.
[syslinux-debian/hramrach.git] / gpxe / src / proto / nfs.c
blob943081a2e93fc86f2649632d2667907887c86175
1 #if 0
3 #include <gpxe/init.h>
4 #include <gpxe/in.h>
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 struct sockaddr_in mount_server;
28 static struct sockaddr_in nfs_server;
29 static unsigned long rpc_id;
31 /**************************************************************************
32 RPC_INIT - set up the ID counter to something fairly random
33 **************************************************************************/
34 void rpc_init(void)
36 unsigned long t;
38 t = currticks();
39 rpc_id = t ^ (t << 8) ^ (t << 16);
42 /**************************************************************************
43 RPC_PRINTERROR - Print a low level RPC error message
44 **************************************************************************/
45 static void rpc_printerror(struct rpc_t *rpc)
47 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
48 rpc->u.reply.astatus) {
49 /* rpc_printerror() is called for any RPC related error,
50 * suppress output if no low level RPC error happened. */
51 DBG("RPC error: (%ld,%ld,%ld)\n", ntohl(rpc->u.reply.rstatus),
52 ntohl(rpc->u.reply.verifier),
53 ntohl(rpc->u.reply.astatus));
57 /**************************************************************************
58 AWAIT_RPC - Wait for an rpc packet
59 **************************************************************************/
60 static int await_rpc(int ival, void *ptr,
61 unsigned short ptype __unused, struct iphdr *ip,
62 struct udphdr *udp, struct tcphdr *tcp __unused)
64 struct rpc_t *rpc;
65 if (!udp)
66 return 0;
67 if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
68 return 0;
69 if (ntohs(udp->dest) != ival)
70 return 0;
71 if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
72 return 0;
73 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
74 if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
75 return 0;
76 if (MSG_REPLY != ntohl(rpc->u.reply.type))
77 return 0;
78 return 1;
81 /**************************************************************************
82 RPC_LOOKUP - Lookup RPC Port numbers
83 **************************************************************************/
84 static int rpc_lookup(struct sockaddr_in *addr, int prog, int ver, int sport)
86 struct rpc_t buf, *rpc;
87 unsigned long id;
88 int retries;
89 long *p;
91 id = rpc_id++;
92 buf.u.call.id = htonl(id);
93 buf.u.call.type = htonl(MSG_CALL);
94 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
95 buf.u.call.prog = htonl(PROG_PORTMAP);
96 buf.u.call.vers = htonl(2); /* portmapper is version 2 */
97 buf.u.call.proc = htonl(PORTMAP_GETPORT);
98 p = (long *)buf.u.call.data;
99 *p++ = 0; *p++ = 0; /* auth credential */
100 *p++ = 0; *p++ = 0; /* auth verifier */
101 *p++ = htonl(prog);
102 *p++ = htonl(ver);
103 *p++ = htonl(IP_UDP);
104 *p++ = 0;
105 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
106 long timeout;
107 udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port,
108 (char *)p - (char *)&buf, &buf);
109 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
110 if (await_reply(await_rpc, sport, &id, timeout)) {
111 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
112 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
113 rpc->u.reply.astatus) {
114 rpc_printerror(rpc);
115 return 0;
116 } else {
117 return ntohl(rpc->u.reply.data[0]);
121 return 0;
124 /**************************************************************************
125 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
126 **************************************************************************/
127 static long *rpc_add_credentials(long *p)
129 int hl;
131 /* Here's the executive summary on authentication requirements of the
132 * various NFS server implementations: Linux accepts both AUTH_NONE
133 * and AUTH_UNIX authentication (also accepts an empty hostname field
134 * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
135 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
136 * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
137 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
138 * hostname). */
140 hl = (hostnamelen + 3) & ~3;
142 /* Provide an AUTH_UNIX credential. */
143 *p++ = htonl(1); /* AUTH_UNIX */
144 *p++ = htonl(hl+20); /* auth length */
145 *p++ = htonl(0); /* stamp */
146 *p++ = htonl(hostnamelen); /* hostname string */
147 if (hostnamelen & 3) {
148 *(p + hostnamelen / 4) = 0; /* add zero padding */
150 memcpy(p, hostname, hostnamelen);
151 p += hl / 4;
152 *p++ = 0; /* uid */
153 *p++ = 0; /* gid */
154 *p++ = 0; /* auxiliary gid list */
156 /* Provide an AUTH_NONE verifier. */
157 *p++ = 0; /* AUTH_NONE */
158 *p++ = 0; /* auth length */
160 return p;
163 /**************************************************************************
164 NFS_PRINTERROR - Print a NFS error message
165 **************************************************************************/
166 static void nfs_printerror(int err)
168 switch (-err) {
169 case NFSERR_PERM:
170 printf("Not owner\n");
171 break;
172 case NFSERR_NOENT:
173 printf("No such file or directory\n");
174 break;
175 case NFSERR_ACCES:
176 printf("Permission denied\n");
177 break;
178 case NFSERR_ISDIR:
179 printf("Directory given where filename expected\n");
180 break;
181 case NFSERR_INVAL:
182 printf("Invalid filehandle\n");
183 break; // INVAL is not defined in NFSv2, some NFS-servers
184 // seem to use it in answers to v2 nevertheless.
185 case 9998:
186 printf("low-level RPC failure (parameter decoding problem?)\n");
187 break;
188 case 9999:
189 printf("low-level RPC failure (authentication problem?)\n");
190 break;
191 default:
192 printf("Unknown NFS error %d\n", -err);
196 /**************************************************************************
197 NFS_MOUNT - Mount an NFS Filesystem
198 **************************************************************************/
199 static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport)
201 struct rpc_t buf, *rpc;
202 unsigned long id;
203 int retries;
204 long *p;
205 int pathlen = strlen(path);
207 id = rpc_id++;
208 buf.u.call.id = htonl(id);
209 buf.u.call.type = htonl(MSG_CALL);
210 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
211 buf.u.call.prog = htonl(PROG_MOUNT);
212 buf.u.call.vers = htonl(1); /* mountd is version 1 */
213 buf.u.call.proc = htonl(MOUNT_ADDENTRY);
214 p = rpc_add_credentials((long *)buf.u.call.data);
215 *p++ = htonl(pathlen);
216 if (pathlen & 3) {
217 *(p + pathlen / 4) = 0; /* add zero padding */
219 memcpy(p, path, pathlen);
220 p += (pathlen + 3) / 4;
221 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
222 long timeout;
223 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
224 (char *)p - (char *)&buf, &buf);
225 timeout = rfc2131_sleep_interval(TIMEOUT, retries);
226 if (await_reply(await_rpc, sport, &id, timeout)) {
227 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
228 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
229 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
230 rpc_printerror(rpc);
231 if (rpc->u.reply.rstatus) {
232 /* RPC failed, no verifier, data[0] */
233 return -9999;
235 if (rpc->u.reply.astatus) {
236 /* RPC couldn't decode parameters */
237 return -9998;
239 return -ntohl(rpc->u.reply.data[0]);
240 } else {
241 memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
242 return 0;
246 return -1;
249 /**************************************************************************
250 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
251 **************************************************************************/
252 static void nfs_umountall(struct sockaddr_in *server)
254 struct rpc_t buf, *rpc;
255 unsigned long id;
256 int retries;
257 long *p;
259 id = rpc_id++;
260 buf.u.call.id = htonl(id);
261 buf.u.call.type = htonl(MSG_CALL);
262 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
263 buf.u.call.prog = htonl(PROG_MOUNT);
264 buf.u.call.vers = htonl(1); /* mountd is version 1 */
265 buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
266 p = rpc_add_credentials((long *)buf.u.call.data);
267 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
268 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
269 udp_transmit(server->sin_addr.s_addr, oport, server->sin_port,
270 (char *)p - (char *)&buf, &buf);
271 if (await_reply(await_rpc, oport, &id, timeout)) {
272 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
273 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
274 rpc->u.reply.astatus) {
275 rpc_printerror(rpc);
277 break;
282 /**************************************************************************
283 NFS_RESET - Reset the NFS subsystem
284 **************************************************************************/
285 static void nfs_reset ( void ) {
286 /* If we have a mount server, call nfs_umountall() */
287 if ( mount_server.sin_addr.s_addr ) {
288 nfs_umountall ( &mount_server );
290 /* Zero the data structures */
291 memset ( &mount_server, 0, sizeof ( mount_server ) );
292 memset ( &nfs_server, 0, sizeof ( nfs_server ) );
295 /***************************************************************************
296 * NFS_READLINK (AH 2003-07-14)
297 * This procedure is called when read of the first block fails -
298 * this probably happens when it's a directory or a symlink
299 * In case of successful readlink(), the dirname is manipulated,
300 * so that inside the nfs() function a recursion can be done.
301 **************************************************************************/
302 static int nfs_readlink(struct sockaddr_in *server, char *fh __unused,
303 char *path, char *nfh, int sport)
305 struct rpc_t buf, *rpc;
306 unsigned long id;
307 long *p;
308 int retries;
309 int pathlen = strlen(path);
311 id = rpc_id++;
312 buf.u.call.id = htonl(id);
313 buf.u.call.type = htonl(MSG_CALL);
314 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
315 buf.u.call.prog = htonl(PROG_NFS);
316 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
317 buf.u.call.proc = htonl(NFS_READLINK);
318 p = rpc_add_credentials((long *)buf.u.call.data);
319 memcpy(p, nfh, NFS_FHSIZE);
320 p += (NFS_FHSIZE / 4);
321 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
322 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
323 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
324 (char *)p - (char *)&buf, &buf);
325 if (await_reply(await_rpc, sport, &id, timeout)) {
326 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
327 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
328 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
329 rpc_printerror(rpc);
330 if (rpc->u.reply.rstatus) {
331 /* RPC failed, no verifier, data[0] */
332 return -9999;
334 if (rpc->u.reply.astatus) {
335 /* RPC couldn't decode parameters */
336 return -9998;
338 return -ntohl(rpc->u.reply.data[0]);
339 } else {
340 // It *is* a link.
341 // If it's a relative link, append everything to dirname, filename TOO!
342 retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
343 if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
344 path[pathlen++] = '/';
345 while ( ( retries + pathlen ) > 298 ) {
346 retries--;
348 if ( retries > 0 ) {
349 memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
350 } else { retries = 0; }
351 path[pathlen + retries] = 0;
352 } else {
353 // Else make it the only path.
354 if ( retries > 298 ) { retries = 298; }
355 memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
356 path[retries] = 0;
358 return 0;
362 return -1;
364 /**************************************************************************
365 NFS_LOOKUP - Lookup Pathname
366 **************************************************************************/
367 static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh,
368 int sport)
370 struct rpc_t buf, *rpc;
371 unsigned long id;
372 long *p;
373 int retries;
374 int pathlen = strlen(path);
376 id = rpc_id++;
377 buf.u.call.id = htonl(id);
378 buf.u.call.type = htonl(MSG_CALL);
379 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
380 buf.u.call.prog = htonl(PROG_NFS);
381 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
382 buf.u.call.proc = htonl(NFS_LOOKUP);
383 p = rpc_add_credentials((long *)buf.u.call.data);
384 memcpy(p, fh, NFS_FHSIZE);
385 p += (NFS_FHSIZE / 4);
386 *p++ = htonl(pathlen);
387 if (pathlen & 3) {
388 *(p + pathlen / 4) = 0; /* add zero padding */
390 memcpy(p, path, pathlen);
391 p += (pathlen + 3) / 4;
392 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
393 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
394 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
395 (char *)p - (char *)&buf, &buf);
396 if (await_reply(await_rpc, sport, &id, timeout)) {
397 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
398 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
399 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
400 rpc_printerror(rpc);
401 if (rpc->u.reply.rstatus) {
402 /* RPC failed, no verifier, data[0] */
403 return -9999;
405 if (rpc->u.reply.astatus) {
406 /* RPC couldn't decode parameters */
407 return -9998;
409 return -ntohl(rpc->u.reply.data[0]);
410 } else {
411 memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
412 return 0;
416 return -1;
419 /**************************************************************************
420 NFS_READ - Read File on NFS Server
421 **************************************************************************/
422 static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len,
423 int sport)
425 struct rpc_t buf, *rpc;
426 unsigned long id;
427 int retries;
428 long *p;
430 static int tokens=0;
432 * Try to implement something similar to a window protocol in
433 * terms of response to losses. On successful receive, increment
434 * the number of tokens by 1 (cap at 256). On failure, halve it.
435 * When the number of tokens is >= 2, use a very short timeout.
438 id = rpc_id++;
439 buf.u.call.id = htonl(id);
440 buf.u.call.type = htonl(MSG_CALL);
441 buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
442 buf.u.call.prog = htonl(PROG_NFS);
443 buf.u.call.vers = htonl(2); /* nfsd is version 2 */
444 buf.u.call.proc = htonl(NFS_READ);
445 p = rpc_add_credentials((long *)buf.u.call.data);
446 memcpy(p, fh, NFS_FHSIZE);
447 p += NFS_FHSIZE / 4;
448 *p++ = htonl(offset);
449 *p++ = htonl(len);
450 *p++ = 0; /* unused parameter */
451 for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
452 long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
453 if (tokens >= 2)
454 timeout = TICKS_PER_SEC/2;
456 udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
457 (char *)p - (char *)&buf, &buf);
458 if (await_reply(await_rpc, sport, &id, timeout)) {
459 if (tokens < 256)
460 tokens++;
461 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
462 if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
463 rpc->u.reply.astatus || rpc->u.reply.data[0]) {
464 rpc_printerror(rpc);
465 if (rpc->u.reply.rstatus) {
466 /* RPC failed, no verifier, data[0] */
467 return -9999;
469 if (rpc->u.reply.astatus) {
470 /* RPC couldn't decode parameters */
471 return -9998;
473 return -ntohl(rpc->u.reply.data[0]);
474 } else {
475 return 0;
477 } else
478 tokens >>= 1;
480 return -1;
483 /**************************************************************************
484 NFS - Download extended BOOTP data, or kernel image from NFS server
485 **************************************************************************/
486 static int nfs ( char *url __unused, struct sockaddr_in *server,
487 char *name, struct buffer *buffer ) {
488 static int recursion = 0;
489 int sport;
490 int err, namelen = strlen(name);
491 char dirname[300], *fname;
492 char dirfh[NFS_FHSIZE]; /* file handle of directory */
493 char filefh[NFS_FHSIZE]; /* file handle of kernel image */
494 int rlen, size, offs, len;
495 struct rpc_t *rpc;
497 sport = oport++;
498 if (oport > START_OPORT+OPORT_SWEEP) {
499 oport = START_OPORT;
502 mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
503 mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
504 if ( ! mount_server.sin_port ) {
505 DBG ( "Cannot get mount port from %s:%d\n",
506 inet_ntoa ( server->sin_addr ), server->sin_port );
507 return 0;
509 nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
510 if ( ! mount_server.sin_port ) {
511 DBG ( "Cannot get nfs port from %s:%d\n",
512 inet_ntoa ( server->sin_addr ), server->sin_port );
513 return 0;
516 if ( name != dirname ) {
517 memcpy(dirname, name, namelen + 1);
519 recursion = 0;
520 nfssymlink:
521 if ( recursion > NFS_MAXLINKDEPTH ) {
522 DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
523 NFS_MAXLINKDEPTH );
524 return 0;
526 recursion++;
527 fname = dirname + (namelen - 1);
528 while (fname >= dirname) {
529 if (*fname == '/') {
530 *fname = '\0';
531 fname++;
532 break;
534 fname--;
536 if (fname < dirname) {
537 DBG("can't parse file name %s\n", name);
538 return 0;
541 err = nfs_mount(&mount_server, dirname, dirfh, sport);
542 if (err) {
543 DBG("mounting %s: ", dirname);
544 nfs_printerror(err);
545 /* just to be sure... */
546 nfs_reset();
547 return 0;
550 err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport);
551 if (err) {
552 DBG("looking up %s: ", fname);
553 nfs_printerror(err);
554 nfs_reset();
555 return 0;
558 offs = 0;
559 size = -1; /* will be set properly with the first reply */
560 len = NFS_READ_SIZE; /* first request is always full size */
561 do {
562 err = nfs_read(&nfs_server, filefh, offs, len, sport);
563 if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
564 // An error occured. NFS servers tend to sending
565 // errors 21 / 22 when symlink instead of real file
566 // is requested. So check if it's a symlink!
567 if ( nfs_readlink(&nfs_server, dirfh, dirname,
568 filefh, sport) == 0 ) {
569 printf("\nLoading symlink:%s ..",dirname);
570 goto nfssymlink;
572 nfs_printerror(err);
573 nfs_reset();
574 return 0;
576 if (err) {
577 printf("\nError reading at offset %d: ", offs);
578 nfs_printerror(err);
579 nfs_reset();
580 return 0;
583 rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
585 /* size must be found out early to allow EOF detection */
586 if (size == -1) {
587 size = ntohl(rpc->u.reply.data[6]);
589 rlen = ntohl(rpc->u.reply.data[18]);
590 if (rlen > len) {
591 rlen = len; /* shouldn't happen... */
594 if ( ! fill_buffer ( buffer, &rpc->u.reply.data[19],
595 offs, rlen ) ) {
596 nfs_reset();
597 return 0;
600 offs += rlen;
601 /* last request is done with matching requested read size */
602 if (size-offs < NFS_READ_SIZE) {
603 len = size-offs;
605 } while (len != 0);
606 /* len == 0 means that all the file has been read */
607 return 1;
610 struct protocol nfs_protocol __protocol = {
611 .name = "nfs",
612 .default_port = SUNRPC_PORT,
613 .load = nfs,
616 #endif