Commit b0baca9820485507a3ec0e5b403a6bf0a57ff2fc

Authored by Guillaume GARDET
Committed by Joe Hershberger
1 parent d23d7bd793

net: NFS: Add NFSv3 support

This patch enables NFSv3 support.
If NFSv2 is available use it as usual.
If NFSv2 is not available, but NFSv3 is available, use NFSv3.
If NFSv2 and NFSv3 are not available, print an error message since NFSv4 is not supported.

Tested on iMX6 sabrelite with 4 Linux NFS servers:
  * NFSv2 + NFSv3 + NFSv4 server: use NFSv2 protocol
  * NFSv2 + NFSv3 server: use NFSv2 protocol
  * NFSv3 + NFSv4 server: use NFSv3 protocol
  * NFSv3 server: use NFSv3 protocol

Signed-off-by: Guillaume GARDET <guillaume.gardet@free.fr>
Cc: Tom Rini <trini@konsulko.com>
Cc: joe.hershberger@ni.com
Acked-by: Joe Hershberger <joe.hershberger@ni.com>

Showing 2 changed files with 242 additions and 47 deletions Side-by-side Diff

... ... @@ -22,6 +22,10 @@
22 22 * possible, maximum 16 steps). There is no clearing of ".."'s inside the
23 23 * path, so please DON'T DO THAT. thx. */
24 24  
  25 +/* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20.
  26 + * NFSv2 is still used by default. But if server does not support NFSv2, then
  27 + * NFSv3 is used, if available on NFS server. */
  28 +
