[PATCH v2 09/11] nfs: switch to nfs3

Uwe Kleine-König u.kleine-koenig at pengutronix.de
Fri Feb 7 16:28:11 EST 2014


This was tested against nfs-kernel-server and unfs3.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig at pengutronix.de>
---

Notes:
    Changes since implicit v1:
    - fix PROG_MOUNT version to 3
    - add empty line as requested by Jean-Christophe
    - use {hton,ntoh}{16,32,64}

 fs/nfs.c | 894 ++++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 625 insertions(+), 269 deletions(-)

diff --git a/fs/nfs.c b/fs/nfs.c
index 79df6a8e4f7a..ab33c91ef0a9 100644
--- a/fs/nfs.c
+++ b/fs/nfs.c
@@ -1,10 +1,12 @@
 /*
  * nfs.c - barebox NFS driver
  *
+ * Copyright (c) 2014 Uwe Kleine-König, Pengutronix
  * Copyright (c) 2012 Sascha Hauer <s.hauer at pengutronix.de>, Pengutronix
  * Copyright (c) Masami Komiya <mkomiya at sonare.it> 2004
  *
- * Based on U-Boot NFS code which is based on NetBSD code
+ * Based on U-Boot NFS code which is based on NetBSD code with
+ * major changes to support nfs3.
  *
  * See file CREDITS for list of people who contributed to this
  * project.
@@ -17,7 +19,6 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
 #include <common.h>
@@ -33,6 +34,7 @@
 #include <linux/err.h>
 #include <kfifo.h>
 #include <sizes.h>
+#include <byteorder.h>
 
 #define SUNRPC_PORT     111
 
@@ -48,34 +50,54 @@
 #define MOUNT_ADDENTRY	1
 #define MOUNT_UMOUNT	3
 
-#define NFS_GETATTR     1
-#define NFS_LOOKUP      4
-#define NFS_READLINK    5
-#define NFS_READ        6
-#define NFS_READDIR	16
-
-#define NFS_FHSIZE      32
-
-enum nfs_stat {
-	NFS_OK		= 0,
-	NFSERR_PERM	= 1,
-	NFSERR_NOENT	= 2,
-	NFSERR_IO	= 5,
-	NFSERR_NXIO	= 6,
-	NFSERR_ACCES	= 13,
-	NFSERR_EXIST	= 17,
-	NFSERR_NODEV	= 19,
-	NFSERR_NOTDIR	= 20,
-	NFSERR_ISDIR	= 21,
-	NFSERR_FBIG	= 27,
-	NFSERR_NOSPC	= 28,
-	NFSERR_ROFS	= 30,
-	NFSERR_NAMETOOLONG=63,
-	NFSERR_NOTEMPTY	= 66,
-	NFSERR_DQUOT	= 69,
-	NFSERR_STALE	= 70,
-	NFSERR_WFLUSH	= 99,
-};
+#define NFSPROC3_GETATTR	1
+#define NFSPROC3_LOOKUP		3
+#define NFSPROC3_READLINK	5
+#define NFSPROC3_READ		6
+#define NFSPROC3_READDIR	16
+
+#define NFS3_FHSIZE      64
+#define NFS3_COOKIEVERFSIZE	8
+
+/* values of enum ftype3 */
+#define NF3REG	1
+#define NF3DIR	2
+#define NF3BLK	3
+#define NF3CHR	4
+#define NF3LNK	5
+#define NF3SOCK	6
+#define NF3FIFO	7
+
+/* values for enum nfsstat3 */
+#define NFS3_OK			    0
+#define NFS3ERR_PERM		    1
+#define NFS3ERR_NOENT		    2
+#define NFS3ERR_IO   		    5
+#define NFS3ERR_NXIO 		    6
+#define NFS3ERR_ACCES		   13
+#define NFS3ERR_EXIST		   17
+#define NFS3ERR_XDEV		   18
+#define NFS3ERR_NODEV		   19
+#define NFS3ERR_NOTDIR		   20
+#define NFS3ERR_ISDIR		   21
+#define NFS3ERR_INVAL		   22
+#define NFS3ERR_FBIG		   27
+#define NFS3ERR_NOSPC		   28
+#define NFS3ERR_ROFS		   30
+#define NFS3ERR_MLINK		   31
+#define NFS3ERR_NAMETOOLONG	   63
+#define NFS3ERR_NOTEMPTY	   66
+#define NFS3ERR_DQUOT		   69
+#define NFS3ERR_STALE		   70
+#define NFS3ERR_REMOTE		   71
+#define NFS3ERR_BADHANDLE 	10001
+#define NFS3ERR_NOT_SYNC	10002
+#define NFS3ERR_BAD_COOKIE	10003
+#define NFS3ERR_NOTSUPP		10004
+#define NFS3ERR_TOOSMALL	10005
+#define NFS3ERR_SERVERFAULT	10006
+#define NFS3ERR_BADTYPE		10007
+#define NFS3ERR_JUKEBOX		10008
 
 static void *nfs_packet;
 static int nfs_len;
@@ -107,51 +129,96 @@ struct nfs_priv {
 	struct net_connection *con;
 	IPaddr_t server;
 	char *path;
-	int mount_port;
-	int nfs_port;
-	unsigned long rpc_id;
-	char rootfh[NFS_FHSIZE];
+	unsigned short mount_port;
+	unsigned short nfs_port;
+	uint32_t rpc_id;
+	uint32_t rootfh_len;
+	char rootfh[NFS3_FHSIZE];
 };
 
 struct file_priv {
 	struct kfifo *fifo;
 	void *buf;
-	char filefh[NFS_FHSIZE];
+	uint32_t filefh_len;
+	char filefh[NFS3_FHSIZE];
 	struct nfs_priv *npriv;
 };
 
 static uint64_t nfs_timer_start;
 
