Move most of the NFS code into common files so that it can be used by an
LWIP port of NFS.

Signed-off-by: Andrew Goodbody <[email protected]>
---
 net/Makefile     |   1 +
 net/nfs-common.c | 861 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 net/nfs-common.h | 123 ++++++++
 net/nfs.c        | 848 +-----------------------------------------------------
 net/nfs.h        |  59 ----
 5 files changed, 998 insertions(+), 894 deletions(-)

diff --git a/net/Makefile b/net/Makefile
index 
468820186cfc8287fb23a81e75f6a3594f46f261..3a32bc8b0e78404c3db2196ed802010988145bc8
 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_$(PHASE_)DM_ETH) += eth-uclass.o
 obj-$(CONFIG_$(PHASE_)BOOTDEV_ETH) += eth_bootdev.o
 obj-$(CONFIG_DM_MDIO)  += mdio-uclass.o
 obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o
+obj-$(CONFIG_CMD_NFS)  += nfs-common.o
 obj-$(CONFIG_$(PHASE_)DM_ETH) += eth_common.o
 obj-y += net-common.o
 endif
diff --git a/net/nfs-common.c b/net/nfs-common.c
new file mode 100644
index 
0000000000000000000000000000000000000000..a6a70677bd2d8350819dd7bce336d35cc3ae7bf8
--- /dev/null
+++ b/net/nfs-common.c
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NFS support driver - based on etherboot and U-BOOT's tftp.c
+ *
+ * Masami Komiya <[email protected]> 2004
+ *
+ */
+
+/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
+ * large portions are copied verbatim) as distributed in OSKit 0.97.  A few
+ * changes were necessary to adapt the code to Etherboot and to fix several
+ * inconsistencies.  Also the RPC message preparation is done "by hand" to
+ * avoid adding netsprintf() which I find hard to understand and use.
+ */
+
+/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
+ * it loads the kernel image off the boot server (ARP_SERVER) and does not
+ * access the client root disk (root-path in dhcpd.conf), which would use
+ * ARP_ROOTSERVER.  The root disk is something the operating system we are
+ * about to load needs to use. This is different from the OSKit 0.97 logic.
+ */
+
+/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
+ * If a symlink is encountered, it is followed as far as possible (recursion
+ * 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.
+ */
+
+/* NOTE 5: NFSv1 support added by Christian Gmeiner, Thomas Rienoessl,
+ * September 27, 2018. As of now, NFSv3 is the default choice. If the server
+ * does not support NFSv3, we fall back to versions 2 or 1.
+ */
+
+#ifdef CONFIG_SYS_DIRECT_FLASH_NFS
+#include <flash.h>
+#endif
+#include <image.h>
+#include <log.h>
+#include <net.h>
+#include <mapmem.h>
+#include "nfs.h"
+#include "nfs-common.h"
+
+static int fs_mounted;
+static char dirfh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */
+static unsigned int dirfh3_length; /* (variable) length of dirfh when NFSv3 */
+static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */
+static unsigned int filefh3_length;    /* (variable) length of filefh when 
NFSv3 */
+
+enum net_loop_state nfs_download_state;
+char *nfs_filename;
+char *nfs_path;
+char nfs_path_buff[2048];
+struct in_addr nfs_server_ip;
+int nfs_server_mount_port;
+int nfs_server_port;
+int nfs_our_port;
+int nfs_state;
+int nfs_timeout_count;
+unsigned long rpc_id;
+int nfs_offset = -1;
+int nfs_len;
+
+const ulong nfs_timeout = CONFIG_NFS_TIMEOUT;
+
+enum nfs_version choosen_nfs_version = NFS_V3;
+
+static inline int store_block(uchar *src, unsigned int offset, unsigned int 
len)
+{
+       ulong newsize = offset + len;
+#ifdef CONFIG_SYS_DIRECT_FLASH_NFS
+       int i, rc = 0;
+
+       for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
+               /* start address in flash? */
+               if (image_load_addr + offset >= flash_info[i].start[0]) {
+                       rc = 1;
+                       break;
+               }
+       }
+
+       if (rc) { /* Flash is destination for this packet */
+               rc = flash_write((uchar *)src, (ulong)image_load_addr + offset,
+                                len);
+               if (rc) {
+                       flash_perror(rc);
+                       return -1;
+               }
+       } else
+#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
+       {
+               void *ptr = map_sysmem(image_load_addr + offset, len);
+
+               memcpy(ptr, src, len);
+               unmap_sysmem(ptr);
+       }
+
+       if (net_boot_file_size < (offset + len))
+               net_boot_file_size = newsize;
+       return 0;
+}
+
+char *nfs_basename(char *path)
+{
+       char *fname;
+
+       fname = path + strlen(path) - 1;
+       while (fname >= path) {
+               if (*fname == '/') {
+                       fname++;
+                       break;
+               }
+               fname--;
+       }
+       return fname;
+}
+
+char *nfs_dirname(char *path)
+{
+       char *fname;
+
+       fname = nfs_basename(path);
+       --fname;
+       *fname = '\0';
+       return path;
+}
+
+/**************************************************************************
+ * RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
+ **************************************************************************
+ */
+static uint32_t *rpc_add_credentials(uint32_t *p)
+{
+       /* Here's the executive summary on authentication requirements of the
+        * various NFS server implementations:  Linux accepts both AUTH_NONE
+        * and AUTH_UNIX authentication (also accepts an empty hostname field
+        * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
+        * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
+        * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
+        * it (if the BOOTP/DHCP reply didn't give one, just use an empty
+        * hostname).
+        */
+
+       /* Provide an AUTH_UNIX credential.  */
+       *p++ = htonl(1);                /* AUTH_UNIX */
+       *p++ = htonl(20);               /* auth length */
+       *p++ = 0;                       /* stamp */
+       *p++ = 0;                       /* hostname string */
+       *p++ = 0;                       /* uid */
+       *p++ = 0;                       /* gid */
+       *p++ = 0;                       /* auxiliary gid list */
+
+       /* Provide an AUTH_NONE verifier.  */
+       *p++ = 0;                       /* AUTH_NONE */
+       *p++ = 0;                       /* auth length */
+
+       return p;
+}
+
+void rpc_req_common(int rpc_prog, int rpc_proc, uint32_t *data, int datalen,
+                   uchar *txbuff, int *pktlen, int *sport)
+{
+       struct rpc_t rpc_pkt;
+       unsigned long id;
+       u32 *p;
+
+       id = ++rpc_id;
+       rpc_pkt.u.call.id = htonl(id);
+       rpc_pkt.u.call.type = htonl(MSG_CALL);
+       rpc_pkt.u.call.rpcvers = htonl(2);      /* use RPC version 2 */
+       rpc_pkt.u.call.prog = htonl(rpc_prog);
+       switch (rpc_prog) {
+       case PROG_NFS:
+               switch (choosen_nfs_version) {
+               case NFS_V1:
+               case NFS_V2:
+                       rpc_pkt.u.call.vers = htonl(2);
+                       break;
+
+               case NFS_V3:
+                       rpc_pkt.u.call.vers = htonl(3);
+                       break;
+
+               case NFS_UNKOWN:
+                       /* nothing to do */
+                       break;
+               }
+               break;
+       case PROG_MOUNT:
+               switch (choosen_nfs_version) {
+               case NFS_V1:
+                       rpc_pkt.u.call.vers = htonl(1);
+                       break;
+
+               case NFS_V2:
+                       rpc_pkt.u.call.vers = htonl(2);
+                       break;
+
+               case NFS_V3:
+                       rpc_pkt.u.call.vers = htonl(3);
+                       break;
+
+               case NFS_UNKOWN:
+                       /* nothing to do */
+                       break;
+               }
+               break;
+       case PROG_PORTMAP:
+       default:
+               rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
+       }
+       rpc_pkt.u.call.proc = htonl(rpc_proc);
+       p = rpc_pkt.u.call.data;
+
+       if (datalen)
+               memcpy(p, data, datalen * sizeof(uint32_t));
+
+       *pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt;
+
+       memcpy(txbuff, &rpc_pkt.u.data[0], *pktlen);
+
+       if (rpc_prog == PROG_PORTMAP)
+               *sport = SUNRPC_PORT;
+       else if (rpc_prog == PROG_MOUNT)
+               *sport = nfs_server_mount_port;
+       else
+               *sport = nfs_server_port;
+}
+
+/**************************************************************************
+ * RPC_LOOKUP - Lookup RPC Port numbers
+ **************************************************************************
+ */
+static void rpc_lookup_req(int prog, int ver)
+{
+       u32 data[16];
+
+       data[0] = 0; data[1] = 0;       /* auth credential */
+       data[2] = 0; data[3] = 0;       /* auth verifier */
+       data[4] = htonl(prog);
+       data[5] = htonl(ver);
+       data[6] = htonl(17);    /* IP_UDP */
+       data[7] = 0;
+       rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
+}
+
+/**************************************************************************
+ * NFS_MOUNT - Mount an NFS Filesystem
+ **************************************************************************
+ */
+static void nfs_mount_req(char *path)
+{
+       u32 data[1024];
+       u32 *p;
+       int len;
+       int pathlen;
+
+       pathlen = strlen(path);
+
+       p = &data[0];
+       p = rpc_add_credentials(p);
+
+       *p++ = htonl(pathlen);
+       if (pathlen & 3)
+               *(p + pathlen / 4) = 0;
+       memcpy(p, path, pathlen);
+       p += (pathlen + 3) / 4;
+
+       len = (uint32_t *)p - (uint32_t *)&data[0];
+
+       rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
+}
+
+/**************************************************************************
+ * NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
+ **************************************************************************
+ */
+static void nfs_umountall_req(void)
+{
+       u32 data[1024];
+       u32 *p;
+       int len;
+
+       if ((nfs_server_mount_port == -1) || !fs_mounted)
+               /* Nothing mounted, nothing to umount */
+               return;
+
+       p = &data[0];
+       p = rpc_add_credentials(p);
+
+       len = (uint32_t *)p - (uint32_t *)&data[0];
+
+       rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
+}
+
+/***************************************************************************
+ * NFS_READLINK (AH 2003-07-14)
+ * This procedure is called when read of the first block fails -
+ * this probably happens when it's a directory or a symlink
+ * In case of successful readlink(), the dirname is manipulated,
+ * so that inside the nfs() function a recursion can be done.
+ ***************************************************************************
+ */
+static void nfs_readlink_req(void)
+{
+       u32 data[1024];
+       u32 *p;
+       int len;
+
+       p = &data[0];
+       p = rpc_add_credentials(p);
+
+       if (choosen_nfs_version != NFS_V3) {
+               memcpy(p, filefh, NFS_FHSIZE);
+               p += (NFS_FHSIZE / 4);
+       } else { /* NFS_V3 */
+               *p++ = htonl(filefh3_length);
+               memcpy(p, filefh, filefh3_length);
+               p += (filefh3_length / 4);
+       }
+
+       len = (uint32_t *)p - (uint32_t *)&data[0];
+
+       rpc_req(PROG_NFS, NFS_READLINK, data, len);
+}
+
+/**************************************************************************
+ * NFS_LOOKUP - Lookup Pathname
+ **************************************************************************
+ */
+static void nfs_lookup_req(char *fname)
+{
+       u32 data[1024];
+       u32 *p;
+       int len;
+       int fnamelen;
+
+       fnamelen = strlen(fname);
+
+       p = &data[0];
+       p = rpc_add_credentials(p);
+
+       if (choosen_nfs_version != NFS_V3) {
+               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 {  /* NFS_V3 */
+               *p++ = htonl(dirfh3_length);    /* Dir handle length */
+               memcpy(p, dirfh, dirfh3_length);
+               p += (dirfh3_length / 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);
+       }
+}
+
+/**************************************************************************
+ * NFS_READ - Read File on NFS Server
+ **************************************************************************
+ */
+static void nfs_read_req(int offset, int readlen)
+{
+       u32 data[1024];
+       u32 *p;
+       int len;
+
+       p = &data[0];
+       p = rpc_add_credentials(p);
+
+       if (choosen_nfs_version != NFS_V3) {
+               memcpy(p, filefh, NFS_FHSIZE);
+               p += (NFS_FHSIZE / 4);
+               *p++ = htonl(offset);
+               *p++ = htonl(readlen);
+               *p++ = 0;
+       } else { /* NFS_V3 */
+               *p++ = htonl(filefh3_length);
+               memcpy(p, filefh, 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];
+
+       rpc_req(PROG_NFS, NFS_READ, data, len);
+}
+
+/**************************************************************************
+ * RPC request dispatcher
+ **************************************************************************
+ */
+void nfs_send(void)
+{
+       switch (nfs_state) {
+       case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
+               if (choosen_nfs_version != NFS_V3)
+                       rpc_lookup_req(PROG_MOUNT, 1);
+               else  /* NFS_V3 */
+                       rpc_lookup_req(PROG_MOUNT, 3);
+               break;
+       case STATE_PRCLOOKUP_PROG_NFS_REQ:
+               if (choosen_nfs_version != NFS_V3)
+                       rpc_lookup_req(PROG_NFS, 2);
+               else  /* NFS_V3 */
+                       rpc_lookup_req(PROG_NFS, 3);
+               break;
+       case STATE_MOUNT_REQ:
+               nfs_mount_req(nfs_path);
+               break;
+       case STATE_UMOUNT_REQ:
+               nfs_umountall_req();
+               break;
+       case STATE_LOOKUP_REQ:
+               nfs_lookup_req(nfs_filename);
+               break;
+       case STATE_READ_REQ:
+               nfs_read_req(nfs_offset, nfs_len);
+               break;
+       case STATE_READLINK_REQ:
+               nfs_readlink_req();
+               break;
+       }
+}
+
+/**************************************************************************
+ * Handlers for the reply from server
+ **************************************************************************
+ */
+
+static int rpc_handle_error(struct rpc_t *rpc_pkt)
+{
+       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 NFS_RPC_SUCCESS: /* Not an error */
+                       break;
+               case NFS_RPC_PROG_MISMATCH: {
+                       /* Remote can't support NFS version */
+                       const int min = ntohl(rpc_pkt->u.reply.data[0]);
+                       const int max = ntohl(rpc_pkt->u.reply.data[1]);
+
+                       if (max < NFS_V1 || max > NFS_V3 || min > NFS_V3) {
+                               puts("*** ERROR: NFS version not supported");
+                               debug(": Requested: V%d, accepted: min V%d - 
max V%d\n",
+                                     choosen_nfs_version,
+                                     ntohl(rpc_pkt->u.reply.data[0]),
+                                     ntohl(rpc_pkt->u.reply.data[1]));
+                               puts("\n");
+                               choosen_nfs_version = NFS_UNKOWN;
+                               break;
+                       }
+
+                       debug("*** Warning: NFS version not supported: 
Requested: V%d, accepted: min V%d - max V%d\n",
+                             choosen_nfs_version,
+                             ntohl(rpc_pkt->u.reply.data[0]),
+                             ntohl(rpc_pkt->u.reply.data[1]));
+                       debug("Will retry with NFSv%d\n", min);
+                       choosen_nfs_version = min;
+                       return -NFS_RPC_PROG_MISMATCH;
+               }
+               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 */
+                       debug("*** ERROR: accept state error (%d)\n",
+                             ntohl(rpc_pkt->u.reply.astatus));
+                       break;
+               }
+               return -1;
+       }
+
+       return 0;
+}
+
+static int rpc_lookup_reply(int prog, uchar *pkt, unsigned int len)
+{
+       struct rpc_t rpc_pkt;
+
+       memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+               return -NFS_RPC_ERR;
+       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+               return -NFS_RPC_DROP;
+
+       if (rpc_pkt.u.reply.rstatus  ||
+           rpc_pkt.u.reply.verifier ||
+           rpc_pkt.u.reply.astatus)
+               return -1;
+
+       switch (prog) {
+       case PROG_MOUNT:
+               nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
+               break;
+       case PROG_NFS:
+               nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
+               break;
+       }
+
+       return 0;
+}
+
+static int nfs_mount_reply(uchar *pkt, unsigned int len)
+{
+       struct rpc_t rpc_pkt;
+       int ret;
+
+       memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+               return -NFS_RPC_ERR;
+       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+               return -NFS_RPC_DROP;
+
+       ret = rpc_handle_error(&rpc_pkt);
+       if (ret)
+               return ret;
+
+       fs_mounted = 1;
+       /*  NFSv2 and NFSv3 use same structure */
+       if (choosen_nfs_version != NFS_V3) {
+               memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
+       } else {
+               dirfh3_length = ntohl(rpc_pkt.u.reply.data[1]);
+               if (dirfh3_length > NFS3_FHSIZE)
+                       dirfh3_length  = NFS3_FHSIZE;
+               memcpy(dirfh, rpc_pkt.u.reply.data + 2, dirfh3_length);
+       }
+
+       return 0;
+}
+
+static int nfs_umountall_reply(uchar *pkt, unsigned int len)
+{
+       struct rpc_t rpc_pkt;
+
+       memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+               return -NFS_RPC_ERR;
+       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+               return -NFS_RPC_DROP;
+
+       if (rpc_pkt.u.reply.rstatus  ||
+           rpc_pkt.u.reply.verifier ||
+           rpc_pkt.u.reply.astatus)
+               return -1;
+
+       fs_mounted = 0;
+       memset(dirfh, 0, sizeof(dirfh));
+
+       return 0;
+}
+
+static int nfs_lookup_reply(uchar *pkt, unsigned int len)
+{
+       struct rpc_t rpc_pkt;
+       int ret;
+
+       memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+               return -NFS_RPC_ERR;
+       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+               return -NFS_RPC_DROP;
+
+       ret = rpc_handle_error(&rpc_pkt);
+       if (ret)
+               return ret;
+
+       if (choosen_nfs_version != NFS_V3) {
+               if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + 
NFS_FHSIZE) > len)
+                       return -NFS_RPC_DROP;
+               memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
+       } else {  /* NFS_V3 */
+               filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
+               if (filefh3_length > NFS3_FHSIZE)
+                       filefh3_length  = NFS3_FHSIZE;
+               memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length);
+       }
+
+       return 0;
+}
+
+static int nfs3_get_attributes_offset(uint32_t *data)
+{
+       if (data[1]) {
+               /* 'attributes_follow' flag is TRUE,
+                * so we have attributes on 21 dwords
+                */
+               /* 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,
+                */
+               return 22;
+       }
+
+       /* 'attributes_follow' flag is FALSE,
+        * so we don't have any attributes
+        */
+       return 1;
+}
+
+static int nfs_readlink_reply(uchar *pkt, unsigned int len)
+{
+       struct rpc_t rpc_pkt;
+       int rlen;
+       int nfsv3_data_offset = 0;
+
+       memcpy((unsigned char *)&rpc_pkt, pkt, len);
+
+       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+               return -NFS_RPC_ERR;
+       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+               return -NFS_RPC_DROP;
+
+       if (rpc_pkt.u.reply.rstatus  ||
+           rpc_pkt.u.reply.verifier ||
+           rpc_pkt.u.reply.astatus  ||
+           rpc_pkt.u.reply.data[0])
+               return -1;
+
+       if (choosen_nfs_version == NFS_V3) {
+               nfsv3_data_offset =
+                       nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
+       }
+
+       /* new path length */
+       rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
+
+       if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) > 
len)
+               return -NFS_RPC_DROP;
+
+       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;
+}
+
+static int nfs_read_reply(uchar *pkt, unsigned int len)
+{
+       struct rpc_t rpc_pkt;
+       int rlen;
+       uchar *data_ptr;
+
+       memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
+
+       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+               return -NFS_RPC_ERR;
+       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+               return -NFS_RPC_DROP;
+
+       if (rpc_pkt.u.reply.rstatus  ||
+           rpc_pkt.u.reply.verifier ||
+           rpc_pkt.u.reply.astatus  ||
+           rpc_pkt.u.reply.data[0]) {
+               if (rpc_pkt.u.reply.rstatus)
+                       return -9999;
+               if (rpc_pkt.u.reply.astatus)
+                       return -9999;
+               return -ntohl(rpc_pkt.u.reply.data[0]);
+       }
+
+       if (nfs_offset != 0 && !((nfs_offset) %
+                       (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
+               puts("\n\t ");
+       if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
+               putc('#');
+
+       if (choosen_nfs_version != NFS_V3) {
+               rlen = ntohl(rpc_pkt.u.reply.data[18]);
+               data_ptr = (uchar *)&rpc_pkt.u.reply.data[19];
+       } else {  /* NFS_V3 */
+               int nfsv3_data_offset =
+                       nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
+
+               /* count value */
+               rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
+               /* Skip unused values :
+                *      EOF:            32 bits value,
+                *      data_size:      32 bits value,
+                */
+               data_ptr = (uchar *)
+                       &rpc_pkt.u.reply.data[4 + nfsv3_data_offset];
+       }
+
+       if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) > 
len)
+               return -9999;
+
+       if (store_block(data_ptr, nfs_offset, rlen))
+               return -9999;
+
+       return rlen;
+}
+
+void nfs_pkt_recv(uchar *pkt, unsigned int len)
+{
+       int rlen;
+       int reply;
+
+       switch (nfs_state) {
+       case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
+               if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
+                       break;
+               nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
+               nfs_send();
+               break;
+
+       case STATE_PRCLOOKUP_PROG_NFS_REQ:
+               if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
+                       break;
+               nfs_state = STATE_MOUNT_REQ;
+               nfs_send();
+               break;
+
+       case STATE_MOUNT_REQ:
+               reply = nfs_mount_reply(pkt, len);
+               if (reply == -NFS_RPC_DROP) {
+                       break;
+               } else if (reply == -NFS_RPC_ERR) {
+                       puts("*** ERROR: Cannot mount\n");
+                       /* just to be sure... */
+                       nfs_state = STATE_UMOUNT_REQ;
+                       nfs_send();
+               } else if (reply == -NFS_RPC_PROG_MISMATCH &&
+                          choosen_nfs_version != NFS_UNKOWN) {
+                       nfs_state = STATE_MOUNT_REQ;
+                       nfs_send();
+               } else {
+                       nfs_state = STATE_LOOKUP_REQ;
+                       nfs_send();
+               }
+               break;
+
+       case STATE_UMOUNT_REQ:
+               reply = nfs_umountall_reply(pkt, len);
+               if (reply == -NFS_RPC_DROP) {
+                       break;
+               } else if (reply == -NFS_RPC_ERR) {
+                       debug("*** ERROR: Cannot umount\n");
+                       net_set_state(NETLOOP_FAIL);
+               } else {
+                       puts("\ndone\n");
+                       net_set_state(nfs_download_state);
+               }
+               break;
+
+       case STATE_LOOKUP_REQ:
+               reply = nfs_lookup_reply(pkt, len);
+               if (reply == -NFS_RPC_DROP) {
+                       break;
+               } else if (reply == -NFS_RPC_ERR) {
+                       puts("*** ERROR: File lookup fail\n");
+                       nfs_state = STATE_UMOUNT_REQ;
+                       nfs_send();
+               } else if (reply == -NFS_RPC_PROG_MISMATCH &&
+                          choosen_nfs_version != NFS_UNKOWN) {
+                       /* 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;
+                       nfs_len = NFS_READ_SIZE;
+                       nfs_send();
+               }
+               break;
+
+       case STATE_READLINK_REQ:
+               reply = nfs_readlink_reply(pkt, len);
+               if (reply == -NFS_RPC_DROP) {
+                       break;
+               } else if (reply == -NFS_RPC_ERR) {
+                       puts("*** ERROR: Symlink fail\n");
+                       nfs_state = STATE_UMOUNT_REQ;
+                       nfs_send();
+               } else {
+                       debug("Symlink --> %s\n", nfs_path);
+                       nfs_filename = nfs_basename(nfs_path);
+                       nfs_path     = nfs_dirname(nfs_path);
+
+                       nfs_state = STATE_MOUNT_REQ;
+                       nfs_send();
+               }
+               break;
+
+       case STATE_READ_REQ:
+               rlen = nfs_read_reply(pkt, len);
+               if (rlen == -NFS_RPC_DROP)
+                       break;
+               nfs_refresh_timeout();
+               if (rlen > 0) {
+                       nfs_offset += rlen;
+                       nfs_send();
+               } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
+                       /* symbolic link */
+                       nfs_state = STATE_READLINK_REQ;
+                       nfs_send();
+               } else {
+                       if (!rlen)
+                               nfs_download_state = NETLOOP_SUCCESS;
+                       if (rlen < 0)
+                               debug("NFS READ error (%d)\n", rlen);
+                       nfs_state = STATE_UMOUNT_REQ;
+                       nfs_send();
+               }
+               break;
+       }
+}
+
diff --git a/net/nfs-common.h b/net/nfs-common.h
new file mode 100644
index 
0000000000000000000000000000000000000000..a19b98b7b1f0cb323d5384ee8c22bf058f6446c9
--- /dev/null
+++ b/net/nfs-common.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Masami Komiya <[email protected]> 2004
+ */
+
+#ifndef __NFS_COMMON_H__
+#define __NFS_COMMON_H__
+
+#define SUNRPC_PORT     111
+
+#define PROG_PORTMAP    100000
+#define PROG_NFS        100003
+#define PROG_MOUNT      100005
+
+#define MSG_CALL        0
+#define MSG_REPLY       1
+
+#define HASHES_PER_LINE 65     /* Number of "loading" hashes per line  */
+#define NFS_RETRY_COUNT 30
+
+#define NFS_RPC_ERR    1
+#define NFS_RPC_DROP   124
+
+#define NFSERR_PERM     1
+#define NFSERR_NOENT    2
+#define NFSERR_ACCES    13
+#define NFSERR_ISDIR    21
+#define NFSERR_INVAL    22
+
+/* 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 */
+};
+
+enum nfs_version {
+       NFS_UNKOWN = 0,
+       NFS_V1 = 1,
+       NFS_V2 = 2,
+       NFS_V3 = 3,
+};
+
+extern enum net_loop_state nfs_download_state;
+extern char *nfs_filename;
+extern char *nfs_path;
+extern char nfs_path_buff[2048];
+extern struct in_addr nfs_server_ip;
+extern int nfs_server_mount_port;
+extern int nfs_server_port;
+extern int nfs_our_port;
+extern int nfs_timeout_count;
+extern unsigned long rpc_id;
+extern int nfs_offset;
+extern int nfs_len;
+
+extern const ulong nfs_timeout;
+
+extern int nfs_state;
+#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
+#define STATE_PRCLOOKUP_PROG_NFS_REQ   2
+#define STATE_MOUNT_REQ                        3
+#define STATE_UMOUNT_REQ               4
+#define STATE_LOOKUP_REQ               5
+#define STATE_READ_REQ                 6
+#define STATE_READLINK_REQ             7
+
+/*
+ * Block size used for NFS read accesses.  A RPC reply packet (including  all
+ * headers) must fit within a single Ethernet frame to avoid fragmentation.
+ * However, if CONFIG_IP_DEFRAG is set, a bigger value could be used.  In any
+ * case, most NFS servers are optimized for a power of 2.
+ */
+#define NFS_READ_SIZE  1024    /* biggest power of two that fits Ether frame */
+#define NFS_MAX_ATTRS  26
+
+struct rpc_t {
+       union {
+               u8 data[NFS_READ_SIZE + (6 + NFS_MAX_ATTRS) *
+                       sizeof(uint32_t)];
+               struct {
+                       u32 id;
+                       u32 type;
+                       u32 rpcvers;
+                       u32 prog;
+                       u32 vers;
+                       u32 proc;
+                       u32 data[1];
+               } call;
+               struct {
+                       u32 id;
+                       u32 type;
+                       u32 rstatus;
+                       u32 verifier;
+                       u32 v2;
+                       u32 astatus;
+                       u32 data[NFS_READ_SIZE / sizeof(u32) +
+                               NFS_MAX_ATTRS];
+               } reply;
+       } u;
+};
+
+char *nfs_basename(char *path);
+char *nfs_dirname(char *path);
+void rpc_req_common(int rpc_prog, int rpc_proc, uint32_t *data, int datalen,
+                   uchar *txbuff, int *pktlen, int *sport);
+void nfs_send(void);
+void nfs_pkt_recv(uchar *pkt, unsigned int len);
+
+extern enum nfs_version choosen_nfs_version;
+
+/*
+ * Implementation specific functions called from common code
+ */
+void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen);
+void nfs_refresh_timeout(void);
+
+/**********************************************************************/
+
+#endif /* __NFS_COMMON_H__ */
diff --git a/net/nfs.c b/net/nfs.c
index 
7bae7c78642cb33e5040c44ed9d0378222069923..f5edc9b043ae1f0f8a1404b28506b8ad120fc5a5
 100644