25 29 #include <common.h>
26 30 #include <command.h>
27 31 #include <net.h>
28 32  
... ... @@ -47,9 +51,12 @@
47 51 static int nfs_len;
48 52 static ulong nfs_timeout = NFS_TIMEOUT;
49 53  
50   -static char dirfh[NFS_FHSIZE]; /* file handle of directory */
51   -static char filefh[NFS_FHSIZE]; /* file handle of kernel image */
  54 +static char dirfh[NFS_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */
  55 +static char filefh[NFS_FHSIZE]; /* NFSv2 file handle */
52 56  
  57 +static char filefh3[NFS3_FHSIZE]; /* NFSv3 file handle */
  58 +static int filefh3_length; /* (variable) length of filefh3 */
  59 +
53 60 static enum net_loop_state nfs_download_state;
54 61 static struct in_addr nfs_server_ip;
55 62 static int nfs_server_mount_port;
... ... @@ -69,6 +76,10 @@
69 76 static char *nfs_path;
70 77 static char nfs_path_buff[2048];
71 78  
  79 +#define NFSV2_FLAG 1
  80 +#define NFSV3_FLAG 1 << 1
  81 +static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG;
  82 +
72 83 static inline int store_block(uchar *src, unsigned offset, unsigned len)
73 84 {
74 85 ulong newsize = offset + len;
... ... @@ -187,7 +198,18 @@
187 198 pkt.u.call.type = htonl(MSG_CALL);
188 199 pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
189 200 pkt.u.call.prog = htonl(rpc_prog);
190   - pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
  201 + switch (rpc_prog) {
  202 + case PROG_NFS:
  203 + if (supported_nfs_versions & NFSV2_FLAG)
  204 + pkt.u.call.vers = htonl(2); /* NFS v2 */
  205 + else /* NFSV3_FLAG */
  206 + pkt.u.call.vers = htonl(3); /* NFS v3 */
  207 + break;
  208 + case PROG_PORTMAP:
  209 + case PROG_MOUNT:
  210 + default:
  211 + pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
  212 + }
191 213 pkt.u.call.proc = htonl(rpc_proc);
192 214 p = (uint32_t *)&(pkt.u.call.data);
193 215  
... ... @@ -223,7 +245,6 @@
223 245 data[5] = htonl(ver);
224 246 data[6] = htonl(17); /* IP_UDP */
225 247 data[7] = 0;
226   -
227 248 rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
228 249 }
229 250  
... ... @@ -290,8 +311,14 @@
290 311 p = &(data[0]);
291 312 p = rpc_add_credentials(p);
292 313  
293   - memcpy(p, filefh, NFS_FHSIZE);
294   - p += (NFS_FHSIZE / 4);
  314 + if (supported_nfs_versions & NFSV2_FLAG) {
  315 + memcpy(p, filefh, NFS_FHSIZE);
  316 + p += (NFS_FHSIZE / 4);
  317 + } else { /* NFSV3_FLAG */
  318 + *p++ = htonl(filefh3_length);
  319 + memcpy(p, filefh3, filefh3_length);
  320 + p += (filefh3_length / 4);
  321 + }
295 322  
296 323 len = (uint32_t *)p - (uint32_t *)&(data[0]);
297 324  
298 325  
299 326  
... ... @@ -313,17 +340,32 @@
313 340 p = &(data[0]);
314 341 p = rpc_add_credentials(p);
315 342  
316   - memcpy(p, dirfh, NFS_FHSIZE);
317   - p += (NFS_FHSIZE / 4);
318   - *p++ = htonl(fnamelen);
319   - if (fnamelen & 3)
320   - *(p + fnamelen / 4) = 0;
321   - memcpy(p, fname, fnamelen);
322   - p += (fnamelen + 3) / 4;
  343 + if (supported_nfs_versions & NFSV2_FLAG) {
  344 + memcpy(p, dirfh, NFS_FHSIZE);
  345 + p += (NFS_FHSIZE / 4);
  346 + *p++ = htonl(fnamelen);
  347 + if (fnamelen & 3)
  348 + *(p + fnamelen / 4) = 0;
  349 + memcpy(p, fname, fnamelen);
  350 + p += (fnamelen + 3) / 4;
323 351  
324   - len = (uint32_t *)p - (uint32_t *)&(data[0]);
  352 + len = (uint32_t *)p - (uint32_t *)&(data[0]);
325 353  
326   - rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
  354 + rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
  355 + } else { /* NFSV3_FLAG */
  356 + *p++ = htonl(NFS_FHSIZE); /* Dir handle length */
  357 + memcpy(p, dirfh, NFS_FHSIZE);
  358 + p += (NFS_FHSIZE / 4);
  359 + *p++ = htonl(fnamelen);
  360 + if (fnamelen & 3)
  361 + *(p + fnamelen / 4) = 0;
  362 + memcpy(p, fname, fnamelen);
  363 + p += (fnamelen + 3) / 4;
  364 +
  365 + len = (uint32_t *)p - (uint32_t *)&(data[0]);
  366 +
  367 + rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len);
  368 + }
327 369 }
328 370  
329 371 /**************************************************************************
... ... @@ -338,11 +380,21 @@
338 380 p = &(data[0]);
339 381 p = rpc_add_credentials(p);
340 382  
341   - memcpy(p, filefh, NFS_FHSIZE);
342   - p += (NFS_FHSIZE / 4);
343   - *p++ = htonl(offset);
344   - *p++ = htonl(readlen);
345   - *p++ = 0;
  383 + if (supported_nfs_versions & NFSV2_FLAG) {
  384 + memcpy(p, filefh, NFS_FHSIZE);
  385 + p += (NFS_FHSIZE / 4);
  386 + *p++ = htonl(offset);
  387 + *p++ = htonl(readlen);
  388 + *p++ = 0;
  389 + } else { /* NFSV3_FLAG */
  390 + *p++ = htonl(filefh3_length);
  391 + memcpy(p, filefh3, filefh3_length);
  392 + p += (filefh3_length / 4);
  393 + *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */
  394 + *p++ = htonl(offset);
  395 + *p++ = htonl(readlen);
  396 + *p++ = 0;
  397 + }