-static int	nfs_state;
+static int nfs_state;
 #define STATE_DONE			1
 #define STATE_START			2
 
-enum ftype {
-	NFNON = 0,
-	NFREG = 1,
-	NFDIR = 2,
-	NFBLK = 3,
-	NFCHR = 4,
-	NFLNK = 5
-};
-
-struct fattr {
-	uint32_t type;
-	uint32_t mode;
-	uint32_t nlink;
-	uint32_t uid;
-	uint32_t gid;
-	uint32_t size;
-	uint32_t blocksize;
-	uint32_t rdev;
-	uint32_t blocks;
-};
-
-struct readdirargs {
-	char filefh[NFS_FHSIZE];
-	uint32_t cookie;
-	uint32_t count;
-};
+/*
+ * common types used in more than one request:
+ *
+ * typedef uint32 count3;
+ * typedef uint32 gid3;
+ * typedef uint32 mode3;
+ * typedef uint32 uid3;
+ *
+ * typedef uint64 cookie3;
+ * typedef uint64 fileid3;
+ * typedef uint64 size3;
+ *
+ * typedef string filename3<>;
+ * typedef string nfspath3<>;
+ *
+ * typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
+ *
+ * enum ftype3 {
+ * 	NF3REG = 1,
+ * 	NF3DIR = 2,
+ * 	NF3BLK = 3,
+ * 	NF3CHR = 4,
+ * 	NF3LNK = 5,
+ * 	NF3SOCK = 6,
+ * 	NF3FIFO = 7
+ * };
+ *
+ * struct specdata3 {
+ * 	uint32 specdata1;
+ * 	uint32 specdata2;
+ * };
+ *
+ * struct nfs_fh3 {
+ * 	opaque data<NFS3_FHSIZE>;
+ * }
+ *
+ * struct nfstime3 {
+ * 	uint32 seconds;
+ * 	uint32 nseconds;
+ * };
+ *
+ * struct fattr3 {
+ * 	ftype3 type;
+ * 	mode3 mode;
+ * 	uint32_t nlink;
+ * 	uid3 uid;
+ * 	gid3 gid;
+ * 	size3 size;
+ * 	size3 used;
+ * 	specdata3 rdev;
+ * 	uint64_t fsid;
+ * 	fileid3 fileid;
+ * 	nfstime3 atime;
+ * 	nfstime3 mtime;
+ * 	nfstime3 ctime;
+ * };
+ *
+ * struct diropargs3 {
+ * 	nfs_fh3 dir;
+ * 	filename3 name;
+ * }
+ *
+ * union post_op_attr switch (bool attributes_follow) {
+ * case TRUE:
+ * 	fattr3 attributes;
+ * case FALSE:
+ * 	void;
+ * };
+ */
 
 struct xdr_stream {
 	__be32 *p;
@@ -162,6 +229,20 @@ struct xdr_stream {
 #define xdr_zero 0
 #define XDR_QUADLEN(l)		(((l) + 3) >> 2)
 
+struct nfs_dir {
+	DIR dir;
+
+	/*
+	 * stream points to the next entry3 in the reply member of READDIR3res
+	 * (if any, to the end indicator otherwise).
+	 */
+	struct xdr_stream stream;
+	struct dirent ent;
+	struct file_priv *priv;
+	uint64_t cookie;
+	char cookieverf[NFS3_COOKIEVERFSIZE];
+};
+
 static void xdr_init(struct xdr_stream *stream, void *buf, int len)
 {
 	stream->p = stream->buf = buf;
@@ -192,8 +273,10 @@ static __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
 	return p;
 }
 
-static int decode_filename(struct xdr_stream *xdr,
-		char *name, u32 *length)
+/*
+ * name is expected to point to a buffer with a size of at least 256 bytes.
+ */
+static int decode_filename(struct xdr_stream *xdr, char *name, u32 *length)
 {
 	__be32 *p;
 	u32 count;
@@ -201,7 +284,7 @@ static int decode_filename(struct xdr_stream *xdr,
 	p = xdr_inline_decode(xdr, 4);
 	if (!p)
 		goto out_overflow;
-	count = be32_to_cpup(p);
+	count = ntoh32(net_read_uint32(p));
 	if (count > 255)
 		goto out_nametoolong;
 	p = xdr_inline_decode(xdr, count);
@@ -211,11 +294,13 @@ static int decode_filename(struct xdr_stream *xdr,
 	name[count] = 0;
 	*length = count;
 	return 0;
+
 out_nametoolong:
-	printk("NFS: returned filename too long: %u\n", count);
+	printf("%s: returned a too long filename: %u\n", __func__, count);
 	return -ENAMETOOLONG;
+
 out_overflow:
-	printf("%s overflow\n",__func__);
+	printf("%s: premature end of packet\n", __func__);
 	return -EIO;
 }
 
@@ -230,10 +315,10 @@ static uint32_t *rpc_add_credentials(uint32_t *p)
 	 */
 
 	/* Provide an AUTH_UNIX credential.  */
-	*p++ = htonl(1);		/* AUTH_UNIX */
-	*p++ = htonl(20);		/* auth length: 20 + strlen(hostname) */
-	*p++ = htonl(0);		/* stamp */
-	*p++ = htonl(0);		/* hostname string length */
+	*p++ = hton32(1);		/* AUTH_UNIX */
+	*p++ = hton32(20);		/* auth length: 20 + strlen(hostname) */
+	*p++ = hton32(0);		/* stamp */
+	*p++ = hton32(0);		/* hostname string length */
 	/* memcpy(p, "", 0); p += 0; <- empty host name */
 
 	*p++ = 0;			/* uid */
@@ -247,7 +332,8 @@ static uint32_t *rpc_add_credentials(uint32_t *p)
 	return p;
 }
 
-static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_id, int *nfserr)
+static int rpc_check_reply(unsigned char *pkt,
+		int rpc_prog, uint32_t rpc_id, int *nfserr)
 {
 	uint32_t *data;
 	struct rpc_reply rpc;
@@ -259,8 +345,8 @@ static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_i
 
 	memcpy(&rpc, pkt, sizeof(rpc));
 
-	if (ntohl(rpc.id) != rpc_id) {
-		if (rpc_id - ntohl(rpc.id) == 1)
+	if (ntoh32(rpc.id) != rpc_id) {
+		if (rpc_id - ntoh32(rpc.id) == 1)
 			/* stale packet, wait a bit longer */
 			return 0;
 
@@ -277,7 +363,7 @@ static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_i
 		return 0;
 
 	data = (uint32_t *)(pkt + sizeof(struct rpc_reply));
-	*nfserr = ntohl(net_read_uint32(data));
+	*nfserr = ntoh32(net_read_uint32(data));
 	*nfserr = -*nfserr;
 
 	debug("%s: state: %d, err %d\n", __func__, nfs_state, *nfserr);
@@ -292,37 +378,41 @@ static int rpc_req(struct nfs_priv *npriv, int rpc_prog, int rpc_proc,
 		uint32_t *data, int datalen)
 {
 	struct rpc_call pkt;
-	unsigned long id;
-	int dport;
+	unsigned short dport;
 	int ret;
 	unsigned char *payload = net_udp_get_payload(npriv->con);
 	int nfserr;
 	int tries = 0;
 
 	npriv->rpc_id++;
-	id = npriv->rpc_id;
 
-	pkt.id = htonl(id);
-	pkt.type = htonl(MSG_CALL);
-	pkt.rpcvers = htonl(2);	/* use RPC version 2 */
-	pkt.prog = htonl(rpc_prog);
-	pkt.vers = htonl(2);	/* portmapper is version 2 */
-	pkt.proc = htonl(rpc_proc);
+	pkt.id = hton32(npriv->rpc_id);
+	pkt.type = hton32(MSG_CALL);
+	pkt.rpcvers = hton32(2);	/* use RPC version 2 */
+	pkt.prog = hton32(rpc_prog);
+	pkt.proc = hton32(rpc_proc);
 
-	memcpy(payload, &pkt, sizeof(pkt));
-	memcpy(payload + sizeof(pkt), data, datalen * sizeof(uint32_t));
+	debug("%s: prog: %d, proc: %d\n", __func__, rpc_prog, rpc_proc);
 
-	if (rpc_prog == PROG_PORTMAP)
+	if (rpc_prog == PROG_PORTMAP) {
 		dport = SUNRPC_PORT;
-	else if (rpc_prog == PROG_MOUNT)
+		pkt.vers = hton32(2);
+	} else if (rpc_prog == PROG_MOUNT) {
 		dport = npriv->mount_port;
-	else
+		pkt.vers = hton32(3);
+	} else {
 		dport = npriv->nfs_port;
+		pkt.vers = hton32(3);
+	}
+
+	memcpy(payload, &pkt, sizeof(pkt));
+	memcpy(payload + sizeof(pkt), data, datalen * sizeof(uint32_t));
 
-	npriv->con->udp->uh_dport = htons(dport);
+	npriv->con->udp->uh_dport = hton16(dport);
 
 again:
-	ret = net_udp_send(npriv->con, sizeof(pkt) + datalen * sizeof(uint32_t));
+	ret = net_udp_send(npriv->con,
+			sizeof(pkt) + datalen * sizeof(uint32_t));
 
 	nfs_timer_start = get_time_ns();
 
@@ -357,7 +447,7 @@ again:
 /*
  * rpc_lookup_req - Lookup RPC Port numbers
  */
-static int rpc_lookup_req(struct nfs_priv *npriv, int prog, int ver)
+static int rpc_lookup_req(struct nfs_priv *npriv, uint32_t prog, uint32_t ver)
 {
 	uint32_t data[16];
 	int ret;
@@ -365,19 +455,149 @@ static int rpc_lookup_req(struct nfs_priv *npriv, int prog, int ver)
 
 	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[4] = hton32(prog);
+	data[5] = hton32(ver);
+	data[6] = hton32(17);	/* IP_UDP */
 	data[7] = 0;
 
 	ret = rpc_req(npriv, PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
 	if (ret)
 		return ret;
 
-	port = ntohl(net_read_uint32(nfs_packet + sizeof(struct rpc_reply)));
+	port = ntoh32(net_read_uint32(nfs_packet + sizeof(struct rpc_reply)));
 	return port;
 }
 
+static uint32_t *nfs_add_uint32(uint32_t *p, uint32_t val)
+{
+	*p++ = hton32(val);
+	return p;
+}
+
+static uint32_t *nfs_add_uint64(uint32_t *p, uint64_t val)
+{
+	uint64_t nval = hton64(val);
+
+	memcpy(p, &nval, 8);
+	return p + 2;
+}
+
+static uint32_t *nfs_add_fh3(uint32_t *p, unsigned fh_len, const char *fh)
+{
+	*p++ = hton32(fh_len);
+
+	/* zero padding */
+	if (fh_len & 3)
+		p[fh_len / 4] = 0;
+
+	memcpy(p, fh, fh_len);
+	p += DIV_ROUND_UP(fh_len, 4);
+	return p;
+}
+
+static uint32_t *nfs_add_filename(uint32_t *p,
+		uint32_t filename_len, const char *filename)
+{
+	*p++ = hton32(filename_len);
+
+	/* zero padding */
+	if (filename_len & 3)
+		p[filename_len / 4] = 0;
+
+	memcpy(p, filename, filename_len);
+	p += DIV_ROUND_UP(filename_len, 4);
+	return p;
+}
+
+/* This is a 1:1 mapping for Linux, the compiler optimizes it out */
+static const struct {
+	uint32_t nfsmode;
+	unsigned short statmode;
+} nfs3_mode_bits[] = {
+	{ 0x00001, S_IXOTH },
+	{ 0x00002, S_IWOTH },
+	{ 0x00004, S_IROTH },
+	{ 0x00008, S_IXGRP },
+	{ 0x00010, S_IWGRP },
+	{ 0x00020, S_IRGRP },
+	{ 0x00040, S_IXUSR },
+	{ 0x00080, S_IWUSR },
+	{ 0x00100, S_IRUSR },
+	{ 0x00200, S_ISVTX },
+	{ 0x00400, S_ISGID },
+	{ 0x00800, S_ISUID },
+};
+
+static int nfs_fattr3_to_stat(uint32_t *p, struct stat *s)
+{
+	uint32_t mode;
+	size_t i;
+
+	/* offsetof(struct fattr3, type) = 0 */
+	switch (ntoh32(net_read_uint32(p + 0))) {
+	case NF3REG:
+		s->st_mode = S_IFREG;
+		break;
+	case NF3DIR:
+		s->st_mode = S_IFDIR;
+		break;
+	case NF3BLK:
+		s->st_mode = S_IFBLK;
+		break;
+	case NF3CHR:
+		s->st_mode = S_IFCHR;
+		break;
+	case NF3LNK:
+		s->st_mode = S_IFLNK;
+		break;
+	case NF3SOCK:
+		s->st_mode = S_IFSOCK;
+		break;
+	case NF3FIFO:
+		s->st_mode = S_IFIFO;
+		break;
+	default:
+		printf("%s: invalid mode %x\n",
+				__func__, ntoh32(net_read_uint32(p + 0)));
+		return -EIO;
+	}
+
+	/* offsetof(struct fattr3, mode) = 4 */
+	mode = ntoh32(net_read_uint32(p + 1));
+	for (i = 0; i < ARRAY_SIZE(nfs3_mode_bits); ++i) {
+		if (mode & nfs3_mode_bits[i].nfsmode)
+			s->st_mode |= nfs3_mode_bits[i].statmode;
+	}
+
+	/* offsetof(struct fattr3, size) = 20 */
+	s->st_size = ntoh64(net_read_uint64(p + 5));
+
+	return 0;
+}
+
+static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct stat **s)
+{
+	struct stat dummy;
+	/*
+	 * union post_op_attr switch (bool attributes_follow) {
+	 * case TRUE:
+	 * 	fattr3 attributes;
+	 * case FALSE:
+	 * 	void;
+	 * };
+	 */
+
+	if (ntoh32(net_read_uint32(p++))) {
+		nfs_fattr3_to_stat(p, s ? *s : &dummy);
+		p += 21;
+	} else if (s) {
+		/* no attributes available */
+		*s = NULL;
+	}
+
+	return p;
+}
+
 /*
  * nfs_mount_req - Mount an NFS Filesystem
  */
@@ -396,7 +616,7 @@ static int nfs_mount_req(struct nfs_priv *npriv)
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	*p++ = htonl(pathlen);
+	*p++ = hton32(pathlen);
 	if (pathlen & 3)
 		*(p + pathlen / 4) = 0;
 
@@ -409,7 +629,16 @@ static int nfs_mount_req(struct nfs_priv *npriv)
 	if (ret)
 		return ret;
 
-	memcpy(npriv->rootfh, nfs_packet + sizeof(struct rpc_reply) + 4, NFS_FHSIZE);
+	p = nfs_packet + sizeof(struct rpc_reply) + 4;
+
+	npriv->rootfh_len = ntoh32(net_read_uint32(p++));
+	if (npriv->rootfh_len > NFS3_FHSIZE) {
+		printf("%s: file handle too big: %lu\n", __func__,
+				(unsigned long)npriv->rootfh_len);
+		return -EIO;
+	}
+	memcpy(npriv->rootfh, p, npriv->rootfh_len);
+	p += DIV_ROUND_UP(npriv->rootfh_len, 4);
 
 	return 0;
 }
@@ -429,12 +658,7 @@ static void nfs_umount_req(struct nfs_priv *npriv)
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	*p++ = htonl(pathlen);
-	if (pathlen & 3)
-		*(p + pathlen / 4) = 0;
-
-	memcpy (p, npriv->path, pathlen);
-	p += (pathlen + 3) / 4;
+	p = nfs_add_filename(p, pathlen, npriv->path);
 
 	len = p - &(data[0]);
 
@@ -443,36 +667,68 @@ static void nfs_umount_req(struct nfs_priv *npriv)
 
 /*
  * nfs_lookup_req - Lookup Pathname
+ *
+ * *s is set to NULL if LOOKUP3resok doesn't contain obj_attributes.
  */
-static int nfs_lookup_req(struct file_priv *priv, const char *filename,
-		int fnamelen)
+static int nfs_lookup_req(struct file_priv *priv,
+		uint32_t filename_len, const char *filename, struct stat **s)
 {
 	uint32_t data[1024];
 	uint32_t *p;
 	int len;
 	int ret;
 
+	/*
+	 * struct LOOKUP3args {
+	 * 	diropargs3 what;
+	 * };
+	 *
+	 * struct LOOKUP3resok {
+	 * 	nfs_fh3 object;
+	 * 	post_op_attr obj_attributes;
+	 * 	post_op_attr dir_attributes;
+	 * };
+	 *
+	 * struct LOOKUP3resfail {
+	 * 	post_op_attr dir_attributes;
+	 * };
+	 *
+	 * union LOOKUP3res switch (nfsstat3 status) {
+	 * case NFS3_OK:
+	 * 	LOOKUP3resok resok;
+	 * default:
+	 * 	LOOKUP3resfail resfail;
+	 * };
+	 */
+
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	memcpy(p, priv->filefh, NFS_FHSIZE);
-
-	p += (NFS_FHSIZE / 4);
-	*p++ = htonl(fnamelen);
+	/* what.dir */
+	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
 
-	if (fnamelen & 3)
-		*(p + fnamelen / 4) = 0;
-
-	memcpy(p, filename, fnamelen);
-	p += (fnamelen + 3) / 4;
+	/* what.name */
+	p = nfs_add_filename(p, filename_len, filename);
 
 	len = p - &(data[0]);
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFS_LOOKUP, data, len);
+	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len);
 	if (ret)
 		return ret;
 
-	memcpy(priv->filefh, nfs_packet + sizeof(struct rpc_reply) + 4, NFS_FHSIZE);
+	p = nfs_packet + sizeof(struct rpc_reply) + 4;
+
+	priv->filefh_len = ntoh32(net_read_uint32(p++));
+	if (priv->filefh_len > NFS3_FHSIZE) {
+		debug("%s: file handle too big: %lu\n", __func__,
+				(unsigned long)priv->filefh_len);
+		return -EIO;
+	}
+	memcpy(priv->filefh, p, priv->filefh_len);
+	p += DIV_ROUND_UP(priv->filefh_len, 4);
+
+	if (s)
+		nfs_read_post_op_attr(p, s);
 
 	return 0;
 }
@@ -483,33 +739,48 @@ static int nfs_attr_req(struct file_priv *priv, struct stat *s)
 	uint32_t *p;
 	int len;
 	int ret;
-	struct fattr *fattr;
-	uint32_t type;
+
+	/*
+	 * struct GETATTR3args {
+	 * 	nfs_fh3 object;
+	 * }
+	 *
+	 * struct GETATTR3resok {
+	 * 	fattr3 obj_attributes;
+	 * };
+	 *
+	 * union GETATTR3res switch (nfsstat3 status) {
+	 * case NFS3_OK:
+	 * 	GETATTR3resok resok;
+	 * default:
+	 * 	void;
+	 * }
+	 */
 
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	memcpy(p, priv->filefh, NFS_FHSIZE);
-	p += (NFS_FHSIZE / 4);
-	*p++ = 0;
+	/* object */
+	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
 
 	len = p - &(data[0]);
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFS_GETATTR, data, len);
+	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_GETATTR, data, len);
 	if (ret)
 		return ret;
 
-	fattr = nfs_packet + sizeof(struct rpc_reply) + 4;
-
-	type = ntohl(net_read_uint32(&fattr->type));
+	p = nfs_packet + sizeof(struct rpc_reply) + 4;
 
-	s->st_size = ntohl(net_read_uint32(&fattr->size));
-	s->st_mode = ntohl(net_read_uint32(&fattr->mode));
+	nfs_fattr3_to_stat(p, s);
 
 	return 0;
 }
 