--- a/net/nfs.c
+++ b/net/nfs.c
@@ -31,745 +31,31 @@
  * September 27, 2018. As of now, NFSv3 is the default choice. If the server
  * does not support NFSv3, we fall back to versions 2 or 1. */
 
-#include <command.h>
 #include <display_options.h>
-#ifdef CONFIG_SYS_DIRECT_FLASH_NFS
-#include <flash.h>
-#endif
 #include <image.h>
 #include <log.h>
 #include <net.h>
 #include <malloc.h>
-#include <mapmem.h>
 #include "nfs.h"
-#include "bootp.h"
+#include "nfs-common.h"
 #include <time.h>
 
-#define HASHES_PER_LINE 65     /* Number of "loading" hashes per line  */
-#define NFS_RETRY_COUNT 30
-
-#define NFS_RPC_ERR    1
-#define NFS_RPC_DROP   124
-
-static int fs_mounted;
-static unsigned long rpc_id;
-static int nfs_offset = -1;
-static int nfs_len;
-static const ulong nfs_timeout = CONFIG_NFS_TIMEOUT;
-
-static char dirfh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */
-static unsigned int dirfh3_length; /* (variable) length of dirfh when NFSv3 */
-static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */
-static unsigned int filefh3_length;    /* (variable) length of filefh when 
NFSv3 */
-
-static enum net_loop_state nfs_download_state;
-static struct in_addr nfs_server_ip;
-static int nfs_server_mount_port;
-static int nfs_server_port;
-static int nfs_our_port;
-static int nfs_timeout_count;
-static int nfs_state;
-#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
-#define STATE_PRCLOOKUP_PROG_NFS_REQ   2
-#define STATE_MOUNT_REQ                        3
-#define STATE_UMOUNT_REQ               4
-#define STATE_LOOKUP_REQ               5
-#define STATE_READ_REQ                 6
-#define STATE_READLINK_REQ             7
-
-static char *nfs_filename;
-static char *nfs_path;
-static char nfs_path_buff[2048];
-
-enum nfs_version {
-       NFS_UNKOWN = 0,
-       NFS_V1 = 1,
-       NFS_V2 = 2,
-       NFS_V3 = 3,
-};
-
-static enum nfs_version choosen_nfs_version = NFS_V3;
-static inline int store_block(uchar *src, unsigned offset, unsigned len)
-{
-       ulong newsize = offset + len;
-#ifdef CONFIG_SYS_DIRECT_FLASH_NFS
-       int i, rc = 0;
-
-       for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
-               /* start address in flash? */
-               if (image_load_addr + offset >= flash_info[i].start[0]) {
-                       rc = 1;
-                       break;
-               }
-       }
-
-       if (rc) { /* Flash is destination for this packet */
-               rc = flash_write((uchar *)src, (ulong)image_load_addr + offset,
-                                len);
-               if (rc) {
-                       flash_perror(rc);
-                       return -1;
-               }
-       } else
-#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
-       {
-               void *ptr = map_sysmem(image_load_addr + offset, len);
-
-               memcpy(ptr, src, len);
-               unmap_sysmem(ptr);
-       }
-
-       if (net_boot_file_size < (offset + len))
-               net_boot_file_size = newsize;
-       return 0;
-}
-
-static char *basename(char *path)
-{
-       char *fname;
-
-       fname = path + strlen(path) - 1;
-       while (fname >= path) {
-               if (*fname == '/') {
-                       fname++;
-                       break;
-               }
-               fname--;
-       }
-       return fname;
-}
-
-static char *dirname(char *path)
-{
-       char *fname;
-
-       fname = basename(path);
-       --fname;
-       *fname = '\0';
-       return path;
-}
-
-/**************************************************************************
-RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
-**************************************************************************/
-static uint32_t *rpc_add_credentials(uint32_t *p)
-{
-       /* Here's the executive summary on authentication requirements of the
-        * various NFS server implementations:  Linux accepts both AUTH_NONE
-        * and AUTH_UNIX authentication (also accepts an empty hostname field
-        * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
-        * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
-        * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
-        * it (if the BOOTP/DHCP reply didn't give one, just use an empty
-        * hostname).  */
-
-       /* Provide an AUTH_UNIX credential.  */
-       *p++ = htonl(1);                /* AUTH_UNIX */
-       *p++ = htonl(20);               /* auth length */
-       *p++ = 0;                       /* stamp */
-       *p++ = 0;                       /* hostname string */
-       *p++ = 0;                       /* uid */
-       *p++ = 0;                       /* gid */
-       *p++ = 0;                       /* auxiliary gid list */
-
-       /* Provide an AUTH_NONE verifier.  */
-       *p++ = 0;                       /* AUTH_NONE */
-       *p++ = 0;                       /* auth length */
-
-       return p;
-}
-
 /**************************************************************************
 RPC_LOOKUP - Lookup RPC Port numbers
 **************************************************************************/