346 398  
347 399 len = (uint32_t *)p - (uint32_t *)&(data[0]);
348 400  
349 401  
... ... @@ -358,10 +410,16 @@
358 410  
359 411 switch (nfs_state) {
360 412 case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
361   - rpc_lookup_req(PROG_MOUNT, 1);
  413 + if (supported_nfs_versions & NFSV2_FLAG)
  414 + rpc_lookup_req(PROG_MOUNT, 1);
  415 + else /* NFSV3_FLAG */
  416 + rpc_lookup_req(PROG_MOUNT, 3);
362 417 break;
363 418 case STATE_PRCLOOKUP_PROG_NFS_REQ:
364   - rpc_lookup_req(PROG_NFS, 2);
  419 + if (supported_nfs_versions & NFSV2_FLAG)
  420 + rpc_lookup_req(PROG_NFS, 2);
  421 + else /* NFSV3_FLAG */
  422 + rpc_lookup_req(PROG_NFS, 3);
365 423 break;
366 424 case STATE_MOUNT_REQ:
367 425 nfs_mount_req(nfs_path);
... ... @@ -435,6 +493,7 @@
435 493 return -1;
436 494  
437 495 fs_mounted = 1;
  496 + /* NFSv2 and NFSv3 use same structure */
438 497 memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
439 498  
440 499 return 0;
441 500  
442 501  
... ... @@ -482,14 +541,33 @@
482 541 rpc_pkt.u.reply.astatus ||
483 542 rpc_pkt.u.reply.data[0]) {
484 543 switch (ntohl(rpc_pkt.u.reply.astatus)) {
485   - case 0: /* Not an error */
  544 + case NFS_RPC_SUCCESS: /* Not an error */
486 545 break;
487   - case 2: /* Remote can't support NFS version */
488   - printf("*** ERROR: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
489   - 2,
490   - ntohl(rpc_pkt.u.reply.data[0]),
491   - ntohl(rpc_pkt.u.reply.data[1]));
  546 + case NFS_RPC_PROG_MISMATCH:
  547 + /* Remote can't support NFS version */
  548 + switch (ntohl(rpc_pkt.u.reply.data[0])) {
  549 + /* Minimal supported NFS version */
  550 + case 3:
  551 + debug("*** Waring: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
  552 + (supported_nfs_versions & NFSV2_FLAG) ? 2 : 3,
  553 + ntohl(rpc_pkt.u.reply.data[0]),
  554 + ntohl(rpc_pkt.u.reply.data[1]));
  555 + debug("Will retry with NFSv3\n");
  556 + /* Clear NFSV2_FLAG from supported versions */
  557 + supported_nfs_versions &= ~NFSV2_FLAG;
  558 + return -NFS_RPC_PROG_MISMATCH;
  559 + case 4:
  560 + default:
  561 + printf("*** ERROR: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
  562 + (supported_nfs_versions & NFSV2_FLAG) ? 2 : 3,
  563 + ntohl(rpc_pkt.u.reply.data[0]),
  564 + ntohl(rpc_pkt.u.reply.data[1]));
  565 + }
492 566 break;
  567 + case NFS_RPC_PROG_UNAVAIL:
  568 + case NFS_RPC_PROC_UNAVAIL:
  569 + case NFS_RPC_GARBAGE_ARGS:
  570 + case NFS_RPC_SYSTEM_ERR:
493 571 default: /* Unknown error on 'accept state' flag */
494 572 printf("*** ERROR: accept state error (%d)\n",
495 573 ntohl(rpc_pkt.u.reply.astatus));
... ... @@ -498,7 +576,14 @@
498 576 return -1;
499 577 }
500 578  
501   - memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
  579 + if (supported_nfs_versions & NFSV2_FLAG) {
  580 + memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
  581 + } else { /* NFSV3_FLAG */
  582 + filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
  583 + if (filefh3_length > NFS3_FHSIZE)
  584 + filefh3_length = NFS3_FHSIZE;
  585 + memcpy(filefh3, rpc_pkt.u.reply.data + 2, filefh3_length);
  586 + }
502 587  
503 588 return 0;
504 589 }
505 590  
... ... @@ -523,18 +608,68 @@
523 608 rpc_pkt.u.reply.data[0])
524 609 return -1;
525 610  
526   - rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */
  611 + if (supported_nfs_versions & NFSV2_FLAG) {
527 612  
528   - if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
529   - int pathlen;
530   - strcat(nfs_path, "/");
531   - pathlen = strlen(nfs_path);
532   - memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]),
533   - rlen);
534   - nfs_path[pathlen + rlen] = 0;
535   - } else {
536   - memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen);
537   - nfs_path[rlen] = 0;
  613 + rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */
  614 +
  615 + if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
  616 + int pathlen;
  617 + strcat(nfs_path, "/");
  618 + pathlen = strlen(nfs_path);
  619 + memcpy(nfs_path + pathlen,
  620 + (uchar *)&(rpc_pkt.u.reply.data[2]),
  621 + rlen);
  622 + nfs_path[pathlen + rlen] = 0;
  623 + } else {
  624 + memcpy(nfs_path,
  625 + (uchar *)&(rpc_pkt.u.reply.data[2]),
  626 + rlen);
  627 + nfs_path[rlen] = 0;
  628 + }
  629 + } else { /* NFSV3_FLAG */
  630 + int nfsv3_data_offset = 0;
  631 + if (ntohl(rpc_pkt.u.reply.data[1]) != 0) {
  632 + /* 'attributes_follow' flag is TRUE,
  633 + * so we have attributes on 21 bytes */
  634 + /* Skip unused values :
  635 + type; 32 bits value,
  636 + mode; 32 bits value,
  637 + nlink; 32 bits value,
  638 + uid; 32 bits value,
  639 + gid; 32 bits value,
  640 + size; 64 bits value,
  641 + used; 64 bits value,
  642 + rdev; 64 bits value,
  643 + fsid; 64 bits value,
  644 + fileid; 64 bits value,
  645 + atime; 64 bits value,
  646 + mtime; 64 bits value,
  647 + ctime; 64 bits value,
  648 + */
  649 + nfsv3_data_offset = 22;
  650 + } else {
  651 + /* 'attributes_follow' flag is FALSE,
  652 + * so we don't have any attributes */
  653 + nfsv3_data_offset = 1;
  654 + }
  655 +
  656 + /* new path length */
  657 + rlen = ntohl(rpc_pkt.u.reply.data[1+nfsv3_data_offset]);
  658 +
  659 + if (*((char *)&(rpc_pkt.u.reply.data[2+nfsv3_data_offset])) != '/') {
  660 + int pathlen;
  661 + strcat(nfs_path, "/");
  662 + pathlen = strlen(nfs_path);
  663 + memcpy(nfs_path + pathlen,
  664 + (uchar *)&(rpc_pkt.u.reply.data[2+nfsv3_data_offset]),
  665 + rlen);
  666 + nfs_path[pathlen + rlen] = 0;
  667 + } else {
  668 + memcpy(nfs_path,
  669 + (uchar *)&(rpc_pkt.u.reply.data[2+nfsv3_data_offset]),
  670 + rlen);
  671 + nfs_path[rlen] = 0;
  672 + }
538 673 }
539 674 return 0;
540 675 }
... ... @@ -543,6 +678,7 @@
543 678 {
544 679 struct rpc_t rpc_pkt;
545 680 int rlen;
  681 + uchar *data_ptr;
546 682  
547 683 debug("%s\n", __func__);
548 684  
549 685  
... ... @@ -570,11 +706,48 @@
570 706 if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
571 707 putc('#');
572 708  
573   - rlen = ntohl(rpc_pkt.u.reply.data[18]);
574   - if (store_block((uchar *)pkt + sizeof(rpc_pkt.u.reply),
575   - nfs_offset, rlen))
576   - return -9999;
  709 + if (supported_nfs_versions & NFSV2_FLAG) {
  710 + rlen = ntohl(rpc_pkt.u.reply.data[18]);
  711 + data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
  712 + } else { /* NFSV3_FLAG */
  713 + if (ntohl(rpc_pkt.u.reply.data[1]) != 0) {
  714 + /* 'attributes_follow' is TRUE,
  715 + * so we have attributes on 21 bytes */
  716 + /* Skip unused values :
  717 + type; 32 bits value,
  718 + mode; 32 bits value,
  719 + nlink; 32 bits value,
  720 + uid; 32 bits value,
  721 + gid; 32 bits value,
  722 + size; 64 bits value,
  723 + used; 64 bits value,
  724 + rdev; 64 bits value,
  725 + fsid; 64 bits value,
  726 + fileid; 64 bits value,
  727 + atime; 64 bits value,
  728 + mtime; 64 bits value,
  729 + ctime; 64 bits value,
  730 + */
  731 + rlen = ntohl(rpc_pkt.u.reply.data[23]); /* count value */
  732 + /* Skip unused values :
  733 + EOF: 32 bits value,
  734 + data_size: 32 bits value,
  735 + */
  736 + data_ptr = (uchar *)&(rpc_pkt.u.reply.data[26]);
  737 + } else {
  738 + /* attributes_follow is FALSE, so we don't have any attributes */
  739 + rlen = ntohl(rpc_pkt.u.reply.data[2]); /* count value */
  740 + /* Skip unused values :
  741 + EOF: 32 bits value,
  742 + data_size: 32 bits value,
  743 + */
  744 + data_ptr = (uchar *)&(rpc_pkt.u.reply.data[5]);
  745 + }
  746 + }
577 747  
  748 + if (store_block(data_ptr, nfs_offset, rlen))
  749 + return -9999;
  750 +
578 751 return rlen;
579 752 }
580 753  
... ... @@ -657,6 +830,13 @@
657 830 puts("*** ERROR: File lookup fail\n");
658 831 nfs_state = STATE_UMOUNT_REQ;
659 832 nfs_send();
  833 + } else if (reply == -NFS_RPC_PROG_MISMATCH && supported_nfs_versions != 0) {
  834 + /* umount */
  835 + nfs_state = STATE_UMOUNT_REQ;
  836 + nfs_send();
  837 + /* And retry with another supported version */
  838 + nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
  839 + nfs_send();
660 840 } else {
661 841 nfs_state = STATE_READ_REQ;
662 842 nfs_offset = 0;
... ... @@ -696,6 +876,8 @@
696 876 } else {
697 877 if (!rlen)
698 878 nfs_download_state = NETLOOP_SUCCESS;
  879 + if (rlen < 0)
  880 + printf("NFS READ error (%d)\n", rlen);
699 881 nfs_state = STATE_UMOUNT_REQ;
700 882 nfs_send();
701 883 }
... ... @@ -25,7 +25,10 @@
25 25 #define NFS_READLINK 5
26 26 #define NFS_READ 6
27 27  
  28 +#define NFS3PROC_LOOKUP 3
  29 +