-static void *nfs_readdirattr_req(struct file_priv *priv, int *plen, uint32_t cookie)
+/*
+ * returns with dir->stream pointing to the first entry
+ * of dirlist3 res.resok.reply
+ */
+static void *nfs_readdirattr_req(struct file_priv *priv, struct nfs_dir *dir)
 {
 	uint32_t data[1024];
 	uint32_t *p;
@@ -517,26 +788,79 @@ static void *nfs_readdirattr_req(struct file_priv *priv, int *plen, uint32_t coo
 	int ret;
 	void *buf;
 
+	/*
+	 * struct READDIR3args {
+	 * 	nfs_fh3 dir;
+	 * 	cookie3 cookie;
+	 * 	cookieverf3 cookieverf;
+	 * 	count3 count;
+	 * };
+	 *
+	 * struct entry3 {
+	 * 	fileid3 fileid;
+	 * 	filename3 name;
+	 * 	cookie3 cookie;
+	 * 	entry3 *nextentry;
+	 * };
+	 *
+	 * struct dirlist3 {
+	 * 	entry3 *entries;
+	 * 	bool eof;
+	 * };
+	 *
+	 * struct READDIR3resok {
+	 * 	post_op_attr dir_attributes;
+	 * 	cookieverf3 cookieverf;
+	 * 	dirlist3 reply;
+	 * };
+	 *
+	 * struct READDIR3resfail {
+	 * 	post_op_attr dir_attributes;
+	 * };
+	 *
+	 * union READDIR3res switch (nfsstat3 status) {
+	 * case NFS3_OK:
+	 * 	READDIR3resok resok;
+	 * default:
+	 * 	READDIR3resfail resfail;
+	 * };
+	 */
+
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	memcpy(p, priv->filefh, NFS_FHSIZE);
-	p += (NFS_FHSIZE / 4);
-	*p++ = htonl(cookie); /* cookie */
-	*p++ = htonl(1024); /* count */
-	*p++ = 0;
+	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+	p = nfs_add_uint64(p, dir->cookie);
 
-	len = p - &(data[0]);
+	memcpy(p, dir->cookieverf, NFS3_COOKIEVERFSIZE);
+	p += NFS3_COOKIEVERFSIZE / 4;
+
+	p = nfs_add_uint32(p, 1024); /* count */
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFS_READDIR, data, len);
+	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READDIR, data, p - data);
 	if (ret)
 		return NULL;
 
