patch-2.2.18 linux/fs/nfs/nfs3xdr.c

Next file: linux/fs/nfs/nfsroot.c
Previous file: linux/fs/nfs/nfs3proc.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/fs/nfs/nfs3xdr.c linux/fs/nfs/nfs3xdr.c
@@ -1,94 +1,160 @@
 /*
- * linux/fs/nfs/nfs2xdr.c
+ * linux/fs/nfs/nfs3xdr.c
  *
  * XDR functions to encode/decode NFSv3 RPC arguments and results.
- * Note: this is incomplete!
  *
- * Copyright (C) 1996 Olaf Kirch
+ * Copyright (C) 1996, 1997 Olaf Kirch
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS3_XDR_TYPES
 
 #include <linux/param.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
-#include <linux/nfs_fs.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
 #include <linux/pagemap.h>
 #include <linux/proc_fs.h>
+#include <linux/kdev_t.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
 
-#ifdef RPC_DEBUG
-# define RPC_FACILITY		RPCDBG_NFS
-#endif
+/* Uncomment this to support servers requiring longword lengths */
+#define NFS_PAD_WRITES		1
 
-#define QUADLEN(len)		(((len) + 3) >> 2)
-static int			nfs_stat_to_errno(int stat);
+#define NFSDBG_FACILITY		NFSDBG_XDR
 
 /* Mapping from NFS error code to "errno" error code. */
 #define errno_NFSERR_IO		EIO
 
+extern int			nfs_stat_to_errno(int);
+
 /*
  * Declare the space requirements for NFS arguments and replies as
  * number of 32bit-words
  */
-#define NFS_fhandle_sz		(1+16)
-#define NFS_sattr_sz		8
-#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
-#define NFS_fattr_sz		17
-#define NFS_info_sz		5
-#define NFS_entry_sz		NFS_filename_sz+3
-
-#define NFS_enc_void_sz		0
-#define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
-#define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
-#define NFS_readargs_sz		NFS_fhandle_sz+3
-#define NFS_writeargs_sz	NFS_fhandle_sz+4
-#define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
-#define NFS_renameargs_sz	NFS_diropargs_sz+NFS_diropargs_sz
-#define NFS_linkargs_sz		NFS_fhandle_sz+NFS_diropargs_sz
-#define NFS_symlinkargs_sz	NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
-#define NFS_readdirargs_sz	NFS_fhandle_sz+2
-
-#define NFS_dec_void_sz		0
-#define NFS_attrstat_sz		1+NFS_fattr_sz
-#define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz	1+NFS_path_sz
-#define NFS_readres_sz		1+NFS_fattr_sz+1
-#define NFS_stat_sz		1
-#define NFS_readdirres_sz	1
-#define NFS_statfsres_sz	1+NFS_info_sz
+#define NFS3_fhandle_sz		1+16
+#define NFS3_fh_sz		NFS3_fhandle_sz	/* shorthand */
+#define NFS3_sattr_sz		15
+#define NFS3_filename_sz	1+(NFS3_MAXNAMLEN>>2)
+#define NFS3_path_sz		1+(NFS3_MAXPATHLEN>>2)
+#define NFS3_fattr_sz		21
+#define NFS3_wcc_attr_sz		6
+#define NFS3_pre_op_attr_sz	1+NFS3_wcc_attr_sz
+#define NFS3_post_op_attr_sz	1+NFS3_fattr_sz
+#define NFS3_wcc_data_sz		NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz
+#define NFS3_fsstat_sz		
+#define NFS3_fsinfo_sz		
+#define NFS3_pathconf_sz		
+#define NFS3_entry_sz		NFS3_filename_sz+3
+
+#define NFS3_enc_void_sz	0
+#define NFS3_sattrargs_sz	NFS3_fh_sz+NFS3_sattr_sz+3
+#define NFS3_diropargs_sz	NFS3_fh_sz+NFS3_filename_sz
+#define NFS3_accessargs_sz	NFS3_fh_sz+1
+#define NFS3_readlinkargs_sz	NFS3_fh_sz
+#define NFS3_readargs_sz	NFS3_fh_sz+3
+#define NFS3_writeargs_sz	NFS3_fh_sz+5
+#define NFS3_createargs_sz	NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_mkdirargs_sz	NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_symlinkargs_sz	NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz
+#define NFS3_mknodargs_sz	NFS3_diropargs_sz+2+NFS3_sattr_sz
+#define NFS3_renameargs_sz	NFS3_diropargs_sz+NFS3_diropargs_sz
+#define NFS3_linkargs_sz		NFS3_fh_sz+NFS3_diropargs_sz
+#define NFS3_readdirargs_sz	NFS3_fh_sz+2
+#define NFS3_commitargs_sz	NFS3_fh_sz+3
+
+#define NFS3_dec_void_sz	0
+#define NFS3_attrstat_sz	1+NFS3_fattr_sz
+#define NFS3_wccstat_sz		1+NFS3_wcc_data_sz
+#define NFS3_lookupres_sz	1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)
+#define NFS3_accessres_sz	1+NFS3_post_op_attr_sz+1
+#define NFS3_readlinkres_sz	1+NFS3_post_op_attr_sz
+#define NFS3_readres_sz		1+NFS3_post_op_attr_sz+3
+#define NFS3_writeres_sz	1+NFS3_wcc_data_sz+4
+#define NFS3_createres_sz	1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_renameres_sz	1+(2 * NFS3_wcc_data_sz)
+#define NFS3_linkres_sz		1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_readdirres_sz	1+NFS3_post_op_attr_sz+2
+#define NFS3_fsstatres_sz	1+NFS3_post_op_attr_sz+13
+#define NFS3_fsinfores_sz	1+NFS3_post_op_attr_sz+12
+#define NFS3_pathconfres_sz	1+NFS3_post_op_attr_sz+6
+#define NFS3_commitres_sz	1+NFS3_wcc_data_sz+2
+
+/*
+ * Map file type to S_IFMT bits
+ */
+static struct {
+	unsigned int	mode;
+	unsigned int	nfs2type;
+} nfs_type2fmt[] = {
+      { 0,		NFNON	},
+      { S_IFREG,	NFREG	},
+      { S_IFDIR,	NFDIR	},
+      { S_IFBLK,	NFBLK	},
+      { S_IFCHR,	NFCHR	},
+      { S_IFLNK,	NFLNK	},
+      { S_IFSOCK,	NFSOCK	},
+      { S_IFIFO,	NFFIFO	},
+      { 0,		NFBAD	}
+};
 
 /*
  * Common NFS XDR functions as inlines
  */
 static inline u32 *
-xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
 {
 	*p++ = htonl(fh->size);
 	memcpy(p, fh->data, fh->size);
-	return p + QUADLEN(fh->size);
+	return p + XDR_QUADLEN(fh->size);
 }
 
 static inline u32 *
-xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
 {
+	/*
+	 * Zero all nonused bytes
+	 */
+	memset((u8 *)fh, 0, sizeof(*fh));
 	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
 		memcpy(fh->data, p, fh->size);
-		return p + QUADLEN(fh->size);
+		return p + XDR_QUADLEN(fh->size);
 	}
 	return NULL;
 }
 
-static inline enum nfs_ftype
-xdr_decode_ftype(u32 type)
+/*
+ * Encode/decode time.
+ * Since the VFS doesn't care for fractional times, we ignore the
+ * nanosecond field.
+ */
+static inline u32 *
+xdr_encode_time(u32 *p, time_t time)
+{
+	*p++ = htonl(time);
+	*p++ = 0;
+	return p;
+}
+
+static inline u32 *
+xdr_decode_time3(u32 *p, u64 *timep)
+{
+	*timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_time3(u32 *p, u64 time)
 {
-	return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type;
+	*p++ = htonl(time >> 32);
+	*p++ = htonl(time & 0xFFFFFFFF);
+	return p;
 }
 
 static inline u32 *
@@ -99,47 +165,119 @@
 	if (*len > maxlen)
 		return NULL;
 	*string = (char *) p;
-	return p + QUADLEN(*len);
+	return p + XDR_QUADLEN(*len);
 }
 
 static inline u32 *
-xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr)
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
 {
-	fattr->type = xdr_decode_ftype(ntohl(*p++));
-	fattr->mode = ntohl(*p++);
+	unsigned int	type;
+	int		fmode;
+
+	type = ntohl(*p++);
+	if (type >= NF3BAD)
+		type = NF3BAD;
+	fmode = nfs_type2fmt[type].mode;
+	fattr->type = nfs_type2fmt[type].nfs2type;
+	fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
 	fattr->nlink = ntohl(*p++);
 	fattr->uid = ntohl(*p++);
 	fattr->gid = ntohl(*p++);
-	fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
-	fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
-	fattr->rdev_maj = ntohl(*p++);
-	fattr->rdev_min = ntohl(*p++);
-	fattr->fsid = ntohl(*p++);
-	fattr->fileid = ntohl(*p++);
-	fattr->atime.seconds = ntohl(*p++);
-	fattr->atime.useconds = ntohl(*p++);
-	fattr->mtime.seconds = ntohl(*p++);
-	fattr->mtime.useconds = ntohl(*p++);
-	fattr->ctime.seconds = ntohl(*p++);
-	fattr->ctime.useconds = ntohl(*p++);
+	p = xdr_decode_hyper(p, &fattr->size);
+	p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
+	/* Turn remote device info into Linux-specific dev_t */
+	fattr->rdev = (ntohl(*p++) << MINORBITS) | (ntohl(*p++) & MINORMASK);
+	p = xdr_decode_hyper(p, &fattr->fsid);
+	p = xdr_decode_hyper(p, &fattr->fileid);
+	p = xdr_decode_time3(p, &fattr->atime);
+	p = xdr_decode_time3(p, &fattr->mtime);
+	p = xdr_decode_time3(p, &fattr->ctime);
+
+	/* Update the mode bits */
+	fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct iattr *attr)
+{
+	if (attr->ia_valid & ATTR_MODE) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_mode);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_UID) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_uid);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_GID) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_gid);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_SIZE) {
+		*p++ = xdr_one;
+		p = xdr_encode_hyper(p, (__u64) attr->ia_size);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_ATIME_SET) {
+		*p++ = xdr_two;
+		p = xdr_encode_time(p, attr->ia_atime);
+	} else if (attr->ia_valid & ATTR_ATIME) {
+		*p++ = xdr_one;
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_MTIME_SET) {
+		*p++ = xdr_two;
+		p = xdr_encode_time(p, attr->ia_mtime);
+	} else if (attr->ia_valid & ATTR_MTIME) {
+		*p++ = xdr_one;
+	} else {
+		*p++ = xdr_zero;
+	}
+	return p;
+}
+
+static inline u32 *
+xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr)
+{
+	p = xdr_decode_hyper(p, &fattr->pre_size);
+	p = xdr_decode_time3(p, &fattr->pre_mtime);
+	p = xdr_decode_time3(p, &fattr->pre_ctime);
+	fattr->valid |= NFS_ATTR_WCC;
+	return p;
+}
+
+static inline u32 *
+xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+	if (*p++)
+		p = xdr_decode_fattr(p, fattr);
 	return p;
 }
 
 static inline u32 *
-xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
 {
-	*p++ = htonl(sattr->mode);
-	*p++ = htonl(sattr->uid);
-	*p++ = htonl(sattr->gid);
-	*p++ = htonl(sattr->size >> 32);
-	*p++ = htonl(sattr->size & 0xFFFFFFFF);
-	*p++ = htonl(sattr->atime.seconds);
-	*p++ = htonl(sattr->atime.useconds);
-	*p++ = htonl(sattr->mtime.seconds);
-	*p++ = htonl(sattr->mtime.useconds);
+	if (*p++)
+		return xdr_decode_wcc_attr(p, fattr);
 	return p;
 }
 
+
+static inline u32 *
+xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
+{
+	p = xdr_decode_pre_op_attr(p, fattr);
+	return xdr_decode_post_op_attr(p, fattr);
+}
+
 /*
  * NFS encode functions
  */
@@ -147,7 +285,7 @@
  * Encode void argument
  */
 static int
-nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
 {
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -155,10 +293,9 @@
 
 /*
  * Encode file handle argument
- * GETATTR, READLINK, STATFS
  */
 static int
-nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
+nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
 {
 	p = xdr_encode_fhandle(p, fh);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -169,23 +306,37 @@
  * Encode SETATTR arguments
  */
 static int
-nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
 	p = xdr_encode_sattr(p, args->sattr);
+	*p++ = htonl(args->guard);
+	if (args->guard)
+		p = xdr_encode_time3(p, args->guardtime);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
  * Encode directory ops argument
- * LOOKUP, REMOVE, RMDIR
  */
 static int
-nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name, args->len);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode access() argument
+ */
+static int
+nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	*p++ = htonl(args->access);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -193,149 +344,178 @@
 /*
  * Arguments to a READ call. Since we read data directly into the page
  * cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page wewant to fetch.
+ * exactly to the page we want to fetch.
  */
 static int
-nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen, buflen;
+	size_t		replen;
+	unsigned int	nr;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
+	p = xdr_encode_hyper(p, args->offset);
 	*p++ = htonl(args->count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-#if 1
+	/* Get the number of buffers in the receive iovec */
+	nr = args->nriov;
+
+	if (nr+1 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+		return -EINVAL;
+	}
+
 	/* set up reply iovec */
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
-	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->count;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = args->count + buflen;
-	req->rq_rnr = 3;
-#else
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
 	req->rq_rvec[0].iov_len  = replen;
-#endif
+
+	/* Copy the iovec */
+	memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+	req->rq_rlen = args->count + replen;
+	req->rq_rnr += nr;
 
 	return 0;
 }
 
 /*
- * Decode READ reply
+ * Write arguments. Splice the buffer to be written into the iovec.
  */
 static int
-nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
-	struct iovec *iov = req->rq_rvec;
-	int	status, count, recvd, hdrlen;
+	unsigned int	nr;
+	u32 count = args->count;
 
-	dprintk("RPC:      readres OK status %lx\n", ntohl(*p));
-	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
-	p = xdr_decode_fattr(p, res->fattr);
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_hyper(p, args->offset);
+	*p++ = htonl(count);
+	*p++ = htonl(args->stable);
+	*p++ = htonl(count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	count = ntohl(*p++);
-	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[2].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
-		return -errno_NFSERR_IO;
-	}
-	if (count > recvd) {
-		printk("NFS: server cheating in read reply: "
-			"count %d > recvd %d\n", count, recvd);
-		count = recvd;
+	/* Get the number of buffers in the send iovec */
+	nr = args->nriov;
+
+	if (nr+2 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs\n");
+		return -EINVAL;
 	}
 
-	dprintk("RPC:      readres OK count %d\n", count);
-	if (count < res->count)
-		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+	/* Copy the iovec */
+	memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
 
-	return count;
-}
+#ifdef NFS_PAD_WRITES
+	/*
+	 * Some old servers require that the message length
+	 * be a multiple of 4, so we pad it here if needed.
+	 */
+	if (count & 3) {
+		struct iovec	*iov = req->rq_svec + nr + 1;
+		int		pad = 4 - (count & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		count += pad;
+		nr++;
+	}
+#endif
+	req->rq_slen += count;
+	req->rq_snr  += nr;
 
+	return 0;
+}
 
 /*
- * Write arguments. Splice the buffer to be written into the iovec.
+ * Encode CREATE arguments
  */
 static int
-nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
-	*p++ = htonl(args->count);
-	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	p = xdr_encode_string(p, args->name, args->len);
 
-	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = args->count;
-	req->rq_slen += args->count;
-	req->rq_snr = 2;
+	*p++ = htonl(args->createmode);
+	if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
+		*p++ = args->verifier[0];
+		*p++ = args->verifier[1];
+	} else
+		p = xdr_encode_sattr(p, args->sattr);
 
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode create arguments
- * CREATE, MKDIR
+ * Encode MKDIR arguments
  */
 static int
-nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode RENAME arguments
+ * Encode SYMLINK arguments
  */
 static int
-nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
+	p = xdr_encode_sattr(p, args->sattr);
+	p = xdr_encode_string(p, args->topath, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode LINK arguments
+ * Encode MKNOD arguments
+ */
+static int
+nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name, args->len);
+	*p++ = htonl(args->type);
+	p = xdr_encode_sattr(p, args->sattr);
+	if (args->type == NF3CHR || args->type == NF3BLK) {
+		*p++ = htonl(args->rdev >> MINORBITS);
+		*p++ = htonl(args->rdev & MINORMASK);
+	}
+
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode RENAME arguments
  */
 static int
-nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode SYMLINK arguments
+ * Encode LINK arguments
  */
 static int
-nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_string(p, args->topath);
-	p = xdr_encode_sattr(p, args->sattr);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -344,129 +524,168 @@
  * Encode arguments to readdir call
  */
 static int
-nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen;
+	size_t		replen;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->cookie);
+	p = xdr_encode_hyper(p, args->cookie);
+	*p++ = args->verf[0];
+	*p++ = args->verf[1];
+	if (args->plus) {
+		/* readdirplus: need dircount + buffer size.
+		 * We just make sure we make dircount big enough */
+		*p++ = htonl(args->bufsiz >> 3);
+	}
 	*p++ = htonl(args->bufsiz);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
 	/* set up reply iovec */
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	/*
-	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
-		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
-	 */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
 	req->rq_rvec[0].iov_len  = replen;
 	req->rq_rvec[1].iov_base = args->buffer;
 	req->rq_rvec[1].iov_len  = args->bufsiz;
 	req->rq_rlen = replen + args->bufsiz;