28 30 #define NFS_FHSIZE 32
  31 +#define NFS3_FHSIZE 64
29 32  
30 33 #define NFSERR_PERM 1
31 34 #define NFSERR_NOENT 2
... ... @@ -44,6 +47,16 @@
44 47 #define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */
45 48 #endif
46 49  
  50 +/* Values for Accept State flag on RPC answers (See: rfc1831) */
  51 +enum rpc_accept_stat {
  52 + NFS_RPC_SUCCESS = 0, /* RPC executed successfully */
  53 + NFS_RPC_PROG_UNAVAIL = 1, /* remote hasn't exported program */
  54 + NFS_RPC_PROG_MISMATCH = 2, /* remote can't support version # */
  55 + NFS_RPC_PROC_UNAVAIL = 3, /* program can't support procedure */
  56 + NFS_RPC_GARBAGE_ARGS = 4, /* procedure can't decode params */
  57 + NFS_RPC_SYSTEM_ERR = 5 /* errors like memory allocation failure */
  58 +};
  59 +
47 60 struct rpc_t {
48 61 union {
49 62 uint8_t data[2048];
... ... @@ -63,7 +76,7 @@
63 76 uint32_t verifier;
64 77 uint32_t v2;
65 78 uint32_t astatus;
66   - uint32_t data[19];
  79 + uint32_t data[NFS_READ_SIZE];
67 80 } reply;
68 81 } u;
69 82 };