-	*plen = nfs_len - sizeof(struct rpc_reply) + 4;
+	p = nfs_packet + sizeof(struct rpc_reply) + 4;
+	p = nfs_read_post_op_attr(p, NULL);
 
-	buf = xzalloc(*plen);
+	/* update cookieverf */
+	memcpy(dir->cookieverf, p, NFS3_COOKIEVERFSIZE);
+	p += NFS3_COOKIEVERFSIZE / 4;
 
-	memcpy(buf, nfs_packet + sizeof(struct rpc_reply) + 4, *plen);
+	len = nfs_packet + nfs_len - (void *)p;
+	if (!len) {
+		printf("%s: huh, no payload left\n", __func__);
+		return NULL;
+	}
+
+	buf = xzalloc(len);
+
+	memcpy(buf, p, len);
+
+	xdr_init(&dir->stream, buf, len);
+
+	/* now xdr points to dirlist3 res.resok.reply */
 
 	return buf;
 }
@@ -544,35 +868,74 @@ static void *nfs_readdirattr_req(struct file_priv *priv, int *plen, uint32_t coo
 /*
  * nfs_read_req - Read File on NFS Server
  */
-static int nfs_read_req(struct file_priv *priv, int offset, int readlen)
+static int nfs_read_req(struct file_priv *priv, uint64_t offset,
+		uint32_t readlen)
 {
 	uint32_t data[1024];
 	uint32_t *p;
-	uint32_t *filedata;
 	int len;
 	int ret;
-	int rlen;
+	uint32_t rlen, eof;
 
+	/*
+	 * struct READ3args {
+	 * 	nfs_fh3 file;
+	 * 	offset3 offset;
+	 * 	count3 count;
+	 * };
+	 *
+	 * struct READ3resok {
+	 * 	post_op_attr file_attributes;
+	 * 	count3 count;
+	 * 	bool eof;
+	 * 	opaque data<>;
+	 * };
+	 *
+	 * struct READ3resfail {
+	 * 	post_op_attr file_attributes;
+	 * };
+	 *
+	 * union READ3res switch (nfsstat3 status) {
+	 * case NFS3_OK:
+	 * 	READ3resok resok;
+	 * default:
+	 * 	READ3resfail resfail;
+	 * };
+	 */
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	memcpy (p, priv->filefh, NFS_FHSIZE);
-	p += (NFS_FHSIZE / 4);
-	*p++ = htonl(offset);
-	*p++ = htonl(readlen);
-	*p++ = 0;
+	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+	p = nfs_add_uint64(p, offset);
+	p = nfs_add_uint32(p, readlen);
 
 	len = p - &(data[0]);
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFS_READ, data, len);
+	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READ, data, len);
 	if (ret)
 		return ret;
 