-	req->rq_rnr = 2;
-
-	/*
-	dprintk("RPC:      readdirargs set up reply vec:\n");
-	dprintk("          rvec[0] = %p/%d\n",
-			req->rq_rvec[0].iov_base,
-			req->rq_rvec[0].iov_len);
-	dprintk("          rvec[1] = %p/%d\n",
-			req->rq_rvec[1].iov_base,
-			req->rq_rvec[1].iov_len);
-	 */
+	req->rq_rnr ++;
 
 	return 0;
 }
 
 /*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- *	entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
- */
-static int
-nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
-{
-	struct iovec		*iov = req->rq_rvec;
-	int			status, nr, len;
-	char			*string, *start;
-	u32			*end;
-	__u32			fileid, cookie, *entry;
-
-	if ((status = ntohl(*p++)))
+ * Decode the result of a readdir call.
+ * We just check for syntactical correctness.
+ */
+static int
+nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
+{
+	struct iovec	*iov = req->rq_rvec;
+	u32		hdrlen;
+	int		status, nr;
+	unsigned int	len;
+	u32		*entry, *end;
+
+	status = ntohl(*p++);
+	/* Decode post_op_attrs */
+	p = xdr_decode_post_op_attr(p, res->dir_attr);
+	if (status)
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk("NFS: Odd RPC header size in readdirres reply\n");
-		return -errno_NFSERR_IO;
+	/* Decode verifier cookie */
+	if (res->verf) {
+		res->verf[0] = *p++;
+		res->verf[1] = *p++;
+	} else {
+		p += 2;
 	}
 
-	p = (u32 *) iov[1].iov_base;
-	end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
-	if (p != res->buffer) {
-		printk("NFS: p != res->buffer in %s:%d!!!\n",
-					__FILE__, __LINE__);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READDIR header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
 
-	entry  = (__u32 *) res->buffer;
-	start  = (char *) res->buffer;
-	string = start + res->bufsiz;
+	p   = (u32 *) iov[1].iov_base;
+	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 	for (nr = 0; *p++; nr++) {
-		fileid = ntohl(*p++);
-
-		len = ntohl(*p++);
-		if ((p + QUADLEN(len) + 3) > end) {
-			printk(KERN_NOTICE
-				"NFS: short packet in readdir reply!\n");
-			break;
-		}
-		if (len > NFS_MAXNAMLEN) {
-			printk("NFS: giant filename in readdir (len %x)!\n",
+		entry = p - 1;
+		if (p + 3 > end)
+			goto short_pkt;
+		p += 2;				/* inode # */
+		len = ntohl(*p++);		/* string length */
+		p += XDR_QUADLEN(len) + 2;	/* name + cookie */
+		if (len > NFS3_MAXNAMLEN) {
+			printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
 						len);
 			return -errno_NFSERR_IO;
 		}
-		string -= len;
-		if ((void *) (entry+3) > (void *) string) {
-			/* 
-			 * This error is impossible as long as the temp
-			 * buffer is no larger than the user buffer. The 
-			 * current packing algorithm uses the same amount
-			 * of space in the user buffer as in the XDR data,
-			 * so it's guaranteed to fit.
-			 */
-			printk("NFS: incorrect buffer size in %s!\n",
-				__FUNCTION__);
-			break;
+
+		if (res->plus) {
+			/* post_op_attr */
+			if (p > end)
+				goto short_pkt;
+			if (*p++)
+				p += 21;
+			if (p > end)
+				goto short_pkt;
+			/* post_op_fh3 */
+			if (*p++) {
+				len = ntohl(*p++);
+				if (len > NFS3_FHSIZE) {
+					printk(KERN_WARNING "NFS: giant filehandle in "
+						"readdir (len %x)!\n", len);
+					return -errno_NFSERR_IO;
+				}
+				p += XDR_QUADLEN(len);
+			}
 		}
+		if (p + 2 > end)
+			goto short_pkt;
+	}
 
-		memmove(string, p, len);
-		p += QUADLEN(len);
-		cookie = ntohl(*p++);
-		/*
-		 * To make everything fit, we encode the length, offset,
-		 * and eof flag into 32 bits. This works for filenames
-		 * up to 32K and PAGE_SIZE up to 64K.
-		 */
-		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
-		*entry++ = fileid;
-		*entry++ = cookie;
-		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
-		/*
-		dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
-			len, string, cookie, status);
-		 */
-	}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
 	return nr;
+ short_pkt:
+	printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
+	/* truncate listing */
+	entry[0] = entry[1] = 0;
+	return nr;
+}
+
+u32 *
+nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+	struct nfs_entry old = *entry;
+
+	if (!*p++) {
+		if (!*p)
+			return ERR_PTR(-EAGAIN);
+		entry->eof = 1;
+		return ERR_PTR(-EBADCOOKIE);
+	}
+
+	p = xdr_decode_hyper(p, &entry->ino);
+	entry->len  = ntohl(*p++);
+	entry->name = (const char *) p;
+	p += XDR_QUADLEN(entry->len);
+	entry->prev_cookie = entry->cookie;
+	p = xdr_decode_hyper(p, &entry->cookie);
+
+	if (plus) {
+		p = xdr_decode_post_op_attr(p, &entry->fattr);
+		/* In fact, a post_op_fh3: */
+		if (*p++) {
+			p = xdr_decode_fhandle(p, &entry->fh);
+			/* Ugh -- server reply was truncated */
+			if (p == NULL) {
+				dprintk("NFS: FH truncated\n");
+				*entry = old;
+				return ERR_PTR(-EAGAIN);
+			}
+		} else {
+			/* If we don't get a file handle, the attrs
+			 * aren't worth a lot. */
+			entry->fattr.valid = 0;
+		}
+	}
+
+	entry->eof = !p[0] && p[1];
+	return p;
+}
+
+/*
+ * Encode COMMIT arguments
+ */
+static int
+nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_hyper(p, args->offset);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
 }
 
 /*
@@ -476,59 +695,89 @@
  * Decode void reply
  */
 static int
-nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
 {
 	return 0;
 }
 
 /*
- * Decode simple status reply
+ * Decode attrstat reply.
  */
 static int
-nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
 	int	status;
 
-	if ((status = ntohl(*p++)) != 0)
-		status = -nfs_stat_to_errno(status);
-	return status;
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_fattr(p, fattr);
+	return 0;
 }
 
 /*
- * Decode attrstat reply
- * GETATTR, SETATTR, WRITE
+ * Decode status+wcc_data reply
+ * SATTR, REMOVE, RMDIR
  */
 static int
-nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
 	int	status;
 
-	dprintk("RPC:      attrstat status %lx\n", ntohl(*p));
 	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
-	xdr_decode_fattr(p, fattr);
-	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
-		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
-	return 0;
+		status = -nfs_stat_to_errno(status);
+	xdr_decode_wcc_data(p, fattr);
+	return status;
 }
 
 /*
- * Decode diropres reply
- * LOOKUP, CREATE, MKDIR
+ * Decode LOOKUP reply
  */
 static int
-nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
 {
 	int	status;
 
-	dprintk("RPC:      diropres status %lx\n", ntohl(*p));
-	if ((status = ntohl(*p++)))
+	if ((status = ntohl(*p++))) {
+		status = -nfs_stat_to_errno(status);
+	} else {
+		if (!(p = xdr_decode_fhandle(p, res->fh)))
+			return -errno_NFSERR_IO;
+		p = xdr_decode_post_op_attr(p, res->fattr);
+	}
+	xdr_decode_post_op_attr(p, res->dir_attr);
+	return status;
+}
+
+/*
+ * Decode ACCESS reply
+ */
+static int
+nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
+{
+	int	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, res->fattr);
+	if (status)
 		return -nfs_stat_to_errno(status);
-	p = xdr_decode_fhandle(p, res->fh);
-	xdr_decode_fattr(p, res->fattr);
-	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
-		res->fattr->type, res->fattr->mode,
-		res->fattr->fsid, res->fattr->fileid);
+	res->access = ntohl(*p++);
+	return 0;
+}
+
+static int
+nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
+{
+	struct rpc_task *task = req->rq_task;
+	struct rpc_auth *auth = task->tk_auth;
+	size_t		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr ++;
 	return 0;
 }
 
@@ -536,155 +785,308 @@
  * Decode READLINK reply
  */
 static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkres *res)
 {
+	struct iovec	*iov = req->rq_rvec;
+	u32		hdrlen;
+	u32	*strlen;
+	char	*string;
 	int	status;
+	unsigned int len;
 
-	if ((status = ntohl(*p++)))
+	status = ntohl(*p++);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+
+	if (status != 0)
 		return -nfs_stat_to_errno(status);
-	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
 
-	/* Caller takes over the buffer here to avoid extra copy */
-	res->buffer = req->rq_task->tk_buffer;
-	req->rq_task->tk_buffer = NULL;
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READLINK header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
+	strlen = (u32*)res->buffer;
+	/* Convert length of symlink */
+	len = ntohl(*strlen);
+	if (len > res->bufsiz - 5)
+		len = res->bufsiz - 5;
+	*strlen = len;
+	/* NULL terminate the string we got */
+	string = (char *)(strlen + 1);
+	string[len] = 0;
 	return 0;
 }
 
 /*
- * Decode STATFS reply
+ * Decode READ reply
  */
 static int
-nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
+	struct iovec *iov = req->rq_rvec;
+	u32	count, ocount, recvd, hdrlen;
 	int	status;
 
-	if ((status = ntohl(*p++)))
+	status = ntohl(*p++);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+
+	if (status != 0)
 		return -nfs_stat_to_errno(status);
