Add basic support for mini2440 board to barebox.
[barebox-mini2440.git] / net / nfs.c
blob4ca1d6e528f74b9c6d76b46be0a2f4668ebb45d5
1 /*
2 * NFS support driver - based on etherboot and barebox'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 <libgen.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <progress.h>
36 #include <linux/err.h>
38 #define SUNRPC_PORT 111
40 #define PROG_PORTMAP 100000
41 #define PROG_NFS 100003
42 #define PROG_MOUNT 100005
44 #define MSG_CALL 0
45 #define MSG_REPLY 1
47 #define PORTMAP_GETPORT 3
49 #define MOUNT_ADDENTRY 1
50 #define MOUNT_UMOUNTALL 4
52 #define NFS_LOOKUP 4
53 #define NFS_READLINK 5
54 #define NFS_READ 6
56 #define NFS_FHSIZE 32
58 enum nfs_stat {
59 NFS_OK = 0,
60 NFSERR_PERM = 1,
61 NFSERR_NOENT = 2,
62 NFSERR_IO = 5,
63 NFSERR_NXIO = 6,
64 NFSERR_ACCES = 13,
65 NFSERR_EXIST = 17,
66 NFSERR_NODEV = 19,
67 NFSERR_NOTDIR = 20,
68 NFSERR_ISDIR = 21,
69 NFSERR_FBIG = 27,
70 NFSERR_NOSPC = 28,
71 NFSERR_ROFS = 30,
72 NFSERR_NAMETOOLONG=63,
73 NFSERR_NOTEMPTY = 66,
74 NFSERR_DQUOT = 69,
75 NFSERR_STALE = 70,
76 NFSERR_WFLUSH = 99,
79 /* Block size used for NFS read accesses. A RPC reply packet (including all
80 * headers) must fit within a single Ethernet frame to avoid fragmentation.
81 * Chosen to be a power of two, as most NFS servers are optimized for this. */
82 #define NFS_READ_SIZE 1024
84 struct rpc_call {
85 uint32_t id;
86 uint32_t type;
87 uint32_t rpcvers;
88 uint32_t prog;
89 uint32_t vers;
90 uint32_t proc;
91 uint32_t data[0];
94 struct rpc_reply {
95 uint32_t id;
96 uint32_t type;
97 uint32_t rstatus;
98 uint32_t verifier;
99 uint32_t v2;
100 uint32_t astatus;
101 uint32_t data[0];
104 struct rpc_t {
105 union {
106 struct {
107 uint32_t id;
108 uint32_t type;
109 uint32_t rpcvers;
110 uint32_t prog;
111 uint32_t vers;
112 uint32_t proc;
113 uint32_t data[1];
114 } call;
115 struct {
116 uint32_t id;
117 uint32_t type;
118 uint32_t rstatus;
119 uint32_t verifier;
120 uint32_t v2;
121 uint32_t astatus;
122 uint32_t data[19];
123 } reply;
124 } u;
127 #define NFS_TIMEOUT 1
129 static unsigned long rpc_id = 0;
130 static int nfs_offset = -1;
131 static uint64_t nfs_timer_start;
132 static int nfs_err;
134 static char dirfh[NFS_FHSIZE]; /* file handle of directory */
135 static char filefh[NFS_FHSIZE]; /* file handle of kernel image */
137 static int nfs_server_mount_port;
138 static int nfs_server_nfs_port;
139 static int nfs_state;
140 #define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
141 #define STATE_PRCLOOKUP_PROG_NFS_REQ 2
142 #define STATE_MOUNT_REQ 3
143 #define STATE_UMOUNT_REQ 4
144 #define STATE_LOOKUP_REQ 5
145 #define STATE_READ_REQ 6
146 #define STATE_READLINK_REQ 7
147 #define STATE_DONE 8
149 static char *nfs_filename;
150 static char *nfs_path;
151 static char nfs_path_buff[2048];
153 static int net_store_fd;
154 static struct net_connection *nfs_con;
156 /**************************************************************************
157 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
158 **************************************************************************/
159 static uint32_t *rpc_add_credentials(uint32_t *p)
161 int hl;
162 int hostnamelen = 0;
164 /* Here's the executive summary on authentication requirements of the
165 * various NFS server implementations: Linux accepts both AUTH_NONE
166 * and AUTH_UNIX authentication (also accepts an empty hostname field
167 * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
168 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
169 * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
170 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
171 * hostname). */
173 hl = (hostnamelen + 3) & ~3;
175 /* Provide an AUTH_UNIX credential. */
176 *p++ = htonl(1); /* AUTH_UNIX */
177 *p++ = htonl(hl+20); /* auth length */
178 *p++ = htonl(0); /* stamp */
179 *p++ = htonl(hostnamelen); /* hostname string */
181 if (hostnamelen & 3)
182 *(p + hostnamelen / 4) = 0; /* add zero padding */
184 /* memcpy(p, hostname, hostnamelen); */ /* empty hostname */
186 p += hl / 4;
187 *p++ = 0; /* uid */
188 *p++ = 0; /* gid */
189 *p++ = 0; /* auxiliary gid list */
191 /* Provide an AUTH_NONE verifier. */
192 *p++ = 0; /* AUTH_NONE */
193 *p++ = 0; /* auth length */
195 return p;
198 /**************************************************************************
199 RPC_LOOKUP - Lookup RPC Port numbers
200 **************************************************************************/
201 static int rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
203 struct rpc_call pkt;
204 unsigned long id;
205 int sport;
206 int ret;
207 unsigned char *payload = net_udp_get_payload(nfs_con);
209 id = ++rpc_id;
210 pkt.id = htonl(id);
211 pkt.type = htonl(MSG_CALL);
212 pkt.rpcvers = htonl(2); /* use RPC version 2 */
213 pkt.prog = htonl(rpc_prog);
214 pkt.vers = htonl(2); /* portmapper is version 2 */
215 pkt.proc = htonl(rpc_proc);
217 memcpy(payload, &pkt, sizeof(pkt));
218 memcpy(payload + sizeof(pkt), data, datalen * sizeof(uint32_t));
220 if (rpc_prog == PROG_PORTMAP)
221 sport = SUNRPC_PORT;
222 else if (rpc_prog == PROG_MOUNT)
223 sport = nfs_server_mount_port;
224 else
225 sport = nfs_server_nfs_port;
227 nfs_con->udp->uh_dport = htons(sport);
228 ret = net_udp_send(nfs_con, sizeof(pkt) + datalen * sizeof(uint32_t));
230 return ret;
233 /**************************************************************************
234 RPC_LOOKUP - Lookup RPC Port numbers
235 **************************************************************************/
236 static void rpc_lookup_req(int prog, int ver)
238 uint32_t data[16];
240 data[0] = 0; data[1] = 0; /* auth credential */
241 data[2] = 0; data[3] = 0; /* auth verifier */
242 data[4] = htonl(prog);
243 data[5] = htonl(ver);
244 data[6] = htonl(17); /* IP_UDP */
245 data[7] = 0;
247 rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
250 /**************************************************************************
251 NFS_MOUNT - Mount an NFS Filesystem
252 **************************************************************************/
253 static void nfs_mount_req(char *path)
255 uint32_t data[1024];
256 uint32_t *p;
257 int len;
258 int pathlen;
260 pathlen = strlen (path);
262 p = &(data[0]);
263 p = rpc_add_credentials(p);
265 *p++ = htonl(pathlen);
266 if (pathlen & 3)
267 *(p + pathlen / 4) = 0;
268 memcpy (p, path, pathlen);
269 p += (pathlen + 3) / 4;
271 len = p - &(data[0]);
273 rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
276 /**************************************************************************
277 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
278 **************************************************************************/
279 static void nfs_umountall_req(void)
281 uint32_t data[1024];
282 uint32_t *p;
283 int len;
285 if (nfs_server_mount_port < 0)
286 /* Nothing mounted, nothing to umount */
287 return;
289 p = &(data[0]);
290 p = rpc_add_credentials(p);
292 len = p - &(data[0]);
294 rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
297 /***************************************************************************
298 * NFS_READLINK (AH 2003-07-14)
299 * This procedure is called when read of the first block fails -
300 * this probably happens when it's a directory or a symlink
301 * In case of successful readlink(), the dirname is manipulated,
302 * so that inside the nfs() function a recursion can be done.
303 **************************************************************************/
304 static void nfs_readlink_req(void)
306 uint32_t data[1024];
307 uint32_t *p;
308 int len;
310 p = &(data[0]);
311 p = rpc_add_credentials(p);
313 memcpy (p, filefh, NFS_FHSIZE);
314 p += (NFS_FHSIZE / 4);
316 len = p - &(data[0]);
318 rpc_req(PROG_NFS, NFS_READLINK, data, len);
321 /**************************************************************************
322 NFS_LOOKUP - Lookup Pathname
323 **************************************************************************/
324 static void nfs_lookup_req(char *fname)
326 uint32_t data[1024];
327 uint32_t *p;
328 int len;
329 int fnamelen;
331 fnamelen = strlen (fname);
333 p = &(data[0]);
334 p = rpc_add_credentials(p);
336 memcpy (p, dirfh, NFS_FHSIZE);
337 p += (NFS_FHSIZE / 4);
338 *p++ = htonl(fnamelen);
339 if (fnamelen & 3)
340 *(p + fnamelen / 4) = 0;
341 memcpy (p, fname, fnamelen);
342 p += (fnamelen + 3) / 4;
344 len = p - &(data[0]);
346 rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
349 /**************************************************************************
350 NFS_READ - Read File on NFS Server
351 **************************************************************************/
352 static void nfs_read_req(int offset, int readlen)
354 uint32_t data[1024];
355 uint32_t *p;
356 int len;
358 p = &(data[0]);
359 p = rpc_add_credentials(p);
361 memcpy (p, filefh, NFS_FHSIZE);
362 p += (NFS_FHSIZE / 4);
363 *p++ = htonl(offset);
364 *p++ = htonl(readlen);
365 *p++ = 0;
367 len = p - &(data[0]);
369 rpc_req(PROG_NFS, NFS_READ, data, len);
372 /**************************************************************************
373 RPC request dispatcher
374 **************************************************************************/
375 static void nfs_send(void)
377 debug("%s\n", __func__);
379 switch (nfs_state) {
380 case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
381 rpc_lookup_req(PROG_MOUNT, 1);
382 break;
383 case STATE_PRCLOOKUP_PROG_NFS_REQ:
384 rpc_lookup_req(PROG_NFS, 2);
385 break;
386 case STATE_MOUNT_REQ:
387 nfs_mount_req(nfs_path);
388 break;
389 case STATE_UMOUNT_REQ:
390 nfs_umountall_req();
391 break;
392 case STATE_LOOKUP_REQ:
393 nfs_lookup_req(nfs_filename);
394 break;
395 case STATE_READ_REQ:
396 nfs_read_req(nfs_offset, NFS_READ_SIZE);
397 break;
398 case STATE_READLINK_REQ:
399 nfs_readlink_req();
400 break;
403 nfs_timer_start = get_time_ns();
406 static int rpc_check_reply(unsigned char *pkt, int isnfs)
408 uint32_t *data;
409 int nfserr;
410 struct rpc_reply rpc;
412 memcpy(&rpc, pkt, sizeof(rpc));
414 if (ntohl(rpc.id) != rpc_id)
415 return -EINVAL;
417 if (rpc.rstatus ||
418 rpc.verifier ||
419 rpc.astatus ) {
420 return -EINVAL;
423 if (!isnfs)
424 return 0;
426 data = (uint32_t *)(pkt + sizeof(struct rpc_reply));
427 nfserr = ntohl(net_read_uint32(data));
429 debug("%s: state: %d, err %d\n", __func__, nfs_state, -nfserr);
431 if (nfserr <= 30)
432 /* These nfs codes correspond with those in errno.h */
433 return -nfserr;
434 if (nfserr == NFSERR_STALE)
435 return -ESTALE;
437 return -EINVAL;
440 static int rpc_lookup_reply(int prog, unsigned char *pkt, unsigned len)
442 uint32_t port;
443 int ret;
445 ret = rpc_check_reply(pkt, 0);
446 if (ret)
447 return ret;
449 port = net_read_uint32((uint32_t *)(pkt + sizeof(struct rpc_reply)));
450 switch (prog) {
451 case PROG_MOUNT:
452 nfs_server_mount_port = ntohl(port);
453 break;
454 case PROG_NFS:
455 nfs_server_nfs_port = ntohl(port);
456 break;
459 return 0;
462 static int nfs_mount_reply(unsigned char *pkt, unsigned len)
464 int ret;
466 ret = rpc_check_reply(pkt, 1);
467 if (ret)
468 return ret;
470 memcpy(dirfh, pkt + sizeof(struct rpc_reply) + 4, NFS_FHSIZE);
472 return 0;
475 static int nfs_umountall_reply(unsigned char *pkt, unsigned len)
477 int ret;
479 ret = rpc_check_reply(pkt, 0);
480 if (ret)
481 return ret;
483 memset(dirfh, 0, sizeof(dirfh));
485 return 0;
488 static int nfs_lookup_reply(unsigned char *pkt, unsigned len)
490 int ret;
492 ret = rpc_check_reply(pkt, 1);
493 if (ret)
494 return ret;
496 memcpy(filefh, pkt + sizeof(struct rpc_reply) + 4, NFS_FHSIZE);
498 return 0;
501 static int nfs_readlink_reply(unsigned char *pkt, unsigned len)
503 uint32_t *data;
504 char *path;
505 int rlen;
506 int ret;
508 ret = rpc_check_reply(pkt, 1);
509 if (ret)
510 return ret;
512 data = (uint32_t *)(pkt + sizeof(struct rpc_reply));
514 data++;
516 rlen = ntohl(net_read_uint32(data)); /* new path length */
518 data++;
519 path = (char *)data;
521 if (*path != '/') {
522 strcat(nfs_path, "/");
523 strncat(nfs_path, path, rlen);
524 } else {
525 memcpy(nfs_path, path, rlen);
526 nfs_path[rlen] = 0;
528 return 0;
531 static int nfs_read_reply(unsigned char *pkt, unsigned len)
533 int rlen;
534 uint32_t *data;
535 int ret;
537 debug("%s\n", __func__);
539 ret = rpc_check_reply(pkt, 1);
540 if (ret)
541 return ret;
543 data = (uint32_t *)(pkt + sizeof(struct rpc_reply));
545 if (!nfs_offset) {
546 uint32_t filesize = ntohl(net_read_uint32(data + 6));
547 init_progression_bar(filesize);
550 rlen = ntohl(net_read_uint32(data + 18));
552 ret = write(net_store_fd, (char *)(data + 19), rlen);
553 if (ret < 0) {
554 perror("write");
555 return ret;
558 return rlen;
561 /**************************************************************************
562 Interfaces of barebox
563 **************************************************************************/
565 static void nfs_handler(char *packet, unsigned len)
567 char *pkt = net_eth_to_udp_payload(packet);
568 int ret;
570 debug("%s\n", __func__);
572 switch (nfs_state) {
573 case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
574 ret = rpc_lookup_reply(PROG_MOUNT, pkt, len);
575 if (ret)
576 goto err_out;
577 nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
578 break;
580 case STATE_PRCLOOKUP_PROG_NFS_REQ:
581 ret = rpc_lookup_reply(PROG_NFS, pkt, len);
582 if (ret)
583 goto err_out;
584 nfs_state = STATE_MOUNT_REQ;
585 break;
587 case STATE_MOUNT_REQ:
588 ret = nfs_mount_reply(pkt, len);
589 if (ret)
590 goto err_out;
592 nfs_state = STATE_LOOKUP_REQ;
593 break;
595 case STATE_UMOUNT_REQ:
596 ret = nfs_umountall_reply(pkt, len);
597 if (ret)
598 nfs_err = ret;
599 nfs_state = STATE_DONE;
600 return;
602 case STATE_LOOKUP_REQ:
603 ret = nfs_lookup_reply(pkt, len);
604 if (ret)
605 goto err_umount;
607 nfs_state = STATE_READ_REQ;
608 nfs_offset = 0;
609 break;
611 case STATE_READLINK_REQ:
612 ret = nfs_readlink_reply(pkt, len);
613 if (ret)
614 goto err_umount;
616 debug("Symlink --> %s\n", nfs_path);
618 nfs_filename = basename(nfs_path);
619 nfs_path = dirname(nfs_path);
621 nfs_state = STATE_MOUNT_REQ;
622 break;
624 case STATE_READ_REQ:
625 ret = nfs_read_reply(pkt, len);
626 nfs_timer_start = get_time_ns();
627 if (ret > 0)
628 nfs_offset += ret;
629 else if (ret == -EISDIR || ret == -EINVAL)
630 /* symbolic link */
631 nfs_state = STATE_READLINK_REQ;
632 else
633 goto err_umount;
634 show_progress(nfs_offset);
635 break;
638 nfs_send();
640 return;
642 err_umount:
643 nfs_state = STATE_UMOUNT_REQ;
644 nfs_err = ret;
645 nfs_send();
646 return;
648 err_out:
649 nfs_state = STATE_DONE;
650 nfs_err = ret;
653 static void nfs_start(char *p)
655 debug("%s\n", __func__);
657 nfs_path = (char *)nfs_path_buff;
659 strcpy(nfs_path, p);
661 nfs_filename = basename (nfs_path);
662 nfs_path = dirname (nfs_path);
664 printf("\nFilename '%s/%s'.\n", nfs_path, nfs_filename);
666 nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
668 nfs_send();
671 static int do_nfs(struct command *cmdtp, int argc, char *argv[])
673 char *localfile;
674 char *remotefile;
676 if (argc < 2)
677 return COMMAND_ERROR_USAGE;
679 remotefile = argv[1];
681 if (argc == 2)
682 localfile = basename(remotefile);
683 else
684 localfile = argv[2];
686 net_store_fd = open(localfile, O_WRONLY | O_CREAT);
687 if (net_store_fd < 0) {
688 perror("open");
689 return 1;
692 nfs_con = net_udp_new(net_get_serverip(), 0, nfs_handler);
693 if (IS_ERR(nfs_con)) {
694 nfs_err = PTR_ERR(nfs_con);
695 goto err_udp;
697 net_udp_bind(nfs_con, 1000);
699 nfs_err = 0;
701 nfs_start(remotefile);
703 while (nfs_state != STATE_DONE) {
704 if (ctrlc()) {
705 nfs_err = -EINTR;
706 break;
708 net_poll();
709 if (is_timeout(nfs_timer_start, NFS_TIMEOUT * SECOND)) {
710 show_progress(-1);
711 nfs_send();
715 net_unregister(nfs_con);
716 err_udp:
717 close(net_store_fd);
718 if (nfs_err) {
719 printf("NFS failed: %s\n", strerror(-nfs_err));
720 unlink(localfile);
723 printf("\n");
725 return nfs_err == 0 ? 0 : 1;
728 static const __maybe_unused char cmd_nfs_help[] =
729 "Usage: nfs <file> [localfile]\n"
730 "Load a file via network using nfs protocol.\n";
732 BAREBOX_CMD_START(nfs)
733 .cmd = do_nfs,
734 .usage = "boot image via network using nfs protocol",
735 BAREBOX_CMD_HELP(cmd_nfs_help)
736 BAREBOX_CMD_END