-static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
+void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
 {
-       struct rpc_t rpc_pkt;
-       unsigned long id;
-       uint32_t *p;
        int pktlen;
        int sport;
 
-       id = ++rpc_id;
-       rpc_pkt.u.call.id = htonl(id);
-       rpc_pkt.u.call.type = htonl(MSG_CALL);
-       rpc_pkt.u.call.rpcvers = htonl(2);      /* use RPC version 2 */
-       rpc_pkt.u.call.prog = htonl(rpc_prog);
-       switch (rpc_prog) {
-       case PROG_NFS:
-               switch (choosen_nfs_version) {
-               case NFS_V1:
-               case NFS_V2:
-                       rpc_pkt.u.call.vers = htonl(2);
-                       break;
-
-               case NFS_V3:
-                       rpc_pkt.u.call.vers = htonl(3);
-                       break;
-
-               case NFS_UNKOWN:
-                       /* nothing to do */
-                       break;
-               }
-               break;
-       case PROG_MOUNT:
-               switch (choosen_nfs_version) {
-               case NFS_V1:
-                       rpc_pkt.u.call.vers = htonl(1);
-                       break;
-
-               case NFS_V2:
-                       rpc_pkt.u.call.vers = htonl(2);
-                       break;
-
-               case NFS_V3:
-                       rpc_pkt.u.call.vers = htonl(3);
-                       break;
-
-               case NFS_UNKOWN:
-                       /* nothing to do */
-                       break;
-               }
-               break;
-       case PROG_PORTMAP:
-       default:
-               rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
-       }
-       rpc_pkt.u.call.proc = htonl(rpc_proc);
-       p = rpc_pkt.u.call.data;
-
-       if (datalen)
-               memcpy(p, data, datalen * sizeof(uint32_t));
-
-       pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt;
-
-       memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
-              &rpc_pkt.u.data[0], pktlen);
-
-       if (rpc_prog == PROG_PORTMAP)
-               sport = SUNRPC_PORT;
-       else if (rpc_prog == PROG_MOUNT)
-               sport = nfs_server_mount_port;
-       else
-               sport = nfs_server_port;
+       rpc_req_common(rpc_prog, rpc_proc, data, datalen,
+                      (char *)net_tx_packet + net_eth_hdr_size()
+                      + IP_UDP_HDR_SIZE, &pktlen, &sport);
 
        net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport,
                            nfs_our_port, pktlen);
 }
 