-	res->tsize = ntohl(*p++);
-	res->bsize = ntohl(*p++);
-	res->blocks = ntohl(*p++);
-	res->bfree = ntohl(*p++);
-	res->bavail = ntohl(*p++);
-	return 0;
+
+	/* Decode reply could and EOF flag. NFSv3 is somewhat redundant
+	 * in that it puts the count both in the res struct and in the
+	 * opaque data count. */
+	count    = ntohl(*p++);
+	res->eof = ntohl(*p++);
+	ocount   = ntohl(*p++);
+
+	if (ocount != count) {
+		printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
+		return -errno_NFSERR_IO;
+	}
+
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READ header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
+	recvd = req->rq_rlen - hdrlen;
+	if (count > recvd) {
+		printk(KERN_WARNING "NFS: server cheating in read reply: "
+			"count %d > recvd %d\n", count, recvd);
+		count = recvd;
+	}
+
+	if (count < res->count) {
+		xdr_zero_iovec(iov+1, req->rq_rnr-1, res->count - count);
+		res->count = count;
+	}
+
+	return count;
 }
 
 /*
- * We need to translate between nfs status return values and
- * the local errno values which may not be the same.
+ * Decode WRITE response
  */
-static struct {
-	int stat;
-	int errno;
-} nfs_errtbl[] = {
-	{ NFS_OK,		0		},
-	{ NFSERR_PERM,		EPERM		},
-	{ NFSERR_NOENT,		ENOENT		},
-	{ NFSERR_IO,		errno_NFSERR_IO	},
-	{ NFSERR_NXIO,		ENXIO		},
-	{ NFSERR_EAGAIN,	EAGAIN		},
-	{ NFSERR_ACCES,		EACCES		},
-	{ NFSERR_EXIST,		EEXIST		},
-	{ NFSERR_XDEV,		EXDEV		},
-	{ NFSERR_NODEV,		ENODEV		},
-	{ NFSERR_NOTDIR,	ENOTDIR		},
-	{ NFSERR_ISDIR,		EISDIR		},
-	{ NFSERR_INVAL,		EINVAL		},
-	{ NFSERR_FBIG,		EFBIG		},
-	{ NFSERR_NOSPC,		ENOSPC		},
-	{ NFSERR_ROFS,		EROFS		},
-	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
-	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
-	{ NFSERR_DQUOT,		EDQUOT		},
-	{ NFSERR_STALE,		ESTALE		},
-#ifdef EWFLUSH
-	{ NFSERR_WFLUSH,	EWFLUSH		},
-#endif
-	{ -1,			EIO		}
-};
+static int
+nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	int	status;
+
+	status = ntohl(*p++);
+	p = xdr_decode_wcc_data(p, res->fattr);
+
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
 
+	res->count = ntohl(*p++);
+	res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
+	res->verf->verifier[0] = *p++;
+	res->verf->verifier[1] = *p++;
+
+	return res->count;
+}
+
+/*
+ * Decode a CREATE response
+ */
 static int
-nfs_stat_to_errno(int stat)
+nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
 {
-	int i;
+	int	status;
 
-	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-		if (nfs_errtbl[i].stat == stat)
-			return nfs_errtbl[i].errno;
+	status = ntohl(*p++);
+	if (status == 0) {
+		if (*p++) {
+			if (!(p = xdr_decode_fhandle(p, res->fh)))
+				return -errno_NFSERR_IO;
+			p = xdr_decode_post_op_attr(p, res->fattr);
+		} else {
+			memset(res->fh, 0, sizeof(*res->fh));
+			/* Do decode post_op_attr but set it to NULL */
+			p = xdr_decode_post_op_attr(p, res->fattr);
+			res->fattr->valid = 0;
+		}
+	} else {
+		status = -nfs_stat_to_errno(status);
 	}
-	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
-	return nfs_errtbl[i].errno;
+	p = xdr_decode_wcc_data(p, res->dir_attr);
+	return status;
 }
 
-#ifndef MAX
-# define MAX(a, b)	(((a) > (b))? (a) : (b))
-#endif
+/*
+ * Decode RENAME reply
+ */
+static int
+nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
+{
+	int	status;
 
-#define PROC(proc, argtype, restype)	\
-    { "nfs_" #proc,					\
-      (kxdrproc_t) nfs_xdr_##argtype,			\
-      (kxdrproc_t) nfs_xdr_##restype,			\
-      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
-    }
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	p = xdr_decode_wcc_data(p, res->fromattr);
+	p = xdr_decode_wcc_data(p, res->toattr);
+	return status;
+}
 
-static struct rpc_procinfo	nfs_procedures[18] = {
-    PROC(null,		enc_void,	dec_void),
-    PROC(getattr,	fhandle,	attrstat),
-    PROC(setattr,	sattrargs,	attrstat),
-    PROC(root,		enc_void,	dec_void),
-    PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	fhandle,	readlinkres),
-    PROC(read,		readargs,	readres),
-    PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	attrstat),
-    PROC(create,	createargs,	diropres),
-    PROC(remove,	diropargs,	stat),
-    PROC(rename,	renameargs,	stat),
-    PROC(link,		linkargs,	stat),
-    PROC(symlink,	symlinkargs,	stat),
-    PROC(mkdir,		createargs,	diropres),
-    PROC(rmdir,		diropargs,	stat),
-    PROC(readdir,	readdirargs,	readdirres),
-    PROC(statfs,	fhandle,	statfsres),
-};
+/*
+ * Decode LINK reply
+ */
+static int
+nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
+{
+	int	status;
 
-static struct rpc_version	nfs_version2 = {
-	2,
-	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
-	nfs_procedures
-};
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+	p = xdr_decode_wcc_data(p, res->dir_attr);
+	return status;
+}
 
