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 **************************************************************************/
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
)
67 if (arptable
[ARP_CLIENT
].ipaddr
.s_addr
!= ip
->dest
.s_addr
)
69 if (ntohs(udp
->dest
) != ival
)
71 if (nic
.packetlen
< ETH_HLEN
+ sizeof(struct iphdr
) + sizeof(struct udphdr
) + 8)
73 rpc
= (struct rpc_t
*)&nic
.packet
[ETH_HLEN
];
74 if (*(unsigned long *)ptr
!= ntohl(rpc
->u
.reply
.id
))
76 if (MSG_REPLY
!= ntohl(rpc
->u
.reply
.type
))
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
;
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 */
103 *p
++ = htonl(IP_UDP
);
105 for (retries
= 0; retries
< MAX_RPC_RETRIES
; retries
++) {
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
) {
117 return ntohl(rpc
->u
.reply
.data
[0]);
124 /**************************************************************************
125 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
126 **************************************************************************/
127 static long *rpc_add_credentials(long *p
)
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
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
);
154 *p
++ = 0; /* auxiliary gid list */
156 /* Provide an AUTH_NONE verifier. */
157 *p
++ = 0; /* AUTH_NONE */
158 *p
++ = 0; /* auth length */
163 /**************************************************************************
164 NFS_PRINTERROR - Print a NFS error message
165 **************************************************************************/
166 static void nfs_printerror(int err
)
170 printf("Not owner\n");
173 printf("No such file or directory\n");
176 printf("Permission denied\n");
179 printf("Directory given where filename expected\n");
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.
186 printf("low-level RPC failure (parameter decoding problem?)\n");
189 printf("low-level RPC failure (authentication problem?)\n");
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
;
205 int pathlen
= strlen(path
);
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
);
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
++) {
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]) {
231 if (rpc
->u
.reply
.rstatus
) {
232 /* RPC failed, no verifier, data[0] */
235 if (rpc
->u
.reply
.astatus
) {
236 /* RPC couldn't decode parameters */
239 return -ntohl(rpc
->u
.reply
.data
[0]);
241 memcpy(fh
, rpc
->u
.reply
.data
+ 1, NFS_FHSIZE
);
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
;
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
) {
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
;
309 int pathlen
= strlen(path
);
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]) {
330 if (rpc
->u
.reply
.rstatus
) {
331 /* RPC failed, no verifier, data[0] */
334 if (rpc
->u
.reply
.astatus
) {
335 /* RPC couldn't decode parameters */
338 return -ntohl(rpc
->u
.reply
.data
[0]);
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 ) {
349 memcpy(path
+ pathlen
, &(rpc
->u
.reply
.data
[2]), retries
+ 1);
350 } else { retries
= 0; }
351 path
[pathlen
+ retries
] = 0;
353 // Else make it the only path.
354 if ( retries
> 298 ) { retries
= 298; }
355 memcpy ( path
, &(rpc
->u
.reply
.data
[2]), retries
+ 1 );
364 /**************************************************************************
365 NFS_LOOKUP - Lookup Pathname
366 **************************************************************************/
367 static int nfs_lookup(struct sockaddr_in
*server
, char *fh
, char *path
, char *nfh
,
370 struct rpc_t buf
, *rpc
;
374 int pathlen
= strlen(path
);
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
);
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]) {
401 if (rpc
->u
.reply
.rstatus
) {
402 /* RPC failed, no verifier, data[0] */
405 if (rpc
->u
.reply
.astatus
) {
406 /* RPC couldn't decode parameters */
409 return -ntohl(rpc
->u
.reply
.data
[0]);
411 memcpy(nfh
, rpc
->u
.reply
.data
+ 1, NFS_FHSIZE
);
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
,
425 struct rpc_t buf
, *rpc
;
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.
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
);
448 *p
++ = htonl(offset
);
450 *p
++ = 0; /* unused parameter */
451 for (retries
= 0; retries
< MAX_RPC_RETRIES
; retries
++) {
452 long timeout
= rfc2131_sleep_interval(TIMEOUT
, retries
);
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
)) {
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]) {
465 if (rpc
->u
.reply
.rstatus
) {
466 /* RPC failed, no verifier, data[0] */
469 if (rpc
->u
.reply
.astatus
) {
470 /* RPC couldn't decode parameters */
473 return -ntohl(rpc
->u
.reply
.data
[0]);
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;
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
;
498 if (oport
> START_OPORT
+OPORT_SWEEP
) {
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
);
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
);
516 if ( name
!= dirname
) {
517 memcpy(dirname
, name
, namelen
+ 1);
521 if ( recursion
> NFS_MAXLINKDEPTH
) {
522 DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
527 fname
= dirname
+ (namelen
- 1);
528 while (fname
>= dirname
) {
536 if (fname
< dirname
) {
537 DBG("can't parse file name %s\n", name
);
541 err
= nfs_mount(&mount_server
, dirname
, dirfh
, sport
);
543 DBG("mounting %s: ", dirname
);
545 /* just to be sure... */
550 err
= nfs_lookup(&nfs_server
, dirfh
, fname
, filefh
, sport
);
552 DBG("looking up %s: ", fname
);
559 size
= -1; /* will be set properly with the first reply */
560 len
= NFS_READ_SIZE
; /* first request is always full size */
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
);
577 printf("\nError reading at offset %d: ", offs
);
583 rpc
= (struct rpc_t
*)&nic
.packet
[ETH_HLEN
];
585 /* size must be found out early to allow EOF detection */
587 size
= ntohl(rpc
->u
.reply
.data
[6]);
589 rlen
= ntohl(rpc
->u
.reply
.data
[18]);
591 rlen
= len
; /* shouldn't happen... */
594 if ( ! fill_buffer ( buffer
, &rpc
->u
.reply
.data
[19],
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 struct protocol nfs_protocol __protocol
= {
612 .default_port
= SUNRPC_PORT
,