[U-Boot] [PATCH] net: NFS: Add NFSv3 support
Guillaume Gardet
guillaume.gardet at free.fr
Mon Jun 20 21:31:12 CEST 2016
Please note that, this patch applies on top of this one:
NFS: Add error message when U-Boot NFS version (V2) is not supported by NFS server
https://patchwork.ozlabs.org/patch/630898/
It would be nice if people could test it on other boards and with other NFS servers.
Guillaume
Le 20/06/2016 21:27, Guillaume GARDET a écrit :
> 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 NFSv3 protocol
> * NFSv3 + NFSv4 server: use NFSv3 protocol
> * NFSv3 server: use NFSv3 protocol
>
> Signed-off-by: Guillaume GARDET <guillaume.gardet at free.fr>
> Cc: Tom Rini <trini at konsulko.com>
> Cc: joe.hershberger at ni.com
>
> ---
> net/nfs.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
> net/nfs.h | 15 +++-
> 2 files changed, 246 insertions(+), 50 deletions(-)
>
> diff --git a/net/nfs.c b/net/nfs.c
> index 0ed47c9..1e920c1 100644
> --- a/net/nfs.c
> +++ b/net/nfs.c
> @@ -22,6 +22,10 @@
> * possible, maximum 16 steps). There is no clearing of ".."'s inside the
> * path, so please DON'T DO THAT. thx. */
>
> +/* NOTE 4: NFSv3 support added by Guillaume GARDET.
> + * NFSv2 is still used by default. But if server does not support NFSv2, then
> + * NFSv3 is used, if available on NFS server. */
> +
> #include <common.h>
> #include <command.h>
> #include <net.h>
> @@ -47,8 +51,11 @@ static int nfs_offset = -1;
> static int nfs_len;
> static ulong nfs_timeout = NFS_TIMEOUT;
>
> -static char dirfh[NFS_FHSIZE]; /* file handle of directory */
> -static char filefh[NFS_FHSIZE]; /* file handle of kernel image */
> +static char dirfh[NFS_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */
> +static char filefh[NFS_FHSIZE]; /* NFSv2 file handle */
> +
> +static char filefh3[NFS3_FHSIZE]; /* NFSv3 file handle */
> +static int filefh3_length; /* (variable) length of filefh3 */
>
> static enum net_loop_state nfs_download_state;
> static struct in_addr nfs_server_ip;
> @@ -70,6 +77,10 @@ static char *nfs_filename;
> static char *nfs_path;
> static char nfs_path_buff[2048];
>
> +#define NFSV2_FLAG 1
> +#define NFSV3_FLAG 1 << 1
> +static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG;
> +
> static inline int store_block(uchar *src, unsigned offset, unsigned len)
> {
> ulong newsize = offset + len;
> @@ -188,7 +199,18 @@ static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
> pkt.u.call.type = htonl(MSG_CALL);
> pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
> pkt.u.call.prog = htonl(rpc_prog);
> - pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
> + switch (rpc_prog) {
> + case PROG_NFS:
> + if (supported_nfs_versions & NFSV2_FLAG)
> + pkt.u.call.vers = htonl(2); /* NFS v2 */
> + else /* NFSV3_FLAG */
> + pkt.u.call.vers = htonl(3); /* NFS v3 */
> + break;
> + case PROG_PORTMAP:
> + case PROG_MOUNT:
> + default:
> + pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
> + }
> pkt.u.call.proc = htonl(rpc_proc);
> p = (uint32_t *)&(pkt.u.call.data);
>
> @@ -224,7 +246,6 @@ static void rpc_lookup_req(int prog, int ver)
> data[5] = htonl(ver);
> data[6] = htonl(17); /* IP_UDP */
> data[7] = 0;
> -
> rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
> }
>
> @@ -291,8 +312,14 @@ static void nfs_readlink_req(void)
> p = &(data[0]);
> p = (uint32_t *)rpc_add_credentials((long *)p);
>
> - memcpy(p, filefh, NFS_FHSIZE);
> - p += (NFS_FHSIZE / 4);
> + if (supported_nfs_versions & NFSV2_FLAG) {
> + memcpy(p, filefh, NFS_FHSIZE);
> + p += (NFS_FHSIZE / 4);
> + } else { /* NFSV3_FLAG */
> + *p++ = htonl(filefh3_length);
> + memcpy(p, filefh3, filefh3_length);
> + p += (filefh3_length / 4);
> + }
>
> len = (uint32_t *)p - (uint32_t *)&(data[0]);
>
> @@ -314,17 +341,32 @@ static void nfs_lookup_req(char *fname)
> p = &(data[0]);
> p = (uint32_t *)rpc_add_credentials((long *)p);
>
> - memcpy(p, dirfh, NFS_FHSIZE);
> - p += (NFS_FHSIZE / 4);
> - *p++ = htonl(fnamelen);
> - if (fnamelen & 3)
> - *(p + fnamelen / 4) = 0;
> - memcpy(p, fname, fnamelen);
> - p += (fnamelen + 3) / 4;
> -
> - len = (uint32_t *)p - (uint32_t *)&(data[0]);
> -
> - rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
> + if (supported_nfs_versions & NFSV2_FLAG) {
> + memcpy(p, dirfh, NFS_FHSIZE);
> + p += (NFS_FHSIZE / 4);
> + *p++ = htonl(fnamelen);
> + if (fnamelen & 3)
> + *(p + fnamelen / 4) = 0;
> + memcpy(p, fname, fnamelen);
> + p += (fnamelen + 3) / 4;
> +
> + len = (uint32_t *)p - (uint32_t *)&(data[0]);
> +
> + rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
> + } else { /* NFSV3_FLAG */
> + *p++ = htonl(NFS_FHSIZE); /* Dir handle length */
> + memcpy(p, dirfh, NFS_FHSIZE);
> + p += (NFS_FHSIZE / 4);
> + *p++ = htonl(fnamelen);
> + if (fnamelen & 3)
> + *(p + fnamelen / 4) = 0;
> + memcpy(p, fname, fnamelen);
> + p += (fnamelen + 3) / 4;
> +
> + len = (uint32_t *)p - (uint32_t *)&(data[0]);
> +
> + rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len);
> + }
> }
>
> /**************************************************************************
> @@ -339,11 +381,21 @@ static void nfs_read_req(int offset, int readlen)
> p = &(data[0]);
> p = (uint32_t *)rpc_add_credentials((long *)p);
>
> - memcpy(p, filefh, NFS_FHSIZE);
> - p += (NFS_FHSIZE / 4);
> - *p++ = htonl(offset);
> - *p++ = htonl(readlen);
> - *p++ = 0;
> + if (supported_nfs_versions & NFSV2_FLAG) {
> + memcpy(p, filefh, NFS_FHSIZE);
> + p += (NFS_FHSIZE / 4);
> + *p++ = htonl(offset);
> + *p++ = htonl(readlen);
> + *p++ = 0;
> + } else { /* NFSV3_FLAG */
> + *p++ = htonl(filefh3_length);
> + memcpy(p, filefh3, filefh3_length);
> + p += (filefh3_length / 4);
> + *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */
> + *p++ = htonl(offset);
> + *p++ = htonl(readlen);
> + *p++ = 0;
> + }
>
> len = (uint32_t *)p - (uint32_t *)&(data[0]);
>
> @@ -359,10 +411,16 @@ static void nfs_send(void)
>
> switch (nfs_state) {
> case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
> - rpc_lookup_req(PROG_MOUNT, 1);
> + if (supported_nfs_versions & NFSV2_FLAG)
> + rpc_lookup_req(PROG_MOUNT, 1);
> + else /* NFSV3_FLAG */
> + rpc_lookup_req(PROG_MOUNT, 3);
> break;
> case STATE_PRCLOOKUP_PROG_NFS_REQ:
> - rpc_lookup_req(PROG_NFS, 2);
> + if (supported_nfs_versions & NFSV2_FLAG)
> + rpc_lookup_req(PROG_NFS, 2);
> + else /* NFSV3_FLAG */
> + rpc_lookup_req(PROG_NFS, 3);
> break;
> case STATE_MOUNT_REQ:
> nfs_mount_req(nfs_path);
> @@ -436,6 +494,7 @@ static int nfs_mount_reply(uchar *pkt, unsigned len)
> return -1;
>
> fs_mounted = 1;
> + /* NFSv2 and NFSv3 use same structure */
> memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
>
> return 0;
> @@ -481,24 +540,51 @@ static int nfs_lookup_reply(uchar *pkt, unsigned len)
> if (rpc_pkt.u.reply.rstatus ||
> rpc_pkt.u.reply.verifier ||
> rpc_pkt.u.reply.astatus ||
> - rpc_pkt.u.reply.data[0]){
> - switch(ntohl(rpc_pkt.u.reply.astatus)){
> - case 0: /* Not an error */
> + rpc_pkt.u.reply.data[0]) {
> + switch (ntohl(rpc_pkt.u.reply.astatus)) {
> + case NFS_RPC_SUCCESS: /* Not an error */
> break;
> - case 2: /* Remote can't support NFS version */
> - printf("*** ERROR: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
> - 2,
> - ntohl(rpc_pkt.u.reply.data[0]),
> - ntohl(rpc_pkt.u.reply.data[1]));
> + case NFS_RPC_PROG_MISMATCH:
> + /* Remote can't support requested NFS version */
> + switch (ntohl(rpc_pkt.u.reply.data[0])) {
> + /* Minimal supported NFS version */
> + case 3:
> + debug("*** Waring: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
> + (supported_nfs_versions & NFSV2_FLAG) ? 2 : 3,
> + ntohl(rpc_pkt.u.reply.data[0]),
> + ntohl(rpc_pkt.u.reply.data[1]));
> + debug("Will retry with NFSv3\n");
> + /* Clear NFSV2_FLAG from supported versions */
> + supported_nfs_versions = supported_nfs_versions & ~NFSV2_FLAG;
> + return -NFS_RPC_PROG_MISMATCH;
> + case 4:
> + default:
> + printf("*** ERROR: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
> + (supported_nfs_versions & NFSV2_FLAG) ? 2 : 3,
> + ntohl(rpc_pkt.u.reply.data[0]),
> + ntohl(rpc_pkt.u.reply.data[1]));
> + }
> break;
> + case NFS_RPC_PROG_UNAVAIL:
> + case NFS_RPC_PROC_UNAVAIL:
> + case NFS_RPC_GARBAGE_ARGS:
> + case NFS_RPC_SYSTEM_ERR:
> default: /* Unknown error on 'accept state' flag */
> - printf("*** ERROR: accept state error (%d)\n", ntohl(rpc_pkt.u.reply.astatus));
> + printf("*** ERROR: accept state error (%d)\n",
> + ntohl(rpc_pkt.u.reply.astatus));
> break;
> }
> return -1;
> }
>
> - memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
> + if (supported_nfs_versions & NFSV2_FLAG) {
> + memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
> + } else { /* NFSV3_FLAG */
> + filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
> + if (filefh3_length > NFS3_FHSIZE)
> + filefh3_length = NFS3_FHSIZE;
> + memcpy(filefh3, rpc_pkt.u.reply.data + 2, filefh3_length);
> + }
>
> return 0;
> }
> @@ -523,18 +609,68 @@ static int nfs_readlink_reply(uchar *pkt, unsigned len)
> rpc_pkt.u.reply.data[0])
> return -1;
>
> - rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */
> + if (supported_nfs_versions & NFSV2_FLAG) {
>
> - if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
> - int pathlen;
> - strcat(nfs_path, "/");
> - pathlen = strlen(nfs_path);
> - memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]),
> - rlen);
> - nfs_path[pathlen + rlen] = 0;
> - } else {
> - memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen);
> - nfs_path[rlen] = 0;
> + rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */
> +
> + if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
> + int pathlen;
> + strcat(nfs_path, "/");
> + pathlen = strlen(nfs_path);
> + memcpy(nfs_path + pathlen,
> + (uchar *)&(rpc_pkt.u.reply.data[2]),
> + rlen);
> + nfs_path[pathlen + rlen] = 0;
> + } else {
> + memcpy(nfs_path,
> + (uchar *)&(rpc_pkt.u.reply.data[2]),
> + rlen);
> + nfs_path[rlen] = 0;
> + }
> + } else { /* NFSV3_FLAG */
> + int nfsv3_data_offset = 0;
> + if (ntohl(rpc_pkt.u.reply.data[1]) != 0) {
> + /* 'attributes_follow' flag is TRUE,
> + * so we have attributes on 21 bytes */
> + /* Skip unused values :
> + type; 32 bits value,
> + mode; 32 bits value,
> + nlink; 32 bits value,
> + uid; 32 bits value,
> + gid; 32 bits value,
> + size; 64 bits value,
> + used; 64 bits value,
> + rdev; 64 bits value,
> + fsid; 64 bits value,
> + fileid; 64 bits value,
> + atime; 64 bits value,
> + mtime; 64 bits value,
> + ctime; 64 bits value,
> + */
> + nfsv3_data_offset = 22;
> + } else {
> + /* 'attributes_follow' flag is FALSE,
> + * so we don't have any attributes */
> + nfsv3_data_offset = 1;
> + }
> +
> + /* new path length */
> + rlen = ntohl(rpc_pkt.u.reply.data[1+nfsv3_data_offset]);
> +
> + if (*((char *)&(rpc_pkt.u.reply.data[2+nfsv3_data_offset])) != '/') {
> + int pathlen;
> + strcat(nfs_path, "/");
> + pathlen = strlen(nfs_path);
> + memcpy(nfs_path + pathlen,
> + (uchar *)&(rpc_pkt.u.reply.data[2+nfsv3_data_offset]),
> + rlen);
> + nfs_path[pathlen + rlen] = 0;
> + } else {
> + memcpy(nfs_path,
> + (uchar *)&(rpc_pkt.u.reply.data[2+nfsv3_data_offset]),
> + rlen);
> + nfs_path[rlen] = 0;
> + }
> }
> return 0;
> }
> @@ -543,6 +679,7 @@ static int nfs_read_reply(uchar *pkt, unsigned len)
> {
> struct rpc_t rpc_pkt;
> int rlen;
> + uchar *data_ptr;
>
> debug("%s\n", __func__);
>
> @@ -570,10 +707,47 @@ static int nfs_read_reply(uchar *pkt, unsigned len)
> if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
> putc('#');
>
> - rlen = ntohl(rpc_pkt.u.reply.data[18]);
> - if (store_block((uchar *)pkt + sizeof(rpc_pkt.u.reply),
> - nfs_offset, rlen))
> - return -9999;
> + if (supported_nfs_versions & NFSV2_FLAG) {
> + rlen = ntohl(rpc_pkt.u.reply.data[18]);
> + data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
> + } else { /* NFSV3_FLAG */
> + if (ntohl(rpc_pkt.u.reply.data[1]) != 0) {
> + /* 'attributes_follow' is TRUE,
> + * so we have attributes on 21 bytes */
> + /* Skip unused values :
> + type; 32 bits value,
> + mode; 32 bits value,
> + nlink; 32 bits value,
> + uid; 32 bits value,
> + gid; 32 bits value,
> + size; 64 bits value,
> + used; 64 bits value,
> + rdev; 64 bits value,
> + fsid; 64 bits value,
> + fileid; 64 bits value,
> + atime; 64 bits value,
> + mtime; 64 bits value,
> + ctime; 64 bits value,
> + */
> + rlen = ntohl(rpc_pkt.u.reply.data[23]); /* count value */
> + /* Skip unused values :
> + EOF: 32 bits value,
> + data_size: 32 bits value,
> + */
> + data_ptr = (uchar *)&(rpc_pkt.u.reply.data[26]);
> + } else {
> + /* attributes_follow is FALSE, so we don't have any attributes */
> + rlen = ntohl(rpc_pkt.u.reply.data[2]); /* count value */
> + /* Skip unused values :
> + EOF: 32 bits value,
> + data_size: 32 bits value,
> + */
> + data_ptr = (uchar *)&(rpc_pkt.u.reply.data[5]);
> + }
> + }
> +
> + if (store_block(data_ptr, nfs_offset, rlen))
> + return -9999;
>
> return rlen;
> }
> @@ -657,6 +831,13 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
> puts("*** ERROR: File lookup fail\n");
> nfs_state = STATE_UMOUNT_REQ;
> nfs_send();
> + } else if (reply == -NFS_RPC_PROG_MISMATCH && supported_nfs_versions != 0) {
> + /* umount */
> + nfs_state = STATE_UMOUNT_REQ;
> + nfs_send();
> + /* And retry with another supported version */
> + nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
> + nfs_send();
> } else {
> nfs_state = STATE_READ_REQ;
> nfs_offset = 0;
> @@ -696,6 +877,8 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
> } else {
> if (!rlen)
> nfs_download_state = NETLOOP_SUCCESS;
> + if (rlen < 0)
> + printf("NFS READ error (%d)\n", rlen);
> nfs_state = STATE_UMOUNT_REQ;
> nfs_send();
> }
> diff --git a/net/nfs.h b/net/nfs.h
> index d69b422..f65d14d 100644
> --- a/net/nfs.h
> +++ b/net/nfs.h
> @@ -25,7 +25,10 @@
> #define NFS_READLINK 5
> #define NFS_READ 6
>
> +#define NFS3PROC_LOOKUP 3
> +
> #define NFS_FHSIZE 32
> +#define NFS3_FHSIZE 64
>
> #define NFSERR_PERM 1
> #define NFSERR_NOENT 2
> @@ -46,6 +49,16 @@
>
> #define NFS_MAXLINKDEPTH 16
>
> +/* Values for Accept State flag on RPC answers (See: rfc1831) */
> +enum rpc_accept_stat {
> + NFS_RPC_SUCCESS = 0, /* RPC executed successfully */
> + NFS_RPC_PROG_UNAVAIL = 1, /* remote hasn't exported program */
> + NFS_RPC_PROG_MISMATCH = 2, /* remote can't support version # */
> + NFS_RPC_PROC_UNAVAIL = 3, /* program can't support procedure */
> + NFS_RPC_GARBAGE_ARGS = 4, /* procedure can't decode params */
> + NFS_RPC_SYSTEM_ERR = 5 /* errors like memory allocation failure */
> +};
> +
> struct rpc_t {
> union {
> uint8_t data[2048];
> @@ -65,7 +78,7 @@ struct rpc_t {
> uint32_t verifier;
> uint32_t v2;
> uint32_t astatus;
> - uint32_t data[19];
> + uint32_t data[NFS_READ_SIZE];
> } reply;
> } u;
> };
More information about the U-Boot
mailing list