-static struct rpc_version *	nfs_version[] = {
-	NULL,
-	NULL,
-	&nfs_version2
-};
+/*
+ * Decode FSSTAT reply
+ */
+static int
+nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	struct nfs_fattr dummy;
+	int		status;
 
-struct rpc_program	nfs_program = {
-	"nfs",
-	NFS_PROGRAM,
-	sizeof(nfs_version) / sizeof(nfs_version[0]),
-	nfs_version,
-	&nfs_rpcstat,
-};
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	p = xdr_decode_hyper(p, &res->tbytes);
+	p = xdr_decode_hyper(p, &res->fbytes);
+	p = xdr_decode_hyper(p, &res->abytes);
+	p = xdr_decode_hyper(p, &res->tfiles);
+	p = xdr_decode_hyper(p, &res->ffiles);
+	p = xdr_decode_hyper(p, &res->afiles);
+
+	/* ignore invarsec */
+	return 0;
+}
 
 /*
- * RPC stats support
+ * Decode FSINFO reply
  */
 static int
-nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
 {
-	return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
+	struct nfs_fattr dummy;
+	int		status;
+
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	res->rtmax  = ntohl(*p++);
+	res->rtpref = ntohl(*p++);
+	res->rtmult = ntohl(*p++);
+	res->wtmax  = ntohl(*p++);
+	res->wtpref = ntohl(*p++);
+	res->wtmult = ntohl(*p++);
+	res->dtpref = ntohl(*p++);
+	p = xdr_decode_hyper(p, &res->maxfilesize);
+
+	/* ignore time_delta and properties */
+	return 0;
 }
 
-static struct proc_dir_entry	proc_nfsclnt = {
-	0, 3, "nfs",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	6, &proc_net_inode_operations,
-	nfs_get_info
+/*
+ * Decode PATHCONF reply
+ */
+static int
+nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	struct nfs_fattr dummy;
+	int		status;
+
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+	res->linkmax = ntohl(*p++);
+	res->namelen = ntohl(*p++);
+
+	/* ignore remaining fields */
+	return 0;
+}
+
+/*
+ * Decode COMMIT reply
+ */
+static int
+nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	int		status;
+
+	status = ntohl(*p++);
+	p = xdr_decode_wcc_data(p, res->fattr);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	res->verf->verifier[0] = *p++;
+	res->verf->verifier[1] = *p++;
+	return 0;
+}
+
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype)				\
+    { "nfs3_" #proc,						\
+      (kxdrproc_t) nfs3_xdr_##argtype,				\
+      (kxdrproc_t) nfs3_xdr_##restype,				\
+      MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2,	\
+      0							\
+    }
+
+static struct rpc_procinfo	nfs3_procedures[22] = {
+  PROC(null,		enc_void,	dec_void),
+  PROC(getattr,		fhandle,	attrstat),
+  PROC(setattr, 	sattrargs,	wccstat),
+  PROC(lookup,		diropargs,	lookupres),
+  PROC(access,		accessargs,	accessres),
+  PROC(readlink,	readlinkargs,	readlinkres),
+  PROC(read,		readargs,	readres),
+  PROC(write,		writeargs,	writeres),
+  PROC(create,		createargs,	createres),
+  PROC(mkdir,		mkdirargs,	createres),
+  PROC(symlink,		symlinkargs,	createres),
+  PROC(mknod,		mknodargs,	createres),
+  PROC(remove,		diropargs,	wccstat),
+  PROC(rmdir,		diropargs,	wccstat),
+  PROC(rename,		renameargs,	renameres),
+  PROC(link,		linkargs,	linkres),
+  PROC(readdir,		readdirargs,	readdirres),
+  PROC(readdirplus,	readdirargs,	readdirres),
+  PROC(fsstat,		fhandle,	fsstatres),
+  PROC(fsinfo,  	fhandle,	fsinfores),
+  PROC(pathconf,	fhandle,	pathconfres),
+  PROC(commit,		commitargs,	commitres),
 };
 
-struct rpc_stat			nfs_rpcstat = {
-	NULL,			/* next */
-	&proc_nfsclnt,		/* /proc/net directory entry */
-	&nfs_program,		/* RPC program */
+struct rpc_version		nfs_version3 = {
+	3,
+	sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
+	nfs3_procedures
 };
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)