diff mbox

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

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

Commit Message

Guillaume GARDET June 23, 2016, 9:10 a.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

---
Changes in V2: 
	* rebase on latest GIT
	* shorten some long lines
	* no functionnal changes

 net/nfs.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 net/nfs.h |  15 +++-
 2 files changed, 242 insertions(+), 47 deletions(-)

Comments

Tom Rini June 23, 2016, 7:08 p.m. UTC | #1
On Thu, Jun 23, 2016 at 11:10:26AM +0200, Guillaume GARDET wrote:

> 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

So, why do we have v2+v3+v4 -> v2 and not v2+v3+v4 -> v3, when we do
v2+v3 -> v3 and v3+v4 -> v3 ?  We should be consistent in preferring
either v2 over v3 or v3 over v2.  Thanks!
Guillaume GARDET June 24, 2016, 7:45 a.m. UTC | #2
Le 23/06/2016 21:08, Tom Rini a écrit :
> On Thu, Jun 23, 2016 at 11:10:26AM +0200, Guillaume GARDET wrote:
>
>> 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
> So, why do we have v2+v3+v4 -> v2 and not v2+v3+v4 -> v3, when we do
> v2+v3 -> v3 and v3+v4 -> v3 ?  We should be consistent in preferring
> either v2 over v3 or v3 over v2.  Thanks!
>

Sorry, it is a typo error. Please read: "NFSv2 + NFSv3 server: use NFSv2 protocol".

As long as NFSv2 is available, we use it. Otherwise, we use v3 if available. As explained above.


Guillaume
Tom Rini June 24, 2016, 3:03 p.m. UTC | #3
On Fri, Jun 24, 2016 at 09:45:38AM +0200, Guillaume Gardet wrote:
> 
> 
> Le 23/06/2016 21:08, Tom Rini a écrit :
> >On Thu, Jun 23, 2016 at 11:10:26AM +0200, Guillaume GARDET wrote:
> >
> >>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
> >So, why do we have v2+v3+v4 -> v2 and not v2+v3+v4 -> v3, when we do
> >v2+v3 -> v3 and v3+v4 -> v3 ?  We should be consistent in preferring
> >either v2 over v3 or v3 over v2.  Thanks!
> >
> 
> Sorry, it is a typo error. Please read: "NFSv2 + NFSv3 server: use NFSv2 protocol".
> 
> As long as NFSv2 is available, we use it. Otherwise, we use v3 if
> available. As explained above.

OK, that makes sense, thanks!  Joe, I assume you can just fix that in
the commit message when you pick this up.
Joe Hershberger June 25, 2016, 7:26 p.m. UTC | #4
On Fri, Jun 24, 2016 at 10:03 AM, Tom Rini <trini@konsulko.com> wrote:
>
> On Fri, Jun 24, 2016 at 09:45:38AM +0200, Guillaume Gardet wrote:
> >
> >
> > Le 23/06/2016 21:08, Tom Rini a écrit :
> > >On Thu, Jun 23, 2016 at 11:10:26AM +0200, Guillaume GARDET wrote:
> > >
> > >>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
> > >So, why do we have v2+v3+v4 -> v2 and not v2+v3+v4 -> v3, when we do
> > >v2+v3 -> v3 and v3+v4 -> v3 ?  We should be consistent in preferring
> > >either v2 over v3 or v3 over v2.  Thanks!
> > >
> >
> > Sorry, it is a typo error. Please read: "NFSv2 + NFSv3 server: use NFSv2 protocol".
> >
> > As long as NFSv2 is available, we use it. Otherwise, we use v3 if
> > available. As explained above.
>
> OK, that makes sense, thanks!  Joe, I assume you can just fix that in
> the commit message when you pick this up.

Yup, no problem.
Guillaume GARDET July 20, 2016, 9:48 a.m. UTC | #5
Le 25/06/2016 à 21:26, Joe Hershberger a écrit :
> On Fri, Jun 24, 2016 at 10:03 AM, Tom Rini <trini@konsulko.com> wrote:
>> On Fri, Jun 24, 2016 at 09:45:38AM +0200, Guillaume Gardet wrote:
>>>
>>> Le 23/06/2016 21:08, Tom Rini a écrit :
>>>> On Thu, Jun 23, 2016 at 11:10:26AM +0200, Guillaume GARDET wrote:
>>>>
>>>>> 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
>>>> So, why do we have v2+v3+v4 -> v2 and not v2+v3+v4 -> v3, when we do
>>>> v2+v3 -> v3 and v3+v4 -> v3 ?  We should be consistent in preferring
>>>> either v2 over v3 or v3 over v2.  Thanks!
>>>>
>>> Sorry, it is a typo error. Please read: "NFSv2 + NFSv3 server: use NFSv2 protocol".
>>>
>>> As long as NFSv2 is available, we use it. Otherwise, we use v3 if
>>> available. As explained above.
>> OK, that makes sense, thanks!  Joe, I assume you can just fix that in
>> the commit message when you pick this up.
> Yup, no problem.
>

Just a friendly reminder for this patch. :)


Guillaume
diff mbox

Patch

diff --git a/net/nfs.c b/net/nfs.c
index f60a037..b5bc414 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, 2016-June-20.
+ * 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;
@@ -483,14 +542,33 @@  static int nfs_lookup_reply(uchar *pkt, unsigned len)
 	    rpc_pkt.u.reply.astatus  ||
 	    rpc_pkt.u.reply.data[0]) {
 		switch (ntohl(rpc_pkt.u.reply.astatus)) {
-		case 0: /* Not an error */
+		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 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 &= ~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));
@@ -499,7 +577,14 @@  static int nfs_lookup_reply(uchar *pkt, unsigned len)
 		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;
 }
@@ -524,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;
 }
@@ -544,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__);
 
@@ -571,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;
 }
@@ -658,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;
@@ -697,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;
 };