-/**************************************************************************
-RPC_LOOKUP - Lookup RPC Port numbers
-**************************************************************************/
-static void rpc_lookup_req(int prog, int ver)
-{
-       uint32_t data[16];
-
-       data[0] = 0; data[1] = 0;       /* auth credential */
-       data[2] = 0; data[3] = 0;       /* auth verifier */
-       data[4] = htonl(prog);
-       data[5] = htonl(ver);
-       data[6] = htonl(17);    /* IP_UDP */
-       data[7] = 0;
-       rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
-}
-
-/**************************************************************************
-NFS_MOUNT - Mount an NFS Filesystem
-**************************************************************************/
-static void nfs_mount_req(char *path)
-{
-       uint32_t data[1024];
-       uint32_t *p;
-       int len;
-       int pathlen;
-
-       pathlen = strlen(path);
-
-       p = &(data[0]);
-       p = rpc_add_credentials(p);
-
-       *p++ = htonl(pathlen);
-       if (pathlen & 3)
-               *(p + pathlen / 4) = 0;
-       memcpy(p, path, pathlen);
-       p += (pathlen + 3) / 4;
-
-       len = (uint32_t *)p - (uint32_t *)&(data[0]);
-
-       rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
-}
-
-/**************************************************************************
-NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
-**************************************************************************/
-static void nfs_umountall_req(void)
-{
-       uint32_t data[1024];
-       uint32_t *p;
-       int len;
-
-       if ((nfs_server_mount_port == -1) || (!fs_mounted))
-               /* Nothing mounted, nothing to umount */
-               return;
-
-       p = &(data[0]);
-       p = rpc_add_credentials(p);
-
-       len = (uint32_t *)p - (uint32_t *)&(data[0]);
-
-       rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
-}
-
-/***************************************************************************
- * NFS_READLINK (AH 2003-07-14)
- * This procedure is called when read of the first block fails -
- * this probably happens when it's a directory or a symlink
- * In case of successful readlink(), the dirname is manipulated,
- * so that inside the nfs() function a recursion can be done.
- **************************************************************************/
-static void nfs_readlink_req(void)
-{
-       uint32_t data[1024];
-       uint32_t *p;
-       int len;
-
-       p = &(data[0]);
-       p = rpc_add_credentials(p);
-
-       if (choosen_nfs_version != NFS_V3) {
-               memcpy(p, filefh, NFS_FHSIZE);
-               p += (NFS_FHSIZE / 4);
-       } else { /* NFS_V3 */
-               *p++ = htonl(filefh3_length);
-               memcpy(p, filefh, filefh3_length);
-               p += (filefh3_length / 4);
-       }
-
-       len = (uint32_t *)p - (uint32_t *)&(data[0]);
-
-       rpc_req(PROG_NFS, NFS_READLINK, data, len);
-}
-
-/**************************************************************************
-NFS_LOOKUP - Lookup Pathname
-**************************************************************************/
-static void nfs_lookup_req(char *fname)
-{
-       uint32_t data[1024];
-       uint32_t *p;
-       int len;
-       int fnamelen;
-
-       fnamelen = strlen(fname);
-
-       p = &(data[0]);
-       p = rpc_add_credentials(p);
-
-       if (choosen_nfs_version != NFS_V3) {
-               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 {  /* NFS_V3 */
-               *p++ = htonl(dirfh3_length);    /* Dir handle length */
-               memcpy(p, dirfh, dirfh3_length);
-               p += (dirfh3_length / 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);
-       }
-}
-
-/**************************************************************************
-NFS_READ - Read File on NFS Server
-**************************************************************************/
-static void nfs_read_req(int offset, int readlen)
-{
-       uint32_t data[1024];
-       uint32_t *p;
-       int len;
-
-       p = &(data[0]);
-       p = rpc_add_credentials(p);
-
-       if (choosen_nfs_version != NFS_V3) {
-               memcpy(p, filefh, NFS_FHSIZE);
-               p += (NFS_FHSIZE / 4);
-               *p++ = htonl(offset);
-               *p++ = htonl(readlen);
-               *p++ = 0;
-       } else { /* NFS_V3 */
-               *p++ = htonl(filefh3_length);
-               memcpy(p, filefh, 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]);
-
-       rpc_req(PROG_NFS, NFS_READ, data, len);
-}
-
-/**************************************************************************
-RPC request dispatcher
-**************************************************************************/
-static void nfs_send(void)
-{
-       debug("%s\n", __func__);
-
-       switch (nfs_state) {
-       case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
-               if (choosen_nfs_version != NFS_V3)
-                       rpc_lookup_req(PROG_MOUNT, 1);
-               else  /* NFS_V3 */
-                       rpc_lookup_req(PROG_MOUNT, 3);
-               break;
-       case STATE_PRCLOOKUP_PROG_NFS_REQ:
-               if (choosen_nfs_version != NFS_V3)
-                       rpc_lookup_req(PROG_NFS, 2);
-               else  /* NFS_V3 */
-                       rpc_lookup_req(PROG_NFS, 3);
-               break;
-       case STATE_MOUNT_REQ:
-               nfs_mount_req(nfs_path);
-               break;
-       case STATE_UMOUNT_REQ:
-               nfs_umountall_req();
-               break;
-       case STATE_LOOKUP_REQ:
-               nfs_lookup_req(nfs_filename);
-               break;
-       case STATE_READ_REQ:
-               nfs_read_req(nfs_offset, nfs_len);
-               break;
-       case STATE_READLINK_REQ:
-               nfs_readlink_req();
-               break;
-       }
-}
-
-/**************************************************************************
-Handlers for the reply from server
-**************************************************************************/
-
-static int rpc_handle_error(struct rpc_t *rpc_pkt)
-{
-       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 NFS_RPC_SUCCESS: /* Not an error */
-                       break;
-               case NFS_RPC_PROG_MISMATCH: {
-                       /* Remote can't support NFS version */
-                       const int min = ntohl(rpc_pkt->u.reply.data[0]);
-                       const int max = ntohl(rpc_pkt->u.reply.data[1]);
-
-                       if (max < NFS_V1 || max > NFS_V3 || min > NFS_V3) {
-                               puts("*** ERROR: NFS version not supported");
-                               debug(": Requested: V%d, accepted: min V%d - 
max V%d\n",
-                                     choosen_nfs_version,
-                                     ntohl(rpc_pkt->u.reply.data[0]),
-                                     ntohl(rpc_pkt->u.reply.data[1]));
-                               puts("\n");
-                               choosen_nfs_version = NFS_UNKOWN;
-                               break;
-                       }
-
-                       debug("*** Warning: NFS version not supported: 
Requested: V%d, accepted: min V%d - max V%d\n",
-                             choosen_nfs_version,
-                             ntohl(rpc_pkt->u.reply.data[0]),
-                             ntohl(rpc_pkt->u.reply.data[1]));
-                       debug("Will retry with NFSv%d\n", min);
-                       choosen_nfs_version = min;
-                       return -NFS_RPC_PROG_MISMATCH;
-               }
-               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 */
-                       debug("*** ERROR: accept state error (%d)\n",
-                             ntohl(rpc_pkt->u.reply.astatus));
-                       break;
-               }
-               return -1;
-       }
-
-       return 0;
-}
-
-static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len)
-{
-       struct rpc_t rpc_pkt;
-
-       memcpy(&rpc_pkt.u.data[0], pkt, len);
-
-       debug("%s\n", __func__);
-
-       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
-               return -NFS_RPC_ERR;
-       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
-               return -NFS_RPC_DROP;
-
-       if (rpc_pkt.u.reply.rstatus  ||
-           rpc_pkt.u.reply.verifier ||
-           rpc_pkt.u.reply.astatus)
-               return -1;
-
-       switch (prog) {
-       case PROG_MOUNT:
-               nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
-               break;
-       case PROG_NFS:
-               nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
-               break;
-       }
-
-       return 0;
-}
-
-static int nfs_mount_reply(uchar *pkt, unsigned len)
-{
-       struct rpc_t rpc_pkt;
-       int ret;
-
-       debug("%s\n", __func__);
-
-       memcpy(&rpc_pkt.u.data[0], pkt, len);
-
-       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
-               return -NFS_RPC_ERR;
-       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
-               return -NFS_RPC_DROP;
-
-       ret = rpc_handle_error(&rpc_pkt);
-       if (ret)
-               return ret;
-
-       fs_mounted = 1;
-       /*  NFSv2 and NFSv3 use same structure */
-       if (choosen_nfs_version != NFS_V3) {
-               memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
-       } else {
-               dirfh3_length = ntohl(rpc_pkt.u.reply.data[1]);
-               if (dirfh3_length > NFS3_FHSIZE)
-                       dirfh3_length  = NFS3_FHSIZE;
-               memcpy(dirfh, rpc_pkt.u.reply.data + 2, dirfh3_length);
-       }
-
-       return 0;
-}
-
-static int nfs_umountall_reply(uchar *pkt, unsigned len)
-{
-       struct rpc_t rpc_pkt;
-
-       debug("%s\n", __func__);
-
-       memcpy(&rpc_pkt.u.data[0], pkt, len);
-
-       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
-               return -NFS_RPC_ERR;
-       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
-               return -NFS_RPC_DROP;
-
-       if (rpc_pkt.u.reply.rstatus  ||
-           rpc_pkt.u.reply.verifier ||
-           rpc_pkt.u.reply.astatus)
-               return -1;
-
-       fs_mounted = 0;
-       memset(dirfh, 0, sizeof(dirfh));
-
-       return 0;
-}
-
-static int nfs_lookup_reply(uchar *pkt, unsigned len)
-{
-       struct rpc_t rpc_pkt;
-       int ret;
-
-       debug("%s\n", __func__);
-
-       memcpy(&rpc_pkt.u.data[0], pkt, len);
-
-       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
-               return -NFS_RPC_ERR;
-       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
-               return -NFS_RPC_DROP;
-
-       ret = rpc_handle_error(&rpc_pkt);
-       if (ret)
-               return ret;
-
-       if (choosen_nfs_version != NFS_V3) {
-               if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) 
+ NFS_FHSIZE) > len)
-                       return -NFS_RPC_DROP;
-               memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
-       } else {  /* NFS_V3 */
-               filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
-               if (filefh3_length > NFS3_FHSIZE)
-                       filefh3_length  = NFS3_FHSIZE;
-               memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length);
-       }
-
-       return 0;
-}
-
-static int nfs3_get_attributes_offset(uint32_t *data)
-{
-       if (data[1]) {
-               /* 'attributes_follow' flag is TRUE,
-                * so we have attributes on 21 dwords */
-               /* 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,
-               */
-               return 22;
-       } else {
-               /* 'attributes_follow' flag is FALSE,
-                * so we don't have any attributes */
-               return 1;
-       }
-}
-
-static int nfs_readlink_reply(uchar *pkt, unsigned len)
-{
-       struct rpc_t rpc_pkt;
-       int rlen;
-       int nfsv3_data_offset = 0;
-
-       debug("%s\n", __func__);
-
-       memcpy((unsigned char *)&rpc_pkt, pkt, len);
-
-       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
-               return -NFS_RPC_ERR;
-       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
-               return -NFS_RPC_DROP;
-
-       if (rpc_pkt.u.reply.rstatus  ||
-           rpc_pkt.u.reply.verifier ||
-           rpc_pkt.u.reply.astatus  ||
-           rpc_pkt.u.reply.data[0])
-               return -1;
-
-       if (choosen_nfs_version == NFS_V3) {
-               nfsv3_data_offset =
-                       nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
-       }
-
-       /* new path length */
-       rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
-
-       if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) 
> len)
-               return -NFS_RPC_DROP;
-
-       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;
-}
-
-static int nfs_read_reply(uchar *pkt, unsigned len)
-{
-       struct rpc_t rpc_pkt;
-       int rlen;
-       uchar *data_ptr;
-
-       debug("%s\n", __func__);
-
-       memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
-
-       if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
-               return -NFS_RPC_ERR;
-       else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
-               return -NFS_RPC_DROP;
-
-       if (rpc_pkt.u.reply.rstatus  ||
-           rpc_pkt.u.reply.verifier ||
-           rpc_pkt.u.reply.astatus  ||
-           rpc_pkt.u.reply.data[0]) {
-               if (rpc_pkt.u.reply.rstatus)
-                       return -9999;
-               if (rpc_pkt.u.reply.astatus)
-                       return -9999;
-               return -ntohl(rpc_pkt.u.reply.data[0]);
-       }
-
-       if ((nfs_offset != 0) && !((nfs_offset) %
-                       (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
-               puts("\n\t ");
-       if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
-               putc('#');
-
-       if (choosen_nfs_version != NFS_V3) {
-               rlen = ntohl(rpc_pkt.u.reply.data[18]);
-               data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
-       } else {  /* NFS_V3 */
-               int nfsv3_data_offset =
-                       nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
-
-               /* count value */
-               rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
-               /* Skip unused values :
-                       EOF:            32 bits value,
-                       data_size:      32 bits value,
-               */
-               data_ptr = (uchar *)
-                       &(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]);
-       }
-
-       if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) 
> len)
-                       return -9999;
-
-       if (store_block(data_ptr, nfs_offset, rlen))
-                       return -9999;
-
-       return rlen;
-}
-
 /**************************************************************************
 Interfaces of U-BOOT
 **************************************************************************/
