From mboxrd@z Thu Jan 1 00:00:00 1970 From: Guillaume Gardet Date: Mon, 20 Jun 2016 21:31:12 +0200 Subject: [U-Boot] [PATCH] net: NFS: Add NFSv3 support In-Reply-To: <1466450845-9165-1-git-send-email-guillaume.gardet@free.fr> References: <1466450845-9165-1-git-send-email-guillaume.gardet@free.fr> Message-ID: <57684480.7000901@free.fr> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 > Cc: Tom Rini > 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 > #include > #include > @@ -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; > };