|
@@ -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;
|
|
@@ -69,6 +76,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;
|
|
@@ -187,7 +198,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);
|
|
|
|
|
@@ -223,7 +245,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);
|
|
|
}
|
|
|
|
|
@@ -290,8 +311,14 @@ static void nfs_readlink_req(void)
|
|
|
p = &(data[0]);
|
|
|
p = rpc_add_credentials(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]);
|
|
|
|
|
@@ -313,17 +340,32 @@ static void nfs_lookup_req(char *fname)
|
|
|
p = &(data[0]);
|
|
|
p = rpc_add_credentials(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);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
@@ -338,11 +380,21 @@ static void nfs_read_req(int offset, int readlen)
|
|
|
p = &(data[0]);
|
|
|
p = rpc_add_credentials(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]);
|
|
|
|
|
@@ -358,10 +410,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);
|
|
@@ -435,6 +493,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;
|
|
@@ -482,14 +541,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));
|
|
@@ -498,7 +576,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;
|
|
|
}
|
|
@@ -523,18 +608,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 +678,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 +706,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 +830,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 +876,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();
|
|
|
}
|