@@ -787,12 +73,14 @@ static void nfs_timeout_handler(void)
        }
 }
 
+void nfs_refresh_timeout(void)
+{
+       net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
+}
+
 static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
                        unsigned src, unsigned len)
 {
-       int rlen;
-       int reply;
-
        debug("%s\n", __func__);
 
        if (len > sizeof(struct rpc_t))
@@ -801,117 +89,7 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct 
in_addr sip,
        if (dest != nfs_our_port)
                return;
 
-       switch (nfs_state) {
-       case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
-               if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
-                       break;
-               nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
-               nfs_send();
-               break;
-
-       case STATE_PRCLOOKUP_PROG_NFS_REQ:
-               if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
-                       break;
-               nfs_state = STATE_MOUNT_REQ;
-               nfs_send();
-               break;
-
-       case STATE_MOUNT_REQ:
-               reply = nfs_mount_reply(pkt, len);
-               if (reply == -NFS_RPC_DROP) {
-                       break;
-               } else if (reply == -NFS_RPC_ERR) {
-                       puts("*** ERROR: Cannot mount\n");
-                       /* just to be sure... */
-                       nfs_state = STATE_UMOUNT_REQ;
-                       nfs_send();
-               } else if (reply == -NFS_RPC_PROG_MISMATCH &&
-                          choosen_nfs_version != NFS_UNKOWN) {
-                       nfs_state = STATE_MOUNT_REQ;
-                       nfs_send();
-               } else {
-                       nfs_state = STATE_LOOKUP_REQ;
-                       nfs_send();
-               }
-               break;
-
-       case STATE_UMOUNT_REQ:
-               reply = nfs_umountall_reply(pkt, len);
-               if (reply == -NFS_RPC_DROP) {
-                       break;
-               } else if (reply == -NFS_RPC_ERR) {
-                       debug("*** ERROR: Cannot umount\n");
-                       net_set_state(NETLOOP_FAIL);
-               } else {
-                       puts("\ndone\n");
-                       net_set_state(nfs_download_state);
-               }
-               break;
-
-       case STATE_LOOKUP_REQ:
-               reply = nfs_lookup_reply(pkt, len);
-               if (reply == -NFS_RPC_DROP) {
-                       break;
-               } else if (reply == -NFS_RPC_ERR) {
-                       puts("*** ERROR: File lookup fail\n");
-                       nfs_state = STATE_UMOUNT_REQ;
-                       nfs_send();
-               } else if (reply == -NFS_RPC_PROG_MISMATCH &&
-                          choosen_nfs_version != NFS_UNKOWN) {
-                       /* 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;
-                       nfs_len = NFS_READ_SIZE;
-                       nfs_send();
-               }
-               break;
-
-       case STATE_READLINK_REQ:
-               reply = nfs_readlink_reply(pkt, len);
-               if (reply == -NFS_RPC_DROP) {
-                       break;
-               } else if (reply == -NFS_RPC_ERR) {
-                       puts("*** ERROR: Symlink fail\n");
-                       nfs_state = STATE_UMOUNT_REQ;
-                       nfs_send();
-               } else {
-                       debug("Symlink --> %s\n", nfs_path);
-                       nfs_filename = basename(nfs_path);
-                       nfs_path     = dirname(nfs_path);
-
-                       nfs_state = STATE_MOUNT_REQ;
-                       nfs_send();
-               }
-               break;
-
-       case STATE_READ_REQ:
-               rlen = nfs_read_reply(pkt, len);
-               if (rlen == -NFS_RPC_DROP)
-                       break;
-               net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
-               if (rlen > 0) {
-                       nfs_offset += rlen;
-                       nfs_send();
-               } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
-                       /* symbolic link */
-                       nfs_state = STATE_READLINK_REQ;
-                       nfs_send();
-               } else {
-                       if (!rlen)
-                               nfs_download_state = NETLOOP_SUCCESS;
-                       if (rlen < 0)
-                               debug("NFS READ error (%d)\n", rlen);
-                       nfs_state = STATE_UMOUNT_REQ;
-                       nfs_send();
-               }
-               break;
-       }
+       nfs_pkt_recv(pkt, len);
 }
 
 void nfs_start(void)