-	filedata = (uint32_t *)(nfs_packet + sizeof(struct rpc_reply));
+	p = nfs_packet + sizeof(struct rpc_reply) + 4;
+
+	p = nfs_read_post_op_attr(p, NULL);
+
+	rlen = ntoh32(net_read_uint32(p));
+
+	/* skip over count */
+	p += 1;
 
-	rlen = ntohl(net_read_uint32(filedata + 18));
+	eof = ntoh32(net_read_uint32(p));
 
-	kfifo_put(priv->fifo, (char *)(filedata + 19), rlen);
+	/*
+	 * skip over eof and count embedded in the representation of data
+	 * assuming it equals rlen above.
+	 */
+	p += 2;
+
+	if (readlen && !rlen && !eof)
+		return -EIO;
+
+	kfifo_put(priv->fifo, (char *)p, rlen);
 
 	return 0;
 }
@@ -611,25 +974,29 @@ static int nfs_truncate(struct device_d *dev, FILE *f, ulong size)
 	return -ENOSYS;
 }
 
-static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename)
+static struct file_priv *nfs_do_open(struct device_d *dev,
+		const char *filename, struct stat **s)
 {
 	struct file_priv *priv;
 	struct nfs_priv *npriv = dev->priv;
 	int ret;
 	const char *tok;
 
+	debug("%s: filename = %s\n", __func__, filename);
 	priv = xzalloc(sizeof(*priv));
 
 	priv->npriv = npriv;
 
 	if (!*filename) {
-		memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE);
+		priv->filefh_len = npriv->rootfh_len;
+		memcpy(priv->filefh, npriv->rootfh, npriv->rootfh_len);
 		return priv;
 	}
 
 	filename++;
 
