2 * NFS support driver - based on etherboot and barebox's tftp.c
4 * Masami Komiya <mkomiya@sonare.it> 2004
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. */
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
47 #define PORTMAP_GETPORT 3
49 #define MOUNT_ADDENTRY 1
50 #define MOUNT_UMOUNTALL 4
53 #define NFS_READLINK 5
72 NFSERR_NAMETOOLONG
=63,
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
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
;
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
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
)
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
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 */
182 *(p
+ hostnamelen
/ 4) = 0; /* add zero padding */
184 /* memcpy(p, hostname, hostnamelen); */ /* empty hostname */
189 *p
++ = 0; /* auxiliary gid list */
191 /* Provide an AUTH_NONE verifier. */
192 *p
++ = 0; /* AUTH_NONE */
193 *p
++ = 0; /* auth length */
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
)
207 unsigned char *payload
= net_udp_get_payload(nfs_con
);
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
)
222 else if (rpc_prog
== PROG_MOUNT
)
223 sport
= nfs_server_mount_port
;
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));
233 /**************************************************************************
234 RPC_LOOKUP - Lookup RPC Port numbers
235 **************************************************************************/
236 static void rpc_lookup_req(int prog
, int ver
)
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 */
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
)
260 pathlen
= strlen (path
);
263 p
= rpc_add_credentials(p
);
265 *p
++ = htonl(pathlen
);
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)
285 if (nfs_server_mount_port
< 0)
286 /* Nothing mounted, nothing to umount */
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)
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
)
331 fnamelen
= strlen (fname
);
334 p
= rpc_add_credentials(p
);
336 memcpy (p
, dirfh
, NFS_FHSIZE
);
337 p
+= (NFS_FHSIZE
/ 4);
338 *p
++ = htonl(fnamelen
);
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
)
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
);
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__
);
380 case STATE_PRCLOOKUP_PROG_MOUNT_REQ
:
381 rpc_lookup_req(PROG_MOUNT
, 1);
383 case STATE_PRCLOOKUP_PROG_NFS_REQ
:
384 rpc_lookup_req(PROG_NFS
, 2);
386 case STATE_MOUNT_REQ
:
387 nfs_mount_req(nfs_path
);
389 case STATE_UMOUNT_REQ
:
392 case STATE_LOOKUP_REQ
:
393 nfs_lookup_req(nfs_filename
);
396 nfs_read_req(nfs_offset
, NFS_READ_SIZE
);
398 case STATE_READLINK_REQ
:
403 nfs_timer_start
= get_time_ns();
406 static int rpc_check_reply(unsigned char *pkt
, int isnfs
)
410 struct rpc_reply rpc
;
412 memcpy(&rpc
, pkt
, sizeof(rpc
));
414 if (ntohl(rpc
.id
) != rpc_id
)
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
);
432 /* These nfs codes correspond with those in errno.h */
434 if (nfserr
== NFSERR_STALE
)
440 static int rpc_lookup_reply(int prog
, unsigned char *pkt
, unsigned len
)
445 ret
= rpc_check_reply(pkt
, 0);
449 port
= net_read_uint32((uint32_t *)(pkt
+ sizeof(struct rpc_reply
)));
452 nfs_server_mount_port
= ntohl(port
);
455 nfs_server_nfs_port
= ntohl(port
);
462 static int nfs_mount_reply(unsigned char *pkt
, unsigned len
)
466 ret
= rpc_check_reply(pkt
, 1);
470 memcpy(dirfh
, pkt
+ sizeof(struct rpc_reply
) + 4, NFS_FHSIZE
);
475 static int nfs_umountall_reply(unsigned char *pkt
, unsigned len
)
479 ret
= rpc_check_reply(pkt
, 0);
483 memset(dirfh
, 0, sizeof(dirfh
));
488 static int nfs_lookup_reply(unsigned char *pkt
, unsigned len
)
492 ret
= rpc_check_reply(pkt
, 1);
496 memcpy(filefh
, pkt
+ sizeof(struct rpc_reply
) + 4, NFS_FHSIZE
);
501 static int nfs_readlink_reply(unsigned char *pkt
, unsigned len
)
508 ret
= rpc_check_reply(pkt
, 1);
512 data
= (uint32_t *)(pkt
+ sizeof(struct rpc_reply
));
516 rlen
= ntohl(net_read_uint32(data
)); /* new path length */
522 strcat(nfs_path
, "/");
523 strncat(nfs_path
, path
, rlen
);
525 memcpy(nfs_path
, path
, rlen
);
531 static int nfs_read_reply(unsigned char *pkt
, unsigned len
)
537 debug("%s\n", __func__
);
539 ret
= rpc_check_reply(pkt
, 1);
543 data
= (uint32_t *)(pkt
+ sizeof(struct rpc_reply
));
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
);
561 /**************************************************************************
562 Interfaces of barebox
563 **************************************************************************/
565 static void nfs_handler(char *packet
, unsigned len
)
567 char *pkt
= net_eth_to_udp_payload(packet
);
570 debug("%s\n", __func__
);
573 case STATE_PRCLOOKUP_PROG_MOUNT_REQ
:
574 ret
= rpc_lookup_reply(PROG_MOUNT
, pkt
, len
);
577 nfs_state
= STATE_PRCLOOKUP_PROG_NFS_REQ
;
580 case STATE_PRCLOOKUP_PROG_NFS_REQ
:
581 ret
= rpc_lookup_reply(PROG_NFS
, pkt
, len
);
584 nfs_state
= STATE_MOUNT_REQ
;
587 case STATE_MOUNT_REQ
:
588 ret
= nfs_mount_reply(pkt
, len
);
592 nfs_state
= STATE_LOOKUP_REQ
;
595 case STATE_UMOUNT_REQ
:
596 ret
= nfs_umountall_reply(pkt
, len
);
599 nfs_state
= STATE_DONE
;
602 case STATE_LOOKUP_REQ
:
603 ret
= nfs_lookup_reply(pkt
, len
);
607 nfs_state
= STATE_READ_REQ
;
611 case STATE_READLINK_REQ
:
612 ret
= nfs_readlink_reply(pkt
, len
);
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
;
625 ret
= nfs_read_reply(pkt
, len
);
626 nfs_timer_start
= get_time_ns();
629 else if (ret
== -EISDIR
|| ret
== -EINVAL
)
631 nfs_state
= STATE_READLINK_REQ
;
634 show_progress(nfs_offset
);
643 nfs_state
= STATE_UMOUNT_REQ
;
649 nfs_state
= STATE_DONE
;
653 static void nfs_start(char *p
)
655 debug("%s\n", __func__
);
657 nfs_path
= (char *)nfs_path_buff
;
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
;
671 static int do_nfs(struct command
*cmdtp
, int argc
, char *argv
[])
677 return COMMAND_ERROR_USAGE
;
679 remotefile
= argv
[1];
682 localfile
= basename(remotefile
);
686 net_store_fd
= open(localfile
, O_WRONLY
| O_CREAT
);
687 if (net_store_fd
< 0) {
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
);
697 net_udp_bind(nfs_con
, 1000);
701 nfs_start(remotefile
);
703 while (nfs_state
!= STATE_DONE
) {
709 if (is_timeout(nfs_timer_start
, NFS_TIMEOUT
* SECOND
)) {
715 net_unregister(nfs_con
);
719 printf("NFS failed: %s\n", strerror(-nfs_err
));
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
)
734 .usage
= "boot image via network using nfs protocol",
735 BAREBOX_CMD_HELP(cmd_nfs_help
)