@@ -940,8 +118,8 @@ void nfs_start(void)
                       nfs_path);
        }
 
-       nfs_filename = basename(nfs_path);
-       nfs_path     = dirname(nfs_path);
+       nfs_filename = nfs_basename(nfs_path);
+       nfs_path     = nfs_dirname(nfs_path);
 
        printf("Using %s device\n", eth_get_name());
 
diff --git a/net/nfs.h b/net/nfs.h
index 
6bf1cb76bd58b0ca123f85d834355e30bf649811..0dd180ce2229ed6640ab8acb913f2876cc5e2772
 100644
--- a/net/nfs.h
+++ b/net/nfs.h
@@ -6,15 +6,6 @@
 #ifndef __NFS_H__
 #define __NFS_H__
 
-#define SUNRPC_PORT     111
-
-#define PROG_PORTMAP    100000
-#define PROG_NFS        100003
-#define PROG_MOUNT      100005
-
-#define MSG_CALL        0
-#define MSG_REPLY       1
-
 #define PORTMAP_GETPORT 3
 
 #define MOUNT_ADDENTRY  1
@@ -29,56 +20,6 @@
 #define NFS_FHSIZE      32
 #define NFS3_FHSIZE     64
 
-#define NFSERR_PERM     1
-#define NFSERR_NOENT    2
-#define NFSERR_ACCES    13
-#define NFSERR_ISDIR    21
-#define NFSERR_INVAL    22
-
-/*
- * Block size used for NFS read accesses.  A RPC reply packet (including  all
- * headers) must fit within a single Ethernet frame to avoid fragmentation.
- * However, if CONFIG_IP_DEFRAG is set, a bigger value could be used.  In any
- * case, most NFS servers are optimized for a power of 2.
- */
-#define NFS_READ_SIZE  1024    /* biggest power of two that fits Ether frame */
-#define NFS_MAX_ATTRS  26
-
-/* 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[NFS_READ_SIZE + (6 + NFS_MAX_ATTRS) *
-                       sizeof(uint32_t)];
-               struct {
-                       uint32_t id;
-                       uint32_t type;
-                       uint32_t rpcvers;
-                       uint32_t prog;
-                       uint32_t vers;
-                       uint32_t proc;
-                       uint32_t data[1];
-               } call;
-               struct {
-                       uint32_t id;
-                       uint32_t type;
-                       uint32_t rstatus;
-                       uint32_t verifier;
-                       uint32_t v2;
-                       uint32_t astatus;
-                       uint32_t data[NFS_READ_SIZE / sizeof(uint32_t) +
-                               NFS_MAX_ATTRS];
-               } reply;
-       } u;
-};
 void nfs_start(void);  /* Begin NFS */
 
 /**********************************************************************/

-- 
2.47.3

Reply via email to