-	memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE);
+	priv->filefh_len = npriv->rootfh_len;
+	memcpy(priv->filefh, npriv->rootfh, NFS3_FHSIZE);
 
 	while (*filename) {
 		size_t flen;
@@ -640,7 +1007,7 @@ static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename)
 		else
 			flen = strlen(filename);
 
-		ret = nfs_lookup_req(priv, filename, flen);
+		ret = nfs_lookup_req(priv, flen, filename, s);
 		if (ret)
 			goto out;
 
@@ -666,19 +1033,28 @@ static void nfs_do_close(struct file_priv *priv)
 	free(priv);
 }
 
-static struct file_priv *nfs_do_stat(struct device_d *dev, const char *filename, struct stat *s)
+static struct file_priv *nfs_do_stat(struct device_d *dev,
+		const char *filename, struct stat *s)
 {
 	struct file_priv *priv;
 	int ret;
+	struct stat **sptr = &s;
 
-	priv = nfs_do_open(dev, filename);
+	debug("%s: filename = %s\n", __func__, filename);
+	priv = nfs_do_open(dev, filename, sptr);
 	if (IS_ERR(priv))
 		return priv;
 
-	ret = nfs_attr_req(priv, s);
-	if (ret) {
-		nfs_do_close(priv);
-		return ERR_PTR(ret);
+	if (!*sptr) {
+		/*
+		 * The nfs server didn't provide obj_attributes in the lookup
+		 * reply, so ask for them explicitly.
+		 */
+		ret = nfs_attr_req(priv, s);
+		if (ret) {
+			nfs_do_close(priv);
+			return ERR_PTR(ret);
+		}
 	}
 
 	return priv;
@@ -688,35 +1064,52 @@ static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size)
 {
 	uint32_t data[1024];
 	uint32_t *p;
-	int len;
+	uint32_t len;
 	int ret;
-	char *path;
-	uint32_t *filedata;
 
+	/*
+	 * struct READLINK3args {
+	 * 	nfs_fh3 symlink;
+	 * };
+	 *
+	 * struct READLINK3resok {
+	 * 	post_op_attr symlink_attributes;
+	 * 	nfspath3 data;
+	 * };
+	 *
+	 * struct READLINK3resfail {
+	 * 	post_op_attr symlink_attributes;
+	 * }
+	 *
+	 * union READLINK3res switch (nfsstat3 status) {
+	 * case NFS3_OK:
+	 * 	READLINK3resok resok;
+	 * default:
+	 * 	READLINK3resfail resfail;
+	 * };
+	 */
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	memcpy(p, priv->filefh, NFS_FHSIZE);
-	p += (NFS_FHSIZE / 4);
+	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
 
 	len = p - &(data[0]);
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFS_READLINK, data, len);
+	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READLINK, data, len);
 	if (ret)
 		return ret;
 
