xref: /openbmc/linux/fs/nfs/nfs3xdr.c (revision e4f93234)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfs/nfs3xdr.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * XDR functions to encode/decode NFSv3 RPC arguments and results.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1996, 1997 Olaf Kirch
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/param.h>
101da177e4SLinus Torvalds #include <linux/time.h>
111da177e4SLinus Torvalds #include <linux/mm.h>
121da177e4SLinus Torvalds #include <linux/errno.h>
131da177e4SLinus Torvalds #include <linux/string.h>
141da177e4SLinus Torvalds #include <linux/in.h>
151da177e4SLinus Torvalds #include <linux/pagemap.h>
161da177e4SLinus Torvalds #include <linux/proc_fs.h>
171da177e4SLinus Torvalds #include <linux/kdev_t.h>
181da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
191da177e4SLinus Torvalds #include <linux/nfs.h>
201da177e4SLinus Torvalds #include <linux/nfs3.h>
211da177e4SLinus Torvalds #include <linux/nfs_fs.h>
22b7fa0554SAndreas Gruenbacher #include <linux/nfsacl.h>
23f7b422b1SDavid Howells #include "internal.h"
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
281da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds /*
311da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
321da177e4SLinus Torvalds  * number of 32bit-words
331da177e4SLinus Torvalds  */
341da177e4SLinus Torvalds #define NFS3_fhandle_sz		(1+16)
351da177e4SLinus Torvalds #define NFS3_fh_sz		(NFS3_fhandle_sz)	/* shorthand */
361da177e4SLinus Torvalds #define NFS3_sattr_sz		(15)
371da177e4SLinus Torvalds #define NFS3_filename_sz	(1+(NFS3_MAXNAMLEN>>2))
381da177e4SLinus Torvalds #define NFS3_path_sz		(1+(NFS3_MAXPATHLEN>>2))
391da177e4SLinus Torvalds #define NFS3_fattr_sz		(21)
40d9c407b1SChuck Lever #define NFS3_cookieverf_sz	(NFS3_COOKIEVERFSIZE>>2)
411da177e4SLinus Torvalds #define NFS3_wcc_attr_sz		(6)
421da177e4SLinus Torvalds #define NFS3_pre_op_attr_sz	(1+NFS3_wcc_attr_sz)
431da177e4SLinus Torvalds #define NFS3_post_op_attr_sz	(1+NFS3_fattr_sz)
441da177e4SLinus Torvalds #define NFS3_wcc_data_sz		(NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
451da177e4SLinus Torvalds #define NFS3_fsstat_sz
461da177e4SLinus Torvalds #define NFS3_fsinfo_sz
471da177e4SLinus Torvalds #define NFS3_pathconf_sz
481da177e4SLinus Torvalds #define NFS3_entry_sz		(NFS3_filename_sz+3)
491da177e4SLinus Torvalds #define NFS3_diropargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
50ad96b5b5SChuck Lever 
51ad96b5b5SChuck Lever #define NFS3_getattrargs_sz	(NFS3_fh_sz)
52ad96b5b5SChuck Lever #define NFS3_setattrargs_sz	(NFS3_fh_sz+NFS3_sattr_sz+3)
53ad96b5b5SChuck Lever #define NFS3_lookupargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
541da177e4SLinus Torvalds #define NFS3_accessargs_sz	(NFS3_fh_sz+1)
551da177e4SLinus Torvalds #define NFS3_readlinkargs_sz	(NFS3_fh_sz)
561da177e4SLinus Torvalds #define NFS3_readargs_sz	(NFS3_fh_sz+3)
571da177e4SLinus Torvalds #define NFS3_writeargs_sz	(NFS3_fh_sz+5)
581da177e4SLinus Torvalds #define NFS3_createargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
591da177e4SLinus Torvalds #define NFS3_mkdirargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
6094a6d753SChuck Lever #define NFS3_symlinkargs_sz	(NFS3_diropargs_sz+1+NFS3_sattr_sz)
611da177e4SLinus Torvalds #define NFS3_mknodargs_sz	(NFS3_diropargs_sz+2+NFS3_sattr_sz)
62ad96b5b5SChuck Lever #define NFS3_removeargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
631da177e4SLinus Torvalds #define NFS3_renameargs_sz	(NFS3_diropargs_sz+NFS3_diropargs_sz)
641da177e4SLinus Torvalds #define NFS3_linkargs_sz		(NFS3_fh_sz+NFS3_diropargs_sz)
65d9c407b1SChuck Lever #define NFS3_readdirargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+3)
66d9c407b1SChuck Lever #define NFS3_readdirplusargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+4)
671da177e4SLinus Torvalds #define NFS3_commitargs_sz	(NFS3_fh_sz+3)
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds #define NFS3_attrstat_sz	(1+NFS3_fattr_sz)
701da177e4SLinus Torvalds #define NFS3_wccstat_sz		(1+NFS3_wcc_data_sz)
714fdc17b2STrond Myklebust #define NFS3_removeres_sz	(NFS3_wccstat_sz)
721da177e4SLinus Torvalds #define NFS3_lookupres_sz	(1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
731da177e4SLinus Torvalds #define NFS3_accessres_sz	(1+NFS3_post_op_attr_sz+1)
741da177e4SLinus Torvalds #define NFS3_readlinkres_sz	(1+NFS3_post_op_attr_sz+1)
751da177e4SLinus Torvalds #define NFS3_readres_sz		(1+NFS3_post_op_attr_sz+3)
761da177e4SLinus Torvalds #define NFS3_writeres_sz	(1+NFS3_wcc_data_sz+4)
771da177e4SLinus Torvalds #define NFS3_createres_sz	(1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
781da177e4SLinus Torvalds #define NFS3_renameres_sz	(1+(2 * NFS3_wcc_data_sz))
791da177e4SLinus Torvalds #define NFS3_linkres_sz		(1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
801da177e4SLinus Torvalds #define NFS3_readdirres_sz	(1+NFS3_post_op_attr_sz+2)
811da177e4SLinus Torvalds #define NFS3_fsstatres_sz	(1+NFS3_post_op_attr_sz+13)
821da177e4SLinus Torvalds #define NFS3_fsinfores_sz	(1+NFS3_post_op_attr_sz+12)
831da177e4SLinus Torvalds #define NFS3_pathconfres_sz	(1+NFS3_post_op_attr_sz+6)
841da177e4SLinus Torvalds #define NFS3_commitres_sz	(1+NFS3_wcc_data_sz+2)
851da177e4SLinus Torvalds 
86b7fa0554SAndreas Gruenbacher #define ACL3_getaclargs_sz	(NFS3_fh_sz+1)
87ae46141fSTrond Myklebust #define ACL3_setaclargs_sz	(NFS3_fh_sz+1+ \
88ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
89ae46141fSTrond Myklebust #define ACL3_getaclres_sz	(1+NFS3_post_op_attr_sz+1+ \
90ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
91b7fa0554SAndreas Gruenbacher #define ACL3_setaclres_sz	(1+NFS3_post_op_attr_sz)
92b7fa0554SAndreas Gruenbacher 
931da177e4SLinus Torvalds /*
941da177e4SLinus Torvalds  * Map file type to S_IFMT bits
951da177e4SLinus Torvalds  */
96bca79478STrond Myklebust static const umode_t nfs_type2fmt[] = {
97bca79478STrond Myklebust 	[NF3BAD] = 0,
98bca79478STrond Myklebust 	[NF3REG] = S_IFREG,
99bca79478STrond Myklebust 	[NF3DIR] = S_IFDIR,
100bca79478STrond Myklebust 	[NF3BLK] = S_IFBLK,
101bca79478STrond Myklebust 	[NF3CHR] = S_IFCHR,
102bca79478STrond Myklebust 	[NF3LNK] = S_IFLNK,
103bca79478STrond Myklebust 	[NF3SOCK] = S_IFSOCK,
104bca79478STrond Myklebust 	[NF3FIFO] = S_IFIFO,
1051da177e4SLinus Torvalds };
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /*
108d9c407b1SChuck Lever  * While encoding arguments, set up the reply buffer in advance to
109d9c407b1SChuck Lever  * receive reply data directly into the page cache.
110d9c407b1SChuck Lever  */
111d9c407b1SChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
112d9c407b1SChuck Lever 				 unsigned int base, unsigned int len,
113d9c407b1SChuck Lever 				 unsigned int bufsize)
114d9c407b1SChuck Lever {
115d9c407b1SChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
116d9c407b1SChuck Lever 	unsigned int replen;
117d9c407b1SChuck Lever 
118d9c407b1SChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
119d9c407b1SChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
120d9c407b1SChuck Lever }
121d9c407b1SChuck Lever 
122e4f93234SChuck Lever /*
123e4f93234SChuck Lever  * Handle decode buffer overflows out-of-line.
124e4f93234SChuck Lever  */
125e4f93234SChuck Lever static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
126e4f93234SChuck Lever {
127e4f93234SChuck Lever 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
128e4f93234SChuck Lever 		"Remaining buffer length is %tu words.\n",
129e4f93234SChuck Lever 		func, xdr->end - xdr->p);
130e4f93234SChuck Lever }
131e4f93234SChuck Lever 
132d9c407b1SChuck Lever 
133d9c407b1SChuck Lever /*
1341da177e4SLinus Torvalds  * Common NFS XDR functions as inlines
1351da177e4SLinus Torvalds  */
136d61005a6SAl Viro static inline __be32 *
137d61005a6SAl Viro xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds 	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
1401da177e4SLinus Torvalds 		memcpy(fh->data, p, fh->size);
1411da177e4SLinus Torvalds 		return p + XDR_QUADLEN(fh->size);
1421da177e4SLinus Torvalds 	}
1431da177e4SLinus Torvalds 	return NULL;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
146babddc72SBryan Schumaker static inline __be32 *
147babddc72SBryan Schumaker xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
148babddc72SBryan Schumaker {
149babddc72SBryan Schumaker 	__be32 *p;
150babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
151babddc72SBryan Schumaker 	if (unlikely(!p))
152babddc72SBryan Schumaker 		goto out_overflow;
153babddc72SBryan Schumaker 	fh->size = ntohl(*p++);
154babddc72SBryan Schumaker 
155babddc72SBryan Schumaker 	if (fh->size <= NFS3_FHSIZE) {
156babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, fh->size);
157babddc72SBryan Schumaker 		if (unlikely(!p))
158babddc72SBryan Schumaker 			goto out_overflow;
159babddc72SBryan Schumaker 		memcpy(fh->data, p, fh->size);
160babddc72SBryan Schumaker 		return p + XDR_QUADLEN(fh->size);
161babddc72SBryan Schumaker 	}
162babddc72SBryan Schumaker 	return NULL;
163babddc72SBryan Schumaker 
164babddc72SBryan Schumaker out_overflow:
165babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
166babddc72SBryan Schumaker 	return ERR_PTR(-EIO);
167babddc72SBryan Schumaker }
168babddc72SBryan Schumaker 
1691da177e4SLinus Torvalds /*
1701da177e4SLinus Torvalds  * Encode/decode time.
1711da177e4SLinus Torvalds  */
172d61005a6SAl Viro static inline __be32 *
173d61005a6SAl Viro xdr_decode_time3(__be32 *p, struct timespec *timep)
1741da177e4SLinus Torvalds {
1751da177e4SLinus Torvalds 	timep->tv_sec = ntohl(*p++);
1761da177e4SLinus Torvalds 	timep->tv_nsec = ntohl(*p++);
1771da177e4SLinus Torvalds 	return p;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
180d61005a6SAl Viro static __be32 *
181d61005a6SAl Viro xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds 	unsigned int	type, major, minor;
184bca79478STrond Myklebust 	umode_t		fmode;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	type = ntohl(*p++);
187bca79478STrond Myklebust 	if (type > NF3FIFO)
188bca79478STrond Myklebust 		type = NF3NON;
189bca79478STrond Myklebust 	fmode = nfs_type2fmt[type];
1901da177e4SLinus Torvalds 	fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
1911da177e4SLinus Torvalds 	fattr->nlink = ntohl(*p++);
1921da177e4SLinus Torvalds 	fattr->uid = ntohl(*p++);
1931da177e4SLinus Torvalds 	fattr->gid = ntohl(*p++);
1941da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->size);
1951da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	/* Turn remote device info into Linux-specific dev_t */
1981da177e4SLinus Torvalds 	major = ntohl(*p++);
1991da177e4SLinus Torvalds 	minor = ntohl(*p++);
2001da177e4SLinus Torvalds 	fattr->rdev = MKDEV(major, minor);
2011da177e4SLinus Torvalds 	if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
2021da177e4SLinus Torvalds 		fattr->rdev = 0;
2031da177e4SLinus Torvalds 
2048b4bdcf8STrond Myklebust 	p = xdr_decode_hyper(p, &fattr->fsid.major);
2058b4bdcf8STrond Myklebust 	fattr->fsid.minor = 0;
2061da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->fileid);
2071da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->atime);
2081da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->mtime);
2091da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->ctime);
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	/* Update the mode bits */
2129e6e70f8STrond Myklebust 	fattr->valid |= NFS_ATTR_FATTR_V3;
2131da177e4SLinus Torvalds 	return p;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds 
216d61005a6SAl Viro static inline __be32 *
217d61005a6SAl Viro xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->pre_size);
2201da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->pre_mtime);
2211da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->pre_ctime);
2229e6e70f8STrond Myklebust 	fattr->valid |= NFS_ATTR_FATTR_PRESIZE
2239e6e70f8STrond Myklebust 		| NFS_ATTR_FATTR_PREMTIME
2249e6e70f8STrond Myklebust 		| NFS_ATTR_FATTR_PRECTIME;
2251da177e4SLinus Torvalds 	return p;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
228d61005a6SAl Viro static inline __be32 *
229d61005a6SAl Viro xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	if (*p++)
2321da177e4SLinus Torvalds 		p = xdr_decode_fattr(p, fattr);
2331da177e4SLinus Torvalds 	return p;
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
236d61005a6SAl Viro static inline __be32 *
237babddc72SBryan Schumaker xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
238babddc72SBryan Schumaker {
239babddc72SBryan Schumaker 	__be32 *p;
240babddc72SBryan Schumaker 
241babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
242babddc72SBryan Schumaker 	if (unlikely(!p))
243babddc72SBryan Schumaker 		goto out_overflow;
244babddc72SBryan Schumaker 	if (ntohl(*p++)) {
245babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, 84);
246babddc72SBryan Schumaker 		if (unlikely(!p))
247babddc72SBryan Schumaker 			goto out_overflow;
248babddc72SBryan Schumaker 		p = xdr_decode_fattr(p, fattr);
249babddc72SBryan Schumaker 	}
250babddc72SBryan Schumaker 	return p;
251babddc72SBryan Schumaker out_overflow:
252babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
253babddc72SBryan Schumaker 	return ERR_PTR(-EIO);
254babddc72SBryan Schumaker }
255babddc72SBryan Schumaker 
256babddc72SBryan Schumaker static inline __be32 *
257d61005a6SAl Viro xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
2581da177e4SLinus Torvalds {
2591da177e4SLinus Torvalds 	if (*p++)
2601da177e4SLinus Torvalds 		return xdr_decode_wcc_attr(p, fattr);
2611da177e4SLinus Torvalds 	return p;
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 
265d61005a6SAl Viro static inline __be32 *
266d61005a6SAl Viro xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds 	p = xdr_decode_pre_op_attr(p, fattr);
2691da177e4SLinus Torvalds 	return xdr_decode_post_op_attr(p, fattr);
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
272d9c407b1SChuck Lever 
273d9c407b1SChuck Lever /*
274d9c407b1SChuck Lever  * Encode/decode NFSv3 basic data types
275d9c407b1SChuck Lever  *
276d9c407b1SChuck Lever  * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
277d9c407b1SChuck Lever  * "NFS Version 3 Protocol Specification".
278d9c407b1SChuck Lever  *
279d9c407b1SChuck Lever  * Not all basic data types have their own encoding and decoding
280d9c407b1SChuck Lever  * functions.  For run-time efficiency, some data types are encoded
281d9c407b1SChuck Lever  * or decoded inline.
282d9c407b1SChuck Lever  */
283d9c407b1SChuck Lever 
284d9c407b1SChuck Lever static void encode_uint32(struct xdr_stream *xdr, u32 value)
285d9c407b1SChuck Lever {
286d9c407b1SChuck Lever 	__be32 *p = xdr_reserve_space(xdr, 4);
287d9c407b1SChuck Lever 	*p = cpu_to_be32(value);
288d9c407b1SChuck Lever }
289d9c407b1SChuck Lever 
290e4f93234SChuck Lever static int decode_uint32(struct xdr_stream *xdr, u32 *value)
291e4f93234SChuck Lever {
292e4f93234SChuck Lever 	__be32 *p;
293e4f93234SChuck Lever 
294e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
295e4f93234SChuck Lever 	if (unlikely(p == NULL))
296e4f93234SChuck Lever 		goto out_overflow;
297e4f93234SChuck Lever 	*value = be32_to_cpup(p);
298e4f93234SChuck Lever 	return 0;
299e4f93234SChuck Lever out_overflow:
300e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
301e4f93234SChuck Lever 	return -EIO;
302e4f93234SChuck Lever }
303e4f93234SChuck Lever 
304e4f93234SChuck Lever static int decode_uint64(struct xdr_stream *xdr, u64 *value)
305e4f93234SChuck Lever {
306e4f93234SChuck Lever 	__be32 *p;
307e4f93234SChuck Lever 
308e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8);
309e4f93234SChuck Lever 	if (unlikely(p == NULL))
310e4f93234SChuck Lever 		goto out_overflow;
311e4f93234SChuck Lever 	xdr_decode_hyper(p, value);
312e4f93234SChuck Lever 	return 0;
313e4f93234SChuck Lever out_overflow:
314e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
315e4f93234SChuck Lever 	return -EIO;
316e4f93234SChuck Lever }
317e4f93234SChuck Lever 
318e4f93234SChuck Lever /*
319e4f93234SChuck Lever  * fileid3
320e4f93234SChuck Lever  *
321e4f93234SChuck Lever  *	typedef uint64 fileid3;
322e4f93234SChuck Lever  */
323e4f93234SChuck Lever static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid)
324e4f93234SChuck Lever {
325e4f93234SChuck Lever 	return decode_uint64(xdr, fileid);
326e4f93234SChuck Lever }
327e4f93234SChuck Lever 
328d9c407b1SChuck Lever /*
329d9c407b1SChuck Lever  * filename3
330d9c407b1SChuck Lever  *
331d9c407b1SChuck Lever  *	typedef string filename3<>;
332d9c407b1SChuck Lever  */
333d9c407b1SChuck Lever static void encode_filename3(struct xdr_stream *xdr,
334d9c407b1SChuck Lever 			     const char *name, u32 length)
335d9c407b1SChuck Lever {
336d9c407b1SChuck Lever 	__be32 *p;
337d9c407b1SChuck Lever 
338d9c407b1SChuck Lever 	BUG_ON(length > NFS3_MAXNAMLEN);
339d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
340d9c407b1SChuck Lever 	xdr_encode_opaque(p, name, length);
341d9c407b1SChuck Lever }
342d9c407b1SChuck Lever 
343e4f93234SChuck Lever static int decode_inline_filename3(struct xdr_stream *xdr,
344e4f93234SChuck Lever 				   const char **name, u32 *length)
345e4f93234SChuck Lever {
346e4f93234SChuck Lever 	__be32 *p;
347e4f93234SChuck Lever 	u32 count;
348e4f93234SChuck Lever 
349e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
350e4f93234SChuck Lever 	if (unlikely(p == NULL))
351e4f93234SChuck Lever 		goto out_overflow;
352e4f93234SChuck Lever 	count = be32_to_cpup(p);
353e4f93234SChuck Lever 	if (count > NFS3_MAXNAMLEN)
354e4f93234SChuck Lever 		goto out_nametoolong;
355e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, count);
356e4f93234SChuck Lever 	if (unlikely(p == NULL))
357e4f93234SChuck Lever 		goto out_overflow;
358e4f93234SChuck Lever 	*name = (const char *)p;
359e4f93234SChuck Lever 	*length = count;
360e4f93234SChuck Lever 	return 0;
361e4f93234SChuck Lever 
362e4f93234SChuck Lever out_nametoolong:
363e4f93234SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
364e4f93234SChuck Lever 	return -ENAMETOOLONG;
365e4f93234SChuck Lever out_overflow:
366e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
367e4f93234SChuck Lever 	return -EIO;
368e4f93234SChuck Lever }
369e4f93234SChuck Lever 
370d9c407b1SChuck Lever /*
371d9c407b1SChuck Lever  * nfspath3
372d9c407b1SChuck Lever  *
373d9c407b1SChuck Lever  *	typedef string nfspath3<>;
374d9c407b1SChuck Lever  */
375d9c407b1SChuck Lever static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
376d9c407b1SChuck Lever 			    const u32 length)
377d9c407b1SChuck Lever {
378d9c407b1SChuck Lever 	BUG_ON(length > NFS3_MAXPATHLEN);
379d9c407b1SChuck Lever 	encode_uint32(xdr, length);
380d9c407b1SChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
381d9c407b1SChuck Lever }
382d9c407b1SChuck Lever 
383e4f93234SChuck Lever static int decode_nfspath3(struct xdr_stream *xdr)
384e4f93234SChuck Lever {
385e4f93234SChuck Lever 	u32 recvd, count;
386e4f93234SChuck Lever 	size_t hdrlen;
387e4f93234SChuck Lever 	__be32 *p;
388e4f93234SChuck Lever 
389e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
390e4f93234SChuck Lever 	if (unlikely(p == NULL))
391e4f93234SChuck Lever 		goto out_overflow;
392e4f93234SChuck Lever 	count = be32_to_cpup(p);
393e4f93234SChuck Lever 	if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
394e4f93234SChuck Lever 		goto out_nametoolong;
395e4f93234SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
396e4f93234SChuck Lever 	recvd = xdr->buf->len - hdrlen;
397e4f93234SChuck Lever 	if (unlikely(count > recvd))
398e4f93234SChuck Lever 		goto out_cheating;
399e4f93234SChuck Lever 
400e4f93234SChuck Lever 	xdr_read_pages(xdr, count);
401e4f93234SChuck Lever 	xdr_terminate_string(xdr->buf, count);
402e4f93234SChuck Lever 	return 0;
403e4f93234SChuck Lever 
404e4f93234SChuck Lever out_nametoolong:
405e4f93234SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", count);
406e4f93234SChuck Lever 	return -ENAMETOOLONG;
407e4f93234SChuck Lever out_cheating:
408e4f93234SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
409e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
410e4f93234SChuck Lever 	return -EIO;
411e4f93234SChuck Lever out_overflow:
412e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
413e4f93234SChuck Lever 	return -EIO;
414e4f93234SChuck Lever }
415e4f93234SChuck Lever 
416d9c407b1SChuck Lever /*
417d9c407b1SChuck Lever  * cookie3
418d9c407b1SChuck Lever  *
419d9c407b1SChuck Lever  *	typedef uint64 cookie3
420d9c407b1SChuck Lever  */
421d9c407b1SChuck Lever static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
422d9c407b1SChuck Lever {
423d9c407b1SChuck Lever 	return xdr_encode_hyper(p, cookie);
424d9c407b1SChuck Lever }
425d9c407b1SChuck Lever 
426e4f93234SChuck Lever static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie)
427e4f93234SChuck Lever {
428e4f93234SChuck Lever 	return decode_uint64(xdr, cookie);
429e4f93234SChuck Lever }
430e4f93234SChuck Lever 
431d9c407b1SChuck Lever /*
432d9c407b1SChuck Lever  * cookieverf3
433d9c407b1SChuck Lever  *
434d9c407b1SChuck Lever  *	typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
435d9c407b1SChuck Lever  */
436d9c407b1SChuck Lever static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
437d9c407b1SChuck Lever {
438d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
439d9c407b1SChuck Lever 	return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
440d9c407b1SChuck Lever }
441d9c407b1SChuck Lever 
442e4f93234SChuck Lever static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier)
443e4f93234SChuck Lever {
444e4f93234SChuck Lever 	__be32 *p;
445e4f93234SChuck Lever 
446e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
447e4f93234SChuck Lever 	if (unlikely(p == NULL))
448e4f93234SChuck Lever 		goto out_overflow;
449e4f93234SChuck Lever 	memcpy(verifier, p, NFS3_COOKIEVERFSIZE);
450e4f93234SChuck Lever 	return 0;
451e4f93234SChuck Lever out_overflow:
452e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
453e4f93234SChuck Lever 	return -EIO;
454e4f93234SChuck Lever }
455e4f93234SChuck Lever 
456d9c407b1SChuck Lever /*
457d9c407b1SChuck Lever  * createverf3
458d9c407b1SChuck Lever  *
459d9c407b1SChuck Lever  *	typedef opaque createverf3[NFS3_CREATEVERFSIZE];
460d9c407b1SChuck Lever  */
461d9c407b1SChuck Lever static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
462d9c407b1SChuck Lever {
463d9c407b1SChuck Lever 	__be32 *p;
464d9c407b1SChuck Lever 
465d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
466d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_CREATEVERFSIZE);
467d9c407b1SChuck Lever }
468d9c407b1SChuck Lever 
469e4f93234SChuck Lever static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier)
470e4f93234SChuck Lever {
471e4f93234SChuck Lever 	__be32 *p;
472e4f93234SChuck Lever 
473e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
474e4f93234SChuck Lever 	if (unlikely(p == NULL))
475e4f93234SChuck Lever 		goto out_overflow;
476e4f93234SChuck Lever 	memcpy(verifier, p, NFS3_WRITEVERFSIZE);
477e4f93234SChuck Lever 	return 0;
478e4f93234SChuck Lever out_overflow:
479e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
480e4f93234SChuck Lever 	return -EIO;
481e4f93234SChuck Lever }
482e4f93234SChuck Lever 
483e4f93234SChuck Lever /*
484e4f93234SChuck Lever  * size3
485e4f93234SChuck Lever  *
486e4f93234SChuck Lever  *	typedef uint64 size3;
487e4f93234SChuck Lever  */
488e4f93234SChuck Lever static __be32 *xdr_decode_size3(__be32 *p, u64 *size)
489e4f93234SChuck Lever {
490e4f93234SChuck Lever 	return xdr_decode_hyper(p, size);
491e4f93234SChuck Lever }
492e4f93234SChuck Lever 
493e4f93234SChuck Lever /*
494e4f93234SChuck Lever  * nfsstat3
495e4f93234SChuck Lever  *
496e4f93234SChuck Lever  *	enum nfsstat3 {
497e4f93234SChuck Lever  *		NFS3_OK = 0,
498e4f93234SChuck Lever  *		...
499e4f93234SChuck Lever  *	}
500e4f93234SChuck Lever  */
501e4f93234SChuck Lever #define NFS3_OK		NFS_OK
502e4f93234SChuck Lever 
503e4f93234SChuck Lever static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status)
504e4f93234SChuck Lever {
505e4f93234SChuck Lever 	__be32 *p;
506e4f93234SChuck Lever 
507e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
508e4f93234SChuck Lever 	if (unlikely(p == NULL))
509e4f93234SChuck Lever 		goto out_overflow;
510e4f93234SChuck Lever 	*status = be32_to_cpup(p);
511e4f93234SChuck Lever 	return 0;
512e4f93234SChuck Lever out_overflow:
513e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
514e4f93234SChuck Lever 	return -EIO;
515e4f93234SChuck Lever }
516e4f93234SChuck Lever 
517d9c407b1SChuck Lever /*
518d9c407b1SChuck Lever  * ftype3
519d9c407b1SChuck Lever  *
520d9c407b1SChuck Lever  *	enum ftype3 {
521d9c407b1SChuck Lever  *		NF3REG	= 1,
522d9c407b1SChuck Lever  *		NF3DIR	= 2,
523d9c407b1SChuck Lever  *		NF3BLK	= 3,
524d9c407b1SChuck Lever  *		NF3CHR	= 4,
525d9c407b1SChuck Lever  *		NF3LNK	= 5,
526d9c407b1SChuck Lever  *		NF3SOCK	= 6,
527d9c407b1SChuck Lever  *		NF3FIFO	= 7
528d9c407b1SChuck Lever  *	};
529d9c407b1SChuck Lever  */
530d9c407b1SChuck Lever static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
531d9c407b1SChuck Lever {
532d9c407b1SChuck Lever 	BUG_ON(type > NF3FIFO);
533d9c407b1SChuck Lever 	encode_uint32(xdr, type);
534d9c407b1SChuck Lever }
535d9c407b1SChuck Lever 
536d9c407b1SChuck Lever /*
537d9c407b1SChuck Lever  * specdata3
538d9c407b1SChuck Lever  *
539d9c407b1SChuck Lever  *     struct specdata3 {
540d9c407b1SChuck Lever  *             uint32  specdata1;
541d9c407b1SChuck Lever  *             uint32  specdata2;
542d9c407b1SChuck Lever  *     };
543d9c407b1SChuck Lever  */
544d9c407b1SChuck Lever static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
545d9c407b1SChuck Lever {
546d9c407b1SChuck Lever 	__be32 *p;
547d9c407b1SChuck Lever 
548d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8);
549d9c407b1SChuck Lever 	*p++ = cpu_to_be32(MAJOR(rdev));
550d9c407b1SChuck Lever 	*p = cpu_to_be32(MINOR(rdev));
551d9c407b1SChuck Lever }
552d9c407b1SChuck Lever 
553d9c407b1SChuck Lever /*
554d9c407b1SChuck Lever  * nfs_fh3
555d9c407b1SChuck Lever  *
556d9c407b1SChuck Lever  *	struct nfs_fh3 {
557d9c407b1SChuck Lever  *		opaque       data<NFS3_FHSIZE>;
558d9c407b1SChuck Lever  *	};
559d9c407b1SChuck Lever  */
560d9c407b1SChuck Lever static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
561d9c407b1SChuck Lever {
562d9c407b1SChuck Lever 	__be32 *p;
563d9c407b1SChuck Lever 
564d9c407b1SChuck Lever 	BUG_ON(fh->size > NFS3_FHSIZE);
565d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + fh->size);
566d9c407b1SChuck Lever 	xdr_encode_opaque(p, fh->data, fh->size);
567d9c407b1SChuck Lever }
568d9c407b1SChuck Lever 
569e4f93234SChuck Lever static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
570e4f93234SChuck Lever {
571e4f93234SChuck Lever 	u32 length;
572e4f93234SChuck Lever 	__be32 *p;
573e4f93234SChuck Lever 
574e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
575e4f93234SChuck Lever 	if (unlikely(p == NULL))
576e4f93234SChuck Lever 		goto out_overflow;
577e4f93234SChuck Lever 	length = be32_to_cpup(p++);
578e4f93234SChuck Lever 	if (unlikely(length > NFS3_FHSIZE))
579e4f93234SChuck Lever 		goto out_toobig;
580e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, length);
581e4f93234SChuck Lever 	if (unlikely(p == NULL))
582e4f93234SChuck Lever 		goto out_overflow;
583e4f93234SChuck Lever 	fh->size = length;
584e4f93234SChuck Lever 	memcpy(fh->data, p, length);
585e4f93234SChuck Lever 	return 0;
586e4f93234SChuck Lever out_toobig:
587e4f93234SChuck Lever 	dprintk("NFS: file handle size (%u) too big\n", length);
588e4f93234SChuck Lever 	return -E2BIG;
589e4f93234SChuck Lever out_overflow:
590e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
591e4f93234SChuck Lever 	return -EIO;
592e4f93234SChuck Lever }
593e4f93234SChuck Lever 
594e4f93234SChuck Lever static void zero_nfs_fh3(struct nfs_fh *fh)
595e4f93234SChuck Lever {
596e4f93234SChuck Lever 	memset(fh, 0, sizeof(*fh));
597e4f93234SChuck Lever }
598e4f93234SChuck Lever 
599d9c407b1SChuck Lever /*
6009d5a6434SChuck Lever  * nfstime3
6019d5a6434SChuck Lever  *
6029d5a6434SChuck Lever  *	struct nfstime3 {
6039d5a6434SChuck Lever  *		uint32	seconds;
6049d5a6434SChuck Lever  *		uint32	nseconds;
6059d5a6434SChuck Lever  *	};
6069d5a6434SChuck Lever  */
6079d5a6434SChuck Lever static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec *timep)
6089d5a6434SChuck Lever {
6099d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
6109d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_nsec);
6119d5a6434SChuck Lever 	return p;
6129d5a6434SChuck Lever }
6139d5a6434SChuck Lever 
6149d5a6434SChuck Lever /*
615d9c407b1SChuck Lever  * sattr3
616d9c407b1SChuck Lever  *
617d9c407b1SChuck Lever  *	enum time_how {
618d9c407b1SChuck Lever  *		DONT_CHANGE		= 0,
619d9c407b1SChuck Lever  *		SET_TO_SERVER_TIME	= 1,
620d9c407b1SChuck Lever  *		SET_TO_CLIENT_TIME	= 2
621d9c407b1SChuck Lever  *	};
622d9c407b1SChuck Lever  *
623d9c407b1SChuck Lever  *	union set_mode3 switch (bool set_it) {
624d9c407b1SChuck Lever  *	case TRUE:
625d9c407b1SChuck Lever  *		mode3	mode;
626d9c407b1SChuck Lever  *	default:
627d9c407b1SChuck Lever  *		void;
628d9c407b1SChuck Lever  *	};
629d9c407b1SChuck Lever  *
630d9c407b1SChuck Lever  *	union set_uid3 switch (bool set_it) {
631d9c407b1SChuck Lever  *	case TRUE:
632d9c407b1SChuck Lever  *		uid3	uid;
633d9c407b1SChuck Lever  *	default:
634d9c407b1SChuck Lever  *		void;
635d9c407b1SChuck Lever  *	};
636d9c407b1SChuck Lever  *
637d9c407b1SChuck Lever  *	union set_gid3 switch (bool set_it) {
638d9c407b1SChuck Lever  *	case TRUE:
639d9c407b1SChuck Lever  *		gid3	gid;
640d9c407b1SChuck Lever  *	default:
641d9c407b1SChuck Lever  *		void;
642d9c407b1SChuck Lever  *	};
643d9c407b1SChuck Lever  *
644d9c407b1SChuck Lever  *	union set_size3 switch (bool set_it) {
645d9c407b1SChuck Lever  *	case TRUE:
646d9c407b1SChuck Lever  *		size3	size;
647d9c407b1SChuck Lever  *	default:
648d9c407b1SChuck Lever  *		void;
649d9c407b1SChuck Lever  *	};
650d9c407b1SChuck Lever  *
651d9c407b1SChuck Lever  *	union set_atime switch (time_how set_it) {
652d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
653d9c407b1SChuck Lever  *		nfstime3	atime;
654d9c407b1SChuck Lever  *	default:
655d9c407b1SChuck Lever  *		void;
656d9c407b1SChuck Lever  *	};
657d9c407b1SChuck Lever  *
658d9c407b1SChuck Lever  *	union set_mtime switch (time_how set_it) {
659d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
660d9c407b1SChuck Lever  *		nfstime3  mtime;
661d9c407b1SChuck Lever  *	default:
662d9c407b1SChuck Lever  *		void;
663d9c407b1SChuck Lever  *	};
664d9c407b1SChuck Lever  *
665d9c407b1SChuck Lever  *	struct sattr3 {
666d9c407b1SChuck Lever  *		set_mode3	mode;
667d9c407b1SChuck Lever  *		set_uid3	uid;
668d9c407b1SChuck Lever  *		set_gid3	gid;
669d9c407b1SChuck Lever  *		set_size3	size;
670d9c407b1SChuck Lever  *		set_atime	atime;
671d9c407b1SChuck Lever  *		set_mtime	mtime;
672d9c407b1SChuck Lever  *	};
673d9c407b1SChuck Lever  */
674d9c407b1SChuck Lever static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
675d9c407b1SChuck Lever {
676d9c407b1SChuck Lever 	u32 nbytes;
677d9c407b1SChuck Lever 	__be32 *p;
678d9c407b1SChuck Lever 
679d9c407b1SChuck Lever 	/*
680d9c407b1SChuck Lever 	 * In order to make only a single xdr_reserve_space() call,
681d9c407b1SChuck Lever 	 * pre-compute the total number of bytes to be reserved.
682d9c407b1SChuck Lever 	 * Six boolean values, one for each set_foo field, are always
683d9c407b1SChuck Lever 	 * present in the encoded result, so start there.
684d9c407b1SChuck Lever 	 */
685d9c407b1SChuck Lever 	nbytes = 6 * 4;
686d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MODE)
687d9c407b1SChuck Lever 		nbytes += 4;
688d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_UID)
689d9c407b1SChuck Lever 		nbytes += 4;
690d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_GID)
691d9c407b1SChuck Lever 		nbytes += 4;
692d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
693d9c407b1SChuck Lever 		nbytes += 8;
694d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
695d9c407b1SChuck Lever 		nbytes += 8;
696d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
697d9c407b1SChuck Lever 		nbytes += 8;
698d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, nbytes);
699d9c407b1SChuck Lever 
7009d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MODE) {
7019d5a6434SChuck Lever 		*p++ = xdr_one;
7029d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
7039d5a6434SChuck Lever 	} else
7049d5a6434SChuck Lever 		*p++ = xdr_zero;
7059d5a6434SChuck Lever 
7069d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_UID) {
7079d5a6434SChuck Lever 		*p++ = xdr_one;
7089d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_uid);
7099d5a6434SChuck Lever 	} else
7109d5a6434SChuck Lever 		*p++ = xdr_zero;
7119d5a6434SChuck Lever 
7129d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_GID) {
7139d5a6434SChuck Lever 		*p++ = xdr_one;
7149d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_gid);
7159d5a6434SChuck Lever 	} else
7169d5a6434SChuck Lever 		*p++ = xdr_zero;
7179d5a6434SChuck Lever 
7189d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_SIZE) {
7199d5a6434SChuck Lever 		*p++ = xdr_one;
7209d5a6434SChuck Lever 		p = xdr_encode_hyper(p, (u64)attr->ia_size);
7219d5a6434SChuck Lever 	} else
7229d5a6434SChuck Lever 		*p++ = xdr_zero;
7239d5a6434SChuck Lever 
7249d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET) {
7259d5a6434SChuck Lever 		*p++ = xdr_two;
7269d5a6434SChuck Lever 		p = xdr_encode_nfstime3(p, &attr->ia_atime);
7279d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_ATIME) {
7289d5a6434SChuck Lever 		*p++ = xdr_one;
7299d5a6434SChuck Lever 	} else
7309d5a6434SChuck Lever 		*p++ = xdr_zero;
7319d5a6434SChuck Lever 
7329d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET) {
7339d5a6434SChuck Lever 		*p++ = xdr_two;
7349d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &attr->ia_mtime);
7359d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_MTIME) {
7369d5a6434SChuck Lever 		*p = xdr_one;
7379d5a6434SChuck Lever 	} else
7389d5a6434SChuck Lever 		*p = xdr_zero;
739d9c407b1SChuck Lever }
740d9c407b1SChuck Lever 
741d9c407b1SChuck Lever /*
742e4f93234SChuck Lever  * fattr3
743e4f93234SChuck Lever  *
744e4f93234SChuck Lever  *	struct fattr3 {
745e4f93234SChuck Lever  *		ftype3		type;
746e4f93234SChuck Lever  *		mode3		mode;
747e4f93234SChuck Lever  *		uint32		nlink;
748e4f93234SChuck Lever  *		uid3		uid;
749e4f93234SChuck Lever  *		gid3		gid;
750e4f93234SChuck Lever  *		size3		size;
751e4f93234SChuck Lever  *		size3		used;
752e4f93234SChuck Lever  *		specdata3	rdev;
753e4f93234SChuck Lever  *		uint64		fsid;
754e4f93234SChuck Lever  *		fileid3		fileid;
755e4f93234SChuck Lever  *		nfstime3	atime;
756e4f93234SChuck Lever  *		nfstime3	mtime;
757e4f93234SChuck Lever  *		nfstime3	ctime;
758e4f93234SChuck Lever  *	};
759e4f93234SChuck Lever  */
760e4f93234SChuck Lever static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
761e4f93234SChuck Lever {
762e4f93234SChuck Lever 	__be32 *p;
763e4f93234SChuck Lever 
764e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2);
765e4f93234SChuck Lever 	if (unlikely(p == NULL))
766e4f93234SChuck Lever 		goto out_overflow;
767e4f93234SChuck Lever 	xdr_decode_fattr(p, fattr);
768e4f93234SChuck Lever 	return 0;
769e4f93234SChuck Lever out_overflow:
770e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
771e4f93234SChuck Lever 	return -EIO;
772e4f93234SChuck Lever }
773e4f93234SChuck Lever 
774e4f93234SChuck Lever /*
775e4f93234SChuck Lever  * post_op_attr
776e4f93234SChuck Lever  *
777e4f93234SChuck Lever  *	union post_op_attr switch (bool attributes_follow) {
778e4f93234SChuck Lever  *	case TRUE:
779e4f93234SChuck Lever  *		fattr3	attributes;
780e4f93234SChuck Lever  *	case FALSE:
781e4f93234SChuck Lever  *		void;
782e4f93234SChuck Lever  *	};
783e4f93234SChuck Lever  */
784e4f93234SChuck Lever static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
785e4f93234SChuck Lever {
786e4f93234SChuck Lever 	__be32 *p;
787e4f93234SChuck Lever 
788e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
789e4f93234SChuck Lever 	if (unlikely(p == NULL))
790e4f93234SChuck Lever 		goto out_overflow;
791e4f93234SChuck Lever 	if (*p != xdr_zero)
792e4f93234SChuck Lever 		return decode_fattr3(xdr, fattr);
793e4f93234SChuck Lever 	return 0;
794e4f93234SChuck Lever out_overflow:
795e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
796e4f93234SChuck Lever 	return -EIO;
797e4f93234SChuck Lever }
798e4f93234SChuck Lever 
799e4f93234SChuck Lever /*
800e4f93234SChuck Lever  * wcc_attr
801e4f93234SChuck Lever  *	struct wcc_attr {
802e4f93234SChuck Lever  *		size3		size;
803e4f93234SChuck Lever  *		nfstime3	mtime;
804e4f93234SChuck Lever  *		nfstime3	ctime;
805e4f93234SChuck Lever  *	};
806e4f93234SChuck Lever  */
807e4f93234SChuck Lever static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
808e4f93234SChuck Lever {
809e4f93234SChuck Lever 	__be32 *p;
810e4f93234SChuck Lever 
811e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2);
812e4f93234SChuck Lever 	if (unlikely(p == NULL))
813e4f93234SChuck Lever 		goto out_overflow;
814e4f93234SChuck Lever 	xdr_decode_wcc_attr(p, fattr);
815e4f93234SChuck Lever 	return 0;
816e4f93234SChuck Lever out_overflow:
817e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
818e4f93234SChuck Lever 	return -EIO;
819e4f93234SChuck Lever }
820e4f93234SChuck Lever 
821e4f93234SChuck Lever /*
822e4f93234SChuck Lever  * pre_op_attr
823e4f93234SChuck Lever  *	union pre_op_attr switch (bool attributes_follow) {
824e4f93234SChuck Lever  *	case TRUE:
825e4f93234SChuck Lever  *		wcc_attr	attributes;
826e4f93234SChuck Lever  *	case FALSE:
827e4f93234SChuck Lever  *		void;
828e4f93234SChuck Lever  *	};
829e4f93234SChuck Lever  *
830e4f93234SChuck Lever  * wcc_data
831e4f93234SChuck Lever  *
832e4f93234SChuck Lever  *	struct wcc_data {
833e4f93234SChuck Lever  *		pre_op_attr	before;
834e4f93234SChuck Lever  *		post_op_attr	after;
835e4f93234SChuck Lever  *	};
836e4f93234SChuck Lever  */
837e4f93234SChuck Lever static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
838e4f93234SChuck Lever {
839e4f93234SChuck Lever 	__be32 *p;
840e4f93234SChuck Lever 
841e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
842e4f93234SChuck Lever 	if (unlikely(p == NULL))
843e4f93234SChuck Lever 		goto out_overflow;
844e4f93234SChuck Lever 	if (*p != xdr_zero)
845e4f93234SChuck Lever 		return decode_wcc_attr(xdr, fattr);
846e4f93234SChuck Lever 	return 0;
847e4f93234SChuck Lever out_overflow:
848e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
849e4f93234SChuck Lever 	return -EIO;
850e4f93234SChuck Lever }
851e4f93234SChuck Lever 
852e4f93234SChuck Lever static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr)
853e4f93234SChuck Lever {
854e4f93234SChuck Lever 	int error;
855e4f93234SChuck Lever 
856e4f93234SChuck Lever 	error = decode_pre_op_attr(xdr, fattr);
857e4f93234SChuck Lever 	if (unlikely(error))
858e4f93234SChuck Lever 		goto out;
859e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, fattr);
860e4f93234SChuck Lever out:
861e4f93234SChuck Lever 	return error;
862e4f93234SChuck Lever }
863e4f93234SChuck Lever 
864e4f93234SChuck Lever /*
865e4f93234SChuck Lever  * post_op_fh3
866e4f93234SChuck Lever  *
867e4f93234SChuck Lever  *	union post_op_fh3 switch (bool handle_follows) {
868e4f93234SChuck Lever  *	case TRUE:
869e4f93234SChuck Lever  *		nfs_fh3  handle;
870e4f93234SChuck Lever  *	case FALSE:
871e4f93234SChuck Lever  *		void;
872e4f93234SChuck Lever  *	};
873e4f93234SChuck Lever  */
874e4f93234SChuck Lever static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
875e4f93234SChuck Lever {
876e4f93234SChuck Lever 	__be32 *p = xdr_inline_decode(xdr, 4);
877e4f93234SChuck Lever 	if (unlikely(p == NULL))
878e4f93234SChuck Lever 		goto out_overflow;
879e4f93234SChuck Lever 	if (*p != xdr_zero)
880e4f93234SChuck Lever 		return decode_nfs_fh3(xdr, fh);
881e4f93234SChuck Lever 	zero_nfs_fh3(fh);
882e4f93234SChuck Lever 	return 0;
883e4f93234SChuck Lever out_overflow:
884e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
885e4f93234SChuck Lever 	return -EIO;
886e4f93234SChuck Lever }
887e4f93234SChuck Lever 
888e4f93234SChuck Lever /*
889d9c407b1SChuck Lever  * diropargs3
890d9c407b1SChuck Lever  *
891d9c407b1SChuck Lever  *	struct diropargs3 {
892d9c407b1SChuck Lever  *		nfs_fh3		dir;
893d9c407b1SChuck Lever  *		filename3	name;
894d9c407b1SChuck Lever  *	};
895d9c407b1SChuck Lever  */
896d9c407b1SChuck Lever static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
897d9c407b1SChuck Lever 			      const char *name, u32 length)
898d9c407b1SChuck Lever {
899d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, fh);
900d9c407b1SChuck Lever 	encode_filename3(xdr, name, length);
901d9c407b1SChuck Lever }
902d9c407b1SChuck Lever 
903d9c407b1SChuck Lever 
9041da177e4SLinus Torvalds /*
905499ff710SChuck Lever  * NFSv3 XDR encode functions
906499ff710SChuck Lever  *
907499ff710SChuck Lever  * NFSv3 argument types are defined in section 3.3 of RFC 1813:
908499ff710SChuck Lever  * "NFS Version 3 Protocol Specification".
9091da177e4SLinus Torvalds  */
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds /*
912d9c407b1SChuck Lever  * 3.3.1  GETATTR3args
913d9c407b1SChuck Lever  *
914d9c407b1SChuck Lever  *	struct GETATTR3args {
915d9c407b1SChuck Lever  *		nfs_fh3  object;
916d9c407b1SChuck Lever  *	};
917d9c407b1SChuck Lever  */
918d9c407b1SChuck Lever static int nfs3_xdr_enc_getattr3args(struct rpc_rqst *req, __be32 *p,
919d9c407b1SChuck Lever 				     const struct nfs_fh *fh)
920d9c407b1SChuck Lever {
921d9c407b1SChuck Lever 	struct xdr_stream xdr;
922d9c407b1SChuck Lever 
923d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
924d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, fh);
925d9c407b1SChuck Lever 	return 0;
926d9c407b1SChuck Lever }
927d9c407b1SChuck Lever 
928d9c407b1SChuck Lever /*
929d9c407b1SChuck Lever  * 3.3.2  SETATTR3args
930d9c407b1SChuck Lever  *
931d9c407b1SChuck Lever  *	union sattrguard3 switch (bool check) {
932d9c407b1SChuck Lever  *	case TRUE:
933d9c407b1SChuck Lever  *		nfstime3  obj_ctime;
934d9c407b1SChuck Lever  *	case FALSE:
935d9c407b1SChuck Lever  *		void;
936d9c407b1SChuck Lever  *	};
937d9c407b1SChuck Lever  *
938d9c407b1SChuck Lever  *	struct SETATTR3args {
939d9c407b1SChuck Lever  *		nfs_fh3		object;
940d9c407b1SChuck Lever  *		sattr3		new_attributes;
941d9c407b1SChuck Lever  *		sattrguard3	guard;
942d9c407b1SChuck Lever  *	};
943d9c407b1SChuck Lever  */
944d9c407b1SChuck Lever static void encode_sattrguard3(struct xdr_stream *xdr,
945d9c407b1SChuck Lever 			       const struct nfs3_sattrargs *args)
946d9c407b1SChuck Lever {
947d9c407b1SChuck Lever 	__be32 *p;
948d9c407b1SChuck Lever 
949d9c407b1SChuck Lever 	if (args->guard) {
950d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4 + 8);
951d9c407b1SChuck Lever 		*p++ = xdr_one;
9529d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &args->guardtime);
953d9c407b1SChuck Lever 	} else {
954d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4);
955d9c407b1SChuck Lever 		*p = xdr_zero;
956d9c407b1SChuck Lever 	}
957d9c407b1SChuck Lever }
958d9c407b1SChuck Lever 
959d9c407b1SChuck Lever static int nfs3_xdr_enc_setattr3args(struct rpc_rqst *req, __be32 *p,
960d9c407b1SChuck Lever 				     const struct nfs3_sattrargs *args)
961d9c407b1SChuck Lever {
962d9c407b1SChuck Lever 	struct xdr_stream xdr;
963d9c407b1SChuck Lever 
964d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
965d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fh);
966d9c407b1SChuck Lever 	encode_sattr3(&xdr, args->sattr);
967d9c407b1SChuck Lever 	encode_sattrguard3(&xdr, args);
968d9c407b1SChuck Lever 	return 0;
969d9c407b1SChuck Lever }
970d9c407b1SChuck Lever 
971d9c407b1SChuck Lever /*
972d9c407b1SChuck Lever  * 3.3.3  LOOKUP3args
973d9c407b1SChuck Lever  *
974d9c407b1SChuck Lever  *	struct LOOKUP3args {
975d9c407b1SChuck Lever  *		diropargs3  what;
976d9c407b1SChuck Lever  *	};
977d9c407b1SChuck Lever  */
978d9c407b1SChuck Lever static int nfs3_xdr_enc_lookup3args(struct rpc_rqst *req, __be32 *p,
979d9c407b1SChuck Lever 				    const struct nfs3_diropargs *args)
980d9c407b1SChuck Lever {
981d9c407b1SChuck Lever 	struct xdr_stream xdr;
982d9c407b1SChuck Lever 
983d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
984d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
985d9c407b1SChuck Lever 	return 0;
986d9c407b1SChuck Lever }
987d9c407b1SChuck Lever 
988d9c407b1SChuck Lever /*
989d9c407b1SChuck Lever  * 3.3.4  ACCESS3args
990d9c407b1SChuck Lever  *
991d9c407b1SChuck Lever  *	struct ACCESS3args {
992d9c407b1SChuck Lever  *		nfs_fh3		object;
993d9c407b1SChuck Lever  *		uint32		access;
994d9c407b1SChuck Lever  *	};
995d9c407b1SChuck Lever  */
996d9c407b1SChuck Lever static void encode_access3args(struct xdr_stream *xdr,
997d9c407b1SChuck Lever 			       const struct nfs3_accessargs *args)
998d9c407b1SChuck Lever {
999d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1000d9c407b1SChuck Lever 	encode_uint32(xdr, args->access);
1001d9c407b1SChuck Lever }
1002d9c407b1SChuck Lever 
1003d9c407b1SChuck Lever static int nfs3_xdr_enc_access3args(struct rpc_rqst *req, __be32 *p,
1004d9c407b1SChuck Lever 				    const struct nfs3_accessargs *args)
1005d9c407b1SChuck Lever {
1006d9c407b1SChuck Lever 	struct xdr_stream xdr;
1007d9c407b1SChuck Lever 
1008d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1009d9c407b1SChuck Lever 	encode_access3args(&xdr, args);
1010d9c407b1SChuck Lever 	return 0;
1011d9c407b1SChuck Lever }
1012d9c407b1SChuck Lever 
1013d9c407b1SChuck Lever /*
1014d9c407b1SChuck Lever  * 3.3.5  READLINK3args
1015d9c407b1SChuck Lever  *
1016d9c407b1SChuck Lever  *	struct READLINK3args {
1017d9c407b1SChuck Lever  *		nfs_fh3	symlink;
1018d9c407b1SChuck Lever  *	};
1019d9c407b1SChuck Lever  */
1020d9c407b1SChuck Lever static int nfs3_xdr_enc_readlink3args(struct rpc_rqst *req, __be32 *p,
1021d9c407b1SChuck Lever 				      const struct nfs3_readlinkargs *args)
1022d9c407b1SChuck Lever {
1023d9c407b1SChuck Lever 	struct xdr_stream xdr;
1024d9c407b1SChuck Lever 
1025d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1026d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fh);
1027d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
1028d9c407b1SChuck Lever 					args->pglen, NFS3_readlinkres_sz);
1029d9c407b1SChuck Lever 	return 0;
1030d9c407b1SChuck Lever }
1031d9c407b1SChuck Lever 
1032d9c407b1SChuck Lever /*
1033d9c407b1SChuck Lever  * 3.3.6  READ3args
1034d9c407b1SChuck Lever  *
1035d9c407b1SChuck Lever  *	struct READ3args {
1036d9c407b1SChuck Lever  *		nfs_fh3		file;
1037d9c407b1SChuck Lever  *		offset3		offset;
1038d9c407b1SChuck Lever  *		count3		count;
1039d9c407b1SChuck Lever  *	};
1040d9c407b1SChuck Lever  */
1041d9c407b1SChuck Lever static void encode_read3args(struct xdr_stream *xdr,
1042d9c407b1SChuck Lever 			     const struct nfs_readargs *args)
1043d9c407b1SChuck Lever {
1044d9c407b1SChuck Lever 	__be32 *p;
1045d9c407b1SChuck Lever 
1046d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1047d9c407b1SChuck Lever 
1048d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1049d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1050d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1051d9c407b1SChuck Lever }
1052d9c407b1SChuck Lever 
1053d9c407b1SChuck Lever static int nfs3_xdr_enc_read3args(struct rpc_rqst *req, __be32 *p,
1054d9c407b1SChuck Lever 				  const struct nfs_readargs *args)
1055d9c407b1SChuck Lever {
1056d9c407b1SChuck Lever 	struct xdr_stream xdr;
1057d9c407b1SChuck Lever 
1058d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1059d9c407b1SChuck Lever 	encode_read3args(&xdr, args);
1060d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
1061d9c407b1SChuck Lever 					args->count, NFS3_readres_sz);
1062d9c407b1SChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
1063d9c407b1SChuck Lever 	return 0;
1064d9c407b1SChuck Lever }
1065d9c407b1SChuck Lever 
1066d9c407b1SChuck Lever /*
1067d9c407b1SChuck Lever  * 3.3.7  WRITE3args
1068d9c407b1SChuck Lever  *
1069d9c407b1SChuck Lever  *	enum stable_how {
1070d9c407b1SChuck Lever  *		UNSTABLE  = 0,
1071d9c407b1SChuck Lever  *		DATA_SYNC = 1,
1072d9c407b1SChuck Lever  *		FILE_SYNC = 2
1073d9c407b1SChuck Lever  *	};
1074d9c407b1SChuck Lever  *
1075d9c407b1SChuck Lever  *	struct WRITE3args {
1076d9c407b1SChuck Lever  *		nfs_fh3		file;
1077d9c407b1SChuck Lever  *		offset3		offset;
1078d9c407b1SChuck Lever  *		count3		count;
1079d9c407b1SChuck Lever  *		stable_how	stable;
1080d9c407b1SChuck Lever  *		opaque		data<>;
1081d9c407b1SChuck Lever  *	};
1082d9c407b1SChuck Lever  */
1083d9c407b1SChuck Lever static void encode_write3args(struct xdr_stream *xdr,
1084d9c407b1SChuck Lever 			      const struct nfs_writeargs *args)
1085d9c407b1SChuck Lever {
1086d9c407b1SChuck Lever 	__be32 *p;
1087d9c407b1SChuck Lever 
1088d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1089d9c407b1SChuck Lever 
1090d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
1091d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1092d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count);
1093d9c407b1SChuck Lever 
1094d9c407b1SChuck Lever 	BUG_ON(args->stable > NFS_FILE_SYNC);
1095d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->stable);
1096d9c407b1SChuck Lever 
1097d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1098d9c407b1SChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
1099d9c407b1SChuck Lever }
1100d9c407b1SChuck Lever 
1101d9c407b1SChuck Lever static int nfs3_xdr_enc_write3args(struct rpc_rqst *req, __be32 *p,
1102d9c407b1SChuck Lever 				   const struct nfs_writeargs *args)
1103d9c407b1SChuck Lever {
1104d9c407b1SChuck Lever 	struct xdr_stream xdr;
1105d9c407b1SChuck Lever 
1106d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1107d9c407b1SChuck Lever 	encode_write3args(&xdr, args);
1108d9c407b1SChuck Lever 	xdr.buf->flags |= XDRBUF_WRITE;
1109d9c407b1SChuck Lever 	return 0;
1110d9c407b1SChuck Lever }
1111d9c407b1SChuck Lever 
1112d9c407b1SChuck Lever /*
1113d9c407b1SChuck Lever  * 3.3.8  CREATE3args
1114d9c407b1SChuck Lever  *
1115d9c407b1SChuck Lever  *	enum createmode3 {
1116d9c407b1SChuck Lever  *		UNCHECKED = 0,
1117d9c407b1SChuck Lever  *		GUARDED   = 1,
1118d9c407b1SChuck Lever  *		EXCLUSIVE = 2
1119d9c407b1SChuck Lever  *	};
1120d9c407b1SChuck Lever  *
1121d9c407b1SChuck Lever  *	union createhow3 switch (createmode3 mode) {
1122d9c407b1SChuck Lever  *	case UNCHECKED:
1123d9c407b1SChuck Lever  *	case GUARDED:
1124d9c407b1SChuck Lever  *		sattr3       obj_attributes;
1125d9c407b1SChuck Lever  *	case EXCLUSIVE:
1126d9c407b1SChuck Lever  *		createverf3  verf;
1127d9c407b1SChuck Lever  *	};
1128d9c407b1SChuck Lever  *
1129d9c407b1SChuck Lever  *	struct CREATE3args {
1130d9c407b1SChuck Lever  *		diropargs3	where;
1131d9c407b1SChuck Lever  *		createhow3	how;
1132d9c407b1SChuck Lever  *	};
1133d9c407b1SChuck Lever  */
1134d9c407b1SChuck Lever static void encode_createhow3(struct xdr_stream *xdr,
1135d9c407b1SChuck Lever 			      const struct nfs3_createargs *args)
1136d9c407b1SChuck Lever {
1137d9c407b1SChuck Lever 	encode_uint32(xdr, args->createmode);
1138d9c407b1SChuck Lever 	switch (args->createmode) {
1139d9c407b1SChuck Lever 	case NFS3_CREATE_UNCHECKED:
1140d9c407b1SChuck Lever 	case NFS3_CREATE_GUARDED:
1141d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
1142d9c407b1SChuck Lever 		break;
1143d9c407b1SChuck Lever 	case NFS3_CREATE_EXCLUSIVE:
1144d9c407b1SChuck Lever 		encode_createverf3(xdr, args->verifier);
1145d9c407b1SChuck Lever 		break;
1146d9c407b1SChuck Lever 	default:
1147d9c407b1SChuck Lever 		BUG();
1148d9c407b1SChuck Lever 	}
1149d9c407b1SChuck Lever }
1150d9c407b1SChuck Lever 
1151d9c407b1SChuck Lever static int nfs3_xdr_enc_create3args(struct rpc_rqst *req, __be32 *p,
1152d9c407b1SChuck Lever 				    const struct nfs3_createargs *args)
1153d9c407b1SChuck Lever {
1154d9c407b1SChuck Lever 	struct xdr_stream xdr;
1155d9c407b1SChuck Lever 
1156d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1157d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
1158d9c407b1SChuck Lever 	encode_createhow3(&xdr, args);
1159d9c407b1SChuck Lever 	return 0;
1160d9c407b1SChuck Lever }
1161d9c407b1SChuck Lever 
1162d9c407b1SChuck Lever /*
1163d9c407b1SChuck Lever  * 3.3.9  MKDIR3args
1164d9c407b1SChuck Lever  *
1165d9c407b1SChuck Lever  *	struct MKDIR3args {
1166d9c407b1SChuck Lever  *		diropargs3	where;
1167d9c407b1SChuck Lever  *		sattr3		attributes;
1168d9c407b1SChuck Lever  *	};
1169d9c407b1SChuck Lever  */
1170d9c407b1SChuck Lever static int nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req, __be32 *p,
1171d9c407b1SChuck Lever 				   const struct nfs3_mkdirargs *args)
1172d9c407b1SChuck Lever {
1173d9c407b1SChuck Lever 	struct xdr_stream xdr;
1174d9c407b1SChuck Lever 
1175d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1176d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
1177d9c407b1SChuck Lever 	encode_sattr3(&xdr, args->sattr);
1178d9c407b1SChuck Lever 	return 0;
1179d9c407b1SChuck Lever }
1180d9c407b1SChuck Lever 
1181d9c407b1SChuck Lever /*
1182d9c407b1SChuck Lever  * 3.3.10  SYMLINK3args
1183d9c407b1SChuck Lever  *
1184d9c407b1SChuck Lever  *	struct symlinkdata3 {
1185d9c407b1SChuck Lever  *		sattr3		symlink_attributes;
1186d9c407b1SChuck Lever  *		nfspath3	symlink_data;
1187d9c407b1SChuck Lever  *	};
1188d9c407b1SChuck Lever  *
1189d9c407b1SChuck Lever  *	struct SYMLINK3args {
1190d9c407b1SChuck Lever  *		diropargs3	where;
1191d9c407b1SChuck Lever  *		symlinkdata3	symlink;
1192d9c407b1SChuck Lever  *	};
1193d9c407b1SChuck Lever  */
1194d9c407b1SChuck Lever static void encode_symlinkdata3(struct xdr_stream *xdr,
1195d9c407b1SChuck Lever 				const struct nfs3_symlinkargs *args)
1196d9c407b1SChuck Lever {
1197d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
1198d9c407b1SChuck Lever 	encode_nfspath3(xdr, args->pages, args->pathlen);
1199d9c407b1SChuck Lever }
1200d9c407b1SChuck Lever 
1201d9c407b1SChuck Lever static int nfs3_xdr_enc_symlink3args(struct rpc_rqst *req, __be32 *p,
1202d9c407b1SChuck Lever 				     const struct nfs3_symlinkargs *args)
1203d9c407b1SChuck Lever {
1204d9c407b1SChuck Lever 	struct xdr_stream xdr;
1205d9c407b1SChuck Lever 
1206d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1207d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fromfh, args->fromname, args->fromlen);
1208d9c407b1SChuck Lever 	encode_symlinkdata3(&xdr, args);
1209d9c407b1SChuck Lever 	return 0;
1210d9c407b1SChuck Lever }
1211d9c407b1SChuck Lever 
1212d9c407b1SChuck Lever /*
1213d9c407b1SChuck Lever  * 3.3.11  MKNOD3args
1214d9c407b1SChuck Lever  *
1215d9c407b1SChuck Lever  *	struct devicedata3 {
1216d9c407b1SChuck Lever  *		sattr3		dev_attributes;
1217d9c407b1SChuck Lever  *		specdata3	spec;
1218d9c407b1SChuck Lever  *	};
1219d9c407b1SChuck Lever  *
1220d9c407b1SChuck Lever  *	union mknoddata3 switch (ftype3 type) {
1221d9c407b1SChuck Lever  *	case NF3CHR:
1222d9c407b1SChuck Lever  *	case NF3BLK:
1223d9c407b1SChuck Lever  *		devicedata3	device;
1224d9c407b1SChuck Lever  *	case NF3SOCK:
1225d9c407b1SChuck Lever  *	case NF3FIFO:
1226d9c407b1SChuck Lever  *		sattr3		pipe_attributes;
1227d9c407b1SChuck Lever  *	default:
1228d9c407b1SChuck Lever  *		void;
1229d9c407b1SChuck Lever  *	};
1230d9c407b1SChuck Lever  *
1231d9c407b1SChuck Lever  *	struct MKNOD3args {
1232d9c407b1SChuck Lever  *		diropargs3	where;
1233d9c407b1SChuck Lever  *		mknoddata3	what;
1234d9c407b1SChuck Lever  *	};
1235d9c407b1SChuck Lever  */
1236d9c407b1SChuck Lever static void encode_devicedata3(struct xdr_stream *xdr,
1237d9c407b1SChuck Lever 			       const struct nfs3_mknodargs *args)
1238d9c407b1SChuck Lever {
1239d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
1240d9c407b1SChuck Lever 	encode_specdata3(xdr, args->rdev);
1241d9c407b1SChuck Lever }
1242d9c407b1SChuck Lever 
1243d9c407b1SChuck Lever static void encode_mknoddata3(struct xdr_stream *xdr,
1244d9c407b1SChuck Lever 			      const struct nfs3_mknodargs *args)
1245d9c407b1SChuck Lever {
1246d9c407b1SChuck Lever 	encode_ftype3(xdr, args->type);
1247d9c407b1SChuck Lever 	switch (args->type) {
1248d9c407b1SChuck Lever 	case NF3CHR:
1249d9c407b1SChuck Lever 	case NF3BLK:
1250d9c407b1SChuck Lever 		encode_devicedata3(xdr, args);
1251d9c407b1SChuck Lever 		break;
1252d9c407b1SChuck Lever 	case NF3SOCK:
1253d9c407b1SChuck Lever 	case NF3FIFO:
1254d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
1255d9c407b1SChuck Lever 		break;
1256d9c407b1SChuck Lever 	case NF3REG:
1257d9c407b1SChuck Lever 	case NF3DIR:
1258d9c407b1SChuck Lever 		break;
1259d9c407b1SChuck Lever 	default:
1260d9c407b1SChuck Lever 		BUG();
1261d9c407b1SChuck Lever 	}
1262d9c407b1SChuck Lever }
1263d9c407b1SChuck Lever 
1264d9c407b1SChuck Lever static int nfs3_xdr_enc_mknod3args(struct rpc_rqst *req, __be32 *p,
1265d9c407b1SChuck Lever 				   const struct nfs3_mknodargs *args)
1266d9c407b1SChuck Lever {
1267d9c407b1SChuck Lever 	struct xdr_stream xdr;
1268d9c407b1SChuck Lever 
1269d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1270d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
1271d9c407b1SChuck Lever 	encode_mknoddata3(&xdr, args);
1272d9c407b1SChuck Lever 	return 0;
1273d9c407b1SChuck Lever }
1274d9c407b1SChuck Lever 
1275d9c407b1SChuck Lever /*
1276d9c407b1SChuck Lever  * 3.3.12  REMOVE3args
1277d9c407b1SChuck Lever  *
1278d9c407b1SChuck Lever  *	struct REMOVE3args {
1279d9c407b1SChuck Lever  *		diropargs3  object;
1280d9c407b1SChuck Lever  *	};
1281d9c407b1SChuck Lever  */
1282d9c407b1SChuck Lever static int nfs3_xdr_enc_remove3args(struct rpc_rqst *req, __be32 *p,
1283d9c407b1SChuck Lever 				    const struct nfs_removeargs *args)
1284d9c407b1SChuck Lever {
1285d9c407b1SChuck Lever 	struct xdr_stream xdr;
1286d9c407b1SChuck Lever 
1287d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1288d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name.name, args->name.len);
1289d9c407b1SChuck Lever 	return 0;
1290d9c407b1SChuck Lever }
1291d9c407b1SChuck Lever 
1292d9c407b1SChuck Lever /*
1293d9c407b1SChuck Lever  * 3.3.14  RENAME3args
1294d9c407b1SChuck Lever  *
1295d9c407b1SChuck Lever  *	struct RENAME3args {
1296d9c407b1SChuck Lever  *		diropargs3	from;
1297d9c407b1SChuck Lever  *		diropargs3	to;
1298d9c407b1SChuck Lever  *	};
1299d9c407b1SChuck Lever  */
1300d9c407b1SChuck Lever static int nfs3_xdr_enc_rename3args(struct rpc_rqst *req, __be32 *p,
1301d9c407b1SChuck Lever 				    const struct nfs_renameargs *args)
1302d9c407b1SChuck Lever {
1303d9c407b1SChuck Lever 	const struct qstr *old = args->old_name;
1304d9c407b1SChuck Lever 	const struct qstr *new = args->new_name;
1305d9c407b1SChuck Lever 	struct xdr_stream xdr;
1306d9c407b1SChuck Lever 
1307d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1308d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->old_dir, old->name, old->len);
1309d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->new_dir, new->name, new->len);
1310d9c407b1SChuck Lever 	return 0;
1311d9c407b1SChuck Lever }
1312d9c407b1SChuck Lever 
1313d9c407b1SChuck Lever /*
1314d9c407b1SChuck Lever  * 3.3.15  LINK3args
1315d9c407b1SChuck Lever  *
1316d9c407b1SChuck Lever  *	struct LINK3args {
1317d9c407b1SChuck Lever  *		nfs_fh3		file;
1318d9c407b1SChuck Lever  *		diropargs3	link;
1319d9c407b1SChuck Lever  *	};
1320d9c407b1SChuck Lever  */
1321d9c407b1SChuck Lever static int nfs3_xdr_enc_link3args(struct rpc_rqst *req, __be32 *p,
1322d9c407b1SChuck Lever 				  const struct nfs3_linkargs *args)
1323d9c407b1SChuck Lever {
1324d9c407b1SChuck Lever 	struct xdr_stream xdr;
1325d9c407b1SChuck Lever 
1326d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1327d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fromfh);
1328d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->tofh, args->toname, args->tolen);
1329d9c407b1SChuck Lever 	return 0;
1330d9c407b1SChuck Lever }
1331d9c407b1SChuck Lever 
1332d9c407b1SChuck Lever /*
1333d9c407b1SChuck Lever  * 3.3.16  READDIR3args
1334d9c407b1SChuck Lever  *
1335d9c407b1SChuck Lever  *	struct READDIR3args {
1336d9c407b1SChuck Lever  *		nfs_fh3		dir;
1337d9c407b1SChuck Lever  *		cookie3		cookie;
1338d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1339d9c407b1SChuck Lever  *		count3		count;
1340d9c407b1SChuck Lever  *	};
1341d9c407b1SChuck Lever  */
1342d9c407b1SChuck Lever static void encode_readdir3args(struct xdr_stream *xdr,
1343d9c407b1SChuck Lever 				const struct nfs3_readdirargs *args)
1344d9c407b1SChuck Lever {
1345d9c407b1SChuck Lever 	__be32 *p;
1346d9c407b1SChuck Lever 
1347d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1348d9c407b1SChuck Lever 
1349d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
1350d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1351d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1352d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1353d9c407b1SChuck Lever }
1354d9c407b1SChuck Lever 
1355d9c407b1SChuck Lever static int nfs3_xdr_enc_readdir3args(struct rpc_rqst *req, __be32 *p,
1356d9c407b1SChuck Lever 				     const struct nfs3_readdirargs *args)
1357d9c407b1SChuck Lever {
1358d9c407b1SChuck Lever 	struct xdr_stream xdr;
1359d9c407b1SChuck Lever 
1360d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1361d9c407b1SChuck Lever 	encode_readdir3args(&xdr, args);
1362d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
1363d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1364d9c407b1SChuck Lever 	return 0;
1365d9c407b1SChuck Lever }
1366d9c407b1SChuck Lever 
1367d9c407b1SChuck Lever /*
1368d9c407b1SChuck Lever  * 3.3.17  READDIRPLUS3args
1369d9c407b1SChuck Lever  *
1370d9c407b1SChuck Lever  *	struct READDIRPLUS3args {
1371d9c407b1SChuck Lever  *		nfs_fh3		dir;
1372d9c407b1SChuck Lever  *		cookie3		cookie;
1373d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1374d9c407b1SChuck Lever  *		count3		dircount;
1375d9c407b1SChuck Lever  *		count3		maxcount;
1376d9c407b1SChuck Lever  *	};
1377d9c407b1SChuck Lever  */
1378d9c407b1SChuck Lever static void encode_readdirplus3args(struct xdr_stream *xdr,
1379d9c407b1SChuck Lever 				    const struct nfs3_readdirargs *args)
1380d9c407b1SChuck Lever {
1381d9c407b1SChuck Lever 	__be32 *p;
1382d9c407b1SChuck Lever 
1383d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1384d9c407b1SChuck Lever 
1385d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
1386d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1387d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1388d9c407b1SChuck Lever 
1389d9c407b1SChuck Lever 	/*
1390d9c407b1SChuck Lever 	 * readdirplus: need dircount + buffer size.
1391d9c407b1SChuck Lever 	 * We just make sure we make dircount big enough
1392d9c407b1SChuck Lever 	 */
1393d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count >> 3);
1394d9c407b1SChuck Lever 
1395d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1396d9c407b1SChuck Lever }
1397d9c407b1SChuck Lever 
1398d9c407b1SChuck Lever static int nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req, __be32 *p,
1399d9c407b1SChuck Lever 					 const struct nfs3_readdirargs *args)
1400d9c407b1SChuck Lever {
1401d9c407b1SChuck Lever 	struct xdr_stream xdr;
1402d9c407b1SChuck Lever 
1403d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1404d9c407b1SChuck Lever 	encode_readdirplus3args(&xdr, args);
1405d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
1406d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1407d9c407b1SChuck Lever 	return 0;
1408d9c407b1SChuck Lever }
1409d9c407b1SChuck Lever 
1410d9c407b1SChuck Lever /*
14111da177e4SLinus Torvalds  * Decode the result of a readdir call.
14121da177e4SLinus Torvalds  * We just check for syntactical correctness.
14131da177e4SLinus Torvalds  */
14141da177e4SLinus Torvalds static int
1415d61005a6SAl Viro nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
14161da177e4SLinus Torvalds {
14171da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
14181da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
14191da177e4SLinus Torvalds 	struct page **page;
1420c957c526SChuck Lever 	size_t hdrlen;
1421afa8ccc9SBryan Schumaker 	u32 recvd, pglen;
1422ac396128STrond Myklebust 	int status;
14231da177e4SLinus Torvalds 
14241da177e4SLinus Torvalds 	status = ntohl(*p++);
14251da177e4SLinus Torvalds 	/* Decode post_op_attrs */
14261da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->dir_attr);
14271da177e4SLinus Torvalds 	if (status)
1428856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
14291da177e4SLinus Torvalds 	/* Decode verifier cookie */
14301da177e4SLinus Torvalds 	if (res->verf) {
14311da177e4SLinus Torvalds 		res->verf[0] = *p++;
14321da177e4SLinus Torvalds 		res->verf[1] = *p++;
14331da177e4SLinus Torvalds 	} else {
14341da177e4SLinus Torvalds 		p += 2;
14351da177e4SLinus Torvalds 	}
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
14381da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1439fe82a183SChuck Lever 		dprintk("NFS: READDIR reply header overflowed:"
1440c957c526SChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
14411da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
14421da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
14431da177e4SLinus Torvalds 		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
14441da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
14451da177e4SLinus Torvalds 	}
14461da177e4SLinus Torvalds 
14471da177e4SLinus Torvalds 	pglen = rcvbuf->page_len;
14481da177e4SLinus Torvalds 	recvd = rcvbuf->len - hdrlen;
14491da177e4SLinus Torvalds 	if (pglen > recvd)
14501da177e4SLinus Torvalds 		pglen = recvd;
14511da177e4SLinus Torvalds 	page = rcvbuf->pages;
1452643f8111SJeff Layton 
1453ac396128STrond Myklebust 	return pglen;
14541da177e4SLinus Torvalds }
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds /*
1457d9c407b1SChuck Lever  * 3.3.21  COMMIT3args
1458d9c407b1SChuck Lever  *
1459d9c407b1SChuck Lever  *	struct COMMIT3args {
1460d9c407b1SChuck Lever  *		nfs_fh3		file;
1461d9c407b1SChuck Lever  *		offset3		offset;
1462d9c407b1SChuck Lever  *		count3		count;
1463d9c407b1SChuck Lever  *	};
1464d9c407b1SChuck Lever  */
1465d9c407b1SChuck Lever static void encode_commit3args(struct xdr_stream *xdr,
1466d9c407b1SChuck Lever 			       const struct nfs_writeargs *args)
1467d9c407b1SChuck Lever {
1468d9c407b1SChuck Lever 	__be32 *p;
1469d9c407b1SChuck Lever 
1470d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1471d9c407b1SChuck Lever 
1472d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1473d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1474d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1475d9c407b1SChuck Lever }
1476d9c407b1SChuck Lever 
1477d9c407b1SChuck Lever static int nfs3_xdr_enc_commit3args(struct rpc_rqst *req, __be32 *p,
1478d9c407b1SChuck Lever 				    const struct nfs_writeargs *args)
1479d9c407b1SChuck Lever {
1480d9c407b1SChuck Lever 	struct xdr_stream xdr;
1481d9c407b1SChuck Lever 
1482d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1483d9c407b1SChuck Lever 	encode_commit3args(&xdr, args);
1484d9c407b1SChuck Lever 	return 0;
1485d9c407b1SChuck Lever }
1486d9c407b1SChuck Lever 
1487b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1488b7fa0554SAndreas Gruenbacher 
1489d9c407b1SChuck Lever static int nfs3_xdr_enc_getacl3args(struct rpc_rqst *req, __be32 *p,
1490d9c407b1SChuck Lever 				    const struct nfs3_getaclargs *args)
1491d9c407b1SChuck Lever {
1492d9c407b1SChuck Lever 	struct xdr_stream xdr;
1493d9c407b1SChuck Lever 
1494d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1495d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fh);
1496d9c407b1SChuck Lever 	encode_uint32(&xdr, args->mask);
1497d9c407b1SChuck Lever 	if (args->mask & (NFS_ACL | NFS_DFACL))
1498d9c407b1SChuck Lever 		prepare_reply_buffer(req, args->pages, 0,
1499d9c407b1SChuck Lever 					NFSACL_MAXPAGES << PAGE_SHIFT,
1500d9c407b1SChuck Lever 					ACL3_getaclres_sz);
1501d9c407b1SChuck Lever 	return 0;
1502d9c407b1SChuck Lever }
1503d9c407b1SChuck Lever 
1504d9c407b1SChuck Lever static int nfs3_xdr_enc_setacl3args(struct rpc_rqst *req, __be32 *p,
1505d9c407b1SChuck Lever 				    const struct nfs3_setaclargs *args)
1506d9c407b1SChuck Lever {
1507d9c407b1SChuck Lever 	struct xdr_stream xdr;
1508d9c407b1SChuck Lever 	unsigned int base;
1509d9c407b1SChuck Lever 	int error;
1510d9c407b1SChuck Lever 
1511d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1512d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, NFS_FH(args->inode));
1513d9c407b1SChuck Lever 	encode_uint32(&xdr, args->mask);
1514d9c407b1SChuck Lever 	if (args->npages != 0)
1515d9c407b1SChuck Lever 		xdr_write_pages(&xdr, args->pages, 0, args->len);
1516d9c407b1SChuck Lever 
1517d9c407b1SChuck Lever 	base = req->rq_slen;
1518d9c407b1SChuck Lever 	error = nfsacl_encode(xdr.buf, base, args->inode,
1519d9c407b1SChuck Lever 			    (args->mask & NFS_ACL) ?
1520d9c407b1SChuck Lever 			    args->acl_access : NULL, 1, 0);
1521d9c407b1SChuck Lever 	BUG_ON(error < 0);
1522d9c407b1SChuck Lever 	error = nfsacl_encode(xdr.buf, base + error, args->inode,
1523d9c407b1SChuck Lever 			    (args->mask & NFS_DFACL) ?
1524d9c407b1SChuck Lever 			    args->acl_default : NULL, 1,
1525d9c407b1SChuck Lever 			    NFS_ACL_DEFAULT);
1526d9c407b1SChuck Lever 	BUG_ON(error < 0);
1527d9c407b1SChuck Lever 	return 0;
1528d9c407b1SChuck Lever }
1529d9c407b1SChuck Lever 
1530b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1531b7fa0554SAndreas Gruenbacher 
15321da177e4SLinus Torvalds /*
15331da177e4SLinus Torvalds  * NFS XDR decode functions
15341da177e4SLinus Torvalds  */
15351da177e4SLinus Torvalds 
15361da177e4SLinus Torvalds /*
15371da177e4SLinus Torvalds  * Decode attrstat reply.
15381da177e4SLinus Torvalds  */
15391da177e4SLinus Torvalds static int
1540d61005a6SAl Viro nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
15411da177e4SLinus Torvalds {
15421da177e4SLinus Torvalds 	int	status;
15431da177e4SLinus Torvalds 
15441da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1545856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
15461da177e4SLinus Torvalds 	xdr_decode_fattr(p, fattr);
15471da177e4SLinus Torvalds 	return 0;
15481da177e4SLinus Torvalds }
15491da177e4SLinus Torvalds 
15501da177e4SLinus Torvalds /*
1551e4f93234SChuck Lever  * 3.3.1  GETATTR3res
1552e4f93234SChuck Lever  *
1553e4f93234SChuck Lever  *	struct GETATTR3resok {
1554e4f93234SChuck Lever  *		fattr3		obj_attributes;
1555e4f93234SChuck Lever  *	};
1556e4f93234SChuck Lever  *
1557e4f93234SChuck Lever  *	union GETATTR3res switch (nfsstat3 status) {
1558e4f93234SChuck Lever  *	case NFS3_OK:
1559e4f93234SChuck Lever  *		GETATTR3resok  resok;
1560e4f93234SChuck Lever  *	default:
1561e4f93234SChuck Lever  *		void;
1562e4f93234SChuck Lever  *	};
1563e4f93234SChuck Lever  */
1564e4f93234SChuck Lever static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req, __be32 *p,
1565e4f93234SChuck Lever 				    struct nfs_fattr *result)
1566e4f93234SChuck Lever {
1567e4f93234SChuck Lever 	struct xdr_stream xdr;
1568e4f93234SChuck Lever 	enum nfs_stat status;
1569e4f93234SChuck Lever 	int error;
1570e4f93234SChuck Lever 
1571e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1572e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
1573e4f93234SChuck Lever 	if (unlikely(error))
1574e4f93234SChuck Lever 		goto out;
1575e4f93234SChuck Lever 	if (status != NFS3_OK)
1576e4f93234SChuck Lever 		goto out_default;
1577e4f93234SChuck Lever 	error = decode_fattr3(&xdr, result);
1578e4f93234SChuck Lever out:
1579e4f93234SChuck Lever 	return error;
1580e4f93234SChuck Lever out_default:
1581e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
1582e4f93234SChuck Lever }
1583e4f93234SChuck Lever 
1584e4f93234SChuck Lever /*
15851da177e4SLinus Torvalds  * Decode status+wcc_data reply
15861da177e4SLinus Torvalds  * SATTR, REMOVE, RMDIR
15871da177e4SLinus Torvalds  */
15881da177e4SLinus Torvalds static int
1589d61005a6SAl Viro nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
15901da177e4SLinus Torvalds {
15911da177e4SLinus Torvalds 	int	status;
15921da177e4SLinus Torvalds 
15931da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1594856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
15951da177e4SLinus Torvalds 	xdr_decode_wcc_data(p, fattr);
15961da177e4SLinus Torvalds 	return status;
15971da177e4SLinus Torvalds }
15981da177e4SLinus Torvalds 
1599e4f93234SChuck Lever /*
1600e4f93234SChuck Lever  * 3.3.2  SETATTR3res
1601e4f93234SChuck Lever  *
1602e4f93234SChuck Lever  *	struct SETATTR3resok {
1603e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1604e4f93234SChuck Lever  *	};
1605e4f93234SChuck Lever  *
1606e4f93234SChuck Lever  *	struct SETATTR3resfail {
1607e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1608e4f93234SChuck Lever  *	};
1609e4f93234SChuck Lever  *
1610e4f93234SChuck Lever  *	union SETATTR3res switch (nfsstat3 status) {
1611e4f93234SChuck Lever  *	case NFS3_OK:
1612e4f93234SChuck Lever  *		SETATTR3resok   resok;
1613e4f93234SChuck Lever  *	default:
1614e4f93234SChuck Lever  *		SETATTR3resfail resfail;
1615e4f93234SChuck Lever  *	};
1616e4f93234SChuck Lever  */
1617e4f93234SChuck Lever static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req, __be32 *p,
1618e4f93234SChuck Lever 				    struct nfs_fattr *result)
1619e4f93234SChuck Lever {
1620e4f93234SChuck Lever 	struct xdr_stream xdr;
1621e4f93234SChuck Lever 	enum nfs_stat status;
1622e4f93234SChuck Lever 	int error;
1623e4f93234SChuck Lever 
1624e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1625e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
1626e4f93234SChuck Lever 	if (unlikely(error))
1627e4f93234SChuck Lever 		goto out;
1628e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result);
1629e4f93234SChuck Lever 	if (unlikely(error))
1630e4f93234SChuck Lever 		goto out;
1631e4f93234SChuck Lever 	if (status != NFS3_OK)
1632e4f93234SChuck Lever 		goto out_status;
1633e4f93234SChuck Lever out:
1634e4f93234SChuck Lever 	return error;
1635e4f93234SChuck Lever out_status:
1636e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
1637e4f93234SChuck Lever }
1638e4f93234SChuck Lever 
16394fdc17b2STrond Myklebust static int
16404fdc17b2STrond Myklebust nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
16414fdc17b2STrond Myklebust {
1642d346890bSTrond Myklebust 	return nfs3_xdr_wccstat(req, p, res->dir_attr);
16434fdc17b2STrond Myklebust }
16444fdc17b2STrond Myklebust 
16451da177e4SLinus Torvalds /*
16461da177e4SLinus Torvalds  * Decode LOOKUP reply
16471da177e4SLinus Torvalds  */
16481da177e4SLinus Torvalds static int
1649d61005a6SAl Viro nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
16501da177e4SLinus Torvalds {
16511da177e4SLinus Torvalds 	int	status;
16521da177e4SLinus Torvalds 
16531da177e4SLinus Torvalds 	if ((status = ntohl(*p++))) {
1654856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
16551da177e4SLinus Torvalds 	} else {
16561da177e4SLinus Torvalds 		if (!(p = xdr_decode_fhandle(p, res->fh)))
16571da177e4SLinus Torvalds 			return -errno_NFSERR_IO;
16581da177e4SLinus Torvalds 		p = xdr_decode_post_op_attr(p, res->fattr);
16591da177e4SLinus Torvalds 	}
16601da177e4SLinus Torvalds 	xdr_decode_post_op_attr(p, res->dir_attr);
16611da177e4SLinus Torvalds 	return status;
16621da177e4SLinus Torvalds }
16631da177e4SLinus Torvalds 
16641da177e4SLinus Torvalds /*
1665e4f93234SChuck Lever  * 3.3.3  LOOKUP3res
1666e4f93234SChuck Lever  *
1667e4f93234SChuck Lever  *	struct LOOKUP3resok {
1668e4f93234SChuck Lever  *		nfs_fh3		object;
1669e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1670e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1671e4f93234SChuck Lever  *	};
1672e4f93234SChuck Lever  *
1673e4f93234SChuck Lever  *	struct LOOKUP3resfail {
1674e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1675e4f93234SChuck Lever  *	};
1676e4f93234SChuck Lever  *
1677e4f93234SChuck Lever  *	union LOOKUP3res switch (nfsstat3 status) {
1678e4f93234SChuck Lever  *	case NFS3_OK:
1679e4f93234SChuck Lever  *		LOOKUP3resok	resok;
1680e4f93234SChuck Lever  *	default:
1681e4f93234SChuck Lever  *		LOOKUP3resfail	resfail;
1682e4f93234SChuck Lever  *	};
1683e4f93234SChuck Lever  */
1684e4f93234SChuck Lever static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req, __be32 *p,
1685e4f93234SChuck Lever 				   struct nfs3_diropres *result)
1686e4f93234SChuck Lever {
1687e4f93234SChuck Lever 	struct xdr_stream xdr;
1688e4f93234SChuck Lever 	enum nfs_stat status;
1689e4f93234SChuck Lever 	int error;
1690e4f93234SChuck Lever 
1691e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1692e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
1693e4f93234SChuck Lever 	if (unlikely(error))
1694e4f93234SChuck Lever 		goto out;
1695e4f93234SChuck Lever 	if (status != NFS3_OK)
1696e4f93234SChuck Lever 		goto out_default;
1697e4f93234SChuck Lever 	error = decode_nfs_fh3(&xdr, result->fh);
1698e4f93234SChuck Lever 	if (unlikely(error))
1699e4f93234SChuck Lever 		goto out;
1700e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
1701e4f93234SChuck Lever 	if (unlikely(error))
1702e4f93234SChuck Lever 		goto out;
1703e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->dir_attr);
1704e4f93234SChuck Lever out:
1705e4f93234SChuck Lever 	return error;
1706e4f93234SChuck Lever out_default:
1707e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->dir_attr);
1708e4f93234SChuck Lever 	if (unlikely(error))
1709e4f93234SChuck Lever 		goto out;
1710e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
1711e4f93234SChuck Lever }
1712e4f93234SChuck Lever 
1713e4f93234SChuck Lever /*
17141da177e4SLinus Torvalds  * Decode ACCESS reply
17151da177e4SLinus Torvalds  */
17161da177e4SLinus Torvalds static int
1717d61005a6SAl Viro nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
17181da177e4SLinus Torvalds {
17191da177e4SLinus Torvalds 	int	status = ntohl(*p++);
17201da177e4SLinus Torvalds 
17211da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
17221da177e4SLinus Torvalds 	if (status)
1723856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
17241da177e4SLinus Torvalds 	res->access = ntohl(*p++);
17251da177e4SLinus Torvalds 	return 0;
17261da177e4SLinus Torvalds }
17271da177e4SLinus Torvalds 
17281da177e4SLinus Torvalds /*
1729e4f93234SChuck Lever  * 3.3.4  ACCESS3res
1730e4f93234SChuck Lever  *
1731e4f93234SChuck Lever  *	struct ACCESS3resok {
1732e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1733e4f93234SChuck Lever  *		uint32		access;
1734e4f93234SChuck Lever  *	};
1735e4f93234SChuck Lever  *
1736e4f93234SChuck Lever  *	struct ACCESS3resfail {
1737e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1738e4f93234SChuck Lever  *	};
1739e4f93234SChuck Lever  *
1740e4f93234SChuck Lever  *	union ACCESS3res switch (nfsstat3 status) {
1741e4f93234SChuck Lever  *	case NFS3_OK:
1742e4f93234SChuck Lever  *		ACCESS3resok	resok;
1743e4f93234SChuck Lever  *	default:
1744e4f93234SChuck Lever  *		ACCESS3resfail	resfail;
1745e4f93234SChuck Lever  *	};
1746e4f93234SChuck Lever  */
1747e4f93234SChuck Lever static int nfs3_xdr_dec_access3res(struct rpc_rqst *req, __be32 *p,
1748e4f93234SChuck Lever 				   struct nfs3_accessres *result)
1749e4f93234SChuck Lever {
1750e4f93234SChuck Lever 	struct xdr_stream xdr;
1751e4f93234SChuck Lever 	enum nfs_stat status;
1752e4f93234SChuck Lever 	int error;
1753e4f93234SChuck Lever 
1754e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1755e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
1756e4f93234SChuck Lever 	if (unlikely(error))
1757e4f93234SChuck Lever 		goto out;
1758e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
1759e4f93234SChuck Lever 	if (unlikely(error))
1760e4f93234SChuck Lever 		goto out;
1761e4f93234SChuck Lever 	if (status != NFS3_OK)
1762e4f93234SChuck Lever 		goto out_default;
1763e4f93234SChuck Lever 	error = decode_uint32(&xdr, &result->access);
1764e4f93234SChuck Lever out:
1765e4f93234SChuck Lever 	return error;
1766e4f93234SChuck Lever out_default:
1767e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
1768e4f93234SChuck Lever }
1769e4f93234SChuck Lever 
1770e4f93234SChuck Lever /*
17711da177e4SLinus Torvalds  * Decode READLINK reply
17721da177e4SLinus Torvalds  */
17731da177e4SLinus Torvalds static int
1774d61005a6SAl Viro nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
17751da177e4SLinus Torvalds {
17761da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
17771da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
1778c957c526SChuck Lever 	size_t hdrlen;
1779c957c526SChuck Lever 	u32 len, recvd;
17801da177e4SLinus Torvalds 	int	status;
17811da177e4SLinus Torvalds 
17821da177e4SLinus Torvalds 	status = ntohl(*p++);
17831da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, fattr);
17841da177e4SLinus Torvalds 
17851da177e4SLinus Torvalds 	if (status != 0)
1786856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
17871da177e4SLinus Torvalds 
17881da177e4SLinus Torvalds 	/* Convert length of symlink */
17891da177e4SLinus Torvalds 	len = ntohl(*p++);
1790c957c526SChuck Lever 	if (len >= rcvbuf->page_len) {
1791fe82a183SChuck Lever 		dprintk("nfs: server returned giant symlink!\n");
17921da177e4SLinus Torvalds 		return -ENAMETOOLONG;
17931da177e4SLinus Torvalds 	}
17941da177e4SLinus Torvalds 
17951da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
17961da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1797fe82a183SChuck Lever 		dprintk("NFS: READLINK reply header overflowed:"
1798c957c526SChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
17991da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
18001da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
1801fe82a183SChuck Lever 		dprintk("NFS: READLINK header is short. "
1802fe82a183SChuck Lever 			"iovec will be shifted.\n");
18031da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
18041da177e4SLinus Torvalds 	}
18051da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
18061da177e4SLinus Torvalds 	if (recvd < len) {
1807fe82a183SChuck Lever 		dprintk("NFS: server cheating in readlink reply: "
18081da177e4SLinus Torvalds 				"count %u > recvd %u\n", len, recvd);
18091da177e4SLinus Torvalds 		return -EIO;
18101da177e4SLinus Torvalds 	}
18111da177e4SLinus Torvalds 
1812b4687da7SChuck Lever 	xdr_terminate_string(rcvbuf, len);
18131da177e4SLinus Torvalds 	return 0;
18141da177e4SLinus Torvalds }
18151da177e4SLinus Torvalds 
18161da177e4SLinus Torvalds /*
1817e4f93234SChuck Lever  * 3.3.5  READLINK3res
1818e4f93234SChuck Lever  *
1819e4f93234SChuck Lever  *	struct READLINK3resok {
1820e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1821e4f93234SChuck Lever  *		nfspath3	data;
1822e4f93234SChuck Lever  *	};
1823e4f93234SChuck Lever  *
1824e4f93234SChuck Lever  *	struct READLINK3resfail {
1825e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1826e4f93234SChuck Lever  *	};
1827e4f93234SChuck Lever  *
1828e4f93234SChuck Lever  *	union READLINK3res switch (nfsstat3 status) {
1829e4f93234SChuck Lever  *	case NFS3_OK:
1830e4f93234SChuck Lever  *		READLINK3resok	resok;
1831e4f93234SChuck Lever  *	default:
1832e4f93234SChuck Lever  *		READLINK3resfail resfail;
1833e4f93234SChuck Lever  *	};
1834e4f93234SChuck Lever  */
1835e4f93234SChuck Lever static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req, __be32 *p,
1836e4f93234SChuck Lever 				     struct nfs_fattr *result)
1837e4f93234SChuck Lever {
1838e4f93234SChuck Lever 	struct xdr_stream xdr;
1839e4f93234SChuck Lever 	enum nfs_stat status;
1840e4f93234SChuck Lever 	int error;
1841e4f93234SChuck Lever 
1842e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1843e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
1844e4f93234SChuck Lever 	if (unlikely(error))
1845e4f93234SChuck Lever 		goto out;
1846e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result);
1847e4f93234SChuck Lever 	if (unlikely(error))
1848e4f93234SChuck Lever 		goto out;
1849e4f93234SChuck Lever 	if (status != NFS3_OK)
1850e4f93234SChuck Lever 		goto out_default;
1851e4f93234SChuck Lever 	error = decode_nfspath3(&xdr);
1852e4f93234SChuck Lever out:
1853e4f93234SChuck Lever 	return error;
1854e4f93234SChuck Lever out_default:
1855e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
1856e4f93234SChuck Lever }
1857e4f93234SChuck Lever 
1858e4f93234SChuck Lever /*
18591da177e4SLinus Torvalds  * Decode READ reply
18601da177e4SLinus Torvalds  */
18611da177e4SLinus Torvalds static int
1862d61005a6SAl Viro nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
18631da177e4SLinus Torvalds {
18641da177e4SLinus Torvalds 	struct kvec *iov = req->rq_rcv_buf.head;
1865c957c526SChuck Lever 	size_t hdrlen;
1866c957c526SChuck Lever 	u32 count, ocount, recvd;
1867c957c526SChuck Lever 	int status;
18681da177e4SLinus Torvalds 
18691da177e4SLinus Torvalds 	status = ntohl(*p++);
18701da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
18711da177e4SLinus Torvalds 
18721da177e4SLinus Torvalds 	if (status != 0)
1873856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
18741da177e4SLinus Torvalds 
1875c957c526SChuck Lever 	/* Decode reply count and EOF flag. NFSv3 is somewhat redundant
18761da177e4SLinus Torvalds 	 * in that it puts the count both in the res struct and in the
18771da177e4SLinus Torvalds 	 * opaque data count. */
18781da177e4SLinus Torvalds 	count    = ntohl(*p++);
18791da177e4SLinus Torvalds 	res->eof = ntohl(*p++);
18801da177e4SLinus Torvalds 	ocount   = ntohl(*p++);
18811da177e4SLinus Torvalds 
18821da177e4SLinus Torvalds 	if (ocount != count) {
1883fe82a183SChuck Lever 		dprintk("NFS: READ count doesn't match RPC opaque count.\n");
18841da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
18851da177e4SLinus Torvalds 	}
18861da177e4SLinus Torvalds 
18871da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
18881da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1889fe82a183SChuck Lever 		dprintk("NFS: READ reply header overflowed:"
1890c957c526SChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
18911da177e4SLinus Torvalds        		return -errno_NFSERR_IO;
18921da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
18931da177e4SLinus Torvalds 		dprintk("NFS: READ header is short. iovec will be shifted.\n");
18941da177e4SLinus Torvalds 		xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
18951da177e4SLinus Torvalds 	}
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
18981da177e4SLinus Torvalds 	if (count > recvd) {
1899fe82a183SChuck Lever 		dprintk("NFS: server cheating in read reply: "
1900c957c526SChuck Lever 			"count %u > recvd %u\n", count, recvd);
19011da177e4SLinus Torvalds 		count = recvd;
19021da177e4SLinus Torvalds 		res->eof = 0;
19031da177e4SLinus Torvalds 	}
19041da177e4SLinus Torvalds 
19051da177e4SLinus Torvalds 	if (count < res->count)
19061da177e4SLinus Torvalds 		res->count = count;
19071da177e4SLinus Torvalds 
19081da177e4SLinus Torvalds 	return count;
19091da177e4SLinus Torvalds }
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds /*
1912e4f93234SChuck Lever  * 3.3.6  READ3res
1913e4f93234SChuck Lever  *
1914e4f93234SChuck Lever  *	struct READ3resok {
1915e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1916e4f93234SChuck Lever  *		count3		count;
1917e4f93234SChuck Lever  *		bool		eof;
1918e4f93234SChuck Lever  *		opaque		data<>;
1919e4f93234SChuck Lever  *	};
1920e4f93234SChuck Lever  *
1921e4f93234SChuck Lever  *	struct READ3resfail {
1922e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1923e4f93234SChuck Lever  *	};
1924e4f93234SChuck Lever  *
1925e4f93234SChuck Lever  *	union READ3res switch (nfsstat3 status) {
1926e4f93234SChuck Lever  *	case NFS3_OK:
1927e4f93234SChuck Lever  *		READ3resok	resok;
1928e4f93234SChuck Lever  *	default:
1929e4f93234SChuck Lever  *		READ3resfail	resfail;
1930e4f93234SChuck Lever  *	};
1931e4f93234SChuck Lever  */
1932e4f93234SChuck Lever static int decode_read3resok(struct xdr_stream *xdr,
1933e4f93234SChuck Lever 			     struct nfs_readres *result)
1934e4f93234SChuck Lever {
1935e4f93234SChuck Lever 	u32 eof, count, ocount, recvd;
1936e4f93234SChuck Lever 	size_t hdrlen;
1937e4f93234SChuck Lever 	__be32 *p;
1938e4f93234SChuck Lever 
1939e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 + 4 + 4);
1940e4f93234SChuck Lever 	if (unlikely(p == NULL))
1941e4f93234SChuck Lever 		goto out_overflow;
1942e4f93234SChuck Lever 	count = be32_to_cpup(p++);
1943e4f93234SChuck Lever 	eof = be32_to_cpup(p++);
1944e4f93234SChuck Lever 	ocount = be32_to_cpup(p++);
1945e4f93234SChuck Lever 	if (unlikely(ocount != count))
1946e4f93234SChuck Lever 		goto out_mismatch;
1947e4f93234SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
1948e4f93234SChuck Lever 	recvd = xdr->buf->len - hdrlen;
1949e4f93234SChuck Lever 	if (unlikely(count > recvd))
1950e4f93234SChuck Lever 		goto out_cheating;
1951e4f93234SChuck Lever 
1952e4f93234SChuck Lever out:
1953e4f93234SChuck Lever 	xdr_read_pages(xdr, count);
1954e4f93234SChuck Lever 	result->eof = eof;
1955e4f93234SChuck Lever 	result->count = count;
1956e4f93234SChuck Lever 	return count;
1957e4f93234SChuck Lever out_mismatch:
1958e4f93234SChuck Lever 	dprintk("NFS: READ count doesn't match length of opaque: "
1959e4f93234SChuck Lever 		"count %u != ocount %u\n", count, ocount);
1960e4f93234SChuck Lever 	return -EIO;
1961e4f93234SChuck Lever out_cheating:
1962e4f93234SChuck Lever 	dprintk("NFS: server cheating in read result: "
1963e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
1964e4f93234SChuck Lever 	count = recvd;
1965e4f93234SChuck Lever 	eof = 0;
1966e4f93234SChuck Lever 	goto out;
1967e4f93234SChuck Lever out_overflow:
1968e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
1969e4f93234SChuck Lever 	return -EIO;
1970e4f93234SChuck Lever }
1971e4f93234SChuck Lever 
1972e4f93234SChuck Lever static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, __be32 *p,
1973e4f93234SChuck Lever 				 struct nfs_readres *result)
1974e4f93234SChuck Lever {
1975e4f93234SChuck Lever 	struct xdr_stream xdr;
1976e4f93234SChuck Lever 	enum nfs_stat status;
1977e4f93234SChuck Lever 	int error;
1978e4f93234SChuck Lever 
1979e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1980e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
1981e4f93234SChuck Lever 	if (unlikely(error))
1982e4f93234SChuck Lever 		goto out;
1983e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
1984e4f93234SChuck Lever 	if (unlikely(error))
1985e4f93234SChuck Lever 		goto out;
1986e4f93234SChuck Lever 	if (status != NFS3_OK)
1987e4f93234SChuck Lever 		goto out_status;
1988e4f93234SChuck Lever 	error = decode_read3resok(&xdr, result);
1989e4f93234SChuck Lever out:
1990e4f93234SChuck Lever 	return error;
1991e4f93234SChuck Lever out_status:
1992e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
1993e4f93234SChuck Lever }
1994e4f93234SChuck Lever 
1995e4f93234SChuck Lever /*
19961da177e4SLinus Torvalds  * Decode WRITE response
19971da177e4SLinus Torvalds  */
19981da177e4SLinus Torvalds static int
1999d61005a6SAl Viro nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
20001da177e4SLinus Torvalds {
20011da177e4SLinus Torvalds 	int	status;
20021da177e4SLinus Torvalds 
20031da177e4SLinus Torvalds 	status = ntohl(*p++);
20041da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->fattr);
20051da177e4SLinus Torvalds 
20061da177e4SLinus Torvalds 	if (status != 0)
2007856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
20081da177e4SLinus Torvalds 
20091da177e4SLinus Torvalds 	res->count = ntohl(*p++);
20101da177e4SLinus Torvalds 	res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
20111da177e4SLinus Torvalds 	res->verf->verifier[0] = *p++;
20121da177e4SLinus Torvalds 	res->verf->verifier[1] = *p++;
20131da177e4SLinus Torvalds 
20141da177e4SLinus Torvalds 	return res->count;
20151da177e4SLinus Torvalds }
20161da177e4SLinus Torvalds 
20171da177e4SLinus Torvalds /*
2018e4f93234SChuck Lever  * 3.3.7  WRITE3res
2019e4f93234SChuck Lever  *
2020e4f93234SChuck Lever  *	enum stable_how {
2021e4f93234SChuck Lever  *		UNSTABLE  = 0,
2022e4f93234SChuck Lever  *		DATA_SYNC = 1,
2023e4f93234SChuck Lever  *		FILE_SYNC = 2
2024e4f93234SChuck Lever  *	};
2025e4f93234SChuck Lever  *
2026e4f93234SChuck Lever  *	struct WRITE3resok {
2027e4f93234SChuck Lever  *		wcc_data	file_wcc;
2028e4f93234SChuck Lever  *		count3		count;
2029e4f93234SChuck Lever  *		stable_how	committed;
2030e4f93234SChuck Lever  *		writeverf3	verf;
2031e4f93234SChuck Lever  *	};
2032e4f93234SChuck Lever  *
2033e4f93234SChuck Lever  *	struct WRITE3resfail {
2034e4f93234SChuck Lever  *		wcc_data	file_wcc;
2035e4f93234SChuck Lever  *	};
2036e4f93234SChuck Lever  *
2037e4f93234SChuck Lever  *	union WRITE3res switch (nfsstat3 status) {
2038e4f93234SChuck Lever  *	case NFS3_OK:
2039e4f93234SChuck Lever  *		WRITE3resok	resok;
2040e4f93234SChuck Lever  *	default:
2041e4f93234SChuck Lever  *		WRITE3resfail	resfail;
2042e4f93234SChuck Lever  *	};
2043e4f93234SChuck Lever  */
2044e4f93234SChuck Lever static int decode_write3resok(struct xdr_stream *xdr,
2045e4f93234SChuck Lever 			      struct nfs_writeres *result)
2046e4f93234SChuck Lever {
2047e4f93234SChuck Lever 	__be32 *p;
2048e4f93234SChuck Lever 
2049e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE);
2050e4f93234SChuck Lever 	if (unlikely(p == NULL))
2051e4f93234SChuck Lever 		goto out_overflow;
2052e4f93234SChuck Lever 	result->count = be32_to_cpup(p++);
2053e4f93234SChuck Lever 	result->verf->committed = be32_to_cpup(p++);
2054e4f93234SChuck Lever 	if (unlikely(result->verf->committed > NFS_FILE_SYNC))
2055e4f93234SChuck Lever 		goto out_badvalue;
2056e4f93234SChuck Lever 	memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE);
2057e4f93234SChuck Lever 	return result->count;
2058e4f93234SChuck Lever out_badvalue:
2059e4f93234SChuck Lever 	dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
2060e4f93234SChuck Lever 	return -EIO;
2061e4f93234SChuck Lever out_overflow:
2062e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2063e4f93234SChuck Lever 	return -EIO;
2064e4f93234SChuck Lever }
2065e4f93234SChuck Lever 
2066e4f93234SChuck Lever static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, __be32 *p,
2067e4f93234SChuck Lever 				  struct nfs_writeres *result)
2068e4f93234SChuck Lever {
2069e4f93234SChuck Lever 	struct xdr_stream xdr;
2070e4f93234SChuck Lever 	enum nfs_stat status;
2071e4f93234SChuck Lever 	int error;
2072e4f93234SChuck Lever 
2073e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2074e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2075e4f93234SChuck Lever 	if (unlikely(error))
2076e4f93234SChuck Lever 		goto out;
2077e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->fattr);
2078e4f93234SChuck Lever 	if (unlikely(error))
2079e4f93234SChuck Lever 		goto out;
2080e4f93234SChuck Lever 	if (status != NFS3_OK)
2081e4f93234SChuck Lever 		goto out_status;
2082e4f93234SChuck Lever 	error = decode_write3resok(&xdr, result);
2083e4f93234SChuck Lever out:
2084e4f93234SChuck Lever 	return error;
2085e4f93234SChuck Lever out_status:
2086e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2087e4f93234SChuck Lever }
2088e4f93234SChuck Lever 
2089e4f93234SChuck Lever /*
20901da177e4SLinus Torvalds  * Decode a CREATE response
20911da177e4SLinus Torvalds  */
20921da177e4SLinus Torvalds static int
2093d61005a6SAl Viro nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
20941da177e4SLinus Torvalds {
20951da177e4SLinus Torvalds 	int	status;
20961da177e4SLinus Torvalds 
20971da177e4SLinus Torvalds 	status = ntohl(*p++);
20981da177e4SLinus Torvalds 	if (status == 0) {
20991da177e4SLinus Torvalds 		if (*p++) {
21001da177e4SLinus Torvalds 			if (!(p = xdr_decode_fhandle(p, res->fh)))
21011da177e4SLinus Torvalds 				return -errno_NFSERR_IO;
21021da177e4SLinus Torvalds 			p = xdr_decode_post_op_attr(p, res->fattr);
21031da177e4SLinus Torvalds 		} else {
21041da177e4SLinus Torvalds 			memset(res->fh, 0, sizeof(*res->fh));
21051da177e4SLinus Torvalds 			/* Do decode post_op_attr but set it to NULL */
21061da177e4SLinus Torvalds 			p = xdr_decode_post_op_attr(p, res->fattr);
21071da177e4SLinus Torvalds 			res->fattr->valid = 0;
21081da177e4SLinus Torvalds 		}
21091da177e4SLinus Torvalds 	} else {
2110856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
21111da177e4SLinus Torvalds 	}
21121da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->dir_attr);
21131da177e4SLinus Torvalds 	return status;
21141da177e4SLinus Torvalds }
21151da177e4SLinus Torvalds 
21161da177e4SLinus Torvalds /*
2117e4f93234SChuck Lever  * 3.3.8  CREATE3res
2118e4f93234SChuck Lever  *
2119e4f93234SChuck Lever  *	struct CREATE3resok {
2120e4f93234SChuck Lever  *		post_op_fh3	obj;
2121e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2122e4f93234SChuck Lever  *		wcc_data	dir_wcc;
2123e4f93234SChuck Lever  *	};
2124e4f93234SChuck Lever  *
2125e4f93234SChuck Lever  *	struct CREATE3resfail {
2126e4f93234SChuck Lever  *		wcc_data	dir_wcc;
2127e4f93234SChuck Lever  *	};
2128e4f93234SChuck Lever  *
2129e4f93234SChuck Lever  *	union CREATE3res switch (nfsstat3 status) {
2130e4f93234SChuck Lever  *	case NFS3_OK:
2131e4f93234SChuck Lever  *		CREATE3resok	resok;
2132e4f93234SChuck Lever  *	default:
2133e4f93234SChuck Lever  *		CREATE3resfail	resfail;
2134e4f93234SChuck Lever  *	};
2135e4f93234SChuck Lever  */
2136e4f93234SChuck Lever static int decode_create3resok(struct xdr_stream *xdr,
2137e4f93234SChuck Lever 			       struct nfs3_diropres *result)
2138e4f93234SChuck Lever {
2139e4f93234SChuck Lever 	int error;
2140e4f93234SChuck Lever 
2141e4f93234SChuck Lever 	error = decode_post_op_fh3(xdr, result->fh);
2142e4f93234SChuck Lever 	if (unlikely(error))
2143e4f93234SChuck Lever 		goto out;
2144e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2145e4f93234SChuck Lever 	if (unlikely(error))
2146e4f93234SChuck Lever 		goto out;
2147e4f93234SChuck Lever 	/* The server isn't required to return a file handle.
2148e4f93234SChuck Lever 	 * If it didn't, force the client to perform a LOOKUP
2149e4f93234SChuck Lever 	 * to determine the correct file handle and attribute
2150e4f93234SChuck Lever 	 * values for the new object. */
2151e4f93234SChuck Lever 	if (result->fh->size == 0)
2152e4f93234SChuck Lever 		result->fattr->valid = 0;
2153e4f93234SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
2154e4f93234SChuck Lever out:
2155e4f93234SChuck Lever 	return error;
2156e4f93234SChuck Lever }
2157e4f93234SChuck Lever 
2158e4f93234SChuck Lever static int nfs3_xdr_dec_create3res(struct rpc_rqst *req, __be32 *p,
2159e4f93234SChuck Lever 				   struct nfs3_diropres *result)
2160e4f93234SChuck Lever {
2161e4f93234SChuck Lever 	struct xdr_stream xdr;
2162e4f93234SChuck Lever 	enum nfs_stat status;
2163e4f93234SChuck Lever 	int error;
2164e4f93234SChuck Lever 
2165e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2166e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2167e4f93234SChuck Lever 	if (unlikely(error))
2168e4f93234SChuck Lever 		goto out;
2169e4f93234SChuck Lever 	if (status != NFS3_OK)
2170e4f93234SChuck Lever 		goto out_default;
2171e4f93234SChuck Lever 	error = decode_create3resok(&xdr, result);
2172e4f93234SChuck Lever out:
2173e4f93234SChuck Lever 	return error;
2174e4f93234SChuck Lever out_default:
2175e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->dir_attr);
2176e4f93234SChuck Lever 	if (unlikely(error))
2177e4f93234SChuck Lever 		goto out;
2178e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2179e4f93234SChuck Lever }
2180e4f93234SChuck Lever 
2181e4f93234SChuck Lever /*
2182e4f93234SChuck Lever  * 3.3.12  REMOVE3res
2183e4f93234SChuck Lever  *
2184e4f93234SChuck Lever  *	struct REMOVE3resok {
2185e4f93234SChuck Lever  *		wcc_data    dir_wcc;
2186e4f93234SChuck Lever  *	};
2187e4f93234SChuck Lever  *
2188e4f93234SChuck Lever  *	struct REMOVE3resfail {
2189e4f93234SChuck Lever  *		wcc_data    dir_wcc;
2190e4f93234SChuck Lever  *	};
2191e4f93234SChuck Lever  *
2192e4f93234SChuck Lever  *	union REMOVE3res switch (nfsstat3 status) {
2193e4f93234SChuck Lever  *	case NFS3_OK:
2194e4f93234SChuck Lever  *		REMOVE3resok   resok;
2195e4f93234SChuck Lever  *	default:
2196e4f93234SChuck Lever  *		REMOVE3resfail resfail;
2197e4f93234SChuck Lever  *	};
2198e4f93234SChuck Lever  */
2199e4f93234SChuck Lever static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req, __be32 *p,
2200e4f93234SChuck Lever 				   struct nfs_removeres *result)
2201e4f93234SChuck Lever {
2202e4f93234SChuck Lever 	struct xdr_stream xdr;
2203e4f93234SChuck Lever 	enum nfs_stat status;
2204e4f93234SChuck Lever 	int error;
2205e4f93234SChuck Lever 
2206e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2207e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2208e4f93234SChuck Lever 	if (unlikely(error))
2209e4f93234SChuck Lever 		goto out;
2210e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->dir_attr);
2211e4f93234SChuck Lever 	if (unlikely(error))
2212e4f93234SChuck Lever 		goto out;
2213e4f93234SChuck Lever 	if (status != NFS3_OK)
2214e4f93234SChuck Lever 		goto out_status;
2215e4f93234SChuck Lever out:
2216e4f93234SChuck Lever 	return error;
2217e4f93234SChuck Lever out_status:
2218e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2219e4f93234SChuck Lever }
2220e4f93234SChuck Lever 
2221e4f93234SChuck Lever /*
22221da177e4SLinus Torvalds  * Decode RENAME reply
22231da177e4SLinus Torvalds  */
22241da177e4SLinus Torvalds static int
2225e8582a8bSJeff Layton nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
22261da177e4SLinus Torvalds {
22271da177e4SLinus Torvalds 	int	status;
22281da177e4SLinus Torvalds 
22291da177e4SLinus Torvalds 	if ((status = ntohl(*p++)) != 0)
2230856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
2231e8582a8bSJeff Layton 	p = xdr_decode_wcc_data(p, res->old_fattr);
2232e8582a8bSJeff Layton 	p = xdr_decode_wcc_data(p, res->new_fattr);
22331da177e4SLinus Torvalds 	return status;
22341da177e4SLinus Torvalds }
22351da177e4SLinus Torvalds 
22361da177e4SLinus Torvalds /*
2237e4f93234SChuck Lever  * 3.3.14  RENAME3res
2238e4f93234SChuck Lever  *
2239e4f93234SChuck Lever  *	struct RENAME3resok {
2240e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
2241e4f93234SChuck Lever  *		wcc_data	todir_wcc;
2242e4f93234SChuck Lever  *	};
2243e4f93234SChuck Lever  *
2244e4f93234SChuck Lever  *	struct RENAME3resfail {
2245e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
2246e4f93234SChuck Lever  *		wcc_data	todir_wcc;
2247e4f93234SChuck Lever  *	};
2248e4f93234SChuck Lever  *
2249e4f93234SChuck Lever  *	union RENAME3res switch (nfsstat3 status) {
2250e4f93234SChuck Lever  *	case NFS3_OK:
2251e4f93234SChuck Lever  *		RENAME3resok   resok;
2252e4f93234SChuck Lever  *	default:
2253e4f93234SChuck Lever  *		RENAME3resfail resfail;
2254e4f93234SChuck Lever  *	};
2255e4f93234SChuck Lever  */
2256e4f93234SChuck Lever static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req, __be32 *p,
2257e4f93234SChuck Lever 				   struct nfs_renameres *result)
2258e4f93234SChuck Lever {
2259e4f93234SChuck Lever 	struct xdr_stream xdr;
2260e4f93234SChuck Lever 	enum nfs_stat status;
2261e4f93234SChuck Lever 	int error;
2262e4f93234SChuck Lever 
2263e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2264e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2265e4f93234SChuck Lever 	if (unlikely(error))
2266e4f93234SChuck Lever 		goto out;
2267e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->old_fattr);
2268e4f93234SChuck Lever 	if (unlikely(error))
2269e4f93234SChuck Lever 		goto out;
2270e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->new_fattr);
2271e4f93234SChuck Lever 	if (unlikely(error))
2272e4f93234SChuck Lever 		goto out;
2273e4f93234SChuck Lever 	if (status != NFS3_OK)
2274e4f93234SChuck Lever 		goto out_status;
2275e4f93234SChuck Lever out:
2276e4f93234SChuck Lever 	return error;
2277e4f93234SChuck Lever out_status:
2278e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2279e4f93234SChuck Lever }
2280e4f93234SChuck Lever 
2281e4f93234SChuck Lever /*
22821da177e4SLinus Torvalds  * Decode LINK reply
22831da177e4SLinus Torvalds  */
22841da177e4SLinus Torvalds static int
2285d61005a6SAl Viro nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
22861da177e4SLinus Torvalds {
22871da177e4SLinus Torvalds 	int	status;
22881da177e4SLinus Torvalds 
22891da177e4SLinus Torvalds 	if ((status = ntohl(*p++)) != 0)
2290856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
22911da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
22921da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->dir_attr);
22931da177e4SLinus Torvalds 	return status;
22941da177e4SLinus Torvalds }
22951da177e4SLinus Torvalds 
22961da177e4SLinus Torvalds /*
2297e4f93234SChuck Lever  * 3.3.15  LINK3res
2298e4f93234SChuck Lever  *
2299e4f93234SChuck Lever  *	struct LINK3resok {
2300e4f93234SChuck Lever  *		post_op_attr	file_attributes;
2301e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
2302e4f93234SChuck Lever  *	};
2303e4f93234SChuck Lever  *
2304e4f93234SChuck Lever  *	struct LINK3resfail {
2305e4f93234SChuck Lever  *		post_op_attr	file_attributes;
2306e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
2307e4f93234SChuck Lever  *	};
2308e4f93234SChuck Lever  *
2309e4f93234SChuck Lever  *	union LINK3res switch (nfsstat3 status) {
2310e4f93234SChuck Lever  *	case NFS3_OK:
2311e4f93234SChuck Lever  *		LINK3resok	resok;
2312e4f93234SChuck Lever  *	default:
2313e4f93234SChuck Lever  *		LINK3resfail	resfail;
2314e4f93234SChuck Lever  *	};
2315e4f93234SChuck Lever  */
2316e4f93234SChuck Lever static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, __be32 *p,
2317e4f93234SChuck Lever 				 struct nfs3_linkres *result)
2318e4f93234SChuck Lever {
2319e4f93234SChuck Lever 	struct xdr_stream xdr;
2320e4f93234SChuck Lever 	enum nfs_stat status;
2321e4f93234SChuck Lever 	int error;
2322e4f93234SChuck Lever 
2323e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2324e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2325e4f93234SChuck Lever 	if (unlikely(error))
2326e4f93234SChuck Lever 		goto out;
2327e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
2328e4f93234SChuck Lever 	if (unlikely(error))
2329e4f93234SChuck Lever 		goto out;
2330e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->dir_attr);
2331e4f93234SChuck Lever 	if (unlikely(error))
2332e4f93234SChuck Lever 		goto out;
2333e4f93234SChuck Lever 	if (status != NFS3_OK)
2334e4f93234SChuck Lever 		goto out_status;
2335e4f93234SChuck Lever out:
2336e4f93234SChuck Lever 	return error;
2337e4f93234SChuck Lever out_status:
2338e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2339e4f93234SChuck Lever }
2340e4f93234SChuck Lever 
2341e4f93234SChuck Lever /**
2342e4f93234SChuck Lever  * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
2343e4f93234SChuck Lever  *			the local page cache
2344e4f93234SChuck Lever  * @xdr: XDR stream where entry resides
2345e4f93234SChuck Lever  * @entry: buffer to fill in with entry data
2346e4f93234SChuck Lever  * @server: nfs_server data for this directory
2347e4f93234SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
2348e4f93234SChuck Lever  *
2349e4f93234SChuck Lever  * Returns the position of the next item in the buffer, or an ERR_PTR.
2350e4f93234SChuck Lever  *
2351e4f93234SChuck Lever  * This function is not invoked during READDIR reply decoding, but
2352e4f93234SChuck Lever  * rather whenever an application invokes the getdents(2) system call
2353e4f93234SChuck Lever  * on a directory already in our cache.
2354e4f93234SChuck Lever  *
2355e4f93234SChuck Lever  * 3.3.16  entry3
2356e4f93234SChuck Lever  *
2357e4f93234SChuck Lever  *	struct entry3 {
2358e4f93234SChuck Lever  *		fileid3		fileid;
2359e4f93234SChuck Lever  *		filename3	name;
2360e4f93234SChuck Lever  *		cookie3		cookie;
2361e4f93234SChuck Lever  *		fhandle3	filehandle;
2362e4f93234SChuck Lever  *		post_op_attr3	attributes;
2363e4f93234SChuck Lever  *		entry3		*nextentry;
2364e4f93234SChuck Lever  *	};
2365e4f93234SChuck Lever  *
2366e4f93234SChuck Lever  * 3.3.17  entryplus3
2367e4f93234SChuck Lever  *	struct entryplus3 {
2368e4f93234SChuck Lever  *		fileid3		fileid;
2369e4f93234SChuck Lever  *		filename3	name;
2370e4f93234SChuck Lever  *		cookie3		cookie;
2371e4f93234SChuck Lever  *		post_op_attr	name_attributes;
2372e4f93234SChuck Lever  *		post_op_fh3	name_handle;
2373e4f93234SChuck Lever  *		entryplus3	*nextentry;
2374e4f93234SChuck Lever  *	};
2375e4f93234SChuck Lever  */
2376e4f93234SChuck Lever __be32 *nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
2377e4f93234SChuck Lever 			   struct nfs_server *server, int plus)
2378e4f93234SChuck Lever {
2379e4f93234SChuck Lever 	struct nfs_entry old = *entry;
2380e4f93234SChuck Lever 	__be32 *p;
2381e4f93234SChuck Lever 	int error;
2382e4f93234SChuck Lever 
2383e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
2384e4f93234SChuck Lever 	if (unlikely(p == NULL))
2385e4f93234SChuck Lever 		goto out_overflow;
2386e4f93234SChuck Lever 	if (*p == xdr_zero) {
2387e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
2388e4f93234SChuck Lever 		if (unlikely(p == NULL))
2389e4f93234SChuck Lever 			goto out_overflow;
2390e4f93234SChuck Lever 		if (*p == xdr_zero)
2391e4f93234SChuck Lever 			return ERR_PTR(-EAGAIN);
2392e4f93234SChuck Lever 		entry->eof = 1;
2393e4f93234SChuck Lever 		return ERR_PTR(-EBADCOOKIE);
2394e4f93234SChuck Lever 	}
2395e4f93234SChuck Lever 
2396e4f93234SChuck Lever 	error = decode_fileid3(xdr, &entry->ino);
2397e4f93234SChuck Lever 	if (unlikely(error))
2398e4f93234SChuck Lever 		return ERR_PTR(error);
2399e4f93234SChuck Lever 
2400e4f93234SChuck Lever 	error = decode_inline_filename3(xdr, &entry->name, &entry->len);
2401e4f93234SChuck Lever 	if (unlikely(error))
2402e4f93234SChuck Lever 		return ERR_PTR(error);
2403e4f93234SChuck Lever 
2404e4f93234SChuck Lever 	entry->prev_cookie = entry->cookie;
2405e4f93234SChuck Lever 	error = decode_cookie3(xdr, &entry->cookie);
2406e4f93234SChuck Lever 	if (unlikely(error))
2407e4f93234SChuck Lever 		return ERR_PTR(error);
2408e4f93234SChuck Lever 
2409e4f93234SChuck Lever 	entry->d_type = DT_UNKNOWN;
2410e4f93234SChuck Lever 
2411e4f93234SChuck Lever 	if (plus) {
2412e4f93234SChuck Lever 		entry->fattr->valid = 0;
2413e4f93234SChuck Lever 		error = decode_post_op_attr(xdr, entry->fattr);
2414e4f93234SChuck Lever 		if (unlikely(error))
2415e4f93234SChuck Lever 			return ERR_PTR(error);
2416e4f93234SChuck Lever 		if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
2417e4f93234SChuck Lever 			entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
2418e4f93234SChuck Lever 
2419e4f93234SChuck Lever 		/* In fact, a post_op_fh3: */
2420e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
2421e4f93234SChuck Lever 		if (unlikely(p == NULL))
2422e4f93234SChuck Lever 			goto out_overflow;
2423e4f93234SChuck Lever 		if (*p != xdr_zero) {
2424e4f93234SChuck Lever 			error = decode_nfs_fh3(xdr, entry->fh);
2425e4f93234SChuck Lever 			if (unlikely(error)) {
2426e4f93234SChuck Lever 				if (error == -E2BIG)
2427e4f93234SChuck Lever 					goto out_truncated;
2428e4f93234SChuck Lever 				return ERR_PTR(error);
2429e4f93234SChuck Lever 			}
2430e4f93234SChuck Lever 		} else
2431e4f93234SChuck Lever 			zero_nfs_fh3(entry->fh);
2432e4f93234SChuck Lever 	}
2433e4f93234SChuck Lever 
2434e4f93234SChuck Lever 	/* Peek at the next entry to see if we're at EOD */
2435e4f93234SChuck Lever 	p = xdr_inline_peek(xdr, 4 + 4);
2436e4f93234SChuck Lever 	entry->eof = 0;
2437e4f93234SChuck Lever 	if (p != NULL)
2438e4f93234SChuck Lever 		entry->eof = (p[0] == xdr_zero) && (p[1] != xdr_zero);
2439e4f93234SChuck Lever 	return p;
2440e4f93234SChuck Lever 
2441e4f93234SChuck Lever out_overflow:
2442e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2443e4f93234SChuck Lever 	return ERR_PTR(-EAGAIN);
2444e4f93234SChuck Lever out_truncated:
2445e4f93234SChuck Lever 	dprintk("NFS: directory entry contains invalid file handle\n");
2446e4f93234SChuck Lever 	*entry = old;
2447e4f93234SChuck Lever 	return ERR_PTR(-EAGAIN);
2448e4f93234SChuck Lever }
2449e4f93234SChuck Lever 
2450e4f93234SChuck Lever /*
2451e4f93234SChuck Lever  * 3.3.16  READDIR3res
2452e4f93234SChuck Lever  *
2453e4f93234SChuck Lever  *	struct dirlist3 {
2454e4f93234SChuck Lever  *		entry3		*entries;
2455e4f93234SChuck Lever  *		bool		eof;
2456e4f93234SChuck Lever  *	};
2457e4f93234SChuck Lever  *
2458e4f93234SChuck Lever  *	struct READDIR3resok {
2459e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2460e4f93234SChuck Lever  *		cookieverf3	cookieverf;
2461e4f93234SChuck Lever  *		dirlist3	reply;
2462e4f93234SChuck Lever  *	};
2463e4f93234SChuck Lever  *
2464e4f93234SChuck Lever  *	struct READDIR3resfail {
2465e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2466e4f93234SChuck Lever  *	};
2467e4f93234SChuck Lever  *
2468e4f93234SChuck Lever  *	union READDIR3res switch (nfsstat3 status) {
2469e4f93234SChuck Lever  *	case NFS3_OK:
2470e4f93234SChuck Lever  *		READDIR3resok	resok;
2471e4f93234SChuck Lever  *	default:
2472e4f93234SChuck Lever  *		READDIR3resfail	resfail;
2473e4f93234SChuck Lever  *	};
2474e4f93234SChuck Lever  *
2475e4f93234SChuck Lever  * Read the directory contents into the page cache, but otherwise
2476e4f93234SChuck Lever  * don't touch them.  The actual decoding is done by nfs3_decode_entry()
2477e4f93234SChuck Lever  * during subsequent nfs_readdir() calls.
2478e4f93234SChuck Lever  */
2479e4f93234SChuck Lever static int decode_dirlist3(struct xdr_stream *xdr)
2480e4f93234SChuck Lever {
2481e4f93234SChuck Lever 	u32 recvd, pglen;
2482e4f93234SChuck Lever 	size_t hdrlen;
2483e4f93234SChuck Lever 
2484e4f93234SChuck Lever 	pglen = xdr->buf->page_len;
2485e4f93234SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
2486e4f93234SChuck Lever 	recvd = xdr->buf->len - hdrlen;
2487e4f93234SChuck Lever 	if (unlikely(pglen > recvd))
2488e4f93234SChuck Lever 		goto out_cheating;
2489e4f93234SChuck Lever out:
2490e4f93234SChuck Lever 	xdr_read_pages(xdr, pglen);
2491e4f93234SChuck Lever 	return pglen;
2492e4f93234SChuck Lever out_cheating:
2493e4f93234SChuck Lever 	dprintk("NFS: server cheating in readdir result: "
2494e4f93234SChuck Lever 		"pglen %u > recvd %u\n", pglen, recvd);
2495e4f93234SChuck Lever 	pglen = recvd;
2496e4f93234SChuck Lever 	goto out;
2497e4f93234SChuck Lever }
2498e4f93234SChuck Lever 
2499e4f93234SChuck Lever static int decode_readdir3resok(struct xdr_stream *xdr,
2500e4f93234SChuck Lever 				struct nfs3_readdirres *result)
2501e4f93234SChuck Lever {
2502e4f93234SChuck Lever 	int error;
2503e4f93234SChuck Lever 
2504e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
2505e4f93234SChuck Lever 	if (unlikely(error))
2506e4f93234SChuck Lever 		goto out;
2507e4f93234SChuck Lever 	/* XXX: do we need to check if result->verf != NULL ? */
2508e4f93234SChuck Lever 	error = decode_cookieverf3(xdr, result->verf);
2509e4f93234SChuck Lever 	if (unlikely(error))
2510e4f93234SChuck Lever 		goto out;
2511e4f93234SChuck Lever 	error = decode_dirlist3(xdr);
2512e4f93234SChuck Lever out:
2513e4f93234SChuck Lever 	return error;
2514e4f93234SChuck Lever }
2515e4f93234SChuck Lever 
2516e4f93234SChuck Lever static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req, __be32 *p,
2517e4f93234SChuck Lever 				    struct nfs3_readdirres *result)
2518e4f93234SChuck Lever {
2519e4f93234SChuck Lever 	struct xdr_stream xdr;
2520e4f93234SChuck Lever 	enum nfs_stat status;
2521e4f93234SChuck Lever 	int error;
2522e4f93234SChuck Lever 
2523e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2524e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2525e4f93234SChuck Lever 	if (unlikely(error))
2526e4f93234SChuck Lever 		goto out;
2527e4f93234SChuck Lever 	if (status != NFS3_OK)
2528e4f93234SChuck Lever 		goto out_default;
2529e4f93234SChuck Lever 	error = decode_readdir3resok(&xdr, result);
2530e4f93234SChuck Lever out:
2531e4f93234SChuck Lever 	return error;
2532e4f93234SChuck Lever out_default:
2533e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->dir_attr);
2534e4f93234SChuck Lever 	if (unlikely(error))
2535e4f93234SChuck Lever 		goto out;
2536e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2537e4f93234SChuck Lever }
2538e4f93234SChuck Lever 
2539e4f93234SChuck Lever /*
25401da177e4SLinus Torvalds  * Decode FSSTAT reply
25411da177e4SLinus Torvalds  */
25421da177e4SLinus Torvalds static int
2543d61005a6SAl Viro nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
25441da177e4SLinus Torvalds {
25451da177e4SLinus Torvalds 	int		status;
25461da177e4SLinus Torvalds 
25471da177e4SLinus Torvalds 	status = ntohl(*p++);
25481da177e4SLinus Torvalds 
25491da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
25501da177e4SLinus Torvalds 	if (status != 0)
2551856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
25521da177e4SLinus Torvalds 
25531da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->tbytes);
25541da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->fbytes);
25551da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->abytes);
25561da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->tfiles);
25571da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->ffiles);
25581da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->afiles);
25591da177e4SLinus Torvalds 
25601da177e4SLinus Torvalds 	/* ignore invarsec */
25611da177e4SLinus Torvalds 	return 0;
25621da177e4SLinus Torvalds }
25631da177e4SLinus Torvalds 
25641da177e4SLinus Torvalds /*
2565e4f93234SChuck Lever  * 3.3.18  FSSTAT3res
2566e4f93234SChuck Lever  *
2567e4f93234SChuck Lever  *	struct FSSTAT3resok {
2568e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2569e4f93234SChuck Lever  *		size3		tbytes;
2570e4f93234SChuck Lever  *		size3		fbytes;
2571e4f93234SChuck Lever  *		size3		abytes;
2572e4f93234SChuck Lever  *		size3		tfiles;
2573e4f93234SChuck Lever  *		size3		ffiles;
2574e4f93234SChuck Lever  *		size3		afiles;
2575e4f93234SChuck Lever  *		uint32		invarsec;
2576e4f93234SChuck Lever  *	};
2577e4f93234SChuck Lever  *
2578e4f93234SChuck Lever  *	struct FSSTAT3resfail {
2579e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2580e4f93234SChuck Lever  *	};
2581e4f93234SChuck Lever  *
2582e4f93234SChuck Lever  *	union FSSTAT3res switch (nfsstat3 status) {
2583e4f93234SChuck Lever  *	case NFS3_OK:
2584e4f93234SChuck Lever  *		FSSTAT3resok	resok;
2585e4f93234SChuck Lever  *	default:
2586e4f93234SChuck Lever  *		FSSTAT3resfail	resfail;
2587e4f93234SChuck Lever  *	};
2588e4f93234SChuck Lever  */
2589e4f93234SChuck Lever static int decode_fsstat3resok(struct xdr_stream *xdr,
2590e4f93234SChuck Lever 			       struct nfs_fsstat *result)
2591e4f93234SChuck Lever {
2592e4f93234SChuck Lever 	__be32 *p;
2593e4f93234SChuck Lever 
2594e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8 * 6 + 4);
2595e4f93234SChuck Lever 	if (unlikely(p == NULL))
2596e4f93234SChuck Lever 		goto out_overflow;
2597e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tbytes);
2598e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->fbytes);
2599e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->abytes);
2600e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tfiles);
2601e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->ffiles);
2602e4f93234SChuck Lever 	xdr_decode_size3(p, &result->afiles);
2603e4f93234SChuck Lever 	/* ignore invarsec */
2604e4f93234SChuck Lever 	return 0;
2605e4f93234SChuck Lever out_overflow:
2606e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2607e4f93234SChuck Lever 	return -EIO;
2608e4f93234SChuck Lever }
2609e4f93234SChuck Lever 
2610e4f93234SChuck Lever static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req, __be32 *p,
2611e4f93234SChuck Lever 				   struct nfs_fsstat *result)
2612e4f93234SChuck Lever {
2613e4f93234SChuck Lever 	struct xdr_stream xdr;
2614e4f93234SChuck Lever 	enum nfs_stat status;
2615e4f93234SChuck Lever 	int error;
2616e4f93234SChuck Lever 
2617e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2618e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2619e4f93234SChuck Lever 	if (unlikely(error))
2620e4f93234SChuck Lever 		goto out;
2621e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
2622e4f93234SChuck Lever 	if (unlikely(error))
2623e4f93234SChuck Lever 		goto out;
2624e4f93234SChuck Lever 	if (status != NFS3_OK)
2625e4f93234SChuck Lever 		goto out_status;
2626e4f93234SChuck Lever 	error = decode_fsstat3resok(&xdr, result);
2627e4f93234SChuck Lever out:
2628e4f93234SChuck Lever 	return error;
2629e4f93234SChuck Lever out_status:
2630e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2631e4f93234SChuck Lever }
2632e4f93234SChuck Lever 
2633e4f93234SChuck Lever /*
26341da177e4SLinus Torvalds  * Decode FSINFO reply
26351da177e4SLinus Torvalds  */
26361da177e4SLinus Torvalds static int
2637d61005a6SAl Viro nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
26381da177e4SLinus Torvalds {
26391da177e4SLinus Torvalds 	int		status;
26401da177e4SLinus Torvalds 
26411da177e4SLinus Torvalds 	status = ntohl(*p++);
26421da177e4SLinus Torvalds 
26431da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
26441da177e4SLinus Torvalds 	if (status != 0)
2645856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
26461da177e4SLinus Torvalds 
26471da177e4SLinus Torvalds 	res->rtmax  = ntohl(*p++);
26481da177e4SLinus Torvalds 	res->rtpref = ntohl(*p++);
26491da177e4SLinus Torvalds 	res->rtmult = ntohl(*p++);
26501da177e4SLinus Torvalds 	res->wtmax  = ntohl(*p++);
26511da177e4SLinus Torvalds 	res->wtpref = ntohl(*p++);
26521da177e4SLinus Torvalds 	res->wtmult = ntohl(*p++);
26531da177e4SLinus Torvalds 	res->dtpref = ntohl(*p++);
26541da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->maxfilesize);
26556b96724eSRicardo Labiaga 	p = xdr_decode_time3(p, &res->time_delta);
26561da177e4SLinus Torvalds 
26576b96724eSRicardo Labiaga 	/* ignore properties */
26581da177e4SLinus Torvalds 	res->lease_time = 0;
26591da177e4SLinus Torvalds 	return 0;
26601da177e4SLinus Torvalds }
26611da177e4SLinus Torvalds 
26621da177e4SLinus Torvalds /*
2663e4f93234SChuck Lever  * 3.3.19  FSINFO3res
2664e4f93234SChuck Lever  *
2665e4f93234SChuck Lever  *	struct FSINFO3resok {
2666e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2667e4f93234SChuck Lever  *		uint32		rtmax;
2668e4f93234SChuck Lever  *		uint32		rtpref;
2669e4f93234SChuck Lever  *		uint32		rtmult;
2670e4f93234SChuck Lever  *		uint32		wtmax;
2671e4f93234SChuck Lever  *		uint32		wtpref;
2672e4f93234SChuck Lever  *		uint32		wtmult;
2673e4f93234SChuck Lever  *		uint32		dtpref;
2674e4f93234SChuck Lever  *		size3		maxfilesize;
2675e4f93234SChuck Lever  *		nfstime3	time_delta;
2676e4f93234SChuck Lever  *		uint32		properties;
2677e4f93234SChuck Lever  *	};
2678e4f93234SChuck Lever  *
2679e4f93234SChuck Lever  *	struct FSINFO3resfail {
2680e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2681e4f93234SChuck Lever  *	};
2682e4f93234SChuck Lever  *
2683e4f93234SChuck Lever  *	union FSINFO3res switch (nfsstat3 status) {
2684e4f93234SChuck Lever  *	case NFS3_OK:
2685e4f93234SChuck Lever  *		FSINFO3resok	resok;
2686e4f93234SChuck Lever  *	default:
2687e4f93234SChuck Lever  *		FSINFO3resfail	resfail;
2688e4f93234SChuck Lever  *	};
2689e4f93234SChuck Lever  */
2690e4f93234SChuck Lever static int decode_fsinfo3resok(struct xdr_stream *xdr,
2691e4f93234SChuck Lever 			       struct nfs_fsinfo *result)
2692e4f93234SChuck Lever {
2693e4f93234SChuck Lever 	__be32 *p;
2694e4f93234SChuck Lever 
2695e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4);
2696e4f93234SChuck Lever 	if (unlikely(p == NULL))
2697e4f93234SChuck Lever 		goto out_overflow;
2698e4f93234SChuck Lever 	result->rtmax  = be32_to_cpup(p++);
2699e4f93234SChuck Lever 	result->rtpref = be32_to_cpup(p++);
2700e4f93234SChuck Lever 	result->rtmult = be32_to_cpup(p++);
2701e4f93234SChuck Lever 	result->wtmax  = be32_to_cpup(p++);
2702e4f93234SChuck Lever 	result->wtpref = be32_to_cpup(p++);
2703e4f93234SChuck Lever 	result->wtmult = be32_to_cpup(p++);
2704e4f93234SChuck Lever 	result->dtpref = be32_to_cpup(p++);
2705e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->maxfilesize);
2706e4f93234SChuck Lever 	xdr_decode_time3(p, &result->time_delta);
2707e4f93234SChuck Lever 
2708e4f93234SChuck Lever 	/* ignore properties */
2709e4f93234SChuck Lever 	result->lease_time = 0;
2710e4f93234SChuck Lever 	return 0;
2711e4f93234SChuck Lever out_overflow:
2712e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2713e4f93234SChuck Lever 	return -EIO;
2714e4f93234SChuck Lever }
2715e4f93234SChuck Lever 
2716e4f93234SChuck Lever static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req, __be32 *p,
2717e4f93234SChuck Lever 				   struct nfs_fsinfo *result)
2718e4f93234SChuck Lever {
2719e4f93234SChuck Lever 	struct xdr_stream xdr;
2720e4f93234SChuck Lever 	enum nfs_stat status;
2721e4f93234SChuck Lever 	int error;
2722e4f93234SChuck Lever 
2723e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2724e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2725e4f93234SChuck Lever 	if (unlikely(error))
2726e4f93234SChuck Lever 		goto out;
2727e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
2728e4f93234SChuck Lever 	if (unlikely(error))
2729e4f93234SChuck Lever 		goto out;
2730e4f93234SChuck Lever 	if (status != NFS3_OK)
2731e4f93234SChuck Lever 		goto out_status;
2732e4f93234SChuck Lever 	error = decode_fsinfo3resok(&xdr, result);
2733e4f93234SChuck Lever out:
2734e4f93234SChuck Lever 	return error;
2735e4f93234SChuck Lever out_status:
2736e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2737e4f93234SChuck Lever }
2738e4f93234SChuck Lever 
2739e4f93234SChuck Lever /*
27401da177e4SLinus Torvalds  * Decode PATHCONF reply
27411da177e4SLinus Torvalds  */
27421da177e4SLinus Torvalds static int
2743d61005a6SAl Viro nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
27441da177e4SLinus Torvalds {
27451da177e4SLinus Torvalds 	int		status;
27461da177e4SLinus Torvalds 
27471da177e4SLinus Torvalds 	status = ntohl(*p++);
27481da177e4SLinus Torvalds 
27491da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
27501da177e4SLinus Torvalds 	if (status != 0)
2751856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
27521da177e4SLinus Torvalds 	res->max_link = ntohl(*p++);
27531da177e4SLinus Torvalds 	res->max_namelen = ntohl(*p++);
27541da177e4SLinus Torvalds 
27551da177e4SLinus Torvalds 	/* ignore remaining fields */
27561da177e4SLinus Torvalds 	return 0;
27571da177e4SLinus Torvalds }
27581da177e4SLinus Torvalds 
27591da177e4SLinus Torvalds /*
2760e4f93234SChuck Lever  * 3.3.20  PATHCONF3res
2761e4f93234SChuck Lever  *
2762e4f93234SChuck Lever  *	struct PATHCONF3resok {
2763e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2764e4f93234SChuck Lever  *		uint32		linkmax;
2765e4f93234SChuck Lever  *		uint32		name_max;
2766e4f93234SChuck Lever  *		bool		no_trunc;
2767e4f93234SChuck Lever  *		bool		chown_restricted;
2768e4f93234SChuck Lever  *		bool		case_insensitive;
2769e4f93234SChuck Lever  *		bool		case_preserving;
2770e4f93234SChuck Lever  *	};
2771e4f93234SChuck Lever  *
2772e4f93234SChuck Lever  *	struct PATHCONF3resfail {
2773e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2774e4f93234SChuck Lever  *	};
2775e4f93234SChuck Lever  *
2776e4f93234SChuck Lever  *	union PATHCONF3res switch (nfsstat3 status) {
2777e4f93234SChuck Lever  *	case NFS3_OK:
2778e4f93234SChuck Lever  *		PATHCONF3resok	resok;
2779e4f93234SChuck Lever  *	default:
2780e4f93234SChuck Lever  *		PATHCONF3resfail resfail;
2781e4f93234SChuck Lever  *	};
2782e4f93234SChuck Lever  */
2783e4f93234SChuck Lever static int decode_pathconf3resok(struct xdr_stream *xdr,
2784e4f93234SChuck Lever 				 struct nfs_pathconf *result)
2785e4f93234SChuck Lever {
2786e4f93234SChuck Lever 	__be32 *p;
2787e4f93234SChuck Lever 
2788e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 6);
2789e4f93234SChuck Lever 	if (unlikely(p == NULL))
2790e4f93234SChuck Lever 		goto out_overflow;
2791e4f93234SChuck Lever 	result->max_link = be32_to_cpup(p++);
2792e4f93234SChuck Lever 	result->max_namelen = be32_to_cpup(p);
2793e4f93234SChuck Lever 	/* ignore remaining fields */
2794e4f93234SChuck Lever 	return 0;
2795e4f93234SChuck Lever out_overflow:
2796e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2797e4f93234SChuck Lever 	return -EIO;
2798e4f93234SChuck Lever }
2799e4f93234SChuck Lever 
2800e4f93234SChuck Lever static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req, __be32 *p,
2801e4f93234SChuck Lever 				     struct nfs_pathconf *result)
2802e4f93234SChuck Lever {
2803e4f93234SChuck Lever 	struct xdr_stream xdr;
2804e4f93234SChuck Lever 	enum nfs_stat status;
2805e4f93234SChuck Lever 	int error;
2806e4f93234SChuck Lever 
2807e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2808e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2809e4f93234SChuck Lever 	if (unlikely(error))
2810e4f93234SChuck Lever 		goto out;
2811e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result->fattr);
2812e4f93234SChuck Lever 	if (unlikely(error))
2813e4f93234SChuck Lever 		goto out;
2814e4f93234SChuck Lever 	if (status != NFS3_OK)
2815e4f93234SChuck Lever 		goto out_status;
2816e4f93234SChuck Lever 	error = decode_pathconf3resok(&xdr, result);
2817e4f93234SChuck Lever out:
2818e4f93234SChuck Lever 	return error;
2819e4f93234SChuck Lever out_status:
2820e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2821e4f93234SChuck Lever }
2822e4f93234SChuck Lever 
2823e4f93234SChuck Lever /*
28241da177e4SLinus Torvalds  * Decode COMMIT reply
28251da177e4SLinus Torvalds  */
28261da177e4SLinus Torvalds static int
2827d61005a6SAl Viro nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
28281da177e4SLinus Torvalds {
28291da177e4SLinus Torvalds 	int		status;
28301da177e4SLinus Torvalds 
28311da177e4SLinus Torvalds 	status = ntohl(*p++);
28321da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->fattr);
28331da177e4SLinus Torvalds 	if (status != 0)
2834856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
28351da177e4SLinus Torvalds 
28361da177e4SLinus Torvalds 	res->verf->verifier[0] = *p++;
28371da177e4SLinus Torvalds 	res->verf->verifier[1] = *p++;
28381da177e4SLinus Torvalds 	return 0;
28391da177e4SLinus Torvalds }
28401da177e4SLinus Torvalds 
2841e4f93234SChuck Lever /*
2842e4f93234SChuck Lever  * 3.3.21  COMMIT3res
2843e4f93234SChuck Lever  *
2844e4f93234SChuck Lever  *	struct COMMIT3resok {
2845e4f93234SChuck Lever  *		wcc_data	file_wcc;
2846e4f93234SChuck Lever  *		writeverf3	verf;
2847e4f93234SChuck Lever  *	};
2848e4f93234SChuck Lever  *
2849e4f93234SChuck Lever  *	struct COMMIT3resfail {
2850e4f93234SChuck Lever  *		wcc_data	file_wcc;
2851e4f93234SChuck Lever  *	};
2852e4f93234SChuck Lever  *
2853e4f93234SChuck Lever  *	union COMMIT3res switch (nfsstat3 status) {
2854e4f93234SChuck Lever  *	case NFS3_OK:
2855e4f93234SChuck Lever  *		COMMIT3resok	resok;
2856e4f93234SChuck Lever  *	default:
2857e4f93234SChuck Lever  *		COMMIT3resfail	resfail;
2858e4f93234SChuck Lever  *	};
2859e4f93234SChuck Lever  */
2860e4f93234SChuck Lever static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, __be32 *p,
2861e4f93234SChuck Lever 				   struct nfs_writeres *result)
2862e4f93234SChuck Lever {
2863e4f93234SChuck Lever 	struct xdr_stream xdr;
2864e4f93234SChuck Lever 	enum nfs_stat status;
2865e4f93234SChuck Lever 	int error;
2866e4f93234SChuck Lever 
2867e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2868e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2869e4f93234SChuck Lever 	if (unlikely(error))
2870e4f93234SChuck Lever 		goto out;
2871e4f93234SChuck Lever 	error = decode_wcc_data(&xdr, result->fattr);
2872e4f93234SChuck Lever 	if (unlikely(error))
2873e4f93234SChuck Lever 		goto out;
2874e4f93234SChuck Lever 	if (status != NFS3_OK)
2875e4f93234SChuck Lever 		goto out_status;
2876e4f93234SChuck Lever 	error = decode_writeverf3(&xdr, result->verf->verifier);
2877e4f93234SChuck Lever out:
2878e4f93234SChuck Lever 	return error;
2879e4f93234SChuck Lever out_status:
2880e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2881e4f93234SChuck Lever }
2882e4f93234SChuck Lever 
2883b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2884b7fa0554SAndreas Gruenbacher /*
2885b7fa0554SAndreas Gruenbacher  * Decode GETACL reply
2886b7fa0554SAndreas Gruenbacher  */
2887b7fa0554SAndreas Gruenbacher static int
2888d61005a6SAl Viro nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
2889b7fa0554SAndreas Gruenbacher 		   struct nfs3_getaclres *res)
2890b7fa0554SAndreas Gruenbacher {
2891b7fa0554SAndreas Gruenbacher 	struct xdr_buf *buf = &req->rq_rcv_buf;
2892b7fa0554SAndreas Gruenbacher 	int status = ntohl(*p++);
2893b7fa0554SAndreas Gruenbacher 	struct posix_acl **acl;
2894b7fa0554SAndreas Gruenbacher 	unsigned int *aclcnt;
2895b7fa0554SAndreas Gruenbacher 	int err, base;
2896b7fa0554SAndreas Gruenbacher 
2897b7fa0554SAndreas Gruenbacher 	if (status != 0)
2898856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
2899b7fa0554SAndreas Gruenbacher 	p = xdr_decode_post_op_attr(p, res->fattr);
2900b7fa0554SAndreas Gruenbacher 	res->mask = ntohl(*p++);
2901b7fa0554SAndreas Gruenbacher 	if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
2902b7fa0554SAndreas Gruenbacher 		return -EINVAL;
2903b7fa0554SAndreas Gruenbacher 	base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
2904b7fa0554SAndreas Gruenbacher 
2905b7fa0554SAndreas Gruenbacher 	acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
2906b7fa0554SAndreas Gruenbacher 	aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
2907b7fa0554SAndreas Gruenbacher 	err = nfsacl_decode(buf, base, aclcnt, acl);
2908b7fa0554SAndreas Gruenbacher 
2909b7fa0554SAndreas Gruenbacher 	acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
2910b7fa0554SAndreas Gruenbacher 	aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
2911b7fa0554SAndreas Gruenbacher 	if (err > 0)
2912b7fa0554SAndreas Gruenbacher 		err = nfsacl_decode(buf, base + err, aclcnt, acl);
2913b7fa0554SAndreas Gruenbacher 	return (err > 0) ? 0 : err;
2914b7fa0554SAndreas Gruenbacher }
2915b7fa0554SAndreas Gruenbacher 
2916e4f93234SChuck Lever static inline int decode_getacl3resok(struct xdr_stream *xdr,
2917e4f93234SChuck Lever 				      struct nfs3_getaclres *result)
2918e4f93234SChuck Lever {
2919e4f93234SChuck Lever 	struct posix_acl **acl;
2920e4f93234SChuck Lever 	unsigned int *aclcnt;
2921e4f93234SChuck Lever 	size_t hdrlen;
2922e4f93234SChuck Lever 	int error;
2923e4f93234SChuck Lever 
2924e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2925e4f93234SChuck Lever 	if (unlikely(error))
2926e4f93234SChuck Lever 		goto out;
2927e4f93234SChuck Lever 	error = decode_uint32(xdr, &result->mask);
2928e4f93234SChuck Lever 	if (unlikely(error))
2929e4f93234SChuck Lever 		goto out;
2930e4f93234SChuck Lever 	error = -EINVAL;
2931e4f93234SChuck Lever 	if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
2932e4f93234SChuck Lever 		goto out;
2933e4f93234SChuck Lever 
2934e4f93234SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
2935e4f93234SChuck Lever 
2936e4f93234SChuck Lever 	acl = NULL;
2937e4f93234SChuck Lever 	if (result->mask & NFS_ACL)
2938e4f93234SChuck Lever 		acl = &result->acl_access;
2939e4f93234SChuck Lever 	aclcnt = NULL;
2940e4f93234SChuck Lever 	if (result->mask & NFS_ACLCNT)
2941e4f93234SChuck Lever 		aclcnt = &result->acl_access_count;
2942e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl);
2943e4f93234SChuck Lever 	if (unlikely(error <= 0))
2944e4f93234SChuck Lever 		goto out;
2945e4f93234SChuck Lever 
2946e4f93234SChuck Lever 	acl = NULL;
2947e4f93234SChuck Lever 	if (result->mask & NFS_DFACL)
2948e4f93234SChuck Lever 		acl = &result->acl_default;
2949e4f93234SChuck Lever 	aclcnt = NULL;
2950e4f93234SChuck Lever 	if (result->mask & NFS_DFACLCNT)
2951e4f93234SChuck Lever 		aclcnt = &result->acl_default_count;
2952e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl);
2953e4f93234SChuck Lever 	if (unlikely(error <= 0))
2954e4f93234SChuck Lever 		return error;
2955e4f93234SChuck Lever 	error = 0;
2956e4f93234SChuck Lever out:
2957e4f93234SChuck Lever 	return error;
2958e4f93234SChuck Lever }
2959e4f93234SChuck Lever 
2960e4f93234SChuck Lever static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req, __be32 *p,
2961e4f93234SChuck Lever 				   struct nfs3_getaclres *result)
2962e4f93234SChuck Lever {
2963e4f93234SChuck Lever 	struct xdr_stream xdr;
2964e4f93234SChuck Lever 	enum nfs_stat status;
2965e4f93234SChuck Lever 	int error;
2966e4f93234SChuck Lever 
2967e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
2968e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
2969e4f93234SChuck Lever 	if (unlikely(error))
2970e4f93234SChuck Lever 		goto out;
2971e4f93234SChuck Lever 	if (status != NFS3_OK)
2972e4f93234SChuck Lever 		goto out_default;
2973e4f93234SChuck Lever 	error = decode_getacl3resok(&xdr, result);
2974e4f93234SChuck Lever out:
2975e4f93234SChuck Lever 	return error;
2976e4f93234SChuck Lever out_default:
2977e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
2978e4f93234SChuck Lever }
2979e4f93234SChuck Lever 
2980b7fa0554SAndreas Gruenbacher /*
2981b7fa0554SAndreas Gruenbacher  * Decode setacl reply.
2982b7fa0554SAndreas Gruenbacher  */
2983b7fa0554SAndreas Gruenbacher static int
2984d61005a6SAl Viro nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
2985b7fa0554SAndreas Gruenbacher {
2986b7fa0554SAndreas Gruenbacher 	int status = ntohl(*p++);
2987b7fa0554SAndreas Gruenbacher 
2988b7fa0554SAndreas Gruenbacher 	if (status)
2989856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
2990b7fa0554SAndreas Gruenbacher 	xdr_decode_post_op_attr(p, fattr);
2991b7fa0554SAndreas Gruenbacher 	return 0;
2992b7fa0554SAndreas Gruenbacher }
2993e4f93234SChuck Lever 
2994e4f93234SChuck Lever static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req, __be32 *p,
2995e4f93234SChuck Lever 				   struct nfs_fattr *result)
2996e4f93234SChuck Lever {
2997e4f93234SChuck Lever 	struct xdr_stream xdr;
2998e4f93234SChuck Lever 	enum nfs_stat status;
2999e4f93234SChuck Lever 	int error;
3000e4f93234SChuck Lever 
3001e4f93234SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
3002e4f93234SChuck Lever 	error = decode_nfsstat3(&xdr, &status);
3003e4f93234SChuck Lever 	if (unlikely(error))
3004e4f93234SChuck Lever 		goto out;
3005e4f93234SChuck Lever 	if (status != NFS3_OK)
3006e4f93234SChuck Lever 		goto out_default;
3007e4f93234SChuck Lever 	error = decode_post_op_attr(&xdr, result);
3008e4f93234SChuck Lever out:
3009e4f93234SChuck Lever 	return error;
3010e4f93234SChuck Lever out_default:
3011e4f93234SChuck Lever 	return nfs_stat_to_errno(status);
3012e4f93234SChuck Lever }
3013e4f93234SChuck Lever 
3014b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
3015b7fa0554SAndreas Gruenbacher 
30161da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
30171da177e4SLinus Torvalds [NFS3PROC_##proc] = {							\
30181da177e4SLinus Torvalds 	.p_proc      = NFS3PROC_##proc,					\
3019ad96b5b5SChuck Lever 	.p_encode    = (kxdrproc_t)nfs3_xdr_enc_##argtype##3args,	\
30201da177e4SLinus Torvalds 	.p_decode    = (kxdrproc_t) nfs3_xdr_##restype,			\
3021ad96b5b5SChuck Lever 	.p_arglen    = NFS3_##argtype##args_sz,				\
30222bea90d4SChuck Lever 	.p_replen    = NFS3_##restype##_sz,				\
3023cc0175c1SChuck Lever 	.p_timer     = timer,						\
3024cc0175c1SChuck Lever 	.p_statidx   = NFS3PROC_##proc,					\
3025cc0175c1SChuck Lever 	.p_name      = #proc,						\
30261da177e4SLinus Torvalds 	}
30271da177e4SLinus Torvalds 
30281da177e4SLinus Torvalds struct rpc_procinfo	nfs3_procedures[] = {
3029ad96b5b5SChuck Lever 	PROC(GETATTR,		getattr,	attrstat,	1),
3030ad96b5b5SChuck Lever 	PROC(SETATTR,		setattr,	wccstat,	0),
3031ad96b5b5SChuck Lever 	PROC(LOOKUP,		lookup,		lookupres,	2),
3032ad96b5b5SChuck Lever 	PROC(ACCESS,		access,		accessres,	1),
3033ad96b5b5SChuck Lever 	PROC(READLINK,		readlink,	readlinkres,	3),
3034ad96b5b5SChuck Lever 	PROC(READ,		read,		readres,	3),
3035ad96b5b5SChuck Lever 	PROC(WRITE,		write,		writeres,	4),
3036ad96b5b5SChuck Lever 	PROC(CREATE,		create,		createres,	0),
3037ad96b5b5SChuck Lever 	PROC(MKDIR,		mkdir,		createres,	0),
3038ad96b5b5SChuck Lever 	PROC(SYMLINK,		symlink,	createres,	0),
3039ad96b5b5SChuck Lever 	PROC(MKNOD,		mknod,		createres,	0),
3040ad96b5b5SChuck Lever 	PROC(REMOVE,		remove,		removeres,	0),
3041ad96b5b5SChuck Lever 	PROC(RMDIR,		lookup,		wccstat,	0),
3042ad96b5b5SChuck Lever 	PROC(RENAME,		rename,		renameres,	0),
3043ad96b5b5SChuck Lever 	PROC(LINK,		link,		linkres,	0),
3044ad96b5b5SChuck Lever 	PROC(READDIR,		readdir,	readdirres,	3),
3045ad96b5b5SChuck Lever 	PROC(READDIRPLUS,	readdirplus,	readdirres,	3),
3046ad96b5b5SChuck Lever 	PROC(FSSTAT,		getattr,	fsstatres,	0),
3047ad96b5b5SChuck Lever 	PROC(FSINFO,		getattr,	fsinfores,	0),
3048ad96b5b5SChuck Lever 	PROC(PATHCONF,		getattr,	pathconfres,	0),
3049ad96b5b5SChuck Lever 	PROC(COMMIT,		commit,		commitres,	5),
30501da177e4SLinus Torvalds };
30511da177e4SLinus Torvalds 
30521da177e4SLinus Torvalds struct rpc_version		nfs_version3 = {
30531da177e4SLinus Torvalds 	.number			= 3,
3054e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
30551da177e4SLinus Torvalds 	.procs			= nfs3_procedures
30561da177e4SLinus Torvalds };
30571da177e4SLinus Torvalds 
3058b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
3059b7fa0554SAndreas Gruenbacher static struct rpc_procinfo	nfs3_acl_procedures[] = {
3060b7fa0554SAndreas Gruenbacher 	[ACLPROC3_GETACL] = {
3061b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_GETACL,
3062ad96b5b5SChuck Lever 		.p_encode = (kxdrproc_t)nfs3_xdr_enc_getacl3args,
3063b7fa0554SAndreas Gruenbacher 		.p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
30642bea90d4SChuck Lever 		.p_arglen = ACL3_getaclargs_sz,
30652bea90d4SChuck Lever 		.p_replen = ACL3_getaclres_sz,
3066b7fa0554SAndreas Gruenbacher 		.p_timer = 1,
3067cc0175c1SChuck Lever 		.p_name = "GETACL",
3068b7fa0554SAndreas Gruenbacher 	},
3069b7fa0554SAndreas Gruenbacher 	[ACLPROC3_SETACL] = {
3070b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_SETACL,
3071ad96b5b5SChuck Lever 		.p_encode = (kxdrproc_t)nfs3_xdr_enc_setacl3args,
3072b7fa0554SAndreas Gruenbacher 		.p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
30732bea90d4SChuck Lever 		.p_arglen = ACL3_setaclargs_sz,
30742bea90d4SChuck Lever 		.p_replen = ACL3_setaclres_sz,
3075b7fa0554SAndreas Gruenbacher 		.p_timer = 0,
3076cc0175c1SChuck Lever 		.p_name = "SETACL",
3077b7fa0554SAndreas Gruenbacher 	},
3078b7fa0554SAndreas Gruenbacher };
3079b7fa0554SAndreas Gruenbacher 
3080b7fa0554SAndreas Gruenbacher struct rpc_version		nfsacl_version3 = {
3081b7fa0554SAndreas Gruenbacher 	.number			= 3,
3082b7fa0554SAndreas Gruenbacher 	.nrprocs		= sizeof(nfs3_acl_procedures)/
3083b7fa0554SAndreas Gruenbacher 				  sizeof(nfs3_acl_procedures[0]),
3084b7fa0554SAndreas Gruenbacher 	.procs			= nfs3_acl_procedures,
3085b7fa0554SAndreas Gruenbacher };
3086b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
3087