pass filename to nfs and tftp start functions
[barebox-mini2440.git] / net / nfs.c
blobda80c2216610212e380b5e79edf1bff8686e99aa
1 /*
2 * NFS support driver - based on etherboot and U-BOOT's tftp.c
4 * Masami Komiya <mkomiya@sonare.it> 2004
6 */
8 /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
9 * large portions are copied verbatim) as distributed in OSKit 0.97. A few
10 * changes were necessary to adapt the code to Etherboot and to fix several
11 * inconsistencies. Also the RPC message preparation is done "by hand" to
12 * avoid adding netsprintf() which I find hard to understand and use. */
14 /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
15 * it loads the kernel image off the boot server (ARP_SERVER) and does not
16 * access the client root disk (root-path in dhcpd.conf), which would use
17 * ARP_ROOTSERVER. The root disk is something the operating system we are
18 * about to load needs to use. This is different from the OSKit 0.97 logic. */
20 /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
21 * If a symlink is encountered, it is followed as far as possible (recursion
22 * possible, maximum 16 steps). There is no clearing of ".."'s inside the
23 * path, so please DON'T DO THAT. thx. */
25 #include <common.h>
26 #include <command.h>
27 #include <clock.h>
28 #include <net.h>
29 #include <malloc.h>
30 #include <libgen.h>
31 #include <fs.h>
32 #include "nfs.h"
34 /*#define NFS_DEBUG*/
36 #define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */
37 #define NFS_TIMEOUT 60
39 static int fs_mounted = 0;
40 static unsigned long rpc_id = 0;
41 static int nfs_offset = -1;
42 static int nfs_len;
44 static char dirfh[NFS_FHSIZE]; /* file handle of directory */
45 static char filefh[NFS_FHSIZE]; /* file handle of kernel image */
47 static int NfsDownloadState;
48 static IPaddr_t NfsServerIP;
49 static int NfsSrvMountPort;
50 static int NfsSrvNfsPort;
51 static int NfsOurPort;
52 static int NfsTimeoutCount;
53 static int NfsState;
54 #define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
55 #define STATE_PRCLOOKUP_PROG_NFS_REQ 2
56 #define STATE_MOUNT_REQ 3
57 #define STATE_UMOUNT_REQ 4
58 #define STATE_LOOKUP_REQ 5
59 #define STATE_READ_REQ 6
60 #define STATE_READLINK_REQ 7
62 static char *nfs_filename;
63 static char *nfs_path;
64 static char nfs_path_buff[2048];
66 extern int net_store_fd;
68 static __inline__ int
69 store_block (uchar * src, unsigned offset, unsigned len)
71 ulong newsize = offset + len;
72 int ret;
74 ret = write(net_store_fd, src, len);
75 if (ret < 0)
76 return ret;
78 if (NetBootFileXferSize < (offset+len))
79 NetBootFileXferSize = newsize;
81 return 0;
84 /**************************************************************************
85 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
86 **************************************************************************/
87 static long *rpc_add_credentials (long *p)
89 int hl;
90 int hostnamelen;
91 char hostname[256];
93 strcpy (hostname, "");
94 hostnamelen=strlen (hostname);
96 /* Here's the executive summary on authentication requirements of the
97 * various NFS server implementations: Linux accepts both AUTH_NONE
98 * and AUTH_UNIX authentication (also accepts an empty hostname field
99 * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
100 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
101 * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
102 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
103 * hostname). */
105 hl = (hostnamelen + 3) & ~3;
107 /* Provide an AUTH_UNIX credential. */
108 *p++ = htonl(1); /* AUTH_UNIX */
109 *p++ = htonl(hl+20); /* auth length */
110 *p++ = htonl(0); /* stamp */
111 *p++ = htonl(hostnamelen); /* hostname string */
112 if (hostnamelen & 3) {
113 *(p + hostnamelen / 4) = 0; /* add zero padding */
115 memcpy (p, hostname, hostnamelen);
116 p += hl / 4;
117 *p++ = 0; /* uid */
118 *p++ = 0; /* gid */
119 *p++ = 0; /* auxiliary gid list */
121 /* Provide an AUTH_NONE verifier. */
122 *p++ = 0; /* AUTH_NONE */
123 *p++ = 0; /* auth length */
125 return p;
128 /**************************************************************************
129 RPC_LOOKUP - Lookup RPC Port numbers
130 **************************************************************************/
131 static void
132 rpc_req (int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
134 struct rpc_t pkt;
135 unsigned long id;
136 uint32_t *p;
137 int pktlen;
138 int sport;
140 id = ++rpc_id;
141 pkt.u.call.id = htonl(id);
142 pkt.u.call.type = htonl(MSG_CALL);
143 pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
144 pkt.u.call.prog = htonl(rpc_prog);
145 pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
146 pkt.u.call.proc = htonl(rpc_proc);
147 p = (uint32_t *)&(pkt.u.call.data);
149 if (datalen)
150 memcpy ((char *)p, (char *)data, datalen*sizeof(uint32_t));
152 pktlen = (char *)p + datalen*sizeof(uint32_t) - (char *)&pkt;
154 memcpy ((char *)NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE, (char *)&pkt, pktlen);
156 if (rpc_prog == PROG_PORTMAP)
157 sport = SUNRPC_PORT;
158 else if (rpc_prog == PROG_MOUNT)
159 sport = NfsSrvMountPort;
160 else
161 sport = NfsSrvNfsPort;
163 NetSendUDPPacket (NetServerEther, NfsServerIP, sport, NfsOurPort, pktlen);
166 /**************************************************************************
167 RPC_LOOKUP - Lookup RPC Port numbers
168 **************************************************************************/
169 static void
170 rpc_lookup_req (int prog, int ver)
172 uint32_t data[16];
174 data[0] = 0; data[1] = 0; /* auth credential */
175 data[2] = 0; data[3] = 0; /* auth verifier */
176 data[4] = htonl(prog);
177 data[5] = htonl(ver);
178 data[6] = htonl(17); /* IP_UDP */
179 data[7] = 0;
181 rpc_req (PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
184 /**************************************************************************
185 NFS_MOUNT - Mount an NFS Filesystem
186 **************************************************************************/
187 static void
188 nfs_mount_req (char *path)
190 uint32_t data[1024];
191 uint32_t *p;
192 int len;
193 int pathlen;
195 pathlen = strlen (path);
197 p = &(data[0]);
198 p = (uint32_t *)rpc_add_credentials((long *)p);
200 *p++ = htonl(pathlen);
201 if (pathlen & 3) *(p + pathlen / 4) = 0;
202 memcpy (p, path, pathlen);
203 p += (pathlen + 3) / 4;
205 len = (uint32_t *)p - (uint32_t *)&(data[0]);
207 rpc_req (PROG_MOUNT, MOUNT_ADDENTRY, data, len);
210 /**************************************************************************
211 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
212 **************************************************************************/
213 static void
214 nfs_umountall_req (void)
216 uint32_t data[1024];
217 uint32_t *p;
218 int len;
220 if ((NfsSrvMountPort == -1) || (!fs_mounted)) {
221 /* Nothing mounted, nothing to umount */
222 return;
225 p = &(data[0]);
226 p = (uint32_t *)rpc_add_credentials ((long *)p);
228 len = (uint32_t *)p - (uint32_t *)&(data[0]);
230 rpc_req (PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
233 /***************************************************************************
234 * NFS_READLINK (AH 2003-07-14)
235 * This procedure is called when read of the first block fails -
236 * this probably happens when it's a directory or a symlink
237 * In case of successful readlink(), the dirname is manipulated,
238 * so that inside the nfs() function a recursion can be done.
239 **************************************************************************/
240 static void
241 nfs_readlink_req (void)
243 uint32_t data[1024];
244 uint32_t *p;
245 int len;
247 p = &(data[0]);
248 p = (uint32_t *)rpc_add_credentials ((long *)p);
250 memcpy (p, filefh, NFS_FHSIZE);
251 p += (NFS_FHSIZE / 4);
253 len = (uint32_t *)p - (uint32_t *)&(data[0]);
255 rpc_req (PROG_NFS, NFS_READLINK, data, len);
258 /**************************************************************************
259 NFS_LOOKUP - Lookup Pathname
260 **************************************************************************/
261 static void
262 nfs_lookup_req (char *fname)
264 uint32_t data[1024];
265 uint32_t *p;
266 int len;
267 int fnamelen;
269 fnamelen = strlen (fname);
271 p = &(data[0]);
272 p = (uint32_t *)rpc_add_credentials ((long *)p);
274 memcpy (p, dirfh, NFS_FHSIZE);
275 p += (NFS_FHSIZE / 4);
276 *p++ = htonl(fnamelen);
277 if (fnamelen & 3) *(p + fnamelen / 4) = 0;
278 memcpy (p, fname, fnamelen);
279 p += (fnamelen + 3) / 4;
281 len = (uint32_t *)p - (uint32_t *)&(data[0]);
283 rpc_req (PROG_NFS, NFS_LOOKUP, data, len);
286 /**************************************************************************
287 NFS_READ - Read File on NFS Server
288 **************************************************************************/
289 static void
290 nfs_read_req (int offset, int readlen)
292 uint32_t data[1024];
293 uint32_t *p;
294 int len;
296 p = &(data[0]);
297 p = (uint32_t *)rpc_add_credentials ((long *)p);
299 memcpy (p, filefh, NFS_FHSIZE);
300 p += (NFS_FHSIZE / 4);
301 *p++ = htonl(offset);
302 *p++ = htonl(readlen);
303 *p++ = 0;
305 len = (uint32_t *)p - (uint32_t *)&(data[0]);
307 rpc_req (PROG_NFS, NFS_READ, data, len);
310 /**************************************************************************
311 RPC request dispatcher
312 **************************************************************************/
314 static void
315 NfsSend (void)
317 #ifdef NFS_DEBUG
318 printf ("%s\n", __FUNCTION__);
319 #endif
321 switch (NfsState) {
322 case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
323 rpc_lookup_req (PROG_MOUNT, 1);
324 break;
325 case STATE_PRCLOOKUP_PROG_NFS_REQ:
326 rpc_lookup_req (PROG_NFS, 2);
327 break;
328 case STATE_MOUNT_REQ:
329 nfs_mount_req (nfs_path);
330 break;
331 case STATE_UMOUNT_REQ:
332 nfs_umountall_req ();
333 break;
334 case STATE_LOOKUP_REQ:
335 nfs_lookup_req (nfs_filename);
336 break;
337 case STATE_READ_REQ:
338 nfs_read_req (nfs_offset, nfs_len);
339 break;
340 case STATE_READLINK_REQ:
341 nfs_readlink_req ();
342 break;
346 /**************************************************************************
347 Handlers for the reply from server
348 **************************************************************************/
350 static int
351 rpc_lookup_reply (int prog, uchar *pkt, unsigned len)
353 struct rpc_t rpc_pkt;
355 memcpy ((unsigned char *)&rpc_pkt, pkt, len);
357 #ifdef NFS_DEBUG
358 printf ("%s\n", __FUNCTION__);
359 #endif
361 if (ntohl(rpc_pkt.u.reply.id) != rpc_id)
362 return -1;
364 if (rpc_pkt.u.reply.rstatus ||
365 rpc_pkt.u.reply.verifier ||
366 rpc_pkt.u.reply.astatus ||
367 rpc_pkt.u.reply.astatus) {
368 return -1;
371 switch (prog) {
372 case PROG_MOUNT:
373 NfsSrvMountPort = ntohl(rpc_pkt.u.reply.data[0]);
374 break;
375 case PROG_NFS:
376 NfsSrvNfsPort = ntohl(rpc_pkt.u.reply.data[0]);
377 break;
380 return 0;
383 static int
384 nfs_mount_reply (uchar *pkt, unsigned len)
386 struct rpc_t rpc_pkt;
388 #ifdef NFS_DEBUG
389 printf ("%s\n", __FUNCTION__);
390 #endif
392 memcpy ((unsigned char *)&rpc_pkt, pkt, len);
394 if (ntohl(rpc_pkt.u.reply.id) != rpc_id)
395 return -1;
397 if (rpc_pkt.u.reply.rstatus ||
398 rpc_pkt.u.reply.verifier ||
399 rpc_pkt.u.reply.astatus ||
400 rpc_pkt.u.reply.data[0]) {
401 return -1;
404 fs_mounted = 1;
405 memcpy (dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
407 return 0;
410 static int
411 nfs_umountall_reply (uchar *pkt, unsigned len)
413 struct rpc_t rpc_pkt;
415 #ifdef NFS_DEBUG
416 printf ("%s\n", __FUNCTION__);
417 #endif
419 memcpy ((unsigned char *)&rpc_pkt, pkt, len);
421 if (ntohl(rpc_pkt.u.reply.id) != rpc_id)
422 return -1;
424 if (rpc_pkt.u.reply.rstatus ||
425 rpc_pkt.u.reply.verifier ||
426 rpc_pkt.u.reply.astatus) {
427 return -1;
430 fs_mounted = 0;
431 memset (dirfh, 0, sizeof(dirfh));
433 return 0;
436 static int
437 nfs_lookup_reply (uchar *pkt, unsigned len)
439 struct rpc_t rpc_pkt;
441 #ifdef NFS_DEBUG
442 printf ("%s\n", __FUNCTION__);
443 #endif
445 memcpy ((unsigned char *)&rpc_pkt, pkt, len);
447 if (ntohl(rpc_pkt.u.reply.id) != rpc_id)
448 return -1;
450 if (rpc_pkt.u.reply.rstatus ||
451 rpc_pkt.u.reply.verifier ||
452 rpc_pkt.u.reply.astatus ||
453 rpc_pkt.u.reply.data[0]) {
454 return -1;
457 memcpy (filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
459 return 0;
462 static int
463 nfs_readlink_reply (uchar *pkt, unsigned len)
465 struct rpc_t rpc_pkt;
466 int rlen;
468 #ifdef NFS_DEBUG
469 printf ("%s\n", __FUNCTION__);
470 #endif
472 memcpy ((unsigned char *)&rpc_pkt, pkt, len);
474 if (ntohl(rpc_pkt.u.reply.id) != rpc_id)
475 return -1;
477 if (rpc_pkt.u.reply.rstatus ||
478 rpc_pkt.u.reply.verifier ||
479 rpc_pkt.u.reply.astatus ||
480 rpc_pkt.u.reply.data[0]) {
481 return -1;
484 rlen = ntohl (rpc_pkt.u.reply.data[1]); /* new path length */
486 if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
487 int pathlen;
488 strcat (nfs_path, "/");
489 pathlen = strlen(nfs_path);
490 memcpy (nfs_path+pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen);
491 nfs_path[pathlen+rlen+1] = 0;
492 } else {
493 memcpy (nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen);
494 nfs_path[rlen] = 0;
496 return 0;
499 static int
500 nfs_read_reply (uchar *pkt, unsigned len)
502 struct rpc_t rpc_pkt;
503 int rlen;
505 #ifdef NFS_DEBUG_nop
506 printf ("%s\n", __FUNCTION__);
507 #endif
509 memcpy ((uchar *)&rpc_pkt, pkt, sizeof(rpc_pkt.u.reply));
511 if (ntohl(rpc_pkt.u.reply.id) != rpc_id)
512 return -1;
514 if (rpc_pkt.u.reply.rstatus ||
515 rpc_pkt.u.reply.verifier ||
516 rpc_pkt.u.reply.astatus ||
517 rpc_pkt.u.reply.data[0]) {
518 if (rpc_pkt.u.reply.rstatus) {
519 return -9999;
521 if (rpc_pkt.u.reply.astatus) {
522 return -9999;
524 return -ntohl(rpc_pkt.u.reply.data[0]);;
527 if ((nfs_offset!=0) && !((nfs_offset) % (NFS_READ_SIZE/2*10*HASHES_PER_LINE))) {
528 puts ("\n\t ");
530 if (!(nfs_offset % ((NFS_READ_SIZE/2)*10))) {
531 putchar ('#');
534 rlen = ntohl(rpc_pkt.u.reply.data[18]);
535 if ( store_block ((uchar *)pkt+sizeof(rpc_pkt.u.reply), nfs_offset, rlen) )
536 return -9999;
538 return rlen;
541 /**************************************************************************
542 Interfaces of U-BOOT
543 **************************************************************************/
545 static void
546 NfsTimeout (void)
548 puts ("Timeout\n");
549 NetState = NETLOOP_FAIL;
550 return;
553 static void
554 NfsHandler (uchar *pkt, unsigned dest, unsigned src, unsigned len)
556 int rlen;
558 #ifdef NFS_DEBUG
559 printf ("%s\n", __FUNCTION__);
560 #endif
562 if (dest != NfsOurPort) return;
564 switch (NfsState) {
565 case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
566 rpc_lookup_reply (PROG_MOUNT, pkt, len);
567 NfsState = STATE_PRCLOOKUP_PROG_NFS_REQ;
568 NfsSend ();
569 break;
571 case STATE_PRCLOOKUP_PROG_NFS_REQ:
572 rpc_lookup_reply (PROG_NFS, pkt, len);
573 NfsState = STATE_MOUNT_REQ;
574 NfsSend ();
575 break;
577 case STATE_MOUNT_REQ:
578 if (nfs_mount_reply(pkt, len)) {
579 puts ("*** ERROR: Cannot mount\n");
580 /* just to be sure... */
581 NfsState = STATE_UMOUNT_REQ;
582 NfsSend ();
583 } else {
584 NfsState = STATE_LOOKUP_REQ;
585 NfsSend ();
587 break;
589 case STATE_UMOUNT_REQ:
590 if (nfs_umountall_reply(pkt, len)) {
591 puts ("*** ERROR: Cannot umount\n");
592 NetState = NETLOOP_FAIL;
593 } else {
594 puts ("\ndone\n");
595 NetState = NfsDownloadState;
597 break;
599 case STATE_LOOKUP_REQ:
600 if (nfs_lookup_reply(pkt, len)) {
601 puts ("*** ERROR: File lookup fail\n");
602 NfsState = STATE_UMOUNT_REQ;
603 NfsSend ();
604 } else {
605 NfsState = STATE_READ_REQ;
606 nfs_offset = 0;
607 nfs_len = NFS_READ_SIZE;
608 NfsSend ();
610 break;
612 case STATE_READLINK_REQ:
613 if (nfs_readlink_reply(pkt, len)) {
614 puts ("*** ERROR: Symlink fail\n");
615 NfsState = STATE_UMOUNT_REQ;
616 NfsSend ();
617 } else {
618 #ifdef NFS_DEBUG
619 printf ("Symlink --> %s\n", nfs_path);
620 #endif
621 nfs_filename = basename (nfs_path);
622 nfs_path = dirname (nfs_path);
624 NfsState = STATE_MOUNT_REQ;
625 NfsSend ();
627 break;
629 case STATE_READ_REQ:
630 rlen = nfs_read_reply (pkt, len);
631 NetSetTimeout (NFS_TIMEOUT * SECOND, NfsTimeout);
632 if (rlen > 0) {
633 nfs_offset += rlen;
634 NfsSend ();
636 else if ((rlen == -NFSERR_ISDIR)||(rlen == -NFSERR_INVAL)) {
637 /* symbolic link */
638 NfsState = STATE_READLINK_REQ;
639 NfsSend ();
640 } else {
641 if ( ! rlen ) NfsDownloadState = NETLOOP_SUCCESS;
642 NfsState = STATE_UMOUNT_REQ;
643 NfsSend ();
645 break;
650 void
651 NfsStart (char *p)
653 #ifdef NFS_DEBUG
654 printf ("%s\n", __FUNCTION__);
655 #endif
656 NfsDownloadState = NETLOOP_FAIL;
658 NfsServerIP = NetServerIP;
659 nfs_path = (char *)nfs_path_buff;
661 if (nfs_path == NULL) {
662 NetState = NETLOOP_FAIL;
663 puts ("*** ERROR: Fail allocate memory\n");
664 return;
667 p = strchr (p, ':');
669 if (p != NULL) {
670 string_to_ip (BootFile, &NfsServerIP);
671 ++p;
672 strcpy (nfs_path, p);
673 } else {
674 strcpy (nfs_path, BootFile);
677 nfs_filename = basename (nfs_path);
678 nfs_path = dirname (nfs_path);
680 #if defined(CONFIG_NET_MULTI)
681 printf ("Using %s device\n", eth_get_name());
682 #endif
684 puts ("File transfer via NFS from server "); print_IPaddr (NfsServerIP);
685 puts ("; our IP address is "); print_IPaddr (NetOurIP);
687 /* Check if we need to send across this subnet */
688 if (NetOurGatewayIP && NetOurSubnetMask) {
689 IPaddr_t OurNet = NetOurIP & NetOurSubnetMask;
690 IPaddr_t ServerNet = NetServerIP & NetOurSubnetMask;
692 if (OurNet != ServerNet) {
693 puts ("; sending through gateway ");
694 print_IPaddr (NetOurGatewayIP) ;
697 printf ("\nFilename '%s/%s'.", nfs_path, nfs_filename);
699 NetSetTimeout (NFS_TIMEOUT * SECOND, NfsTimeout);
700 NetSetHandler (NfsHandler);
702 NfsTimeoutCount = 0;
703 NfsState = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
705 /*FIX ME !!!*/
706 NfsOurPort = 1000;
708 /* zero out server ether in case the server ip has changed */
709 memset (NetServerEther, 0, 6);
711 NfsSend ();