-	filedata = nfs_packet + sizeof(struct rpc_reply);
-	filedata++;
+	p = nfs_packet + sizeof(struct rpc_reply) + 4;
 
-	len = ntohl(net_read_uint32(filedata)); /* new path length */
-	filedata++;
+	p = nfs_read_post_op_attr(p, NULL);
 
-	path = (char *)filedata;
+	len = ntoh32(net_read_uint32(p)); /* new path length */
+	p++;
 
 	if (len > size)
-		len = size;
+		return -ENOMEM;
 
-	memcpy(buf, path, len);
+	memcpy(buf, p, len);
 
 	return 0;
 }
@@ -726,9 +1119,8 @@ static int nfs_readlink(struct device_d *dev, const char *filename,
 {
 	struct file_priv *priv;
 	int ret;
-	struct stat s;
 
-	priv = nfs_do_stat(dev, filename, &s);
+	priv = nfs_do_open(dev, filename, NULL);
 	if (IS_ERR(priv))
 		return PTR_ERR(priv);
 
@@ -780,32 +1172,17 @@ static int nfs_write(struct device_d *_dev, FILE *file, const void *inbuf,
 static int nfs_read(struct device_d *dev, FILE *file, void *buf, size_t insize)
 {
 	struct file_priv *priv = file->inode;
-	int now, outsize = 0, ret, pos = file->pos;
-
-	while (insize) {
-		now = kfifo_get(priv->fifo, buf, insize);
-		outsize += now;
-		buf += now;
-		insize -= now;
-
-		if (insize) {
-			/* do not use min as insize is a size_t */
-			if (insize < 1024)
-				now = insize;
-			else
-				now = 1024;
-
-			if (pos + now > file->size)
-				now = file->size - pos;
-
-			ret = nfs_read_req(priv, pos, now);
-			if (ret)
-				return ret;
-			pos += now;
-		}
+
+	if (insize > 1024)
+		insize = 1024;
+
+	if (insize && !kfifo_len(priv->fifo)) {
+		int ret = nfs_read_req(priv, file->pos, insize);
+		if (ret)
+			return ret;
 	}
 
-	return outsize;
+	return kfifo_get(priv->fifo, buf, insize);
 }
 
 static loff_t nfs_lseek(struct device_d *dev, FILE *file, loff_t pos)
@@ -818,110 +1195,89 @@ static loff_t nfs_lseek(struct device_d *dev, FILE *file, loff_t pos)
 	return file->pos;
 }
 
-struct nfs_dir {
-	DIR dir;
-	struct xdr_stream stream;
-	struct dirent ent;
-	struct file_priv *priv;
-	uint32_t cookie;
-};
-
 static DIR *nfs_opendir(struct device_d *dev, const char *pathname)
 {
 	struct file_priv *priv;
-	struct stat s;
-	int ret;
-	void *buf;
+	void *buf = NULL;
 	struct nfs_dir *dir;
-	int len;
 
-	priv = nfs_do_open(dev, pathname);
+	priv = nfs_do_open(dev, pathname, NULL);
 	if (IS_ERR(priv))
 		return NULL;
 
-	ret = nfs_attr_req(priv, &s);
-	if (ret)
-		return NULL;
-
-	if (!S_ISDIR(s.st_mode))
-		return NULL;
-
 	dir = xzalloc(sizeof(*dir));
 	dir->priv = priv;
 
-	buf = nfs_readdirattr_req(priv, &len, 0);
-	if (!buf)
-		return NULL;
-
-	xdr_init(&dir->stream, buf, len);
+	/* cookie == 0 and cookieverf == 0 means start of dir */
+	buf = nfs_readdirattr_req(priv, dir);
+	if (!buf) {
+		debug("%s: nfs_readdirattr_req failed\n", __func__);
+		goto err;
+	}
 
 	return &dir->dir;
+
+err:
+	free(buf);
+	free(dir);
+	nfs_do_close(priv);
+	return NULL;
 }
 
 static struct dirent *nfs_readdir(struct device_d *dev, DIR *dir)
 {
-	struct nfs_dir *ndir = (void *)dir;
-	__be32 *p;
+	struct nfs_dir *ndir = container_of(dir, struct nfs_dir, dir);
+	uint32_t *p;
 	int ret;
 	int len;
 	struct xdr_stream *xdr = &ndir->stream;
 
 again:
 	p = xdr_inline_decode(xdr, 4);
-	if (!p)
-		goto out_overflow;
+	if (!p) {
+		printf("%s: premature end of packet\n", __func__);
+		return NULL;
+	}
 
-	if (*p++ == xdr_zero) {
+	if (!net_read_uint32(p)) {
+		/* eof? */
 		p = xdr_inline_decode(xdr, 4);
-		if (!p)
-			goto out_overflow;
-		if (*p++ == xdr_zero) {
-			void *buf;
-			int len;
-
-			/*
-			 * End of current entries, read next chunk.
-			 */
+		if (!p) {
+			printf("%s: premature end of packet\n", __func__);
+			return NULL;
+		}
+		if (net_read_uint32(p))
+			return NULL;
 
-			free(ndir->stream.buf);
+		if (!nfs_readdirattr_req(ndir->priv, ndir)) {
+			printf("%s: nfs_readdirattr_req failed\n", __func__);
+			return NULL;
+		}
 
-			buf = nfs_readdirattr_req(ndir->priv, &len, ndir->cookie);
-			if (!buf)
-				return NULL;
+		goto again;
+	}
 
-			xdr_init(&ndir->stream, buf, len);
+	/* there is another entry available in the last reply */
 
-			goto again;
-		}
-		return NULL; /* -EINVAL */
+	/* skip over fileid */
+	p = xdr_inline_decode(xdr, 8);
+	if (!p) {
+		printf("%s: premature end of packet\n", __func__);
+		return NULL;
 	}
 
-	p = xdr_inline_decode(xdr, 4);
-	if (!p)
-		goto out_overflow;
-
 	ret = decode_filename(xdr, ndir->ent.d_name, &len);
 	if (ret)
 		return NULL;
 
-	/*
-	 * The type (size and byte order) of nfscookie isn't defined in
-	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
-	 */
-	p = xdr_inline_decode(xdr, 4);
-	if (!p)
-		goto out_overflow;
-
-	ndir->cookie = be32_to_cpup(p);
+	p = xdr_inline_decode(xdr, 8);
+	if (!p) {
+		printf("%s: premature end of packet\n", __func__);
+		return NULL;
+	}
+	ndir->cookie = ntoh64(net_read_uint64(p));
 
 	return &ndir->ent;
-
-out_overflow:
-
-	printf("nfs: overflow error\n");
-
-	return NULL;
-
 }
 
 static int nfs_closedir(struct device_d *dev, DIR *dir)
@@ -983,14 +1339,14 @@ static int nfs_probe(struct device_d *dev)
 	/* Need a priviliged source port */
 	net_udp_bind(npriv->con, 1000);
 
-	ret = rpc_lookup_req(npriv, PROG_MOUNT, 2);
+	ret = rpc_lookup_req(npriv, PROG_MOUNT, 3);
 	if (ret < 0) {
 		printf("lookup mount port failed with %d\n", ret);
 		goto err2;
 	}
 	npriv->mount_port = ret;
 
-	ret = rpc_lookup_req(npriv, PROG_NFS, 2);
+	ret = rpc_lookup_req(npriv, PROG_NFS, 3);
 	if (ret < 0) {
 		printf("lookup nfs port failed with %d\n", ret);
 		goto err2;
-- 
1.8.5.2




More information about the barebox mailing list