diff mbox

[U-Boot] net: NFS: Add NFSv3 support

Message ID 1466450845-9165-1-git-send-email-guillaume.gardet@free.fr
State Superseded
Delegated to: Joe Hershberger
Headers show

Commit Message

Guillaume GARDET June 20, 2016, 7:27 p.m. UTC
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@free.fr>
Cc: Tom Rini <trini@konsulko.com>
Cc: joe.hershberger@ni.com

---
 net/nfs.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 net/nfs.h |  15 +++-
 2 files changed, 246 insertions(+), 50 deletions(-)

Comments

Guillaume GARDET June 20, 2016, 7:31 p.m. UTC | #1
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@free.fr>
> Cc: Tom Rini <trini@konsulko.com>
> Cc: joe.hershberger@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;
>   };
Joe Hershberger June 20, 2016, 9:43 p.m. UTC | #2
On Mon, Jun 20, 2016 at 2:31 PM, Guillaume Gardet
<guillaume.gardet@free.fr> wrote:
> 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/

Please make sure to run your patches through scripts/checkpatch.pl.

Thanks,
-Joe
Guillaume GARDET June 21, 2016, 8:42 a.m. UTC | #3
Hi,

Le 20/06/2016 23:43, Joe Hershberger a écrit :
> On Mon, Jun 20, 2016 at 2:31 PM, Guillaume Gardet
> <guillaume.gardet@free.fr> wrote:
>> 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/
> Please make sure to run your patches through scripts/checkpatch.pl.

I did for the NFSv3 patch. There are only a few lines longer than 80 char, but not very easy to wrap.

For the previous patch, do you want a V2? You told me that you will take care to fix it manually.

If you don't want to include the previous patch for 2016.07 release, then you can drop it, and I can rebase this patch. Just tell me.


Guillaume

>
> Thanks,
> -Joe
>
diff mbox

Patch

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;
 };