[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