xref: /openbmc/linux/fs/nfs/nfs3xdr.c (revision 9d5a6434)
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 
107babddc72SBryan Schumaker static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
108babddc72SBryan Schumaker {
109babddc72SBryan Schumaker 	dprintk("nfs: %s: prematurely hit end of receive buffer. "
110babddc72SBryan Schumaker 		"Remaining buffer length is %tu words.\n",
111babddc72SBryan Schumaker 		func, xdr->end - xdr->p);
112babddc72SBryan Schumaker }
113babddc72SBryan Schumaker 
1141da177e4SLinus Torvalds /*
115d9c407b1SChuck Lever  * While encoding arguments, set up the reply buffer in advance to
116d9c407b1SChuck Lever  * receive reply data directly into the page cache.
117d9c407b1SChuck Lever  */
118d9c407b1SChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
119d9c407b1SChuck Lever 				 unsigned int base, unsigned int len,
120d9c407b1SChuck Lever 				 unsigned int bufsize)
121d9c407b1SChuck Lever {
122d9c407b1SChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
123d9c407b1SChuck Lever 	unsigned int replen;
124d9c407b1SChuck Lever 
125d9c407b1SChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
126d9c407b1SChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
127d9c407b1SChuck Lever }
128d9c407b1SChuck Lever 
129d9c407b1SChuck Lever 
130d9c407b1SChuck Lever /*
1311da177e4SLinus Torvalds  * Common NFS XDR functions as inlines
1321da177e4SLinus Torvalds  */
133d61005a6SAl Viro static inline __be32 *
134d61005a6SAl Viro xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
1351da177e4SLinus Torvalds {
1361da177e4SLinus Torvalds 	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
1371da177e4SLinus Torvalds 		memcpy(fh->data, p, fh->size);
1381da177e4SLinus Torvalds 		return p + XDR_QUADLEN(fh->size);
1391da177e4SLinus Torvalds 	}
1401da177e4SLinus Torvalds 	return NULL;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143babddc72SBryan Schumaker static inline __be32 *
144babddc72SBryan Schumaker xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
145babddc72SBryan Schumaker {
146babddc72SBryan Schumaker 	__be32 *p;
147babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
148babddc72SBryan Schumaker 	if (unlikely(!p))
149babddc72SBryan Schumaker 		goto out_overflow;
150babddc72SBryan Schumaker 	fh->size = ntohl(*p++);
151babddc72SBryan Schumaker 
152babddc72SBryan Schumaker 	if (fh->size <= NFS3_FHSIZE) {
153babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, fh->size);
154babddc72SBryan Schumaker 		if (unlikely(!p))
155babddc72SBryan Schumaker 			goto out_overflow;
156babddc72SBryan Schumaker 		memcpy(fh->data, p, fh->size);
157babddc72SBryan Schumaker 		return p + XDR_QUADLEN(fh->size);
158babddc72SBryan Schumaker 	}
159babddc72SBryan Schumaker 	return NULL;
160babddc72SBryan Schumaker 
161babddc72SBryan Schumaker out_overflow:
162babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
163babddc72SBryan Schumaker 	return ERR_PTR(-EIO);
164babddc72SBryan Schumaker }
165babddc72SBryan Schumaker 
1661da177e4SLinus Torvalds /*
1671da177e4SLinus Torvalds  * Encode/decode time.
1681da177e4SLinus Torvalds  */
169d61005a6SAl Viro static inline __be32 *
170d61005a6SAl Viro xdr_decode_time3(__be32 *p, struct timespec *timep)
1711da177e4SLinus Torvalds {
1721da177e4SLinus Torvalds 	timep->tv_sec = ntohl(*p++);
1731da177e4SLinus Torvalds 	timep->tv_nsec = ntohl(*p++);
1741da177e4SLinus Torvalds 	return p;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
177d61005a6SAl Viro static __be32 *
178d61005a6SAl Viro xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	unsigned int	type, major, minor;
181bca79478STrond Myklebust 	umode_t		fmode;
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	type = ntohl(*p++);
184bca79478STrond Myklebust 	if (type > NF3FIFO)
185bca79478STrond Myklebust 		type = NF3NON;
186bca79478STrond Myklebust 	fmode = nfs_type2fmt[type];
1871da177e4SLinus Torvalds 	fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
1881da177e4SLinus Torvalds 	fattr->nlink = ntohl(*p++);
1891da177e4SLinus Torvalds 	fattr->uid = ntohl(*p++);
1901da177e4SLinus Torvalds 	fattr->gid = ntohl(*p++);
1911da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->size);
1921da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	/* Turn remote device info into Linux-specific dev_t */
1951da177e4SLinus Torvalds 	major = ntohl(*p++);
1961da177e4SLinus Torvalds 	minor = ntohl(*p++);
1971da177e4SLinus Torvalds 	fattr->rdev = MKDEV(major, minor);
1981da177e4SLinus Torvalds 	if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
1991da177e4SLinus Torvalds 		fattr->rdev = 0;
2001da177e4SLinus Torvalds 
2018b4bdcf8STrond Myklebust 	p = xdr_decode_hyper(p, &fattr->fsid.major);
2028b4bdcf8STrond Myklebust 	fattr->fsid.minor = 0;
2031da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->fileid);
2041da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->atime);
2051da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->mtime);
2061da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->ctime);
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	/* Update the mode bits */
2099e6e70f8STrond Myklebust 	fattr->valid |= NFS_ATTR_FATTR_V3;
2101da177e4SLinus Torvalds 	return p;
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
213d61005a6SAl Viro static inline __be32 *
214d61005a6SAl Viro xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &fattr->pre_size);
2171da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->pre_mtime);
2181da177e4SLinus Torvalds 	p = xdr_decode_time3(p, &fattr->pre_ctime);
2199e6e70f8STrond Myklebust 	fattr->valid |= NFS_ATTR_FATTR_PRESIZE
2209e6e70f8STrond Myklebust 		| NFS_ATTR_FATTR_PREMTIME
2219e6e70f8STrond Myklebust 		| NFS_ATTR_FATTR_PRECTIME;
2221da177e4SLinus Torvalds 	return p;
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
225d61005a6SAl Viro static inline __be32 *
226d61005a6SAl Viro xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	if (*p++)
2291da177e4SLinus Torvalds 		p = xdr_decode_fattr(p, fattr);
2301da177e4SLinus Torvalds 	return p;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
233d61005a6SAl Viro static inline __be32 *
234babddc72SBryan Schumaker xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
235babddc72SBryan Schumaker {
236babddc72SBryan Schumaker 	__be32 *p;
237babddc72SBryan Schumaker 
238babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
239babddc72SBryan Schumaker 	if (unlikely(!p))
240babddc72SBryan Schumaker 		goto out_overflow;
241babddc72SBryan Schumaker 	if (ntohl(*p++)) {
242babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, 84);
243babddc72SBryan Schumaker 		if (unlikely(!p))
244babddc72SBryan Schumaker 			goto out_overflow;
245babddc72SBryan Schumaker 		p = xdr_decode_fattr(p, fattr);
246babddc72SBryan Schumaker 	}
247babddc72SBryan Schumaker 	return p;
248babddc72SBryan Schumaker out_overflow:
249babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
250babddc72SBryan Schumaker 	return ERR_PTR(-EIO);
251babddc72SBryan Schumaker }
252babddc72SBryan Schumaker 
253babddc72SBryan Schumaker static inline __be32 *
254d61005a6SAl Viro xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
2551da177e4SLinus Torvalds {
2561da177e4SLinus Torvalds 	if (*p++)
2571da177e4SLinus Torvalds 		return xdr_decode_wcc_attr(p, fattr);
2581da177e4SLinus Torvalds 	return p;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 
262d61005a6SAl Viro static inline __be32 *
263d61005a6SAl Viro xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
2641da177e4SLinus Torvalds {
2651da177e4SLinus Torvalds 	p = xdr_decode_pre_op_attr(p, fattr);
2661da177e4SLinus Torvalds 	return xdr_decode_post_op_attr(p, fattr);
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
269d9c407b1SChuck Lever 
270d9c407b1SChuck Lever /*
271d9c407b1SChuck Lever  * Encode/decode NFSv3 basic data types
272d9c407b1SChuck Lever  *
273d9c407b1SChuck Lever  * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
274d9c407b1SChuck Lever  * "NFS Version 3 Protocol Specification".
275d9c407b1SChuck Lever  *
276d9c407b1SChuck Lever  * Not all basic data types have their own encoding and decoding
277d9c407b1SChuck Lever  * functions.  For run-time efficiency, some data types are encoded
278d9c407b1SChuck Lever  * or decoded inline.
279d9c407b1SChuck Lever  */
280d9c407b1SChuck Lever 
281d9c407b1SChuck Lever static void encode_uint32(struct xdr_stream *xdr, u32 value)
282d9c407b1SChuck Lever {
283d9c407b1SChuck Lever 	__be32 *p = xdr_reserve_space(xdr, 4);
284d9c407b1SChuck Lever 	*p = cpu_to_be32(value);
285d9c407b1SChuck Lever }
286d9c407b1SChuck Lever 
287d9c407b1SChuck Lever /*
288d9c407b1SChuck Lever  * filename3
289d9c407b1SChuck Lever  *
290d9c407b1SChuck Lever  *	typedef string filename3<>;
291d9c407b1SChuck Lever  */
292d9c407b1SChuck Lever static void encode_filename3(struct xdr_stream *xdr,
293d9c407b1SChuck Lever 			     const char *name, u32 length)
294d9c407b1SChuck Lever {
295d9c407b1SChuck Lever 	__be32 *p;
296d9c407b1SChuck Lever 
297d9c407b1SChuck Lever 	BUG_ON(length > NFS3_MAXNAMLEN);
298d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
299d9c407b1SChuck Lever 	xdr_encode_opaque(p, name, length);
300d9c407b1SChuck Lever }
301d9c407b1SChuck Lever 
302d9c407b1SChuck Lever /*
303d9c407b1SChuck Lever  * nfspath3
304d9c407b1SChuck Lever  *
305d9c407b1SChuck Lever  *	typedef string nfspath3<>;
306d9c407b1SChuck Lever  */
307d9c407b1SChuck Lever static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
308d9c407b1SChuck Lever 			    const u32 length)
309d9c407b1SChuck Lever {
310d9c407b1SChuck Lever 	BUG_ON(length > NFS3_MAXPATHLEN);
311d9c407b1SChuck Lever 	encode_uint32(xdr, length);
312d9c407b1SChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
313d9c407b1SChuck Lever }
314d9c407b1SChuck Lever 
315d9c407b1SChuck Lever /*
316d9c407b1SChuck Lever  * cookie3
317d9c407b1SChuck Lever  *
318d9c407b1SChuck Lever  *	typedef uint64 cookie3
319d9c407b1SChuck Lever  */
320d9c407b1SChuck Lever static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
321d9c407b1SChuck Lever {
322d9c407b1SChuck Lever 	return xdr_encode_hyper(p, cookie);
323d9c407b1SChuck Lever }
324d9c407b1SChuck Lever 
325d9c407b1SChuck Lever /*
326d9c407b1SChuck Lever  * cookieverf3
327d9c407b1SChuck Lever  *
328d9c407b1SChuck Lever  *	typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
329d9c407b1SChuck Lever  */
330d9c407b1SChuck Lever static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
331d9c407b1SChuck Lever {
332d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
333d9c407b1SChuck Lever 	return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
334d9c407b1SChuck Lever }
335d9c407b1SChuck Lever 
336d9c407b1SChuck Lever /*
337d9c407b1SChuck Lever  * createverf3
338d9c407b1SChuck Lever  *
339d9c407b1SChuck Lever  *	typedef opaque createverf3[NFS3_CREATEVERFSIZE];
340d9c407b1SChuck Lever  */
341d9c407b1SChuck Lever static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
342d9c407b1SChuck Lever {
343d9c407b1SChuck Lever 	__be32 *p;
344d9c407b1SChuck Lever 
345d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
346d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_CREATEVERFSIZE);
347d9c407b1SChuck Lever }
348d9c407b1SChuck Lever 
349d9c407b1SChuck Lever /*
350d9c407b1SChuck Lever  * ftype3
351d9c407b1SChuck Lever  *
352d9c407b1SChuck Lever  *	enum ftype3 {
353d9c407b1SChuck Lever  *		NF3REG	= 1,
354d9c407b1SChuck Lever  *		NF3DIR	= 2,
355d9c407b1SChuck Lever  *		NF3BLK	= 3,
356d9c407b1SChuck Lever  *		NF3CHR	= 4,
357d9c407b1SChuck Lever  *		NF3LNK	= 5,
358d9c407b1SChuck Lever  *		NF3SOCK	= 6,
359d9c407b1SChuck Lever  *		NF3FIFO	= 7
360d9c407b1SChuck Lever  *	};
361d9c407b1SChuck Lever  */
362d9c407b1SChuck Lever static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
363d9c407b1SChuck Lever {
364d9c407b1SChuck Lever 	BUG_ON(type > NF3FIFO);
365d9c407b1SChuck Lever 	encode_uint32(xdr, type);
366d9c407b1SChuck Lever }
367d9c407b1SChuck Lever 
368d9c407b1SChuck Lever /*
369d9c407b1SChuck Lever  * specdata3
370d9c407b1SChuck Lever  *
371d9c407b1SChuck Lever  *     struct specdata3 {
372d9c407b1SChuck Lever  *             uint32  specdata1;
373d9c407b1SChuck Lever  *             uint32  specdata2;
374d9c407b1SChuck Lever  *     };
375d9c407b1SChuck Lever  */
376d9c407b1SChuck Lever static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
377d9c407b1SChuck Lever {
378d9c407b1SChuck Lever 	__be32 *p;
379d9c407b1SChuck Lever 
380d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8);
381d9c407b1SChuck Lever 	*p++ = cpu_to_be32(MAJOR(rdev));
382d9c407b1SChuck Lever 	*p = cpu_to_be32(MINOR(rdev));
383d9c407b1SChuck Lever }
384d9c407b1SChuck Lever 
385d9c407b1SChuck Lever /*
386d9c407b1SChuck Lever  * nfs_fh3
387d9c407b1SChuck Lever  *
388d9c407b1SChuck Lever  *	struct nfs_fh3 {
389d9c407b1SChuck Lever  *		opaque       data<NFS3_FHSIZE>;
390d9c407b1SChuck Lever  *	};
391d9c407b1SChuck Lever  */
392d9c407b1SChuck Lever static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
393d9c407b1SChuck Lever {
394d9c407b1SChuck Lever 	__be32 *p;
395d9c407b1SChuck Lever 
396d9c407b1SChuck Lever 	BUG_ON(fh->size > NFS3_FHSIZE);
397d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + fh->size);
398d9c407b1SChuck Lever 	xdr_encode_opaque(p, fh->data, fh->size);
399d9c407b1SChuck Lever }
400d9c407b1SChuck Lever 
401d9c407b1SChuck Lever /*
4029d5a6434SChuck Lever  * nfstime3
4039d5a6434SChuck Lever  *
4049d5a6434SChuck Lever  *	struct nfstime3 {
4059d5a6434SChuck Lever  *		uint32	seconds;
4069d5a6434SChuck Lever  *		uint32	nseconds;
4079d5a6434SChuck Lever  *	};
4089d5a6434SChuck Lever  */
4099d5a6434SChuck Lever static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec *timep)
4109d5a6434SChuck Lever {
4119d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
4129d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_nsec);
4139d5a6434SChuck Lever 	return p;
4149d5a6434SChuck Lever }
4159d5a6434SChuck Lever 
4169d5a6434SChuck Lever /*
417d9c407b1SChuck Lever  * sattr3
418d9c407b1SChuck Lever  *
419d9c407b1SChuck Lever  *	enum time_how {
420d9c407b1SChuck Lever  *		DONT_CHANGE		= 0,
421d9c407b1SChuck Lever  *		SET_TO_SERVER_TIME	= 1,
422d9c407b1SChuck Lever  *		SET_TO_CLIENT_TIME	= 2
423d9c407b1SChuck Lever  *	};
424d9c407b1SChuck Lever  *
425d9c407b1SChuck Lever  *	union set_mode3 switch (bool set_it) {
426d9c407b1SChuck Lever  *	case TRUE:
427d9c407b1SChuck Lever  *		mode3	mode;
428d9c407b1SChuck Lever  *	default:
429d9c407b1SChuck Lever  *		void;
430d9c407b1SChuck Lever  *	};
431d9c407b1SChuck Lever  *
432d9c407b1SChuck Lever  *	union set_uid3 switch (bool set_it) {
433d9c407b1SChuck Lever  *	case TRUE:
434d9c407b1SChuck Lever  *		uid3	uid;
435d9c407b1SChuck Lever  *	default:
436d9c407b1SChuck Lever  *		void;
437d9c407b1SChuck Lever  *	};
438d9c407b1SChuck Lever  *
439d9c407b1SChuck Lever  *	union set_gid3 switch (bool set_it) {
440d9c407b1SChuck Lever  *	case TRUE:
441d9c407b1SChuck Lever  *		gid3	gid;
442d9c407b1SChuck Lever  *	default:
443d9c407b1SChuck Lever  *		void;
444d9c407b1SChuck Lever  *	};
445d9c407b1SChuck Lever  *
446d9c407b1SChuck Lever  *	union set_size3 switch (bool set_it) {
447d9c407b1SChuck Lever  *	case TRUE:
448d9c407b1SChuck Lever  *		size3	size;
449d9c407b1SChuck Lever  *	default:
450d9c407b1SChuck Lever  *		void;
451d9c407b1SChuck Lever  *	};
452d9c407b1SChuck Lever  *
453d9c407b1SChuck Lever  *	union set_atime switch (time_how set_it) {
454d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
455d9c407b1SChuck Lever  *		nfstime3	atime;
456d9c407b1SChuck Lever  *	default:
457d9c407b1SChuck Lever  *		void;
458d9c407b1SChuck Lever  *	};
459d9c407b1SChuck Lever  *
460d9c407b1SChuck Lever  *	union set_mtime switch (time_how set_it) {
461d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
462d9c407b1SChuck Lever  *		nfstime3  mtime;
463d9c407b1SChuck Lever  *	default:
464d9c407b1SChuck Lever  *		void;
465d9c407b1SChuck Lever  *	};
466d9c407b1SChuck Lever  *
467d9c407b1SChuck Lever  *	struct sattr3 {
468d9c407b1SChuck Lever  *		set_mode3	mode;
469d9c407b1SChuck Lever  *		set_uid3	uid;
470d9c407b1SChuck Lever  *		set_gid3	gid;
471d9c407b1SChuck Lever  *		set_size3	size;
472d9c407b1SChuck Lever  *		set_atime	atime;
473d9c407b1SChuck Lever  *		set_mtime	mtime;
474d9c407b1SChuck Lever  *	};
475d9c407b1SChuck Lever  */
476d9c407b1SChuck Lever static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
477d9c407b1SChuck Lever {
478d9c407b1SChuck Lever 	u32 nbytes;
479d9c407b1SChuck Lever 	__be32 *p;
480d9c407b1SChuck Lever 
481d9c407b1SChuck Lever 	/*
482d9c407b1SChuck Lever 	 * In order to make only a single xdr_reserve_space() call,
483d9c407b1SChuck Lever 	 * pre-compute the total number of bytes to be reserved.
484d9c407b1SChuck Lever 	 * Six boolean values, one for each set_foo field, are always
485d9c407b1SChuck Lever 	 * present in the encoded result, so start there.
486d9c407b1SChuck Lever 	 */
487d9c407b1SChuck Lever 	nbytes = 6 * 4;
488d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MODE)
489d9c407b1SChuck Lever 		nbytes += 4;
490d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_UID)
491d9c407b1SChuck Lever 		nbytes += 4;
492d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_GID)
493d9c407b1SChuck Lever 		nbytes += 4;
494d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
495d9c407b1SChuck Lever 		nbytes += 8;
496d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
497d9c407b1SChuck Lever 		nbytes += 8;
498d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
499d9c407b1SChuck Lever 		nbytes += 8;
500d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, nbytes);
501d9c407b1SChuck Lever 
5029d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MODE) {
5039d5a6434SChuck Lever 		*p++ = xdr_one;
5049d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
5059d5a6434SChuck Lever 	} else
5069d5a6434SChuck Lever 		*p++ = xdr_zero;
5079d5a6434SChuck Lever 
5089d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_UID) {
5099d5a6434SChuck Lever 		*p++ = xdr_one;
5109d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_uid);
5119d5a6434SChuck Lever 	} else
5129d5a6434SChuck Lever 		*p++ = xdr_zero;
5139d5a6434SChuck Lever 
5149d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_GID) {
5159d5a6434SChuck Lever 		*p++ = xdr_one;
5169d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_gid);
5179d5a6434SChuck Lever 	} else
5189d5a6434SChuck Lever 		*p++ = xdr_zero;
5199d5a6434SChuck Lever 
5209d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_SIZE) {
5219d5a6434SChuck Lever 		*p++ = xdr_one;
5229d5a6434SChuck Lever 		p = xdr_encode_hyper(p, (u64)attr->ia_size);
5239d5a6434SChuck Lever 	} else
5249d5a6434SChuck Lever 		*p++ = xdr_zero;
5259d5a6434SChuck Lever 
5269d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET) {
5279d5a6434SChuck Lever 		*p++ = xdr_two;
5289d5a6434SChuck Lever 		p = xdr_encode_nfstime3(p, &attr->ia_atime);
5299d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_ATIME) {
5309d5a6434SChuck Lever 		*p++ = xdr_one;
5319d5a6434SChuck Lever 	} else
5329d5a6434SChuck Lever 		*p++ = xdr_zero;
5339d5a6434SChuck Lever 
5349d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET) {
5359d5a6434SChuck Lever 		*p++ = xdr_two;
5369d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &attr->ia_mtime);
5379d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_MTIME) {
5389d5a6434SChuck Lever 		*p = xdr_one;
5399d5a6434SChuck Lever 	} else
5409d5a6434SChuck Lever 		*p = xdr_zero;
541d9c407b1SChuck Lever }
542d9c407b1SChuck Lever 
543d9c407b1SChuck Lever /*
544d9c407b1SChuck Lever  * diropargs3
545d9c407b1SChuck Lever  *
546d9c407b1SChuck Lever  *	struct diropargs3 {
547d9c407b1SChuck Lever  *		nfs_fh3		dir;
548d9c407b1SChuck Lever  *		filename3	name;
549d9c407b1SChuck Lever  *	};
550d9c407b1SChuck Lever  */
551d9c407b1SChuck Lever static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
552d9c407b1SChuck Lever 			      const char *name, u32 length)
553d9c407b1SChuck Lever {
554d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, fh);
555d9c407b1SChuck Lever 	encode_filename3(xdr, name, length);
556d9c407b1SChuck Lever }
557d9c407b1SChuck Lever 
558d9c407b1SChuck Lever 
5591da177e4SLinus Torvalds /*
560499ff710SChuck Lever  * NFSv3 XDR encode functions
561499ff710SChuck Lever  *
562499ff710SChuck Lever  * NFSv3 argument types are defined in section 3.3 of RFC 1813:
563499ff710SChuck Lever  * "NFS Version 3 Protocol Specification".
5641da177e4SLinus Torvalds  */
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds /*
567d9c407b1SChuck Lever  * 3.3.1  GETATTR3args
568d9c407b1SChuck Lever  *
569d9c407b1SChuck Lever  *	struct GETATTR3args {
570d9c407b1SChuck Lever  *		nfs_fh3  object;
571d9c407b1SChuck Lever  *	};
572d9c407b1SChuck Lever  */
573d9c407b1SChuck Lever static int nfs3_xdr_enc_getattr3args(struct rpc_rqst *req, __be32 *p,
574d9c407b1SChuck Lever 				     const struct nfs_fh *fh)
575d9c407b1SChuck Lever {
576d9c407b1SChuck Lever 	struct xdr_stream xdr;
577d9c407b1SChuck Lever 
578d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
579d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, fh);
580d9c407b1SChuck Lever 	return 0;
581d9c407b1SChuck Lever }
582d9c407b1SChuck Lever 
583d9c407b1SChuck Lever /*
584d9c407b1SChuck Lever  * 3.3.2  SETATTR3args
585d9c407b1SChuck Lever  *
586d9c407b1SChuck Lever  *	union sattrguard3 switch (bool check) {
587d9c407b1SChuck Lever  *	case TRUE:
588d9c407b1SChuck Lever  *		nfstime3  obj_ctime;
589d9c407b1SChuck Lever  *	case FALSE:
590d9c407b1SChuck Lever  *		void;
591d9c407b1SChuck Lever  *	};
592d9c407b1SChuck Lever  *
593d9c407b1SChuck Lever  *	struct SETATTR3args {
594d9c407b1SChuck Lever  *		nfs_fh3		object;
595d9c407b1SChuck Lever  *		sattr3		new_attributes;
596d9c407b1SChuck Lever  *		sattrguard3	guard;
597d9c407b1SChuck Lever  *	};
598d9c407b1SChuck Lever  */
599d9c407b1SChuck Lever static void encode_sattrguard3(struct xdr_stream *xdr,
600d9c407b1SChuck Lever 			       const struct nfs3_sattrargs *args)
601d9c407b1SChuck Lever {
602d9c407b1SChuck Lever 	__be32 *p;
603d9c407b1SChuck Lever 
604d9c407b1SChuck Lever 	if (args->guard) {
605d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4 + 8);
606d9c407b1SChuck Lever 		*p++ = xdr_one;
6079d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &args->guardtime);
608d9c407b1SChuck Lever 	} else {
609d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4);
610d9c407b1SChuck Lever 		*p = xdr_zero;
611d9c407b1SChuck Lever 	}
612d9c407b1SChuck Lever }
613d9c407b1SChuck Lever 
614d9c407b1SChuck Lever static int nfs3_xdr_enc_setattr3args(struct rpc_rqst *req, __be32 *p,
615d9c407b1SChuck Lever 				     const struct nfs3_sattrargs *args)
616d9c407b1SChuck Lever {
617d9c407b1SChuck Lever 	struct xdr_stream xdr;
618d9c407b1SChuck Lever 
619d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
620d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fh);
621d9c407b1SChuck Lever 	encode_sattr3(&xdr, args->sattr);
622d9c407b1SChuck Lever 	encode_sattrguard3(&xdr, args);
623d9c407b1SChuck Lever 	return 0;
624d9c407b1SChuck Lever }
625d9c407b1SChuck Lever 
626d9c407b1SChuck Lever /*
627d9c407b1SChuck Lever  * 3.3.3  LOOKUP3args
628d9c407b1SChuck Lever  *
629d9c407b1SChuck Lever  *	struct LOOKUP3args {
630d9c407b1SChuck Lever  *		diropargs3  what;
631d9c407b1SChuck Lever  *	};
632d9c407b1SChuck Lever  */
633d9c407b1SChuck Lever static int nfs3_xdr_enc_lookup3args(struct rpc_rqst *req, __be32 *p,
634d9c407b1SChuck Lever 				    const struct nfs3_diropargs *args)
635d9c407b1SChuck Lever {
636d9c407b1SChuck Lever 	struct xdr_stream xdr;
637d9c407b1SChuck Lever 
638d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
639d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
640d9c407b1SChuck Lever 	return 0;
641d9c407b1SChuck Lever }
642d9c407b1SChuck Lever 
643d9c407b1SChuck Lever /*
644d9c407b1SChuck Lever  * 3.3.4  ACCESS3args
645d9c407b1SChuck Lever  *
646d9c407b1SChuck Lever  *	struct ACCESS3args {
647d9c407b1SChuck Lever  *		nfs_fh3		object;
648d9c407b1SChuck Lever  *		uint32		access;
649d9c407b1SChuck Lever  *	};
650d9c407b1SChuck Lever  */
651d9c407b1SChuck Lever static void encode_access3args(struct xdr_stream *xdr,
652d9c407b1SChuck Lever 			       const struct nfs3_accessargs *args)
653d9c407b1SChuck Lever {
654d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
655d9c407b1SChuck Lever 	encode_uint32(xdr, args->access);
656d9c407b1SChuck Lever }
657d9c407b1SChuck Lever 
658d9c407b1SChuck Lever static int nfs3_xdr_enc_access3args(struct rpc_rqst *req, __be32 *p,
659d9c407b1SChuck Lever 				    const struct nfs3_accessargs *args)
660d9c407b1SChuck Lever {
661d9c407b1SChuck Lever 	struct xdr_stream xdr;
662d9c407b1SChuck Lever 
663d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
664d9c407b1SChuck Lever 	encode_access3args(&xdr, args);
665d9c407b1SChuck Lever 	return 0;
666d9c407b1SChuck Lever }
667d9c407b1SChuck Lever 
668d9c407b1SChuck Lever /*
669d9c407b1SChuck Lever  * 3.3.5  READLINK3args
670d9c407b1SChuck Lever  *
671d9c407b1SChuck Lever  *	struct READLINK3args {
672d9c407b1SChuck Lever  *		nfs_fh3	symlink;
673d9c407b1SChuck Lever  *	};
674d9c407b1SChuck Lever  */
675d9c407b1SChuck Lever static int nfs3_xdr_enc_readlink3args(struct rpc_rqst *req, __be32 *p,
676d9c407b1SChuck Lever 				      const struct nfs3_readlinkargs *args)
677d9c407b1SChuck Lever {
678d9c407b1SChuck Lever 	struct xdr_stream xdr;
679d9c407b1SChuck Lever 
680d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
681d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fh);
682d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
683d9c407b1SChuck Lever 					args->pglen, NFS3_readlinkres_sz);
684d9c407b1SChuck Lever 	return 0;
685d9c407b1SChuck Lever }
686d9c407b1SChuck Lever 
687d9c407b1SChuck Lever /*
688d9c407b1SChuck Lever  * 3.3.6  READ3args
689d9c407b1SChuck Lever  *
690d9c407b1SChuck Lever  *	struct READ3args {
691d9c407b1SChuck Lever  *		nfs_fh3		file;
692d9c407b1SChuck Lever  *		offset3		offset;
693d9c407b1SChuck Lever  *		count3		count;
694d9c407b1SChuck Lever  *	};
695d9c407b1SChuck Lever  */
696d9c407b1SChuck Lever static void encode_read3args(struct xdr_stream *xdr,
697d9c407b1SChuck Lever 			     const struct nfs_readargs *args)
698d9c407b1SChuck Lever {
699d9c407b1SChuck Lever 	__be32 *p;
700d9c407b1SChuck Lever 
701d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
702d9c407b1SChuck Lever 
703d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
704d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
705d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
706d9c407b1SChuck Lever }
707d9c407b1SChuck Lever 
708d9c407b1SChuck Lever static int nfs3_xdr_enc_read3args(struct rpc_rqst *req, __be32 *p,
709d9c407b1SChuck Lever 				  const struct nfs_readargs *args)
710d9c407b1SChuck Lever {
711d9c407b1SChuck Lever 	struct xdr_stream xdr;
712d9c407b1SChuck Lever 
713d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
714d9c407b1SChuck Lever 	encode_read3args(&xdr, args);
715d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
716d9c407b1SChuck Lever 					args->count, NFS3_readres_sz);
717d9c407b1SChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
718d9c407b1SChuck Lever 	return 0;
719d9c407b1SChuck Lever }
720d9c407b1SChuck Lever 
721d9c407b1SChuck Lever /*
722d9c407b1SChuck Lever  * 3.3.7  WRITE3args
723d9c407b1SChuck Lever  *
724d9c407b1SChuck Lever  *	enum stable_how {
725d9c407b1SChuck Lever  *		UNSTABLE  = 0,
726d9c407b1SChuck Lever  *		DATA_SYNC = 1,
727d9c407b1SChuck Lever  *		FILE_SYNC = 2
728d9c407b1SChuck Lever  *	};
729d9c407b1SChuck Lever  *
730d9c407b1SChuck Lever  *	struct WRITE3args {
731d9c407b1SChuck Lever  *		nfs_fh3		file;
732d9c407b1SChuck Lever  *		offset3		offset;
733d9c407b1SChuck Lever  *		count3		count;
734d9c407b1SChuck Lever  *		stable_how	stable;
735d9c407b1SChuck Lever  *		opaque		data<>;
736d9c407b1SChuck Lever  *	};
737d9c407b1SChuck Lever  */
738d9c407b1SChuck Lever static void encode_write3args(struct xdr_stream *xdr,
739d9c407b1SChuck Lever 			      const struct nfs_writeargs *args)
740d9c407b1SChuck Lever {
741d9c407b1SChuck Lever 	__be32 *p;
742d9c407b1SChuck Lever 
743d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
744d9c407b1SChuck Lever 
745d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
746d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
747d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count);
748d9c407b1SChuck Lever 
749d9c407b1SChuck Lever 	BUG_ON(args->stable > NFS_FILE_SYNC);
750d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->stable);
751d9c407b1SChuck Lever 
752d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
753d9c407b1SChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
754d9c407b1SChuck Lever }
755d9c407b1SChuck Lever 
756d9c407b1SChuck Lever static int nfs3_xdr_enc_write3args(struct rpc_rqst *req, __be32 *p,
757d9c407b1SChuck Lever 				   const struct nfs_writeargs *args)
758d9c407b1SChuck Lever {
759d9c407b1SChuck Lever 	struct xdr_stream xdr;
760d9c407b1SChuck Lever 
761d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
762d9c407b1SChuck Lever 	encode_write3args(&xdr, args);
763d9c407b1SChuck Lever 	xdr.buf->flags |= XDRBUF_WRITE;
764d9c407b1SChuck Lever 	return 0;
765d9c407b1SChuck Lever }
766d9c407b1SChuck Lever 
767d9c407b1SChuck Lever /*
768d9c407b1SChuck Lever  * 3.3.8  CREATE3args
769d9c407b1SChuck Lever  *
770d9c407b1SChuck Lever  *	enum createmode3 {
771d9c407b1SChuck Lever  *		UNCHECKED = 0,
772d9c407b1SChuck Lever  *		GUARDED   = 1,
773d9c407b1SChuck Lever  *		EXCLUSIVE = 2
774d9c407b1SChuck Lever  *	};
775d9c407b1SChuck Lever  *
776d9c407b1SChuck Lever  *	union createhow3 switch (createmode3 mode) {
777d9c407b1SChuck Lever  *	case UNCHECKED:
778d9c407b1SChuck Lever  *	case GUARDED:
779d9c407b1SChuck Lever  *		sattr3       obj_attributes;
780d9c407b1SChuck Lever  *	case EXCLUSIVE:
781d9c407b1SChuck Lever  *		createverf3  verf;
782d9c407b1SChuck Lever  *	};
783d9c407b1SChuck Lever  *
784d9c407b1SChuck Lever  *	struct CREATE3args {
785d9c407b1SChuck Lever  *		diropargs3	where;
786d9c407b1SChuck Lever  *		createhow3	how;
787d9c407b1SChuck Lever  *	};
788d9c407b1SChuck Lever  */
789d9c407b1SChuck Lever static void encode_createhow3(struct xdr_stream *xdr,
790d9c407b1SChuck Lever 			      const struct nfs3_createargs *args)
791d9c407b1SChuck Lever {
792d9c407b1SChuck Lever 	encode_uint32(xdr, args->createmode);
793d9c407b1SChuck Lever 	switch (args->createmode) {
794d9c407b1SChuck Lever 	case NFS3_CREATE_UNCHECKED:
795d9c407b1SChuck Lever 	case NFS3_CREATE_GUARDED:
796d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
797d9c407b1SChuck Lever 		break;
798d9c407b1SChuck Lever 	case NFS3_CREATE_EXCLUSIVE:
799d9c407b1SChuck Lever 		encode_createverf3(xdr, args->verifier);
800d9c407b1SChuck Lever 		break;
801d9c407b1SChuck Lever 	default:
802d9c407b1SChuck Lever 		BUG();
803d9c407b1SChuck Lever 	}
804d9c407b1SChuck Lever }
805d9c407b1SChuck Lever 
806d9c407b1SChuck Lever static int nfs3_xdr_enc_create3args(struct rpc_rqst *req, __be32 *p,
807d9c407b1SChuck Lever 				    const struct nfs3_createargs *args)
808d9c407b1SChuck Lever {
809d9c407b1SChuck Lever 	struct xdr_stream xdr;
810d9c407b1SChuck Lever 
811d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
812d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
813d9c407b1SChuck Lever 	encode_createhow3(&xdr, args);
814d9c407b1SChuck Lever 	return 0;
815d9c407b1SChuck Lever }
816d9c407b1SChuck Lever 
817d9c407b1SChuck Lever /*
818d9c407b1SChuck Lever  * 3.3.9  MKDIR3args
819d9c407b1SChuck Lever  *
820d9c407b1SChuck Lever  *	struct MKDIR3args {
821d9c407b1SChuck Lever  *		diropargs3	where;
822d9c407b1SChuck Lever  *		sattr3		attributes;
823d9c407b1SChuck Lever  *	};
824d9c407b1SChuck Lever  */
825d9c407b1SChuck Lever static int nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req, __be32 *p,
826d9c407b1SChuck Lever 				   const struct nfs3_mkdirargs *args)
827d9c407b1SChuck Lever {
828d9c407b1SChuck Lever 	struct xdr_stream xdr;
829d9c407b1SChuck Lever 
830d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
831d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
832d9c407b1SChuck Lever 	encode_sattr3(&xdr, args->sattr);
833d9c407b1SChuck Lever 	return 0;
834d9c407b1SChuck Lever }
835d9c407b1SChuck Lever 
836d9c407b1SChuck Lever /*
837d9c407b1SChuck Lever  * 3.3.10  SYMLINK3args
838d9c407b1SChuck Lever  *
839d9c407b1SChuck Lever  *	struct symlinkdata3 {
840d9c407b1SChuck Lever  *		sattr3		symlink_attributes;
841d9c407b1SChuck Lever  *		nfspath3	symlink_data;
842d9c407b1SChuck Lever  *	};
843d9c407b1SChuck Lever  *
844d9c407b1SChuck Lever  *	struct SYMLINK3args {
845d9c407b1SChuck Lever  *		diropargs3	where;
846d9c407b1SChuck Lever  *		symlinkdata3	symlink;
847d9c407b1SChuck Lever  *	};
848d9c407b1SChuck Lever  */
849d9c407b1SChuck Lever static void encode_symlinkdata3(struct xdr_stream *xdr,
850d9c407b1SChuck Lever 				const struct nfs3_symlinkargs *args)
851d9c407b1SChuck Lever {
852d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
853d9c407b1SChuck Lever 	encode_nfspath3(xdr, args->pages, args->pathlen);
854d9c407b1SChuck Lever }
855d9c407b1SChuck Lever 
856d9c407b1SChuck Lever static int nfs3_xdr_enc_symlink3args(struct rpc_rqst *req, __be32 *p,
857d9c407b1SChuck Lever 				     const struct nfs3_symlinkargs *args)
858d9c407b1SChuck Lever {
859d9c407b1SChuck Lever 	struct xdr_stream xdr;
860d9c407b1SChuck Lever 
861d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
862d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fromfh, args->fromname, args->fromlen);
863d9c407b1SChuck Lever 	encode_symlinkdata3(&xdr, args);
864d9c407b1SChuck Lever 	return 0;
865d9c407b1SChuck Lever }
866d9c407b1SChuck Lever 
867d9c407b1SChuck Lever /*
868d9c407b1SChuck Lever  * 3.3.11  MKNOD3args
869d9c407b1SChuck Lever  *
870d9c407b1SChuck Lever  *	struct devicedata3 {
871d9c407b1SChuck Lever  *		sattr3		dev_attributes;
872d9c407b1SChuck Lever  *		specdata3	spec;
873d9c407b1SChuck Lever  *	};
874d9c407b1SChuck Lever  *
875d9c407b1SChuck Lever  *	union mknoddata3 switch (ftype3 type) {
876d9c407b1SChuck Lever  *	case NF3CHR:
877d9c407b1SChuck Lever  *	case NF3BLK:
878d9c407b1SChuck Lever  *		devicedata3	device;
879d9c407b1SChuck Lever  *	case NF3SOCK:
880d9c407b1SChuck Lever  *	case NF3FIFO:
881d9c407b1SChuck Lever  *		sattr3		pipe_attributes;
882d9c407b1SChuck Lever  *	default:
883d9c407b1SChuck Lever  *		void;
884d9c407b1SChuck Lever  *	};
885d9c407b1SChuck Lever  *
886d9c407b1SChuck Lever  *	struct MKNOD3args {
887d9c407b1SChuck Lever  *		diropargs3	where;
888d9c407b1SChuck Lever  *		mknoddata3	what;
889d9c407b1SChuck Lever  *	};
890d9c407b1SChuck Lever  */
891d9c407b1SChuck Lever static void encode_devicedata3(struct xdr_stream *xdr,
892d9c407b1SChuck Lever 			       const struct nfs3_mknodargs *args)
893d9c407b1SChuck Lever {
894d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
895d9c407b1SChuck Lever 	encode_specdata3(xdr, args->rdev);
896d9c407b1SChuck Lever }
897d9c407b1SChuck Lever 
898d9c407b1SChuck Lever static void encode_mknoddata3(struct xdr_stream *xdr,
899d9c407b1SChuck Lever 			      const struct nfs3_mknodargs *args)
900d9c407b1SChuck Lever {
901d9c407b1SChuck Lever 	encode_ftype3(xdr, args->type);
902d9c407b1SChuck Lever 	switch (args->type) {
903d9c407b1SChuck Lever 	case NF3CHR:
904d9c407b1SChuck Lever 	case NF3BLK:
905d9c407b1SChuck Lever 		encode_devicedata3(xdr, args);
906d9c407b1SChuck Lever 		break;
907d9c407b1SChuck Lever 	case NF3SOCK:
908d9c407b1SChuck Lever 	case NF3FIFO:
909d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
910d9c407b1SChuck Lever 		break;
911d9c407b1SChuck Lever 	case NF3REG:
912d9c407b1SChuck Lever 	case NF3DIR:
913d9c407b1SChuck Lever 		break;
914d9c407b1SChuck Lever 	default:
915d9c407b1SChuck Lever 		BUG();
916d9c407b1SChuck Lever 	}
917d9c407b1SChuck Lever }
918d9c407b1SChuck Lever 
919d9c407b1SChuck Lever static int nfs3_xdr_enc_mknod3args(struct rpc_rqst *req, __be32 *p,
920d9c407b1SChuck Lever 				   const struct nfs3_mknodargs *args)
921d9c407b1SChuck Lever {
922d9c407b1SChuck Lever 	struct xdr_stream xdr;
923d9c407b1SChuck Lever 
924d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
925d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name, args->len);
926d9c407b1SChuck Lever 	encode_mknoddata3(&xdr, args);
927d9c407b1SChuck Lever 	return 0;
928d9c407b1SChuck Lever }
929d9c407b1SChuck Lever 
930d9c407b1SChuck Lever /*
931d9c407b1SChuck Lever  * 3.3.12  REMOVE3args
932d9c407b1SChuck Lever  *
933d9c407b1SChuck Lever  *	struct REMOVE3args {
934d9c407b1SChuck Lever  *		diropargs3  object;
935d9c407b1SChuck Lever  *	};
936d9c407b1SChuck Lever  */
937d9c407b1SChuck Lever static int nfs3_xdr_enc_remove3args(struct rpc_rqst *req, __be32 *p,
938d9c407b1SChuck Lever 				    const struct nfs_removeargs *args)
939d9c407b1SChuck Lever {
940d9c407b1SChuck Lever 	struct xdr_stream xdr;
941d9c407b1SChuck Lever 
942d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
943d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->fh, args->name.name, args->name.len);
944d9c407b1SChuck Lever 	return 0;
945d9c407b1SChuck Lever }
946d9c407b1SChuck Lever 
947d9c407b1SChuck Lever /*
948d9c407b1SChuck Lever  * 3.3.14  RENAME3args
949d9c407b1SChuck Lever  *
950d9c407b1SChuck Lever  *	struct RENAME3args {
951d9c407b1SChuck Lever  *		diropargs3	from;
952d9c407b1SChuck Lever  *		diropargs3	to;
953d9c407b1SChuck Lever  *	};
954d9c407b1SChuck Lever  */
955d9c407b1SChuck Lever static int nfs3_xdr_enc_rename3args(struct rpc_rqst *req, __be32 *p,
956d9c407b1SChuck Lever 				    const struct nfs_renameargs *args)
957d9c407b1SChuck Lever {
958d9c407b1SChuck Lever 	const struct qstr *old = args->old_name;
959d9c407b1SChuck Lever 	const struct qstr *new = args->new_name;
960d9c407b1SChuck Lever 	struct xdr_stream xdr;
961d9c407b1SChuck Lever 
962d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
963d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->old_dir, old->name, old->len);
964d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->new_dir, new->name, new->len);
965d9c407b1SChuck Lever 	return 0;
966d9c407b1SChuck Lever }
967d9c407b1SChuck Lever 
968d9c407b1SChuck Lever /*
969d9c407b1SChuck Lever  * 3.3.15  LINK3args
970d9c407b1SChuck Lever  *
971d9c407b1SChuck Lever  *	struct LINK3args {
972d9c407b1SChuck Lever  *		nfs_fh3		file;
973d9c407b1SChuck Lever  *		diropargs3	link;
974d9c407b1SChuck Lever  *	};
975d9c407b1SChuck Lever  */
976d9c407b1SChuck Lever static int nfs3_xdr_enc_link3args(struct rpc_rqst *req, __be32 *p,
977d9c407b1SChuck Lever 				  const struct nfs3_linkargs *args)
978d9c407b1SChuck Lever {
979d9c407b1SChuck Lever 	struct xdr_stream xdr;
980d9c407b1SChuck Lever 
981d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
982d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fromfh);
983d9c407b1SChuck Lever 	encode_diropargs3(&xdr, args->tofh, args->toname, args->tolen);
984d9c407b1SChuck Lever 	return 0;
985d9c407b1SChuck Lever }
986d9c407b1SChuck Lever 
987d9c407b1SChuck Lever /*
988d9c407b1SChuck Lever  * 3.3.16  READDIR3args
989d9c407b1SChuck Lever  *
990d9c407b1SChuck Lever  *	struct READDIR3args {
991d9c407b1SChuck Lever  *		nfs_fh3		dir;
992d9c407b1SChuck Lever  *		cookie3		cookie;
993d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
994d9c407b1SChuck Lever  *		count3		count;
995d9c407b1SChuck Lever  *	};
996d9c407b1SChuck Lever  */
997d9c407b1SChuck Lever static void encode_readdir3args(struct xdr_stream *xdr,
998d9c407b1SChuck Lever 				const struct nfs3_readdirargs *args)
999d9c407b1SChuck Lever {
1000d9c407b1SChuck Lever 	__be32 *p;
1001d9c407b1SChuck Lever 
1002d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1003d9c407b1SChuck Lever 
1004d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
1005d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1006d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1007d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1008d9c407b1SChuck Lever }
1009d9c407b1SChuck Lever 
1010d9c407b1SChuck Lever static int nfs3_xdr_enc_readdir3args(struct rpc_rqst *req, __be32 *p,
1011d9c407b1SChuck Lever 				     const struct nfs3_readdirargs *args)
1012d9c407b1SChuck Lever {
1013d9c407b1SChuck Lever 	struct xdr_stream xdr;
1014d9c407b1SChuck Lever 
1015d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1016d9c407b1SChuck Lever 	encode_readdir3args(&xdr, args);
1017d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
1018d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1019d9c407b1SChuck Lever 	return 0;
1020d9c407b1SChuck Lever }
1021d9c407b1SChuck Lever 
1022d9c407b1SChuck Lever /*
1023d9c407b1SChuck Lever  * 3.3.17  READDIRPLUS3args
1024d9c407b1SChuck Lever  *
1025d9c407b1SChuck Lever  *	struct READDIRPLUS3args {
1026d9c407b1SChuck Lever  *		nfs_fh3		dir;
1027d9c407b1SChuck Lever  *		cookie3		cookie;
1028d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1029d9c407b1SChuck Lever  *		count3		dircount;
1030d9c407b1SChuck Lever  *		count3		maxcount;
1031d9c407b1SChuck Lever  *	};
1032d9c407b1SChuck Lever  */
1033d9c407b1SChuck Lever static void encode_readdirplus3args(struct xdr_stream *xdr,
1034d9c407b1SChuck Lever 				    const struct nfs3_readdirargs *args)
1035d9c407b1SChuck Lever {
1036d9c407b1SChuck Lever 	__be32 *p;
1037d9c407b1SChuck Lever 
1038d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1039d9c407b1SChuck Lever 
1040d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
1041d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1042d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1043d9c407b1SChuck Lever 
1044d9c407b1SChuck Lever 	/*
1045d9c407b1SChuck Lever 	 * readdirplus: need dircount + buffer size.
1046d9c407b1SChuck Lever 	 * We just make sure we make dircount big enough
1047d9c407b1SChuck Lever 	 */
1048d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count >> 3);
1049d9c407b1SChuck Lever 
1050d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1051d9c407b1SChuck Lever }
1052d9c407b1SChuck Lever 
1053d9c407b1SChuck Lever static int nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req, __be32 *p,
1054d9c407b1SChuck Lever 					 const struct nfs3_readdirargs *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_readdirplus3args(&xdr, args);
1060d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
1061d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1062d9c407b1SChuck Lever 	return 0;
1063d9c407b1SChuck Lever }
1064d9c407b1SChuck Lever 
1065d9c407b1SChuck Lever /*
10661da177e4SLinus Torvalds  * Decode the result of a readdir call.
10671da177e4SLinus Torvalds  * We just check for syntactical correctness.
10681da177e4SLinus Torvalds  */
10691da177e4SLinus Torvalds static int
1070d61005a6SAl Viro nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
10711da177e4SLinus Torvalds {
10721da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
10731da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
10741da177e4SLinus Torvalds 	struct page **page;
1075c957c526SChuck Lever 	size_t hdrlen;
1076afa8ccc9SBryan Schumaker 	u32 recvd, pglen;
1077ac396128STrond Myklebust 	int status;
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 	status = ntohl(*p++);
10801da177e4SLinus Torvalds 	/* Decode post_op_attrs */
10811da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->dir_attr);
10821da177e4SLinus Torvalds 	if (status)
1083856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
10841da177e4SLinus Torvalds 	/* Decode verifier cookie */
10851da177e4SLinus Torvalds 	if (res->verf) {
10861da177e4SLinus Torvalds 		res->verf[0] = *p++;
10871da177e4SLinus Torvalds 		res->verf[1] = *p++;
10881da177e4SLinus Torvalds 	} else {
10891da177e4SLinus Torvalds 		p += 2;
10901da177e4SLinus Torvalds 	}
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
10931da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1094fe82a183SChuck Lever 		dprintk("NFS: READDIR reply header overflowed:"
1095c957c526SChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
10961da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
10971da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
10981da177e4SLinus Torvalds 		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
10991da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
11001da177e4SLinus Torvalds 	}
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds 	pglen = rcvbuf->page_len;
11031da177e4SLinus Torvalds 	recvd = rcvbuf->len - hdrlen;
11041da177e4SLinus Torvalds 	if (pglen > recvd)
11051da177e4SLinus Torvalds 		pglen = recvd;
11061da177e4SLinus Torvalds 	page = rcvbuf->pages;
1107643f8111SJeff Layton 
1108ac396128STrond Myklebust 	return pglen;
11091da177e4SLinus Torvalds }
11101da177e4SLinus Torvalds 
11110dbb4c67SAl Viro __be32 *
111282f2e547SBryan Schumaker nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
11131da177e4SLinus Torvalds {
1114babddc72SBryan Schumaker 	__be32 *p;
11151da177e4SLinus Torvalds 	struct nfs_entry old = *entry;
11161da177e4SLinus Torvalds 
1117babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
1118babddc72SBryan Schumaker 	if (unlikely(!p))
1119babddc72SBryan Schumaker 		goto out_overflow;
1120babddc72SBryan Schumaker 	if (!ntohl(*p++)) {
1121babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, 4);
1122babddc72SBryan Schumaker 		if (unlikely(!p))
1123babddc72SBryan Schumaker 			goto out_overflow;
1124babddc72SBryan Schumaker 		if (!ntohl(*p++))
11251da177e4SLinus Torvalds 			return ERR_PTR(-EAGAIN);
11261da177e4SLinus Torvalds 		entry->eof = 1;
11271da177e4SLinus Torvalds 		return ERR_PTR(-EBADCOOKIE);
11281da177e4SLinus Torvalds 	}
11291da177e4SLinus Torvalds 
1130babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 12);
1131babddc72SBryan Schumaker 	if (unlikely(!p))
1132babddc72SBryan Schumaker 		goto out_overflow;
11331da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &entry->ino);
11341da177e4SLinus Torvalds 	entry->len  = ntohl(*p++);
1135babddc72SBryan Schumaker 
1136babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, entry->len + 8);
1137babddc72SBryan Schumaker 	if (unlikely(!p))
1138babddc72SBryan Schumaker 		goto out_overflow;
11391da177e4SLinus Torvalds 	entry->name = (const char *) p;
11401da177e4SLinus Torvalds 	p += XDR_QUADLEN(entry->len);
11411da177e4SLinus Torvalds 	entry->prev_cookie = entry->cookie;
11421da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &entry->cookie);
11431da177e4SLinus Torvalds 
11440b26a0bfSTrond Myklebust 	entry->d_type = DT_UNKNOWN;
11451da177e4SLinus Torvalds 	if (plus) {
11461da177e4SLinus Torvalds 		entry->fattr->valid = 0;
1147babddc72SBryan Schumaker 		p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
1148babddc72SBryan Schumaker 		if (IS_ERR(p))
1149babddc72SBryan Schumaker 			goto out_overflow_exit;
11500b26a0bfSTrond Myklebust 		entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
11511da177e4SLinus Torvalds 		/* In fact, a post_op_fh3: */
1152babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, 4);
1153babddc72SBryan Schumaker 		if (unlikely(!p))
1154babddc72SBryan Schumaker 			goto out_overflow;
11551da177e4SLinus Torvalds 		if (*p++) {
1156babddc72SBryan Schumaker 			p = xdr_decode_fhandle_stream(xdr, entry->fh);
1157babddc72SBryan Schumaker 			if (IS_ERR(p))
1158babddc72SBryan Schumaker 				goto out_overflow_exit;
11591da177e4SLinus Torvalds 			/* Ugh -- server reply was truncated */
11601da177e4SLinus Torvalds 			if (p == NULL) {
11611da177e4SLinus Torvalds 				dprintk("NFS: FH truncated\n");
11621da177e4SLinus Torvalds 				*entry = old;
11631da177e4SLinus Torvalds 				return ERR_PTR(-EAGAIN);
11641da177e4SLinus Torvalds 			}
11651da177e4SLinus Torvalds 		} else
11661da177e4SLinus Torvalds 			memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
11671da177e4SLinus Torvalds 	}
11681da177e4SLinus Torvalds 
1169babddc72SBryan Schumaker 	p = xdr_inline_peek(xdr, 8);
1170babddc72SBryan Schumaker 	if (p != NULL)
11711da177e4SLinus Torvalds 		entry->eof = !p[0] && p[1];
1172babddc72SBryan Schumaker 	else
1173babddc72SBryan Schumaker 		entry->eof = 0;
1174babddc72SBryan Schumaker 
11751da177e4SLinus Torvalds 	return p;
1176babddc72SBryan Schumaker 
1177babddc72SBryan Schumaker out_overflow:
1178babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
1179babddc72SBryan Schumaker out_overflow_exit:
1180463a376eSTrond Myklebust 	return ERR_PTR(-EAGAIN);
11811da177e4SLinus Torvalds }
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds /*
1184d9c407b1SChuck Lever  * 3.3.21  COMMIT3args
1185d9c407b1SChuck Lever  *
1186d9c407b1SChuck Lever  *	struct COMMIT3args {
1187d9c407b1SChuck Lever  *		nfs_fh3		file;
1188d9c407b1SChuck Lever  *		offset3		offset;
1189d9c407b1SChuck Lever  *		count3		count;
1190d9c407b1SChuck Lever  *	};
1191d9c407b1SChuck Lever  */
1192d9c407b1SChuck Lever static void encode_commit3args(struct xdr_stream *xdr,
1193d9c407b1SChuck Lever 			       const struct nfs_writeargs *args)
1194d9c407b1SChuck Lever {
1195d9c407b1SChuck Lever 	__be32 *p;
1196d9c407b1SChuck Lever 
1197d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1198d9c407b1SChuck Lever 
1199d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1200d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1201d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1202d9c407b1SChuck Lever }
1203d9c407b1SChuck Lever 
1204d9c407b1SChuck Lever static int nfs3_xdr_enc_commit3args(struct rpc_rqst *req, __be32 *p,
1205d9c407b1SChuck Lever 				    const struct nfs_writeargs *args)
1206d9c407b1SChuck Lever {
1207d9c407b1SChuck Lever 	struct xdr_stream xdr;
1208d9c407b1SChuck Lever 
1209d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1210d9c407b1SChuck Lever 	encode_commit3args(&xdr, args);
1211d9c407b1SChuck Lever 	return 0;
1212d9c407b1SChuck Lever }
1213d9c407b1SChuck Lever 
1214b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1215b7fa0554SAndreas Gruenbacher 
1216d9c407b1SChuck Lever static int nfs3_xdr_enc_getacl3args(struct rpc_rqst *req, __be32 *p,
1217d9c407b1SChuck Lever 				    const struct nfs3_getaclargs *args)
1218d9c407b1SChuck Lever {
1219d9c407b1SChuck Lever 	struct xdr_stream xdr;
1220d9c407b1SChuck Lever 
1221d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1222d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, args->fh);
1223d9c407b1SChuck Lever 	encode_uint32(&xdr, args->mask);
1224d9c407b1SChuck Lever 	if (args->mask & (NFS_ACL | NFS_DFACL))
1225d9c407b1SChuck Lever 		prepare_reply_buffer(req, args->pages, 0,
1226d9c407b1SChuck Lever 					NFSACL_MAXPAGES << PAGE_SHIFT,
1227d9c407b1SChuck Lever 					ACL3_getaclres_sz);
1228d9c407b1SChuck Lever 	return 0;
1229d9c407b1SChuck Lever }
1230d9c407b1SChuck Lever 
1231d9c407b1SChuck Lever static int nfs3_xdr_enc_setacl3args(struct rpc_rqst *req, __be32 *p,
1232d9c407b1SChuck Lever 				    const struct nfs3_setaclargs *args)
1233d9c407b1SChuck Lever {
1234d9c407b1SChuck Lever 	struct xdr_stream xdr;
1235d9c407b1SChuck Lever 	unsigned int base;
1236d9c407b1SChuck Lever 	int error;
1237d9c407b1SChuck Lever 
1238d9c407b1SChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1239d9c407b1SChuck Lever 	encode_nfs_fh3(&xdr, NFS_FH(args->inode));
1240d9c407b1SChuck Lever 	encode_uint32(&xdr, args->mask);
1241d9c407b1SChuck Lever 	if (args->npages != 0)
1242d9c407b1SChuck Lever 		xdr_write_pages(&xdr, args->pages, 0, args->len);
1243d9c407b1SChuck Lever 
1244d9c407b1SChuck Lever 	base = req->rq_slen;
1245d9c407b1SChuck Lever 	error = nfsacl_encode(xdr.buf, base, args->inode,
1246d9c407b1SChuck Lever 			    (args->mask & NFS_ACL) ?
1247d9c407b1SChuck Lever 			    args->acl_access : NULL, 1, 0);
1248d9c407b1SChuck Lever 	BUG_ON(error < 0);
1249d9c407b1SChuck Lever 	error = nfsacl_encode(xdr.buf, base + error, args->inode,
1250d9c407b1SChuck Lever 			    (args->mask & NFS_DFACL) ?
1251d9c407b1SChuck Lever 			    args->acl_default : NULL, 1,
1252d9c407b1SChuck Lever 			    NFS_ACL_DEFAULT);
1253d9c407b1SChuck Lever 	BUG_ON(error < 0);
1254d9c407b1SChuck Lever 	return 0;
1255d9c407b1SChuck Lever }
1256d9c407b1SChuck Lever 
1257b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1258b7fa0554SAndreas Gruenbacher 
12591da177e4SLinus Torvalds /*
12601da177e4SLinus Torvalds  * NFS XDR decode functions
12611da177e4SLinus Torvalds  */
12621da177e4SLinus Torvalds 
12631da177e4SLinus Torvalds /*
12641da177e4SLinus Torvalds  * Decode attrstat reply.
12651da177e4SLinus Torvalds  */
12661da177e4SLinus Torvalds static int
1267d61005a6SAl Viro nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
12681da177e4SLinus Torvalds {
12691da177e4SLinus Torvalds 	int	status;
12701da177e4SLinus Torvalds 
12711da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1272856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
12731da177e4SLinus Torvalds 	xdr_decode_fattr(p, fattr);
12741da177e4SLinus Torvalds 	return 0;
12751da177e4SLinus Torvalds }
12761da177e4SLinus Torvalds 
12771da177e4SLinus Torvalds /*
12781da177e4SLinus Torvalds  * Decode status+wcc_data reply
12791da177e4SLinus Torvalds  * SATTR, REMOVE, RMDIR
12801da177e4SLinus Torvalds  */
12811da177e4SLinus Torvalds static int
1282d61005a6SAl Viro nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
12831da177e4SLinus Torvalds {
12841da177e4SLinus Torvalds 	int	status;
12851da177e4SLinus Torvalds 
12861da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1287856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
12881da177e4SLinus Torvalds 	xdr_decode_wcc_data(p, fattr);
12891da177e4SLinus Torvalds 	return status;
12901da177e4SLinus Torvalds }
12911da177e4SLinus Torvalds 
12924fdc17b2STrond Myklebust static int
12934fdc17b2STrond Myklebust nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
12944fdc17b2STrond Myklebust {
1295d346890bSTrond Myklebust 	return nfs3_xdr_wccstat(req, p, res->dir_attr);
12964fdc17b2STrond Myklebust }
12974fdc17b2STrond Myklebust 
12981da177e4SLinus Torvalds /*
12991da177e4SLinus Torvalds  * Decode LOOKUP reply
13001da177e4SLinus Torvalds  */
13011da177e4SLinus Torvalds static int
1302d61005a6SAl Viro nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
13031da177e4SLinus Torvalds {
13041da177e4SLinus Torvalds 	int	status;
13051da177e4SLinus Torvalds 
13061da177e4SLinus Torvalds 	if ((status = ntohl(*p++))) {
1307856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
13081da177e4SLinus Torvalds 	} else {
13091da177e4SLinus Torvalds 		if (!(p = xdr_decode_fhandle(p, res->fh)))
13101da177e4SLinus Torvalds 			return -errno_NFSERR_IO;
13111da177e4SLinus Torvalds 		p = xdr_decode_post_op_attr(p, res->fattr);
13121da177e4SLinus Torvalds 	}
13131da177e4SLinus Torvalds 	xdr_decode_post_op_attr(p, res->dir_attr);
13141da177e4SLinus Torvalds 	return status;
13151da177e4SLinus Torvalds }
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds /*
13181da177e4SLinus Torvalds  * Decode ACCESS reply
13191da177e4SLinus Torvalds  */
13201da177e4SLinus Torvalds static int
1321d61005a6SAl Viro nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
13221da177e4SLinus Torvalds {
13231da177e4SLinus Torvalds 	int	status = ntohl(*p++);
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
13261da177e4SLinus Torvalds 	if (status)
1327856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
13281da177e4SLinus Torvalds 	res->access = ntohl(*p++);
13291da177e4SLinus Torvalds 	return 0;
13301da177e4SLinus Torvalds }
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds /*
13331da177e4SLinus Torvalds  * Decode READLINK reply
13341da177e4SLinus Torvalds  */
13351da177e4SLinus Torvalds static int
1336d61005a6SAl Viro nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
13371da177e4SLinus Torvalds {
13381da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
13391da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
1340c957c526SChuck Lever 	size_t hdrlen;
1341c957c526SChuck Lever 	u32 len, recvd;
13421da177e4SLinus Torvalds 	int	status;
13431da177e4SLinus Torvalds 
13441da177e4SLinus Torvalds 	status = ntohl(*p++);
13451da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, fattr);
13461da177e4SLinus Torvalds 
13471da177e4SLinus Torvalds 	if (status != 0)
1348856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
13491da177e4SLinus Torvalds 
13501da177e4SLinus Torvalds 	/* Convert length of symlink */
13511da177e4SLinus Torvalds 	len = ntohl(*p++);
1352c957c526SChuck Lever 	if (len >= rcvbuf->page_len) {
1353fe82a183SChuck Lever 		dprintk("nfs: server returned giant symlink!\n");
13541da177e4SLinus Torvalds 		return -ENAMETOOLONG;
13551da177e4SLinus Torvalds 	}
13561da177e4SLinus Torvalds 
13571da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
13581da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1359fe82a183SChuck Lever 		dprintk("NFS: READLINK reply header overflowed:"
1360c957c526SChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
13611da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
13621da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
1363fe82a183SChuck Lever 		dprintk("NFS: READLINK header is short. "
1364fe82a183SChuck Lever 			"iovec will be shifted.\n");
13651da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
13661da177e4SLinus Torvalds 	}
13671da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
13681da177e4SLinus Torvalds 	if (recvd < len) {
1369fe82a183SChuck Lever 		dprintk("NFS: server cheating in readlink reply: "
13701da177e4SLinus Torvalds 				"count %u > recvd %u\n", len, recvd);
13711da177e4SLinus Torvalds 		return -EIO;
13721da177e4SLinus Torvalds 	}
13731da177e4SLinus Torvalds 
1374b4687da7SChuck Lever 	xdr_terminate_string(rcvbuf, len);
13751da177e4SLinus Torvalds 	return 0;
13761da177e4SLinus Torvalds }
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds /*
13791da177e4SLinus Torvalds  * Decode READ reply
13801da177e4SLinus Torvalds  */
13811da177e4SLinus Torvalds static int
1382d61005a6SAl Viro nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
13831da177e4SLinus Torvalds {
13841da177e4SLinus Torvalds 	struct kvec *iov = req->rq_rcv_buf.head;
1385c957c526SChuck Lever 	size_t hdrlen;
1386c957c526SChuck Lever 	u32 count, ocount, recvd;
1387c957c526SChuck Lever 	int status;
13881da177e4SLinus Torvalds 
13891da177e4SLinus Torvalds 	status = ntohl(*p++);
13901da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 	if (status != 0)
1393856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
13941da177e4SLinus Torvalds 
1395c957c526SChuck Lever 	/* Decode reply count and EOF flag. NFSv3 is somewhat redundant
13961da177e4SLinus Torvalds 	 * in that it puts the count both in the res struct and in the
13971da177e4SLinus Torvalds 	 * opaque data count. */
13981da177e4SLinus Torvalds 	count    = ntohl(*p++);
13991da177e4SLinus Torvalds 	res->eof = ntohl(*p++);
14001da177e4SLinus Torvalds 	ocount   = ntohl(*p++);
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds 	if (ocount != count) {
1403fe82a183SChuck Lever 		dprintk("NFS: READ count doesn't match RPC opaque count.\n");
14041da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
14051da177e4SLinus Torvalds 	}
14061da177e4SLinus Torvalds 
14071da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
14081da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1409fe82a183SChuck Lever 		dprintk("NFS: READ reply header overflowed:"
1410c957c526SChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
14111da177e4SLinus Torvalds        		return -errno_NFSERR_IO;
14121da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
14131da177e4SLinus Torvalds 		dprintk("NFS: READ header is short. iovec will be shifted.\n");
14141da177e4SLinus Torvalds 		xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
14151da177e4SLinus Torvalds 	}
14161da177e4SLinus Torvalds 
14171da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
14181da177e4SLinus Torvalds 	if (count > recvd) {
1419fe82a183SChuck Lever 		dprintk("NFS: server cheating in read reply: "
1420c957c526SChuck Lever 			"count %u > recvd %u\n", count, recvd);
14211da177e4SLinus Torvalds 		count = recvd;
14221da177e4SLinus Torvalds 		res->eof = 0;
14231da177e4SLinus Torvalds 	}
14241da177e4SLinus Torvalds 
14251da177e4SLinus Torvalds 	if (count < res->count)
14261da177e4SLinus Torvalds 		res->count = count;
14271da177e4SLinus Torvalds 
14281da177e4SLinus Torvalds 	return count;
14291da177e4SLinus Torvalds }
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds /*
14321da177e4SLinus Torvalds  * Decode WRITE response
14331da177e4SLinus Torvalds  */
14341da177e4SLinus Torvalds static int
1435d61005a6SAl Viro nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
14361da177e4SLinus Torvalds {
14371da177e4SLinus Torvalds 	int	status;
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds 	status = ntohl(*p++);
14401da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->fattr);
14411da177e4SLinus Torvalds 
14421da177e4SLinus Torvalds 	if (status != 0)
1443856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds 	res->count = ntohl(*p++);
14461da177e4SLinus Torvalds 	res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
14471da177e4SLinus Torvalds 	res->verf->verifier[0] = *p++;
14481da177e4SLinus Torvalds 	res->verf->verifier[1] = *p++;
14491da177e4SLinus Torvalds 
14501da177e4SLinus Torvalds 	return res->count;
14511da177e4SLinus Torvalds }
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds /*
14541da177e4SLinus Torvalds  * Decode a CREATE response
14551da177e4SLinus Torvalds  */
14561da177e4SLinus Torvalds static int
1457d61005a6SAl Viro nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
14581da177e4SLinus Torvalds {
14591da177e4SLinus Torvalds 	int	status;
14601da177e4SLinus Torvalds 
14611da177e4SLinus Torvalds 	status = ntohl(*p++);
14621da177e4SLinus Torvalds 	if (status == 0) {
14631da177e4SLinus Torvalds 		if (*p++) {
14641da177e4SLinus Torvalds 			if (!(p = xdr_decode_fhandle(p, res->fh)))
14651da177e4SLinus Torvalds 				return -errno_NFSERR_IO;
14661da177e4SLinus Torvalds 			p = xdr_decode_post_op_attr(p, res->fattr);
14671da177e4SLinus Torvalds 		} else {
14681da177e4SLinus Torvalds 			memset(res->fh, 0, sizeof(*res->fh));
14691da177e4SLinus Torvalds 			/* Do decode post_op_attr but set it to NULL */
14701da177e4SLinus Torvalds 			p = xdr_decode_post_op_attr(p, res->fattr);
14711da177e4SLinus Torvalds 			res->fattr->valid = 0;
14721da177e4SLinus Torvalds 		}
14731da177e4SLinus Torvalds 	} else {
1474856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
14751da177e4SLinus Torvalds 	}
14761da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->dir_attr);
14771da177e4SLinus Torvalds 	return status;
14781da177e4SLinus Torvalds }
14791da177e4SLinus Torvalds 
14801da177e4SLinus Torvalds /*
14811da177e4SLinus Torvalds  * Decode RENAME reply
14821da177e4SLinus Torvalds  */
14831da177e4SLinus Torvalds static int
1484e8582a8bSJeff Layton nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
14851da177e4SLinus Torvalds {
14861da177e4SLinus Torvalds 	int	status;
14871da177e4SLinus Torvalds 
14881da177e4SLinus Torvalds 	if ((status = ntohl(*p++)) != 0)
1489856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
1490e8582a8bSJeff Layton 	p = xdr_decode_wcc_data(p, res->old_fattr);
1491e8582a8bSJeff Layton 	p = xdr_decode_wcc_data(p, res->new_fattr);
14921da177e4SLinus Torvalds 	return status;
14931da177e4SLinus Torvalds }
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds /*
14961da177e4SLinus Torvalds  * Decode LINK reply
14971da177e4SLinus Torvalds  */
14981da177e4SLinus Torvalds static int
1499d61005a6SAl Viro nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
15001da177e4SLinus Torvalds {
15011da177e4SLinus Torvalds 	int	status;
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 	if ((status = ntohl(*p++)) != 0)
1504856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
15051da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
15061da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->dir_attr);
15071da177e4SLinus Torvalds 	return status;
15081da177e4SLinus Torvalds }
15091da177e4SLinus Torvalds 
15101da177e4SLinus Torvalds /*
15111da177e4SLinus Torvalds  * Decode FSSTAT reply
15121da177e4SLinus Torvalds  */
15131da177e4SLinus Torvalds static int
1514d61005a6SAl Viro nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
15151da177e4SLinus Torvalds {
15161da177e4SLinus Torvalds 	int		status;
15171da177e4SLinus Torvalds 
15181da177e4SLinus Torvalds 	status = ntohl(*p++);
15191da177e4SLinus Torvalds 
15201da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
15211da177e4SLinus Torvalds 	if (status != 0)
1522856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
15231da177e4SLinus Torvalds 
15241da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->tbytes);
15251da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->fbytes);
15261da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->abytes);
15271da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->tfiles);
15281da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->ffiles);
15291da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->afiles);
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds 	/* ignore invarsec */
15321da177e4SLinus Torvalds 	return 0;
15331da177e4SLinus Torvalds }
15341da177e4SLinus Torvalds 
15351da177e4SLinus Torvalds /*
15361da177e4SLinus Torvalds  * Decode FSINFO reply
15371da177e4SLinus Torvalds  */
15381da177e4SLinus Torvalds static int
1539d61005a6SAl Viro nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
15401da177e4SLinus Torvalds {
15411da177e4SLinus Torvalds 	int		status;
15421da177e4SLinus Torvalds 
15431da177e4SLinus Torvalds 	status = ntohl(*p++);
15441da177e4SLinus Torvalds 
15451da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
15461da177e4SLinus Torvalds 	if (status != 0)
1547856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
15481da177e4SLinus Torvalds 
15491da177e4SLinus Torvalds 	res->rtmax  = ntohl(*p++);
15501da177e4SLinus Torvalds 	res->rtpref = ntohl(*p++);
15511da177e4SLinus Torvalds 	res->rtmult = ntohl(*p++);
15521da177e4SLinus Torvalds 	res->wtmax  = ntohl(*p++);
15531da177e4SLinus Torvalds 	res->wtpref = ntohl(*p++);
15541da177e4SLinus Torvalds 	res->wtmult = ntohl(*p++);
15551da177e4SLinus Torvalds 	res->dtpref = ntohl(*p++);
15561da177e4SLinus Torvalds 	p = xdr_decode_hyper(p, &res->maxfilesize);
15576b96724eSRicardo Labiaga 	p = xdr_decode_time3(p, &res->time_delta);
15581da177e4SLinus Torvalds 
15596b96724eSRicardo Labiaga 	/* ignore properties */
15601da177e4SLinus Torvalds 	res->lease_time = 0;
15611da177e4SLinus Torvalds 	return 0;
15621da177e4SLinus Torvalds }
15631da177e4SLinus Torvalds 
15641da177e4SLinus Torvalds /*
15651da177e4SLinus Torvalds  * Decode PATHCONF reply
15661da177e4SLinus Torvalds  */
15671da177e4SLinus Torvalds static int
1568d61005a6SAl Viro nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
15691da177e4SLinus Torvalds {
15701da177e4SLinus Torvalds 	int		status;
15711da177e4SLinus Torvalds 
15721da177e4SLinus Torvalds 	status = ntohl(*p++);
15731da177e4SLinus Torvalds 
15741da177e4SLinus Torvalds 	p = xdr_decode_post_op_attr(p, res->fattr);
15751da177e4SLinus Torvalds 	if (status != 0)
1576856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
15771da177e4SLinus Torvalds 	res->max_link = ntohl(*p++);
15781da177e4SLinus Torvalds 	res->max_namelen = ntohl(*p++);
15791da177e4SLinus Torvalds 
15801da177e4SLinus Torvalds 	/* ignore remaining fields */
15811da177e4SLinus Torvalds 	return 0;
15821da177e4SLinus Torvalds }
15831da177e4SLinus Torvalds 
15841da177e4SLinus Torvalds /*
15851da177e4SLinus Torvalds  * Decode COMMIT reply
15861da177e4SLinus Torvalds  */
15871da177e4SLinus Torvalds static int
1588d61005a6SAl Viro nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
15891da177e4SLinus Torvalds {
15901da177e4SLinus Torvalds 	int		status;
15911da177e4SLinus Torvalds 
15921da177e4SLinus Torvalds 	status = ntohl(*p++);
15931da177e4SLinus Torvalds 	p = xdr_decode_wcc_data(p, res->fattr);
15941da177e4SLinus Torvalds 	if (status != 0)
1595856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
15961da177e4SLinus Torvalds 
15971da177e4SLinus Torvalds 	res->verf->verifier[0] = *p++;
15981da177e4SLinus Torvalds 	res->verf->verifier[1] = *p++;
15991da177e4SLinus Torvalds 	return 0;
16001da177e4SLinus Torvalds }
16011da177e4SLinus Torvalds 
1602b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1603b7fa0554SAndreas Gruenbacher /*
1604b7fa0554SAndreas Gruenbacher  * Decode GETACL reply
1605b7fa0554SAndreas Gruenbacher  */
1606b7fa0554SAndreas Gruenbacher static int
1607d61005a6SAl Viro nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
1608b7fa0554SAndreas Gruenbacher 		   struct nfs3_getaclres *res)
1609b7fa0554SAndreas Gruenbacher {
1610b7fa0554SAndreas Gruenbacher 	struct xdr_buf *buf = &req->rq_rcv_buf;
1611b7fa0554SAndreas Gruenbacher 	int status = ntohl(*p++);
1612b7fa0554SAndreas Gruenbacher 	struct posix_acl **acl;
1613b7fa0554SAndreas Gruenbacher 	unsigned int *aclcnt;
1614b7fa0554SAndreas Gruenbacher 	int err, base;
1615b7fa0554SAndreas Gruenbacher 
1616b7fa0554SAndreas Gruenbacher 	if (status != 0)
1617856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
1618b7fa0554SAndreas Gruenbacher 	p = xdr_decode_post_op_attr(p, res->fattr);
1619b7fa0554SAndreas Gruenbacher 	res->mask = ntohl(*p++);
1620b7fa0554SAndreas Gruenbacher 	if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
1621b7fa0554SAndreas Gruenbacher 		return -EINVAL;
1622b7fa0554SAndreas Gruenbacher 	base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
1623b7fa0554SAndreas Gruenbacher 
1624b7fa0554SAndreas Gruenbacher 	acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
1625b7fa0554SAndreas Gruenbacher 	aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
1626b7fa0554SAndreas Gruenbacher 	err = nfsacl_decode(buf, base, aclcnt, acl);
1627b7fa0554SAndreas Gruenbacher 
1628b7fa0554SAndreas Gruenbacher 	acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
1629b7fa0554SAndreas Gruenbacher 	aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
1630b7fa0554SAndreas Gruenbacher 	if (err > 0)
1631b7fa0554SAndreas Gruenbacher 		err = nfsacl_decode(buf, base + err, aclcnt, acl);
1632b7fa0554SAndreas Gruenbacher 	return (err > 0) ? 0 : err;
1633b7fa0554SAndreas Gruenbacher }
1634b7fa0554SAndreas Gruenbacher 
1635b7fa0554SAndreas Gruenbacher /*
1636b7fa0554SAndreas Gruenbacher  * Decode setacl reply.
1637b7fa0554SAndreas Gruenbacher  */
1638b7fa0554SAndreas Gruenbacher static int
1639d61005a6SAl Viro nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
1640b7fa0554SAndreas Gruenbacher {
1641b7fa0554SAndreas Gruenbacher 	int status = ntohl(*p++);
1642b7fa0554SAndreas Gruenbacher 
1643b7fa0554SAndreas Gruenbacher 	if (status)
1644856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
1645b7fa0554SAndreas Gruenbacher 	xdr_decode_post_op_attr(p, fattr);
1646b7fa0554SAndreas Gruenbacher 	return 0;
1647b7fa0554SAndreas Gruenbacher }
1648b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1649b7fa0554SAndreas Gruenbacher 
16501da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
16511da177e4SLinus Torvalds [NFS3PROC_##proc] = {							\
16521da177e4SLinus Torvalds 	.p_proc      = NFS3PROC_##proc,					\
1653ad96b5b5SChuck Lever 	.p_encode    = (kxdrproc_t)nfs3_xdr_enc_##argtype##3args,	\
16541da177e4SLinus Torvalds 	.p_decode    = (kxdrproc_t) nfs3_xdr_##restype,			\
1655ad96b5b5SChuck Lever 	.p_arglen    = NFS3_##argtype##args_sz,				\
16562bea90d4SChuck Lever 	.p_replen    = NFS3_##restype##_sz,				\
1657cc0175c1SChuck Lever 	.p_timer     = timer,						\
1658cc0175c1SChuck Lever 	.p_statidx   = NFS3PROC_##proc,					\
1659cc0175c1SChuck Lever 	.p_name      = #proc,						\
16601da177e4SLinus Torvalds 	}
16611da177e4SLinus Torvalds 
16621da177e4SLinus Torvalds struct rpc_procinfo	nfs3_procedures[] = {
1663ad96b5b5SChuck Lever 	PROC(GETATTR,		getattr,	attrstat,	1),
1664ad96b5b5SChuck Lever 	PROC(SETATTR,		setattr,	wccstat,	0),
1665ad96b5b5SChuck Lever 	PROC(LOOKUP,		lookup,		lookupres,	2),
1666ad96b5b5SChuck Lever 	PROC(ACCESS,		access,		accessres,	1),
1667ad96b5b5SChuck Lever 	PROC(READLINK,		readlink,	readlinkres,	3),
1668ad96b5b5SChuck Lever 	PROC(READ,		read,		readres,	3),
1669ad96b5b5SChuck Lever 	PROC(WRITE,		write,		writeres,	4),
1670ad96b5b5SChuck Lever 	PROC(CREATE,		create,		createres,	0),
1671ad96b5b5SChuck Lever 	PROC(MKDIR,		mkdir,		createres,	0),
1672ad96b5b5SChuck Lever 	PROC(SYMLINK,		symlink,	createres,	0),
1673ad96b5b5SChuck Lever 	PROC(MKNOD,		mknod,		createres,	0),
1674ad96b5b5SChuck Lever 	PROC(REMOVE,		remove,		removeres,	0),
1675ad96b5b5SChuck Lever 	PROC(RMDIR,		lookup,		wccstat,	0),
1676ad96b5b5SChuck Lever 	PROC(RENAME,		rename,		renameres,	0),
1677ad96b5b5SChuck Lever 	PROC(LINK,		link,		linkres,	0),
1678ad96b5b5SChuck Lever 	PROC(READDIR,		readdir,	readdirres,	3),
1679ad96b5b5SChuck Lever 	PROC(READDIRPLUS,	readdirplus,	readdirres,	3),
1680ad96b5b5SChuck Lever 	PROC(FSSTAT,		getattr,	fsstatres,	0),
1681ad96b5b5SChuck Lever 	PROC(FSINFO,		getattr,	fsinfores,	0),
1682ad96b5b5SChuck Lever 	PROC(PATHCONF,		getattr,	pathconfres,	0),
1683ad96b5b5SChuck Lever 	PROC(COMMIT,		commit,		commitres,	5),
16841da177e4SLinus Torvalds };
16851da177e4SLinus Torvalds 
16861da177e4SLinus Torvalds struct rpc_version		nfs_version3 = {
16871da177e4SLinus Torvalds 	.number			= 3,
1688e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
16891da177e4SLinus Torvalds 	.procs			= nfs3_procedures
16901da177e4SLinus Torvalds };
16911da177e4SLinus Torvalds 
1692b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1693b7fa0554SAndreas Gruenbacher static struct rpc_procinfo	nfs3_acl_procedures[] = {
1694b7fa0554SAndreas Gruenbacher 	[ACLPROC3_GETACL] = {
1695b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_GETACL,
1696ad96b5b5SChuck Lever 		.p_encode = (kxdrproc_t)nfs3_xdr_enc_getacl3args,
1697b7fa0554SAndreas Gruenbacher 		.p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
16982bea90d4SChuck Lever 		.p_arglen = ACL3_getaclargs_sz,
16992bea90d4SChuck Lever 		.p_replen = ACL3_getaclres_sz,
1700b7fa0554SAndreas Gruenbacher 		.p_timer = 1,
1701cc0175c1SChuck Lever 		.p_name = "GETACL",
1702b7fa0554SAndreas Gruenbacher 	},
1703b7fa0554SAndreas Gruenbacher 	[ACLPROC3_SETACL] = {
1704b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_SETACL,
1705ad96b5b5SChuck Lever 		.p_encode = (kxdrproc_t)nfs3_xdr_enc_setacl3args,
1706b7fa0554SAndreas Gruenbacher 		.p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
17072bea90d4SChuck Lever 		.p_arglen = ACL3_setaclargs_sz,
17082bea90d4SChuck Lever 		.p_replen = ACL3_setaclres_sz,
1709b7fa0554SAndreas Gruenbacher 		.p_timer = 0,
1710cc0175c1SChuck Lever 		.p_name = "SETACL",
1711b7fa0554SAndreas Gruenbacher 	},
1712b7fa0554SAndreas Gruenbacher };
1713b7fa0554SAndreas Gruenbacher 
1714b7fa0554SAndreas Gruenbacher struct rpc_version		nfsacl_version3 = {
1715b7fa0554SAndreas Gruenbacher 	.number			= 3,
1716b7fa0554SAndreas Gruenbacher 	.nrprocs		= sizeof(nfs3_acl_procedures)/
1717b7fa0554SAndreas Gruenbacher 				  sizeof(nfs3_acl_procedures[0]),
1718b7fa0554SAndreas Gruenbacher 	.procs			= nfs3_acl_procedures,
1719b7fa0554SAndreas Gruenbacher };
1720b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1721