xref: /openbmc/linux/fs/nfs/nfs3xdr.c (revision 9ed5af26)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/nfs/nfs3xdr.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * XDR functions to encode/decode NFSv3 RPC arguments and results.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1996, 1997 Olaf Kirch
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/param.h>
111da177e4SLinus Torvalds #include <linux/time.h>
121da177e4SLinus Torvalds #include <linux/mm.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/string.h>
151da177e4SLinus Torvalds #include <linux/in.h>
161da177e4SLinus Torvalds #include <linux/pagemap.h>
171da177e4SLinus Torvalds #include <linux/proc_fs.h>
181da177e4SLinus Torvalds #include <linux/kdev_t.h>
191da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
201da177e4SLinus Torvalds #include <linux/nfs.h>
211da177e4SLinus Torvalds #include <linux/nfs3.h>
221da177e4SLinus Torvalds #include <linux/nfs_fs.h>
23b7fa0554SAndreas Gruenbacher #include <linux/nfsacl.h>
24f23f6584SChuck Lever #include "nfstrace.h"
25f7b422b1SDavid Howells #include "internal.h"
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
301da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /*
331da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
341da177e4SLinus Torvalds  * number of 32bit-words
351da177e4SLinus Torvalds  */
36*9ed5af26STrond Myklebust #define NFS3_pagepad_sz		(1) /* Page padding */
371da177e4SLinus Torvalds #define NFS3_fhandle_sz		(1+16)
381da177e4SLinus Torvalds #define NFS3_fh_sz		(NFS3_fhandle_sz)	/* shorthand */
391da177e4SLinus Torvalds #define NFS3_sattr_sz		(15)
401da177e4SLinus Torvalds #define NFS3_filename_sz	(1+(NFS3_MAXNAMLEN>>2))
411da177e4SLinus Torvalds #define NFS3_path_sz		(1+(NFS3_MAXPATHLEN>>2))
421da177e4SLinus Torvalds #define NFS3_fattr_sz		(21)
43d9c407b1SChuck Lever #define NFS3_cookieverf_sz	(NFS3_COOKIEVERFSIZE>>2)
441da177e4SLinus Torvalds #define NFS3_wcc_attr_sz	(6)
451da177e4SLinus Torvalds #define NFS3_pre_op_attr_sz	(1+NFS3_wcc_attr_sz)
461da177e4SLinus Torvalds #define NFS3_post_op_attr_sz	(1+NFS3_fattr_sz)
471da177e4SLinus Torvalds #define NFS3_wcc_data_sz	(NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
481da177e4SLinus Torvalds #define NFS3_diropargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
49ad96b5b5SChuck Lever 
50ad96b5b5SChuck Lever #define NFS3_getattrargs_sz	(NFS3_fh_sz)
51ad96b5b5SChuck Lever #define NFS3_setattrargs_sz	(NFS3_fh_sz+NFS3_sattr_sz+3)
52ad96b5b5SChuck Lever #define NFS3_lookupargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
531da177e4SLinus Torvalds #define NFS3_accessargs_sz	(NFS3_fh_sz+1)
541da177e4SLinus Torvalds #define NFS3_readlinkargs_sz	(NFS3_fh_sz)
551da177e4SLinus Torvalds #define NFS3_readargs_sz	(NFS3_fh_sz+3)
561da177e4SLinus Torvalds #define NFS3_writeargs_sz	(NFS3_fh_sz+5)
571da177e4SLinus Torvalds #define NFS3_createargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
581da177e4SLinus Torvalds #define NFS3_mkdirargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
5994a6d753SChuck Lever #define NFS3_symlinkargs_sz	(NFS3_diropargs_sz+1+NFS3_sattr_sz)
601da177e4SLinus Torvalds #define NFS3_mknodargs_sz	(NFS3_diropargs_sz+2+NFS3_sattr_sz)
61ad96b5b5SChuck Lever #define NFS3_removeargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
621da177e4SLinus Torvalds #define NFS3_renameargs_sz	(NFS3_diropargs_sz+NFS3_diropargs_sz)
631da177e4SLinus Torvalds #define NFS3_linkargs_sz		(NFS3_fh_sz+NFS3_diropargs_sz)
64d9c407b1SChuck Lever #define NFS3_readdirargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+3)
65d9c407b1SChuck Lever #define NFS3_readdirplusargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+4)
661da177e4SLinus Torvalds #define NFS3_commitargs_sz	(NFS3_fh_sz+3)
671da177e4SLinus Torvalds 
68f5fc3c50SChuck Lever #define NFS3_getattrres_sz	(1+NFS3_fattr_sz)
69f5fc3c50SChuck Lever #define NFS3_setattrres_sz	(1+NFS3_wcc_data_sz)
70f5fc3c50SChuck Lever #define NFS3_removeres_sz	(NFS3_setattrres_sz)
711da177e4SLinus Torvalds #define NFS3_lookupres_sz	(1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
721da177e4SLinus Torvalds #define NFS3_accessres_sz	(1+NFS3_post_op_attr_sz+1)
73*9ed5af26STrond Myklebust #define NFS3_readlinkres_sz	(1+NFS3_post_op_attr_sz+1+NFS3_pagepad_sz)
74*9ed5af26STrond Myklebust #define NFS3_readres_sz		(1+NFS3_post_op_attr_sz+3+NFS3_pagepad_sz)
751da177e4SLinus Torvalds #define NFS3_writeres_sz	(1+NFS3_wcc_data_sz+4)
761da177e4SLinus Torvalds #define NFS3_createres_sz	(1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
771da177e4SLinus Torvalds #define NFS3_renameres_sz	(1+(2 * NFS3_wcc_data_sz))
781da177e4SLinus Torvalds #define NFS3_linkres_sz		(1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
79*9ed5af26STrond Myklebust #define NFS3_readdirres_sz	(1+NFS3_post_op_attr_sz+2+NFS3_pagepad_sz)
801da177e4SLinus Torvalds #define NFS3_fsstatres_sz	(1+NFS3_post_op_attr_sz+13)
811da177e4SLinus Torvalds #define NFS3_fsinfores_sz	(1+NFS3_post_op_attr_sz+12)
821da177e4SLinus Torvalds #define NFS3_pathconfres_sz	(1+NFS3_post_op_attr_sz+6)
831da177e4SLinus Torvalds #define NFS3_commitres_sz	(1+NFS3_wcc_data_sz+2)
841da177e4SLinus Torvalds 
85b7fa0554SAndreas Gruenbacher #define ACL3_getaclargs_sz	(NFS3_fh_sz+1)
86ae46141fSTrond Myklebust #define ACL3_setaclargs_sz	(NFS3_fh_sz+1+ \
87ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
88ae46141fSTrond Myklebust #define ACL3_getaclres_sz	(1+NFS3_post_op_attr_sz+1+ \
89*9ed5af26STrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)+\
90*9ed5af26STrond Myklebust 				NFS3_pagepad_sz)
91b7fa0554SAndreas Gruenbacher #define ACL3_setaclres_sz	(1+NFS3_post_op_attr_sz)
92b7fa0554SAndreas Gruenbacher 
935e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat);
945e7e5a0dSBryan Schumaker 
951da177e4SLinus Torvalds /*
961da177e4SLinus Torvalds  * Map file type to S_IFMT bits
971da177e4SLinus Torvalds  */
98bca79478STrond Myklebust static const umode_t nfs_type2fmt[] = {
99bca79478STrond Myklebust 	[NF3BAD] = 0,
100bca79478STrond Myklebust 	[NF3REG] = S_IFREG,
101bca79478STrond Myklebust 	[NF3DIR] = S_IFDIR,
102bca79478STrond Myklebust 	[NF3BLK] = S_IFBLK,
103bca79478STrond Myklebust 	[NF3CHR] = S_IFCHR,
104bca79478STrond Myklebust 	[NF3LNK] = S_IFLNK,
105bca79478STrond Myklebust 	[NF3SOCK] = S_IFSOCK,
106bca79478STrond Myklebust 	[NF3FIFO] = S_IFIFO,
1071da177e4SLinus Torvalds };
1081da177e4SLinus Torvalds 
109264d948cSTrond Myklebust static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt)
110264d948cSTrond Myklebust {
111264d948cSTrond Myklebust 	if (clnt && clnt->cl_cred)
112264d948cSTrond Myklebust 		return clnt->cl_cred->user_ns;
113264d948cSTrond Myklebust 	return &init_user_ns;
114264d948cSTrond Myklebust }
115264d948cSTrond Myklebust 
116264d948cSTrond Myklebust static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp)
117264d948cSTrond Myklebust {
118264d948cSTrond Myklebust 	if (rqstp->rq_task)
119264d948cSTrond Myklebust 		return rpc_userns(rqstp->rq_task->tk_client);
120264d948cSTrond Myklebust 	return &init_user_ns;
121264d948cSTrond Myklebust }
122264d948cSTrond Myklebust 
1231da177e4SLinus Torvalds /*
124d9c407b1SChuck Lever  * Encode/decode NFSv3 basic data types
125d9c407b1SChuck Lever  *
126d9c407b1SChuck Lever  * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
127d9c407b1SChuck Lever  * "NFS Version 3 Protocol Specification".
128d9c407b1SChuck Lever  *
129d9c407b1SChuck Lever  * Not all basic data types have their own encoding and decoding
130d9c407b1SChuck Lever  * functions.  For run-time efficiency, some data types are encoded
131d9c407b1SChuck Lever  * or decoded inline.
132d9c407b1SChuck Lever  */
133d9c407b1SChuck Lever 
134d9c407b1SChuck Lever static void encode_uint32(struct xdr_stream *xdr, u32 value)
135d9c407b1SChuck Lever {
136d9c407b1SChuck Lever 	__be32 *p = xdr_reserve_space(xdr, 4);
137d9c407b1SChuck Lever 	*p = cpu_to_be32(value);
138d9c407b1SChuck Lever }
139d9c407b1SChuck Lever 
140e4f93234SChuck Lever static int decode_uint32(struct xdr_stream *xdr, u32 *value)
141e4f93234SChuck Lever {
142e4f93234SChuck Lever 	__be32 *p;
143e4f93234SChuck Lever 
144e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
145eb72f484SChuck Lever 	if (unlikely(!p))
146eb72f484SChuck Lever 		return -EIO;
147e4f93234SChuck Lever 	*value = be32_to_cpup(p);
148e4f93234SChuck Lever 	return 0;
149e4f93234SChuck Lever }
150e4f93234SChuck Lever 
151e4f93234SChuck Lever static int decode_uint64(struct xdr_stream *xdr, u64 *value)
152e4f93234SChuck Lever {
153e4f93234SChuck Lever 	__be32 *p;
154e4f93234SChuck Lever 
155e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8);
156eb72f484SChuck Lever 	if (unlikely(!p))
157eb72f484SChuck Lever 		return -EIO;
158e4f93234SChuck Lever 	xdr_decode_hyper(p, value);
159e4f93234SChuck Lever 	return 0;
160e4f93234SChuck Lever }
161e4f93234SChuck Lever 
162e4f93234SChuck Lever /*
163e4f93234SChuck Lever  * fileid3
164e4f93234SChuck Lever  *
165e4f93234SChuck Lever  *	typedef uint64 fileid3;
166e4f93234SChuck Lever  */
167f6048709SChuck Lever static __be32 *xdr_decode_fileid3(__be32 *p, u64 *fileid)
168f6048709SChuck Lever {
169f6048709SChuck Lever 	return xdr_decode_hyper(p, fileid);
170f6048709SChuck Lever }
171f6048709SChuck Lever 
172e4f93234SChuck Lever static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid)
173e4f93234SChuck Lever {
174e4f93234SChuck Lever 	return decode_uint64(xdr, fileid);
175e4f93234SChuck Lever }
176e4f93234SChuck Lever 
177d9c407b1SChuck Lever /*
178d9c407b1SChuck Lever  * filename3
179d9c407b1SChuck Lever  *
180d9c407b1SChuck Lever  *	typedef string filename3<>;
181d9c407b1SChuck Lever  */
182d9c407b1SChuck Lever static void encode_filename3(struct xdr_stream *xdr,
183d9c407b1SChuck Lever 			     const char *name, u32 length)
184d9c407b1SChuck Lever {
185d9c407b1SChuck Lever 	__be32 *p;
186d9c407b1SChuck Lever 
1877fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS3_MAXNAMLEN);
188d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
189d9c407b1SChuck Lever 	xdr_encode_opaque(p, name, length);
190d9c407b1SChuck Lever }
191d9c407b1SChuck Lever 
192e4f93234SChuck Lever static int decode_inline_filename3(struct xdr_stream *xdr,
193e4f93234SChuck Lever 				   const char **name, u32 *length)
194e4f93234SChuck Lever {
195e4f93234SChuck Lever 	__be32 *p;
196e4f93234SChuck Lever 	u32 count;
197e4f93234SChuck Lever 
198e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
199eb72f484SChuck Lever 	if (unlikely(!p))
200eb72f484SChuck Lever 		return -EIO;
201e4f93234SChuck Lever 	count = be32_to_cpup(p);
202e4f93234SChuck Lever 	if (count > NFS3_MAXNAMLEN)
203e4f93234SChuck Lever 		goto out_nametoolong;
204e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, count);
205eb72f484SChuck Lever 	if (unlikely(!p))
206eb72f484SChuck Lever 		return -EIO;
207e4f93234SChuck Lever 	*name = (const char *)p;
208e4f93234SChuck Lever 	*length = count;
209e4f93234SChuck Lever 	return 0;
210e4f93234SChuck Lever 
211e4f93234SChuck Lever out_nametoolong:
212e4f93234SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
213e4f93234SChuck Lever 	return -ENAMETOOLONG;
214e4f93234SChuck Lever }
215e4f93234SChuck Lever 
216d9c407b1SChuck Lever /*
217d9c407b1SChuck Lever  * nfspath3
218d9c407b1SChuck Lever  *
219d9c407b1SChuck Lever  *	typedef string nfspath3<>;
220d9c407b1SChuck Lever  */
221d9c407b1SChuck Lever static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
222d9c407b1SChuck Lever 			    const u32 length)
223d9c407b1SChuck Lever {
224d9c407b1SChuck Lever 	encode_uint32(xdr, length);
225d9c407b1SChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
226d9c407b1SChuck Lever }
227d9c407b1SChuck Lever 
228e4f93234SChuck Lever static int decode_nfspath3(struct xdr_stream *xdr)
229e4f93234SChuck Lever {
230e4f93234SChuck Lever 	u32 recvd, count;
231e4f93234SChuck Lever 	__be32 *p;
232e4f93234SChuck Lever 
233e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
234eb72f484SChuck Lever 	if (unlikely(!p))
235eb72f484SChuck Lever 		return -EIO;
236e4f93234SChuck Lever 	count = be32_to_cpup(p);
237e4f93234SChuck Lever 	if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
238e4f93234SChuck Lever 		goto out_nametoolong;
23964bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
240e4f93234SChuck Lever 	if (unlikely(count > recvd))
241e4f93234SChuck Lever 		goto out_cheating;
242e4f93234SChuck Lever 	xdr_terminate_string(xdr->buf, count);
243e4f93234SChuck Lever 	return 0;
244e4f93234SChuck Lever 
245e4f93234SChuck Lever out_nametoolong:
246e4f93234SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", count);
247e4f93234SChuck Lever 	return -ENAMETOOLONG;
248e4f93234SChuck Lever out_cheating:
249e4f93234SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
250e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
251e4f93234SChuck Lever 	return -EIO;
252e4f93234SChuck Lever }
253e4f93234SChuck Lever 
254d9c407b1SChuck Lever /*
255d9c407b1SChuck Lever  * cookie3
256d9c407b1SChuck Lever  *
257d9c407b1SChuck Lever  *	typedef uint64 cookie3
258d9c407b1SChuck Lever  */
259d9c407b1SChuck Lever static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
260d9c407b1SChuck Lever {
261d9c407b1SChuck Lever 	return xdr_encode_hyper(p, cookie);
262d9c407b1SChuck Lever }
263d9c407b1SChuck Lever 
264e4f93234SChuck Lever static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie)
265e4f93234SChuck Lever {
266e4f93234SChuck Lever 	return decode_uint64(xdr, cookie);
267e4f93234SChuck Lever }
268e4f93234SChuck Lever 
269d9c407b1SChuck Lever /*
270d9c407b1SChuck Lever  * cookieverf3
271d9c407b1SChuck Lever  *
272d9c407b1SChuck Lever  *	typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
273d9c407b1SChuck Lever  */
274d9c407b1SChuck Lever static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
275d9c407b1SChuck Lever {
276d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
277d9c407b1SChuck Lever 	return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
278d9c407b1SChuck Lever }
279d9c407b1SChuck Lever 
280e4f93234SChuck Lever static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier)
281e4f93234SChuck Lever {
282e4f93234SChuck Lever 	__be32 *p;
283e4f93234SChuck Lever 
284e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
285eb72f484SChuck Lever 	if (unlikely(!p))
286eb72f484SChuck Lever 		return -EIO;
287e4f93234SChuck Lever 	memcpy(verifier, p, NFS3_COOKIEVERFSIZE);
288e4f93234SChuck Lever 	return 0;
289e4f93234SChuck Lever }
290e4f93234SChuck Lever 
291d9c407b1SChuck Lever /*
292d9c407b1SChuck Lever  * createverf3
293d9c407b1SChuck Lever  *
294d9c407b1SChuck Lever  *	typedef opaque createverf3[NFS3_CREATEVERFSIZE];
295d9c407b1SChuck Lever  */
296d9c407b1SChuck Lever static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
297d9c407b1SChuck Lever {
298d9c407b1SChuck Lever 	__be32 *p;
299d9c407b1SChuck Lever 
300d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
301d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_CREATEVERFSIZE);
302d9c407b1SChuck Lever }
303d9c407b1SChuck Lever 
3042f2c63bcSTrond Myklebust static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
305e4f93234SChuck Lever {
306e4f93234SChuck Lever 	__be32 *p;
307e4f93234SChuck Lever 
308e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
309eb72f484SChuck Lever 	if (unlikely(!p))
310eb72f484SChuck Lever 		return -EIO;
3112f2c63bcSTrond Myklebust 	memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
312e4f93234SChuck Lever 	return 0;
313e4f93234SChuck Lever }
314e4f93234SChuck Lever 
315e4f93234SChuck Lever /*
316e4f93234SChuck Lever  * size3
317e4f93234SChuck Lever  *
318e4f93234SChuck Lever  *	typedef uint64 size3;
319e4f93234SChuck Lever  */
320e4f93234SChuck Lever static __be32 *xdr_decode_size3(__be32 *p, u64 *size)
321e4f93234SChuck Lever {
322e4f93234SChuck Lever 	return xdr_decode_hyper(p, size);
323e4f93234SChuck Lever }
324e4f93234SChuck Lever 
325e4f93234SChuck Lever /*
326e4f93234SChuck Lever  * nfsstat3
327e4f93234SChuck Lever  *
328e4f93234SChuck Lever  *	enum nfsstat3 {
329e4f93234SChuck Lever  *		NFS3_OK = 0,
330e4f93234SChuck Lever  *		...
331e4f93234SChuck Lever  *	}
332e4f93234SChuck Lever  */
333e4f93234SChuck Lever #define NFS3_OK		NFS_OK
334e4f93234SChuck Lever 
335e4f93234SChuck Lever static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status)
336e4f93234SChuck Lever {
337e4f93234SChuck Lever 	__be32 *p;
338e4f93234SChuck Lever 
339e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
340eb72f484SChuck Lever 	if (unlikely(!p))
341eb72f484SChuck Lever 		return -EIO;
342f23f6584SChuck Lever 	if (unlikely(*p != cpu_to_be32(NFS3_OK)))
343f23f6584SChuck Lever 		goto out_status;
344f23f6584SChuck Lever 	*status = 0;
345f23f6584SChuck Lever 	return 0;
346f23f6584SChuck Lever out_status:
347e4f93234SChuck Lever 	*status = be32_to_cpup(p);
34862a92ba9SChuck Lever 	trace_nfs_xdr_status(xdr, (int)*status);
349e4f93234SChuck Lever 	return 0;
350e4f93234SChuck Lever }
351e4f93234SChuck Lever 
352d9c407b1SChuck Lever /*
353d9c407b1SChuck Lever  * ftype3
354d9c407b1SChuck Lever  *
355d9c407b1SChuck Lever  *	enum ftype3 {
356d9c407b1SChuck Lever  *		NF3REG	= 1,
357d9c407b1SChuck Lever  *		NF3DIR	= 2,
358d9c407b1SChuck Lever  *		NF3BLK	= 3,
359d9c407b1SChuck Lever  *		NF3CHR	= 4,
360d9c407b1SChuck Lever  *		NF3LNK	= 5,
361d9c407b1SChuck Lever  *		NF3SOCK	= 6,
362d9c407b1SChuck Lever  *		NF3FIFO	= 7
363d9c407b1SChuck Lever  *	};
364d9c407b1SChuck Lever  */
365d9c407b1SChuck Lever static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
366d9c407b1SChuck Lever {
367d9c407b1SChuck Lever 	encode_uint32(xdr, type);
368d9c407b1SChuck Lever }
369d9c407b1SChuck Lever 
370f6048709SChuck Lever static __be32 *xdr_decode_ftype3(__be32 *p, umode_t *mode)
371f6048709SChuck Lever {
372f6048709SChuck Lever 	u32 type;
373f6048709SChuck Lever 
374f6048709SChuck Lever 	type = be32_to_cpup(p++);
375f6048709SChuck Lever 	if (type > NF3FIFO)
376f6048709SChuck Lever 		type = NF3NON;
377f6048709SChuck Lever 	*mode = nfs_type2fmt[type];
378f6048709SChuck Lever 	return p;
379f6048709SChuck Lever }
380f6048709SChuck Lever 
381d9c407b1SChuck Lever /*
382d9c407b1SChuck Lever  * specdata3
383d9c407b1SChuck Lever  *
384d9c407b1SChuck Lever  *     struct specdata3 {
385d9c407b1SChuck Lever  *             uint32  specdata1;
386d9c407b1SChuck Lever  *             uint32  specdata2;
387d9c407b1SChuck Lever  *     };
388d9c407b1SChuck Lever  */
389d9c407b1SChuck Lever static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
390d9c407b1SChuck Lever {
391d9c407b1SChuck Lever 	__be32 *p;
392d9c407b1SChuck Lever 
393d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8);
394d9c407b1SChuck Lever 	*p++ = cpu_to_be32(MAJOR(rdev));
395d9c407b1SChuck Lever 	*p = cpu_to_be32(MINOR(rdev));
396d9c407b1SChuck Lever }
397d9c407b1SChuck Lever 
398f6048709SChuck Lever static __be32 *xdr_decode_specdata3(__be32 *p, dev_t *rdev)
399f6048709SChuck Lever {
400f6048709SChuck Lever 	unsigned int major, minor;
401f6048709SChuck Lever 
402f6048709SChuck Lever 	major = be32_to_cpup(p++);
403f6048709SChuck Lever 	minor = be32_to_cpup(p++);
404f6048709SChuck Lever 	*rdev = MKDEV(major, minor);
405f6048709SChuck Lever 	if (MAJOR(*rdev) != major || MINOR(*rdev) != minor)
406f6048709SChuck Lever 		*rdev = 0;
407f6048709SChuck Lever 	return p;
408f6048709SChuck Lever }
409f6048709SChuck Lever 
410d9c407b1SChuck Lever /*
411d9c407b1SChuck Lever  * nfs_fh3
412d9c407b1SChuck Lever  *
413d9c407b1SChuck Lever  *	struct nfs_fh3 {
414d9c407b1SChuck Lever  *		opaque       data<NFS3_FHSIZE>;
415d9c407b1SChuck Lever  *	};
416d9c407b1SChuck Lever  */
417d9c407b1SChuck Lever static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
418d9c407b1SChuck Lever {
419d9c407b1SChuck Lever 	__be32 *p;
420d9c407b1SChuck Lever 
4217fc38846STrond Myklebust 	WARN_ON_ONCE(fh->size > NFS3_FHSIZE);
422d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + fh->size);
423d9c407b1SChuck Lever 	xdr_encode_opaque(p, fh->data, fh->size);
424d9c407b1SChuck Lever }
425d9c407b1SChuck Lever 
426e4f93234SChuck Lever static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
427e4f93234SChuck Lever {
428e4f93234SChuck Lever 	u32 length;
429e4f93234SChuck Lever 	__be32 *p;
430e4f93234SChuck Lever 
431e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
432eb72f484SChuck Lever 	if (unlikely(!p))
433eb72f484SChuck Lever 		return -EIO;
434e4f93234SChuck Lever 	length = be32_to_cpup(p++);
435e4f93234SChuck Lever 	if (unlikely(length > NFS3_FHSIZE))
436e4f93234SChuck Lever 		goto out_toobig;
437e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, length);
438eb72f484SChuck Lever 	if (unlikely(!p))
439eb72f484SChuck Lever 		return -EIO;
440e4f93234SChuck Lever 	fh->size = length;
441e4f93234SChuck Lever 	memcpy(fh->data, p, length);
442e4f93234SChuck Lever 	return 0;
443e4f93234SChuck Lever out_toobig:
444e4f93234SChuck Lever 	dprintk("NFS: file handle size (%u) too big\n", length);
445e4f93234SChuck Lever 	return -E2BIG;
446e4f93234SChuck Lever }
447e4f93234SChuck Lever 
448e4f93234SChuck Lever static void zero_nfs_fh3(struct nfs_fh *fh)
449e4f93234SChuck Lever {
450e4f93234SChuck Lever 	memset(fh, 0, sizeof(*fh));
451e4f93234SChuck Lever }
452e4f93234SChuck Lever 
453d9c407b1SChuck Lever /*
4549d5a6434SChuck Lever  * nfstime3
4559d5a6434SChuck Lever  *
4569d5a6434SChuck Lever  *	struct nfstime3 {
4579d5a6434SChuck Lever  *		uint32	seconds;
4589d5a6434SChuck Lever  *		uint32	nseconds;
4599d5a6434SChuck Lever  *	};
4609d5a6434SChuck Lever  */
4616430b323STrond Myklebust static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec64 *timep)
4629d5a6434SChuck Lever {
4636430b323STrond Myklebust 	*p++ = cpu_to_be32((u32)timep->tv_sec);
4649d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_nsec);
4659d5a6434SChuck Lever 	return p;
4669d5a6434SChuck Lever }
4679d5a6434SChuck Lever 
468e86d5a02STrond Myklebust static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec64 *timep)
469f6048709SChuck Lever {
470f6048709SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
471f6048709SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++);
472f6048709SChuck Lever 	return p;
473f6048709SChuck Lever }
474f6048709SChuck Lever 
4759d5a6434SChuck Lever /*
476d9c407b1SChuck Lever  * sattr3
477d9c407b1SChuck Lever  *
478d9c407b1SChuck Lever  *	enum time_how {
479d9c407b1SChuck Lever  *		DONT_CHANGE		= 0,
480d9c407b1SChuck Lever  *		SET_TO_SERVER_TIME	= 1,
481d9c407b1SChuck Lever  *		SET_TO_CLIENT_TIME	= 2
482d9c407b1SChuck Lever  *	};
483d9c407b1SChuck Lever  *
484d9c407b1SChuck Lever  *	union set_mode3 switch (bool set_it) {
485d9c407b1SChuck Lever  *	case TRUE:
486d9c407b1SChuck Lever  *		mode3	mode;
487d9c407b1SChuck Lever  *	default:
488d9c407b1SChuck Lever  *		void;
489d9c407b1SChuck Lever  *	};
490d9c407b1SChuck Lever  *
491d9c407b1SChuck Lever  *	union set_uid3 switch (bool set_it) {
492d9c407b1SChuck Lever  *	case TRUE:
493d9c407b1SChuck Lever  *		uid3	uid;
494d9c407b1SChuck Lever  *	default:
495d9c407b1SChuck Lever  *		void;
496d9c407b1SChuck Lever  *	};
497d9c407b1SChuck Lever  *
498d9c407b1SChuck Lever  *	union set_gid3 switch (bool set_it) {
499d9c407b1SChuck Lever  *	case TRUE:
500d9c407b1SChuck Lever  *		gid3	gid;
501d9c407b1SChuck Lever  *	default:
502d9c407b1SChuck Lever  *		void;
503d9c407b1SChuck Lever  *	};
504d9c407b1SChuck Lever  *
505d9c407b1SChuck Lever  *	union set_size3 switch (bool set_it) {
506d9c407b1SChuck Lever  *	case TRUE:
507d9c407b1SChuck Lever  *		size3	size;
508d9c407b1SChuck Lever  *	default:
509d9c407b1SChuck Lever  *		void;
510d9c407b1SChuck Lever  *	};
511d9c407b1SChuck Lever  *
512d9c407b1SChuck Lever  *	union set_atime switch (time_how set_it) {
513d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
514d9c407b1SChuck Lever  *		nfstime3	atime;
515d9c407b1SChuck Lever  *	default:
516d9c407b1SChuck Lever  *		void;
517d9c407b1SChuck Lever  *	};
518d9c407b1SChuck Lever  *
519d9c407b1SChuck Lever  *	union set_mtime switch (time_how set_it) {
520d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
521d9c407b1SChuck Lever  *		nfstime3  mtime;
522d9c407b1SChuck Lever  *	default:
523d9c407b1SChuck Lever  *		void;
524d9c407b1SChuck Lever  *	};
525d9c407b1SChuck Lever  *
526d9c407b1SChuck Lever  *	struct sattr3 {
527d9c407b1SChuck Lever  *		set_mode3	mode;
528d9c407b1SChuck Lever  *		set_uid3	uid;
529d9c407b1SChuck Lever  *		set_gid3	gid;
530d9c407b1SChuck Lever  *		set_size3	size;
531d9c407b1SChuck Lever  *		set_atime	atime;
532d9c407b1SChuck Lever  *		set_mtime	mtime;
533d9c407b1SChuck Lever  *	};
534d9c407b1SChuck Lever  */
535264d948cSTrond Myklebust static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr,
536264d948cSTrond Myklebust 		struct user_namespace *userns)
537d9c407b1SChuck Lever {
538d9c407b1SChuck Lever 	u32 nbytes;
539d9c407b1SChuck Lever 	__be32 *p;
540d9c407b1SChuck Lever 
541d9c407b1SChuck Lever 	/*
542d9c407b1SChuck Lever 	 * In order to make only a single xdr_reserve_space() call,
543d9c407b1SChuck Lever 	 * pre-compute the total number of bytes to be reserved.
544d9c407b1SChuck Lever 	 * Six boolean values, one for each set_foo field, are always
545d9c407b1SChuck Lever 	 * present in the encoded result, so start there.
546d9c407b1SChuck Lever 	 */
547d9c407b1SChuck Lever 	nbytes = 6 * 4;
548d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MODE)
549d9c407b1SChuck Lever 		nbytes += 4;
550d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_UID)
551d9c407b1SChuck Lever 		nbytes += 4;
552d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_GID)
553d9c407b1SChuck Lever 		nbytes += 4;
554d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
555d9c407b1SChuck Lever 		nbytes += 8;
556d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
557d9c407b1SChuck Lever 		nbytes += 8;
558d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
559d9c407b1SChuck Lever 		nbytes += 8;
560d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, nbytes);
561d9c407b1SChuck Lever 
5629d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MODE) {
5639d5a6434SChuck Lever 		*p++ = xdr_one;
5649d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
5659d5a6434SChuck Lever 	} else
5669d5a6434SChuck Lever 		*p++ = xdr_zero;
5679d5a6434SChuck Lever 
5689d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_UID) {
5699d5a6434SChuck Lever 		*p++ = xdr_one;
570264d948cSTrond Myklebust 		*p++ = cpu_to_be32(from_kuid_munged(userns, attr->ia_uid));
5719d5a6434SChuck Lever 	} else
5729d5a6434SChuck Lever 		*p++ = xdr_zero;
5739d5a6434SChuck Lever 
5749d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_GID) {
5759d5a6434SChuck Lever 		*p++ = xdr_one;
576264d948cSTrond Myklebust 		*p++ = cpu_to_be32(from_kgid_munged(userns, attr->ia_gid));
5779d5a6434SChuck Lever 	} else
5789d5a6434SChuck Lever 		*p++ = xdr_zero;
5799d5a6434SChuck Lever 
5809d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_SIZE) {
5819d5a6434SChuck Lever 		*p++ = xdr_one;
5829d5a6434SChuck Lever 		p = xdr_encode_hyper(p, (u64)attr->ia_size);
5839d5a6434SChuck Lever 	} else
5849d5a6434SChuck Lever 		*p++ = xdr_zero;
5859d5a6434SChuck Lever 
5869d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET) {
5879d5a6434SChuck Lever 		*p++ = xdr_two;
5886430b323STrond Myklebust 		p = xdr_encode_nfstime3(p, &attr->ia_atime);
5899d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_ATIME) {
5909d5a6434SChuck Lever 		*p++ = xdr_one;
5919d5a6434SChuck Lever 	} else
5929d5a6434SChuck Lever 		*p++ = xdr_zero;
5939d5a6434SChuck Lever 
5949d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET) {
5959d5a6434SChuck Lever 		*p++ = xdr_two;
5966430b323STrond Myklebust 		xdr_encode_nfstime3(p, &attr->ia_mtime);
5979d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_MTIME) {
5989d5a6434SChuck Lever 		*p = xdr_one;
5999d5a6434SChuck Lever 	} else
6009d5a6434SChuck Lever 		*p = xdr_zero;
601d9c407b1SChuck Lever }
602d9c407b1SChuck Lever 
603d9c407b1SChuck Lever /*
604e4f93234SChuck Lever  * fattr3
605e4f93234SChuck Lever  *
606e4f93234SChuck Lever  *	struct fattr3 {
607e4f93234SChuck Lever  *		ftype3		type;
608e4f93234SChuck Lever  *		mode3		mode;
609e4f93234SChuck Lever  *		uint32		nlink;
610e4f93234SChuck Lever  *		uid3		uid;
611e4f93234SChuck Lever  *		gid3		gid;
612e4f93234SChuck Lever  *		size3		size;
613e4f93234SChuck Lever  *		size3		used;
614e4f93234SChuck Lever  *		specdata3	rdev;
615e4f93234SChuck Lever  *		uint64		fsid;
616e4f93234SChuck Lever  *		fileid3		fileid;
617e4f93234SChuck Lever  *		nfstime3	atime;
618e4f93234SChuck Lever  *		nfstime3	mtime;
619e4f93234SChuck Lever  *		nfstime3	ctime;
620e4f93234SChuck Lever  *	};
621e4f93234SChuck Lever  */
622264d948cSTrond Myklebust static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr,
623264d948cSTrond Myklebust 		struct user_namespace *userns)
624e4f93234SChuck Lever {
625f6048709SChuck Lever 	umode_t fmode;
626e4f93234SChuck Lever 	__be32 *p;
627e4f93234SChuck Lever 
628e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2);
629eb72f484SChuck Lever 	if (unlikely(!p))
630eb72f484SChuck Lever 		return -EIO;
631f6048709SChuck Lever 
632f6048709SChuck Lever 	p = xdr_decode_ftype3(p, &fmode);
633f6048709SChuck Lever 
634f6048709SChuck Lever 	fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;
635f6048709SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
636264d948cSTrond Myklebust 	fattr->uid = make_kuid(userns, be32_to_cpup(p++));
63757a38daeSEric W. Biederman 	if (!uid_valid(fattr->uid))
63857a38daeSEric W. Biederman 		goto out_uid;
639264d948cSTrond Myklebust 	fattr->gid = make_kgid(userns, be32_to_cpup(p++));
64057a38daeSEric W. Biederman 	if (!gid_valid(fattr->gid))
64157a38daeSEric W. Biederman 		goto out_gid;
642f6048709SChuck Lever 
643f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->size);
644f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->du.nfs3.used);
645f6048709SChuck Lever 	p = xdr_decode_specdata3(p, &fattr->rdev);
646f6048709SChuck Lever 
647f6048709SChuck Lever 	p = xdr_decode_hyper(p, &fattr->fsid.major);
648f6048709SChuck Lever 	fattr->fsid.minor = 0;
649f6048709SChuck Lever 
650f6048709SChuck Lever 	p = xdr_decode_fileid3(p, &fattr->fileid);
651f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->atime);
652f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->mtime);
653f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->ctime);
6543a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
655f6048709SChuck Lever 
656f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V3;
657e4f93234SChuck Lever 	return 0;
65857a38daeSEric W. Biederman out_uid:
65957a38daeSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
66057a38daeSEric W. Biederman 	return -EINVAL;
66157a38daeSEric W. Biederman out_gid:
66257a38daeSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
66357a38daeSEric W. Biederman 	return -EINVAL;
664e4f93234SChuck Lever }
665e4f93234SChuck Lever 
666e4f93234SChuck Lever /*
667e4f93234SChuck Lever  * post_op_attr
668e4f93234SChuck Lever  *
669e4f93234SChuck Lever  *	union post_op_attr switch (bool attributes_follow) {
670e4f93234SChuck Lever  *	case TRUE:
671e4f93234SChuck Lever  *		fattr3	attributes;
672e4f93234SChuck Lever  *	case FALSE:
673e4f93234SChuck Lever  *		void;
674e4f93234SChuck Lever  *	};
675e4f93234SChuck Lever  */
676264d948cSTrond Myklebust static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
677264d948cSTrond Myklebust 		struct user_namespace *userns)
678e4f93234SChuck Lever {
679e4f93234SChuck Lever 	__be32 *p;
680e4f93234SChuck Lever 
681e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
682eb72f484SChuck Lever 	if (unlikely(!p))
683eb72f484SChuck Lever 		return -EIO;
684e4f93234SChuck Lever 	if (*p != xdr_zero)
685264d948cSTrond Myklebust 		return decode_fattr3(xdr, fattr, userns);
686e4f93234SChuck Lever 	return 0;
687e4f93234SChuck Lever }
688e4f93234SChuck Lever 
689e4f93234SChuck Lever /*
690e4f93234SChuck Lever  * wcc_attr
691e4f93234SChuck Lever  *	struct wcc_attr {
692e4f93234SChuck Lever  *		size3		size;
693e4f93234SChuck Lever  *		nfstime3	mtime;
694e4f93234SChuck Lever  *		nfstime3	ctime;
695e4f93234SChuck Lever  *	};
696e4f93234SChuck Lever  */
697e4f93234SChuck Lever static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
698e4f93234SChuck Lever {
699e4f93234SChuck Lever 	__be32 *p;
700e4f93234SChuck Lever 
701e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2);
702eb72f484SChuck Lever 	if (unlikely(!p))
703eb72f484SChuck Lever 		return -EIO;
704f6048709SChuck Lever 
705f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_PRESIZE
7063a1556e8STrond Myklebust 		| NFS_ATTR_FATTR_PRECHANGE
707f6048709SChuck Lever 		| NFS_ATTR_FATTR_PREMTIME
708f6048709SChuck Lever 		| NFS_ATTR_FATTR_PRECTIME;
709f6048709SChuck Lever 
710f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->pre_size);
711f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
712f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->pre_ctime);
7133a1556e8STrond Myklebust 	fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime);
714f6048709SChuck Lever 
715e4f93234SChuck Lever 	return 0;
716e4f93234SChuck Lever }
717e4f93234SChuck Lever 
718e4f93234SChuck Lever /*
719e4f93234SChuck Lever  * pre_op_attr
720e4f93234SChuck Lever  *	union pre_op_attr switch (bool attributes_follow) {
721e4f93234SChuck Lever  *	case TRUE:
722e4f93234SChuck Lever  *		wcc_attr	attributes;
723e4f93234SChuck Lever  *	case FALSE:
724e4f93234SChuck Lever  *		void;
725e4f93234SChuck Lever  *	};
726e4f93234SChuck Lever  *
727e4f93234SChuck Lever  * wcc_data
728e4f93234SChuck Lever  *
729e4f93234SChuck Lever  *	struct wcc_data {
730e4f93234SChuck Lever  *		pre_op_attr	before;
731e4f93234SChuck Lever  *		post_op_attr	after;
732e4f93234SChuck Lever  *	};
733e4f93234SChuck Lever  */
734e4f93234SChuck Lever static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
735e4f93234SChuck Lever {
736e4f93234SChuck Lever 	__be32 *p;
737e4f93234SChuck Lever 
738e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
739eb72f484SChuck Lever 	if (unlikely(!p))
740eb72f484SChuck Lever 		return -EIO;
741e4f93234SChuck Lever 	if (*p != xdr_zero)
742e4f93234SChuck Lever 		return decode_wcc_attr(xdr, fattr);
743e4f93234SChuck Lever 	return 0;
744e4f93234SChuck Lever }
745e4f93234SChuck Lever 
746264d948cSTrond Myklebust static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr,
747264d948cSTrond Myklebust 		struct user_namespace *userns)
748e4f93234SChuck Lever {
749e4f93234SChuck Lever 	int error;
750e4f93234SChuck Lever 
751e4f93234SChuck Lever 	error = decode_pre_op_attr(xdr, fattr);
752e4f93234SChuck Lever 	if (unlikely(error))
753e4f93234SChuck Lever 		goto out;
754264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, fattr, userns);
755e4f93234SChuck Lever out:
756e4f93234SChuck Lever 	return error;
757e4f93234SChuck Lever }
758e4f93234SChuck Lever 
759e4f93234SChuck Lever /*
760e4f93234SChuck Lever  * post_op_fh3
761e4f93234SChuck Lever  *
762e4f93234SChuck Lever  *	union post_op_fh3 switch (bool handle_follows) {
763e4f93234SChuck Lever  *	case TRUE:
764e4f93234SChuck Lever  *		nfs_fh3  handle;
765e4f93234SChuck Lever  *	case FALSE:
766e4f93234SChuck Lever  *		void;
767e4f93234SChuck Lever  *	};
768e4f93234SChuck Lever  */
769e4f93234SChuck Lever static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
770e4f93234SChuck Lever {
771e4f93234SChuck Lever 	__be32 *p = xdr_inline_decode(xdr, 4);
772eb72f484SChuck Lever 	if (unlikely(!p))
773eb72f484SChuck Lever 		return -EIO;
774e4f93234SChuck Lever 	if (*p != xdr_zero)
775e4f93234SChuck Lever 		return decode_nfs_fh3(xdr, fh);
776e4f93234SChuck Lever 	zero_nfs_fh3(fh);
777e4f93234SChuck Lever 	return 0;
778e4f93234SChuck Lever }
779e4f93234SChuck Lever 
780e4f93234SChuck Lever /*
781d9c407b1SChuck Lever  * diropargs3
782d9c407b1SChuck Lever  *
783d9c407b1SChuck Lever  *	struct diropargs3 {
784d9c407b1SChuck Lever  *		nfs_fh3		dir;
785d9c407b1SChuck Lever  *		filename3	name;
786d9c407b1SChuck Lever  *	};
787d9c407b1SChuck Lever  */
788d9c407b1SChuck Lever static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
789d9c407b1SChuck Lever 			      const char *name, u32 length)
790d9c407b1SChuck Lever {
791d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, fh);
792d9c407b1SChuck Lever 	encode_filename3(xdr, name, length);
793d9c407b1SChuck Lever }
794d9c407b1SChuck Lever 
795d9c407b1SChuck Lever 
7961da177e4SLinus Torvalds /*
797499ff710SChuck Lever  * NFSv3 XDR encode functions
798499ff710SChuck Lever  *
799499ff710SChuck Lever  * NFSv3 argument types are defined in section 3.3 of RFC 1813:
800499ff710SChuck Lever  * "NFS Version 3 Protocol Specification".
8011da177e4SLinus Torvalds  */
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds /*
804d9c407b1SChuck Lever  * 3.3.1  GETATTR3args
805d9c407b1SChuck Lever  *
806d9c407b1SChuck Lever  *	struct GETATTR3args {
807d9c407b1SChuck Lever  *		nfs_fh3  object;
808d9c407b1SChuck Lever  *	};
809d9c407b1SChuck Lever  */
8109f06c719SChuck Lever static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
8119f06c719SChuck Lever 				      struct xdr_stream *xdr,
812fcc85819SChristoph Hellwig 				      const void *data)
813d9c407b1SChuck Lever {
814fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
815fcc85819SChristoph Hellwig 
8169f06c719SChuck Lever 	encode_nfs_fh3(xdr, fh);
817d9c407b1SChuck Lever }
818d9c407b1SChuck Lever 
819d9c407b1SChuck Lever /*
820d9c407b1SChuck Lever  * 3.3.2  SETATTR3args
821d9c407b1SChuck Lever  *
822d9c407b1SChuck Lever  *	union sattrguard3 switch (bool check) {
823d9c407b1SChuck Lever  *	case TRUE:
824d9c407b1SChuck Lever  *		nfstime3  obj_ctime;
825d9c407b1SChuck Lever  *	case FALSE:
826d9c407b1SChuck Lever  *		void;
827d9c407b1SChuck Lever  *	};
828d9c407b1SChuck Lever  *
829d9c407b1SChuck Lever  *	struct SETATTR3args {
830d9c407b1SChuck Lever  *		nfs_fh3		object;
831d9c407b1SChuck Lever  *		sattr3		new_attributes;
832d9c407b1SChuck Lever  *		sattrguard3	guard;
833d9c407b1SChuck Lever  *	};
834d9c407b1SChuck Lever  */
835d9c407b1SChuck Lever static void encode_sattrguard3(struct xdr_stream *xdr,
836d9c407b1SChuck Lever 			       const struct nfs3_sattrargs *args)
837d9c407b1SChuck Lever {
838d9c407b1SChuck Lever 	__be32 *p;
839d9c407b1SChuck Lever 
840d9c407b1SChuck Lever 	if (args->guard) {
841d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4 + 8);
842d9c407b1SChuck Lever 		*p++ = xdr_one;
8439d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &args->guardtime);
844d9c407b1SChuck Lever 	} else {
845d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4);
846d9c407b1SChuck Lever 		*p = xdr_zero;
847d9c407b1SChuck Lever 	}
848d9c407b1SChuck Lever }
849d9c407b1SChuck Lever 
8509f06c719SChuck Lever static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
8519f06c719SChuck Lever 				      struct xdr_stream *xdr,
852fcc85819SChristoph Hellwig 				      const void *data)
853d9c407b1SChuck Lever {
854fcc85819SChristoph Hellwig 	const struct nfs3_sattrargs *args = data;
8559f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
856264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, rpc_rqst_userns(req));
8579f06c719SChuck Lever 	encode_sattrguard3(xdr, args);
858d9c407b1SChuck Lever }
859d9c407b1SChuck Lever 
860d9c407b1SChuck Lever /*
861d9c407b1SChuck Lever  * 3.3.3  LOOKUP3args
862d9c407b1SChuck Lever  *
863d9c407b1SChuck Lever  *	struct LOOKUP3args {
864d9c407b1SChuck Lever  *		diropargs3  what;
865d9c407b1SChuck Lever  *	};
866d9c407b1SChuck Lever  */
8679f06c719SChuck Lever static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
8689f06c719SChuck Lever 				     struct xdr_stream *xdr,
869fcc85819SChristoph Hellwig 				     const void *data)
870d9c407b1SChuck Lever {
871fcc85819SChristoph Hellwig 	const struct nfs3_diropargs *args = data;
872fcc85819SChristoph Hellwig 
8739f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
874d9c407b1SChuck Lever }
875d9c407b1SChuck Lever 
876d9c407b1SChuck Lever /*
877d9c407b1SChuck Lever  * 3.3.4  ACCESS3args
878d9c407b1SChuck Lever  *
879d9c407b1SChuck Lever  *	struct ACCESS3args {
880d9c407b1SChuck Lever  *		nfs_fh3		object;
881d9c407b1SChuck Lever  *		uint32		access;
882d9c407b1SChuck Lever  *	};
883d9c407b1SChuck Lever  */
884d9c407b1SChuck Lever static void encode_access3args(struct xdr_stream *xdr,
885d9c407b1SChuck Lever 			       const struct nfs3_accessargs *args)
886d9c407b1SChuck Lever {
887d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
888d9c407b1SChuck Lever 	encode_uint32(xdr, args->access);
889d9c407b1SChuck Lever }
890d9c407b1SChuck Lever 
8919f06c719SChuck Lever static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
8929f06c719SChuck Lever 				     struct xdr_stream *xdr,
893fcc85819SChristoph Hellwig 				     const void *data)
894d9c407b1SChuck Lever {
895fcc85819SChristoph Hellwig 	const struct nfs3_accessargs *args = data;
896fcc85819SChristoph Hellwig 
8979f06c719SChuck Lever 	encode_access3args(xdr, args);
898d9c407b1SChuck Lever }
899d9c407b1SChuck Lever 
900d9c407b1SChuck Lever /*
901d9c407b1SChuck Lever  * 3.3.5  READLINK3args
902d9c407b1SChuck Lever  *
903d9c407b1SChuck Lever  *	struct READLINK3args {
904d9c407b1SChuck Lever  *		nfs_fh3	symlink;
905d9c407b1SChuck Lever  *	};
906d9c407b1SChuck Lever  */
9079f06c719SChuck Lever static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
9089f06c719SChuck Lever 				       struct xdr_stream *xdr,
909fcc85819SChristoph Hellwig 				       const void *data)
910d9c407b1SChuck Lever {
911fcc85819SChristoph Hellwig 	const struct nfs3_readlinkargs *args = data;
912fcc85819SChristoph Hellwig 
9139f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
914*9ed5af26STrond Myklebust 	rpc_prepare_reply_pages(req, args->pages, args->pgbase, args->pglen,
915*9ed5af26STrond Myklebust 				NFS3_readlinkres_sz - NFS3_pagepad_sz);
916d9c407b1SChuck Lever }
917d9c407b1SChuck Lever 
918d9c407b1SChuck Lever /*
919d9c407b1SChuck Lever  * 3.3.6  READ3args
920d9c407b1SChuck Lever  *
921d9c407b1SChuck Lever  *	struct READ3args {
922d9c407b1SChuck Lever  *		nfs_fh3		file;
923d9c407b1SChuck Lever  *		offset3		offset;
924d9c407b1SChuck Lever  *		count3		count;
925d9c407b1SChuck Lever  *	};
926d9c407b1SChuck Lever  */
927d9c407b1SChuck Lever static void encode_read3args(struct xdr_stream *xdr,
9283c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
929d9c407b1SChuck Lever {
930d9c407b1SChuck Lever 	__be32 *p;
931d9c407b1SChuck Lever 
932d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
933d9c407b1SChuck Lever 
934d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
935d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
936d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
937d9c407b1SChuck Lever }
938d9c407b1SChuck Lever 
9399f06c719SChuck Lever static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
9409f06c719SChuck Lever 				   struct xdr_stream *xdr,
941fcc85819SChristoph Hellwig 				   const void *data)
942d9c407b1SChuck Lever {
943fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
944*9ed5af26STrond Myklebust 	unsigned int replen = args->replen ? args->replen :
945*9ed5af26STrond Myklebust 					     NFS3_readres_sz - NFS3_pagepad_sz;
946fcc85819SChristoph Hellwig 
9479f06c719SChuck Lever 	encode_read3args(xdr, args);
948cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
9498d8928d8STrond Myklebust 				args->count, replen);
950d9c407b1SChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
951d9c407b1SChuck Lever }
952d9c407b1SChuck Lever 
953d9c407b1SChuck Lever /*
954d9c407b1SChuck Lever  * 3.3.7  WRITE3args
955d9c407b1SChuck Lever  *
956d9c407b1SChuck Lever  *	enum stable_how {
957d9c407b1SChuck Lever  *		UNSTABLE  = 0,
958d9c407b1SChuck Lever  *		DATA_SYNC = 1,
959d9c407b1SChuck Lever  *		FILE_SYNC = 2
960d9c407b1SChuck Lever  *	};
961d9c407b1SChuck Lever  *
962d9c407b1SChuck Lever  *	struct WRITE3args {
963d9c407b1SChuck Lever  *		nfs_fh3		file;
964d9c407b1SChuck Lever  *		offset3		offset;
965d9c407b1SChuck Lever  *		count3		count;
966d9c407b1SChuck Lever  *		stable_how	stable;
967d9c407b1SChuck Lever  *		opaque		data<>;
968d9c407b1SChuck Lever  *	};
969d9c407b1SChuck Lever  */
970d9c407b1SChuck Lever static void encode_write3args(struct xdr_stream *xdr,
9713c6b899cSAnna Schumaker 			      const struct nfs_pgio_args *args)
972d9c407b1SChuck Lever {
973d9c407b1SChuck Lever 	__be32 *p;
974d9c407b1SChuck Lever 
975d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
976d9c407b1SChuck Lever 
977d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
978d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
979d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count);
980d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->stable);
981d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
982d9c407b1SChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
983d9c407b1SChuck Lever }
984d9c407b1SChuck Lever 
9859f06c719SChuck Lever static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
9869f06c719SChuck Lever 				    struct xdr_stream *xdr,
987fcc85819SChristoph Hellwig 				    const void *data)
988d9c407b1SChuck Lever {
989fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
990fcc85819SChristoph Hellwig 
9919f06c719SChuck Lever 	encode_write3args(xdr, args);
9929f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
993d9c407b1SChuck Lever }
994d9c407b1SChuck Lever 
995d9c407b1SChuck Lever /*
996d9c407b1SChuck Lever  * 3.3.8  CREATE3args
997d9c407b1SChuck Lever  *
998d9c407b1SChuck Lever  *	enum createmode3 {
999d9c407b1SChuck Lever  *		UNCHECKED = 0,
1000d9c407b1SChuck Lever  *		GUARDED   = 1,
1001d9c407b1SChuck Lever  *		EXCLUSIVE = 2
1002d9c407b1SChuck Lever  *	};
1003d9c407b1SChuck Lever  *
1004d9c407b1SChuck Lever  *	union createhow3 switch (createmode3 mode) {
1005d9c407b1SChuck Lever  *	case UNCHECKED:
1006d9c407b1SChuck Lever  *	case GUARDED:
1007d9c407b1SChuck Lever  *		sattr3       obj_attributes;
1008d9c407b1SChuck Lever  *	case EXCLUSIVE:
1009d9c407b1SChuck Lever  *		createverf3  verf;
1010d9c407b1SChuck Lever  *	};
1011d9c407b1SChuck Lever  *
1012d9c407b1SChuck Lever  *	struct CREATE3args {
1013d9c407b1SChuck Lever  *		diropargs3	where;
1014d9c407b1SChuck Lever  *		createhow3	how;
1015d9c407b1SChuck Lever  *	};
1016d9c407b1SChuck Lever  */
1017d9c407b1SChuck Lever static void encode_createhow3(struct xdr_stream *xdr,
1018264d948cSTrond Myklebust 			      const struct nfs3_createargs *args,
1019264d948cSTrond Myklebust 			      struct user_namespace *userns)
1020d9c407b1SChuck Lever {
1021d9c407b1SChuck Lever 	encode_uint32(xdr, args->createmode);
1022d9c407b1SChuck Lever 	switch (args->createmode) {
1023d9c407b1SChuck Lever 	case NFS3_CREATE_UNCHECKED:
1024d9c407b1SChuck Lever 	case NFS3_CREATE_GUARDED:
1025264d948cSTrond Myklebust 		encode_sattr3(xdr, args->sattr, userns);
1026d9c407b1SChuck Lever 		break;
1027d9c407b1SChuck Lever 	case NFS3_CREATE_EXCLUSIVE:
1028d9c407b1SChuck Lever 		encode_createverf3(xdr, args->verifier);
1029d9c407b1SChuck Lever 		break;
1030d9c407b1SChuck Lever 	default:
1031d9c407b1SChuck Lever 		BUG();
1032d9c407b1SChuck Lever 	}
1033d9c407b1SChuck Lever }
1034d9c407b1SChuck Lever 
10359f06c719SChuck Lever static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
10369f06c719SChuck Lever 				     struct xdr_stream *xdr,
1037fcc85819SChristoph Hellwig 				     const void *data)
1038d9c407b1SChuck Lever {
1039fcc85819SChristoph Hellwig 	const struct nfs3_createargs *args = data;
1040fcc85819SChristoph Hellwig 
10419f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
1042264d948cSTrond Myklebust 	encode_createhow3(xdr, args, rpc_rqst_userns(req));
1043d9c407b1SChuck Lever }
1044d9c407b1SChuck Lever 
1045d9c407b1SChuck Lever /*
1046d9c407b1SChuck Lever  * 3.3.9  MKDIR3args
1047d9c407b1SChuck Lever  *
1048d9c407b1SChuck Lever  *	struct MKDIR3args {
1049d9c407b1SChuck Lever  *		diropargs3	where;
1050d9c407b1SChuck Lever  *		sattr3		attributes;
1051d9c407b1SChuck Lever  *	};
1052d9c407b1SChuck Lever  */
10539f06c719SChuck Lever static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
10549f06c719SChuck Lever 				    struct xdr_stream *xdr,
1055fcc85819SChristoph Hellwig 				    const void *data)
1056d9c407b1SChuck Lever {
1057fcc85819SChristoph Hellwig 	const struct nfs3_mkdirargs *args = data;
1058fcc85819SChristoph Hellwig 
10599f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
1060264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, rpc_rqst_userns(req));
1061d9c407b1SChuck Lever }
1062d9c407b1SChuck Lever 
1063d9c407b1SChuck Lever /*
1064d9c407b1SChuck Lever  * 3.3.10  SYMLINK3args
1065d9c407b1SChuck Lever  *
1066d9c407b1SChuck Lever  *	struct symlinkdata3 {
1067d9c407b1SChuck Lever  *		sattr3		symlink_attributes;
1068d9c407b1SChuck Lever  *		nfspath3	symlink_data;
1069d9c407b1SChuck Lever  *	};
1070d9c407b1SChuck Lever  *
1071d9c407b1SChuck Lever  *	struct SYMLINK3args {
1072d9c407b1SChuck Lever  *		diropargs3	where;
1073d9c407b1SChuck Lever  *		symlinkdata3	symlink;
1074d9c407b1SChuck Lever  *	};
1075d9c407b1SChuck Lever  */
1076d9c407b1SChuck Lever static void encode_symlinkdata3(struct xdr_stream *xdr,
1077264d948cSTrond Myklebust 				const void *data,
1078264d948cSTrond Myklebust 				struct user_namespace *userns)
1079d9c407b1SChuck Lever {
1080fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1081fcc85819SChristoph Hellwig 
1082264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, userns);
1083d9c407b1SChuck Lever 	encode_nfspath3(xdr, args->pages, args->pathlen);
1084d9c407b1SChuck Lever }
1085d9c407b1SChuck Lever 
10869f06c719SChuck Lever static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
10879f06c719SChuck Lever 				      struct xdr_stream *xdr,
1088fcc85819SChristoph Hellwig 				      const void *data)
1089d9c407b1SChuck Lever {
1090fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1091fcc85819SChristoph Hellwig 
10929f06c719SChuck Lever 	encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
1093264d948cSTrond Myklebust 	encode_symlinkdata3(xdr, args, rpc_rqst_userns(req));
10942fcc213aSChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
1095d9c407b1SChuck Lever }
1096d9c407b1SChuck Lever 
1097d9c407b1SChuck Lever /*
1098d9c407b1SChuck Lever  * 3.3.11  MKNOD3args
1099d9c407b1SChuck Lever  *
1100d9c407b1SChuck Lever  *	struct devicedata3 {
1101d9c407b1SChuck Lever  *		sattr3		dev_attributes;
1102d9c407b1SChuck Lever  *		specdata3	spec;
1103d9c407b1SChuck Lever  *	};
1104d9c407b1SChuck Lever  *
1105d9c407b1SChuck Lever  *	union mknoddata3 switch (ftype3 type) {
1106d9c407b1SChuck Lever  *	case NF3CHR:
1107d9c407b1SChuck Lever  *	case NF3BLK:
1108d9c407b1SChuck Lever  *		devicedata3	device;
1109d9c407b1SChuck Lever  *	case NF3SOCK:
1110d9c407b1SChuck Lever  *	case NF3FIFO:
1111d9c407b1SChuck Lever  *		sattr3		pipe_attributes;
1112d9c407b1SChuck Lever  *	default:
1113d9c407b1SChuck Lever  *		void;
1114d9c407b1SChuck Lever  *	};
1115d9c407b1SChuck Lever  *
1116d9c407b1SChuck Lever  *	struct MKNOD3args {
1117d9c407b1SChuck Lever  *		diropargs3	where;
1118d9c407b1SChuck Lever  *		mknoddata3	what;
1119d9c407b1SChuck Lever  *	};
1120d9c407b1SChuck Lever  */
1121d9c407b1SChuck Lever static void encode_devicedata3(struct xdr_stream *xdr,
1122264d948cSTrond Myklebust 			       const struct nfs3_mknodargs *args,
1123264d948cSTrond Myklebust 			       struct user_namespace *userns)
1124d9c407b1SChuck Lever {
1125264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, userns);
1126d9c407b1SChuck Lever 	encode_specdata3(xdr, args->rdev);
1127d9c407b1SChuck Lever }
1128d9c407b1SChuck Lever 
1129d9c407b1SChuck Lever static void encode_mknoddata3(struct xdr_stream *xdr,
1130264d948cSTrond Myklebust 			      const struct nfs3_mknodargs *args,
1131264d948cSTrond Myklebust 			      struct user_namespace *userns)
1132d9c407b1SChuck Lever {
1133d9c407b1SChuck Lever 	encode_ftype3(xdr, args->type);
1134d9c407b1SChuck Lever 	switch (args->type) {
1135d9c407b1SChuck Lever 	case NF3CHR:
1136d9c407b1SChuck Lever 	case NF3BLK:
1137264d948cSTrond Myklebust 		encode_devicedata3(xdr, args, userns);
1138d9c407b1SChuck Lever 		break;
1139d9c407b1SChuck Lever 	case NF3SOCK:
1140d9c407b1SChuck Lever 	case NF3FIFO:
1141264d948cSTrond Myklebust 		encode_sattr3(xdr, args->sattr, userns);
1142d9c407b1SChuck Lever 		break;
1143d9c407b1SChuck Lever 	case NF3REG:
1144d9c407b1SChuck Lever 	case NF3DIR:
1145d9c407b1SChuck Lever 		break;
1146d9c407b1SChuck Lever 	default:
1147d9c407b1SChuck Lever 		BUG();
1148d9c407b1SChuck Lever 	}
1149d9c407b1SChuck Lever }
1150d9c407b1SChuck Lever 
11519f06c719SChuck Lever static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
11529f06c719SChuck Lever 				    struct xdr_stream *xdr,
1153fcc85819SChristoph Hellwig 				    const void *data)
1154d9c407b1SChuck Lever {
1155fcc85819SChristoph Hellwig 	const struct nfs3_mknodargs *args = data;
1156fcc85819SChristoph Hellwig 
11579f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
1158264d948cSTrond Myklebust 	encode_mknoddata3(xdr, args, rpc_rqst_userns(req));
1159d9c407b1SChuck Lever }
1160d9c407b1SChuck Lever 
1161d9c407b1SChuck Lever /*
1162d9c407b1SChuck Lever  * 3.3.12  REMOVE3args
1163d9c407b1SChuck Lever  *
1164d9c407b1SChuck Lever  *	struct REMOVE3args {
1165d9c407b1SChuck Lever  *		diropargs3  object;
1166d9c407b1SChuck Lever  *	};
1167d9c407b1SChuck Lever  */
11689f06c719SChuck Lever static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
11699f06c719SChuck Lever 				     struct xdr_stream *xdr,
1170fcc85819SChristoph Hellwig 				     const void *data)
1171d9c407b1SChuck Lever {
1172fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
1173fcc85819SChristoph Hellwig 
11749f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
1175d9c407b1SChuck Lever }
1176d9c407b1SChuck Lever 
1177d9c407b1SChuck Lever /*
1178d9c407b1SChuck Lever  * 3.3.14  RENAME3args
1179d9c407b1SChuck Lever  *
1180d9c407b1SChuck Lever  *	struct RENAME3args {
1181d9c407b1SChuck Lever  *		diropargs3	from;
1182d9c407b1SChuck Lever  *		diropargs3	to;
1183d9c407b1SChuck Lever  *	};
1184d9c407b1SChuck Lever  */
11859f06c719SChuck Lever static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
11869f06c719SChuck Lever 				     struct xdr_stream *xdr,
1187fcc85819SChristoph Hellwig 				     const void *data)
1188d9c407b1SChuck Lever {
1189fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
1190d9c407b1SChuck Lever 	const struct qstr *old = args->old_name;
1191d9c407b1SChuck Lever 	const struct qstr *new = args->new_name;
1192d9c407b1SChuck Lever 
11939f06c719SChuck Lever 	encode_diropargs3(xdr, args->old_dir, old->name, old->len);
11949f06c719SChuck Lever 	encode_diropargs3(xdr, args->new_dir, new->name, new->len);
1195d9c407b1SChuck Lever }
1196d9c407b1SChuck Lever 
1197d9c407b1SChuck Lever /*
1198d9c407b1SChuck Lever  * 3.3.15  LINK3args
1199d9c407b1SChuck Lever  *
1200d9c407b1SChuck Lever  *	struct LINK3args {
1201d9c407b1SChuck Lever  *		nfs_fh3		file;
1202d9c407b1SChuck Lever  *		diropargs3	link;
1203d9c407b1SChuck Lever  *	};
1204d9c407b1SChuck Lever  */
12059f06c719SChuck Lever static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
12069f06c719SChuck Lever 				   struct xdr_stream *xdr,
1207fcc85819SChristoph Hellwig 				   const void *data)
1208d9c407b1SChuck Lever {
1209fcc85819SChristoph Hellwig 	const struct nfs3_linkargs *args = data;
1210fcc85819SChristoph Hellwig 
12119f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fromfh);
12129f06c719SChuck Lever 	encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
1213d9c407b1SChuck Lever }
1214d9c407b1SChuck Lever 
1215d9c407b1SChuck Lever /*
1216d9c407b1SChuck Lever  * 3.3.16  READDIR3args
1217d9c407b1SChuck Lever  *
1218d9c407b1SChuck Lever  *	struct READDIR3args {
1219d9c407b1SChuck Lever  *		nfs_fh3		dir;
1220d9c407b1SChuck Lever  *		cookie3		cookie;
1221d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1222d9c407b1SChuck Lever  *		count3		count;
1223d9c407b1SChuck Lever  *	};
1224d9c407b1SChuck Lever  */
1225d9c407b1SChuck Lever static void encode_readdir3args(struct xdr_stream *xdr,
1226d9c407b1SChuck Lever 				const struct nfs3_readdirargs *args)
1227d9c407b1SChuck Lever {
1228d9c407b1SChuck Lever 	__be32 *p;
1229d9c407b1SChuck Lever 
1230d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1231d9c407b1SChuck Lever 
1232d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
1233d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1234d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1235d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1236d9c407b1SChuck Lever }
1237d9c407b1SChuck Lever 
12389f06c719SChuck Lever static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
12399f06c719SChuck Lever 				      struct xdr_stream *xdr,
1240fcc85819SChristoph Hellwig 				      const void *data)
1241d9c407b1SChuck Lever {
1242fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1243fcc85819SChristoph Hellwig 
12449f06c719SChuck Lever 	encode_readdir3args(xdr, args);
1245*9ed5af26STrond Myklebust 	rpc_prepare_reply_pages(req, args->pages, 0, args->count,
1246*9ed5af26STrond Myklebust 				NFS3_readdirres_sz - NFS3_pagepad_sz);
1247d9c407b1SChuck Lever }
1248d9c407b1SChuck Lever 
1249d9c407b1SChuck Lever /*
1250d9c407b1SChuck Lever  * 3.3.17  READDIRPLUS3args
1251d9c407b1SChuck Lever  *
1252d9c407b1SChuck Lever  *	struct READDIRPLUS3args {
1253d9c407b1SChuck Lever  *		nfs_fh3		dir;
1254d9c407b1SChuck Lever  *		cookie3		cookie;
1255d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1256d9c407b1SChuck Lever  *		count3		dircount;
1257d9c407b1SChuck Lever  *		count3		maxcount;
1258d9c407b1SChuck Lever  *	};
1259d9c407b1SChuck Lever  */
1260d9c407b1SChuck Lever static void encode_readdirplus3args(struct xdr_stream *xdr,
1261d9c407b1SChuck Lever 				    const struct nfs3_readdirargs *args)
1262d9c407b1SChuck Lever {
1263d9c407b1SChuck Lever 	__be32 *p;
1264d9c407b1SChuck Lever 
1265d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1266d9c407b1SChuck Lever 
1267d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
1268d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1269d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1270d9c407b1SChuck Lever 
1271d9c407b1SChuck Lever 	/*
1272d9c407b1SChuck Lever 	 * readdirplus: need dircount + buffer size.
1273d9c407b1SChuck Lever 	 * We just make sure we make dircount big enough
1274d9c407b1SChuck Lever 	 */
1275d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count >> 3);
1276d9c407b1SChuck Lever 
1277d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1278d9c407b1SChuck Lever }
1279d9c407b1SChuck Lever 
12809f06c719SChuck Lever static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
12819f06c719SChuck Lever 					  struct xdr_stream *xdr,
1282fcc85819SChristoph Hellwig 					  const void *data)
1283d9c407b1SChuck Lever {
1284fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1285fcc85819SChristoph Hellwig 
12869f06c719SChuck Lever 	encode_readdirplus3args(xdr, args);
1287*9ed5af26STrond Myklebust 	rpc_prepare_reply_pages(req, args->pages, 0, args->count,
1288*9ed5af26STrond Myklebust 				NFS3_readdirres_sz - NFS3_pagepad_sz);
1289d9c407b1SChuck Lever }
1290d9c407b1SChuck Lever 
1291d9c407b1SChuck Lever /*
1292d9c407b1SChuck Lever  * 3.3.21  COMMIT3args
1293d9c407b1SChuck Lever  *
1294d9c407b1SChuck Lever  *	struct COMMIT3args {
1295d9c407b1SChuck Lever  *		nfs_fh3		file;
1296d9c407b1SChuck Lever  *		offset3		offset;
1297d9c407b1SChuck Lever  *		count3		count;
1298d9c407b1SChuck Lever  *	};
1299d9c407b1SChuck Lever  */
1300d9c407b1SChuck Lever static void encode_commit3args(struct xdr_stream *xdr,
13010b7c0153SFred Isaman 			       const struct nfs_commitargs *args)
1302d9c407b1SChuck Lever {
1303d9c407b1SChuck Lever 	__be32 *p;
1304d9c407b1SChuck Lever 
1305d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1306d9c407b1SChuck Lever 
1307d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1308d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1309d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1310d9c407b1SChuck Lever }
1311d9c407b1SChuck Lever 
13129f06c719SChuck Lever static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
13139f06c719SChuck Lever 				     struct xdr_stream *xdr,
1314fcc85819SChristoph Hellwig 				     const void *data)
1315d9c407b1SChuck Lever {
1316fcc85819SChristoph Hellwig 	const struct nfs_commitargs *args = data;
1317fcc85819SChristoph Hellwig 
13189f06c719SChuck Lever 	encode_commit3args(xdr, args);
1319d9c407b1SChuck Lever }
1320d9c407b1SChuck Lever 
1321b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1322b7fa0554SAndreas Gruenbacher 
13239f06c719SChuck Lever static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
13249f06c719SChuck Lever 				     struct xdr_stream *xdr,
1325fcc85819SChristoph Hellwig 				     const void *data)
1326d9c407b1SChuck Lever {
1327fcc85819SChristoph Hellwig 	const struct nfs3_getaclargs *args = data;
1328fcc85819SChristoph Hellwig 
13299f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
13309f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1331431f6eb3STrond Myklebust 	if (args->mask & (NFS_ACL | NFS_DFACL)) {
1332cf500bacSChuck Lever 		rpc_prepare_reply_pages(req, args->pages, 0,
1333d9c407b1SChuck Lever 					NFSACL_MAXPAGES << PAGE_SHIFT,
1334*9ed5af26STrond Myklebust 					ACL3_getaclres_sz - NFS3_pagepad_sz);
1335431f6eb3STrond Myklebust 		req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
1336431f6eb3STrond Myklebust 	}
1337d9c407b1SChuck Lever }
1338d9c407b1SChuck Lever 
13399f06c719SChuck Lever static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
13409f06c719SChuck Lever 				     struct xdr_stream *xdr,
1341fcc85819SChristoph Hellwig 				     const void *data)
1342d9c407b1SChuck Lever {
1343fcc85819SChristoph Hellwig 	const struct nfs3_setaclargs *args = data;
1344d9c407b1SChuck Lever 	unsigned int base;
1345d9c407b1SChuck Lever 	int error;
1346d9c407b1SChuck Lever 
13479f06c719SChuck Lever 	encode_nfs_fh3(xdr, NFS_FH(args->inode));
13489f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1349d9c407b1SChuck Lever 
1350d9c407b1SChuck Lever 	base = req->rq_slen;
1351ee5dc773SChuck Lever 	if (args->npages != 0)
1352ee5dc773SChuck Lever 		xdr_write_pages(xdr, args->pages, 0, args->len);
1353ee5dc773SChuck Lever 	else
1354d683cc49SChuck Lever 		xdr_reserve_space(xdr, args->len);
1355ee5dc773SChuck Lever 
13569f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base, args->inode,
1357d9c407b1SChuck Lever 			    (args->mask & NFS_ACL) ?
1358d9c407b1SChuck Lever 			    args->acl_access : NULL, 1, 0);
13597fc38846STrond Myklebust 	/* FIXME: this is just broken */
1360d9c407b1SChuck Lever 	BUG_ON(error < 0);
13619f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base + error, args->inode,
1362d9c407b1SChuck Lever 			    (args->mask & NFS_DFACL) ?
1363d9c407b1SChuck Lever 			    args->acl_default : NULL, 1,
1364d9c407b1SChuck Lever 			    NFS_ACL_DEFAULT);
1365d9c407b1SChuck Lever 	BUG_ON(error < 0);
1366d9c407b1SChuck Lever }
1367d9c407b1SChuck Lever 
1368b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1369b7fa0554SAndreas Gruenbacher 
13701da177e4SLinus Torvalds /*
1371b2cdd9c9SChuck Lever  * NFSv3 XDR decode functions
1372b2cdd9c9SChuck Lever  *
1373b2cdd9c9SChuck Lever  * NFSv3 result types are defined in section 3.3 of RFC 1813:
1374b2cdd9c9SChuck Lever  * "NFS Version 3 Protocol Specification".
13751da177e4SLinus Torvalds  */
13761da177e4SLinus Torvalds 
13771da177e4SLinus Torvalds /*
1378e4f93234SChuck Lever  * 3.3.1  GETATTR3res
1379e4f93234SChuck Lever  *
1380e4f93234SChuck Lever  *	struct GETATTR3resok {
1381e4f93234SChuck Lever  *		fattr3		obj_attributes;
1382e4f93234SChuck Lever  *	};
1383e4f93234SChuck Lever  *
1384e4f93234SChuck Lever  *	union GETATTR3res switch (nfsstat3 status) {
1385e4f93234SChuck Lever  *	case NFS3_OK:
1386e4f93234SChuck Lever  *		GETATTR3resok  resok;
1387e4f93234SChuck Lever  *	default:
1388e4f93234SChuck Lever  *		void;
1389e4f93234SChuck Lever  *	};
1390e4f93234SChuck Lever  */
1391bf269551SChuck Lever static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
1392bf269551SChuck Lever 				    struct xdr_stream *xdr,
1393fc016483SChristoph Hellwig 				    void *result)
1394e4f93234SChuck Lever {
1395e4f93234SChuck Lever 	enum nfs_stat status;
1396e4f93234SChuck Lever 	int error;
1397e4f93234SChuck Lever 
1398bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1399e4f93234SChuck Lever 	if (unlikely(error))
1400e4f93234SChuck Lever 		goto out;
1401e4f93234SChuck Lever 	if (status != NFS3_OK)
1402e4f93234SChuck Lever 		goto out_default;
1403264d948cSTrond Myklebust 	error = decode_fattr3(xdr, result, rpc_rqst_userns(req));
1404e4f93234SChuck Lever out:
1405e4f93234SChuck Lever 	return error;
1406e4f93234SChuck Lever out_default:
14075e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1408e4f93234SChuck Lever }
1409e4f93234SChuck Lever 
1410e4f93234SChuck Lever /*
1411e4f93234SChuck Lever  * 3.3.2  SETATTR3res
1412e4f93234SChuck Lever  *
1413e4f93234SChuck Lever  *	struct SETATTR3resok {
1414e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1415e4f93234SChuck Lever  *	};
1416e4f93234SChuck Lever  *
1417e4f93234SChuck Lever  *	struct SETATTR3resfail {
1418e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1419e4f93234SChuck Lever  *	};
1420e4f93234SChuck Lever  *
1421e4f93234SChuck Lever  *	union SETATTR3res switch (nfsstat3 status) {
1422e4f93234SChuck Lever  *	case NFS3_OK:
1423e4f93234SChuck Lever  *		SETATTR3resok   resok;
1424e4f93234SChuck Lever  *	default:
1425e4f93234SChuck Lever  *		SETATTR3resfail resfail;
1426e4f93234SChuck Lever  *	};
1427e4f93234SChuck Lever  */
1428bf269551SChuck Lever static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
1429bf269551SChuck Lever 				    struct xdr_stream *xdr,
1430fc016483SChristoph Hellwig 				    void *result)
1431e4f93234SChuck Lever {
1432e4f93234SChuck Lever 	enum nfs_stat status;
1433e4f93234SChuck Lever 	int error;
1434e4f93234SChuck Lever 
1435bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1436e4f93234SChuck Lever 	if (unlikely(error))
1437e4f93234SChuck Lever 		goto out;
1438264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result, rpc_rqst_userns(req));
1439e4f93234SChuck Lever 	if (unlikely(error))
1440e4f93234SChuck Lever 		goto out;
1441e4f93234SChuck Lever 	if (status != NFS3_OK)
1442e4f93234SChuck Lever 		goto out_status;
1443e4f93234SChuck Lever out:
1444e4f93234SChuck Lever 	return error;
1445e4f93234SChuck Lever out_status:
14465e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1447e4f93234SChuck Lever }
1448e4f93234SChuck Lever 
14491da177e4SLinus Torvalds /*
1450e4f93234SChuck Lever  * 3.3.3  LOOKUP3res
1451e4f93234SChuck Lever  *
1452e4f93234SChuck Lever  *	struct LOOKUP3resok {
1453e4f93234SChuck Lever  *		nfs_fh3		object;
1454e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1455e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1456e4f93234SChuck Lever  *	};
1457e4f93234SChuck Lever  *
1458e4f93234SChuck Lever  *	struct LOOKUP3resfail {
1459e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1460e4f93234SChuck Lever  *	};
1461e4f93234SChuck Lever  *
1462e4f93234SChuck Lever  *	union LOOKUP3res switch (nfsstat3 status) {
1463e4f93234SChuck Lever  *	case NFS3_OK:
1464e4f93234SChuck Lever  *		LOOKUP3resok	resok;
1465e4f93234SChuck Lever  *	default:
1466e4f93234SChuck Lever  *		LOOKUP3resfail	resfail;
1467e4f93234SChuck Lever  *	};
1468e4f93234SChuck Lever  */
1469bf269551SChuck Lever static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
1470bf269551SChuck Lever 				   struct xdr_stream *xdr,
1471fc016483SChristoph Hellwig 				   void *data)
1472e4f93234SChuck Lever {
1473264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1474fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1475e4f93234SChuck Lever 	enum nfs_stat status;
1476e4f93234SChuck Lever 	int error;
1477e4f93234SChuck Lever 
1478bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1479e4f93234SChuck Lever 	if (unlikely(error))
1480e4f93234SChuck Lever 		goto out;
1481e4f93234SChuck Lever 	if (status != NFS3_OK)
1482e4f93234SChuck Lever 		goto out_default;
1483bf269551SChuck Lever 	error = decode_nfs_fh3(xdr, result->fh);
1484e4f93234SChuck Lever 	if (unlikely(error))
1485e4f93234SChuck Lever 		goto out;
1486264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
1487e4f93234SChuck Lever 	if (unlikely(error))
1488e4f93234SChuck Lever 		goto out;
1489264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, userns);
1490e4f93234SChuck Lever out:
1491e4f93234SChuck Lever 	return error;
1492e4f93234SChuck Lever out_default:
1493264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, userns);
1494e4f93234SChuck Lever 	if (unlikely(error))
1495e4f93234SChuck Lever 		goto out;
14965e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1497e4f93234SChuck Lever }
1498e4f93234SChuck Lever 
1499e4f93234SChuck Lever /*
1500e4f93234SChuck Lever  * 3.3.4  ACCESS3res
1501e4f93234SChuck Lever  *
1502e4f93234SChuck Lever  *	struct ACCESS3resok {
1503e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1504e4f93234SChuck Lever  *		uint32		access;
1505e4f93234SChuck Lever  *	};
1506e4f93234SChuck Lever  *
1507e4f93234SChuck Lever  *	struct ACCESS3resfail {
1508e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1509e4f93234SChuck Lever  *	};
1510e4f93234SChuck Lever  *
1511e4f93234SChuck Lever  *	union ACCESS3res switch (nfsstat3 status) {
1512e4f93234SChuck Lever  *	case NFS3_OK:
1513e4f93234SChuck Lever  *		ACCESS3resok	resok;
1514e4f93234SChuck Lever  *	default:
1515e4f93234SChuck Lever  *		ACCESS3resfail	resfail;
1516e4f93234SChuck Lever  *	};
1517e4f93234SChuck Lever  */
1518bf269551SChuck Lever static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
1519bf269551SChuck Lever 				   struct xdr_stream *xdr,
1520fc016483SChristoph Hellwig 				   void *data)
1521e4f93234SChuck Lever {
1522fc016483SChristoph Hellwig 	struct nfs3_accessres *result = data;
1523e4f93234SChuck Lever 	enum nfs_stat status;
1524e4f93234SChuck Lever 	int error;
1525e4f93234SChuck Lever 
1526bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1527e4f93234SChuck Lever 	if (unlikely(error))
1528e4f93234SChuck Lever 		goto out;
1529264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
1530e4f93234SChuck Lever 	if (unlikely(error))
1531e4f93234SChuck Lever 		goto out;
1532e4f93234SChuck Lever 	if (status != NFS3_OK)
1533e4f93234SChuck Lever 		goto out_default;
1534bf269551SChuck Lever 	error = decode_uint32(xdr, &result->access);
1535e4f93234SChuck Lever out:
1536e4f93234SChuck Lever 	return error;
1537e4f93234SChuck Lever out_default:
15385e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1539e4f93234SChuck Lever }
1540e4f93234SChuck Lever 
1541e4f93234SChuck Lever /*
1542e4f93234SChuck Lever  * 3.3.5  READLINK3res
1543e4f93234SChuck Lever  *
1544e4f93234SChuck Lever  *	struct READLINK3resok {
1545e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1546e4f93234SChuck Lever  *		nfspath3	data;
1547e4f93234SChuck Lever  *	};
1548e4f93234SChuck Lever  *
1549e4f93234SChuck Lever  *	struct READLINK3resfail {
1550e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1551e4f93234SChuck Lever  *	};
1552e4f93234SChuck Lever  *
1553e4f93234SChuck Lever  *	union READLINK3res switch (nfsstat3 status) {
1554e4f93234SChuck Lever  *	case NFS3_OK:
1555e4f93234SChuck Lever  *		READLINK3resok	resok;
1556e4f93234SChuck Lever  *	default:
1557e4f93234SChuck Lever  *		READLINK3resfail resfail;
1558e4f93234SChuck Lever  *	};
1559e4f93234SChuck Lever  */
1560bf269551SChuck Lever static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
1561bf269551SChuck Lever 				     struct xdr_stream *xdr,
1562fc016483SChristoph Hellwig 				     void *result)
1563e4f93234SChuck Lever {
1564e4f93234SChuck Lever 	enum nfs_stat status;
1565e4f93234SChuck Lever 	int error;
1566e4f93234SChuck Lever 
1567bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1568e4f93234SChuck Lever 	if (unlikely(error))
1569e4f93234SChuck Lever 		goto out;
1570264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result, rpc_rqst_userns(req));
1571e4f93234SChuck Lever 	if (unlikely(error))
1572e4f93234SChuck Lever 		goto out;
1573e4f93234SChuck Lever 	if (status != NFS3_OK)
1574e4f93234SChuck Lever 		goto out_default;
1575bf269551SChuck Lever 	error = decode_nfspath3(xdr);
1576e4f93234SChuck Lever out:
1577e4f93234SChuck Lever 	return error;
1578e4f93234SChuck Lever out_default:
15795e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1580e4f93234SChuck Lever }
1581e4f93234SChuck Lever 
1582e4f93234SChuck Lever /*
1583e4f93234SChuck Lever  * 3.3.6  READ3res
1584e4f93234SChuck Lever  *
1585e4f93234SChuck Lever  *	struct READ3resok {
1586e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1587e4f93234SChuck Lever  *		count3		count;
1588e4f93234SChuck Lever  *		bool		eof;
1589e4f93234SChuck Lever  *		opaque		data<>;
1590e4f93234SChuck Lever  *	};
1591e4f93234SChuck Lever  *
1592e4f93234SChuck Lever  *	struct READ3resfail {
1593e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1594e4f93234SChuck Lever  *	};
1595e4f93234SChuck Lever  *
1596e4f93234SChuck Lever  *	union READ3res switch (nfsstat3 status) {
1597e4f93234SChuck Lever  *	case NFS3_OK:
1598e4f93234SChuck Lever  *		READ3resok	resok;
1599e4f93234SChuck Lever  *	default:
1600e4f93234SChuck Lever  *		READ3resfail	resfail;
1601e4f93234SChuck Lever  *	};
1602e4f93234SChuck Lever  */
1603e4f93234SChuck Lever static int decode_read3resok(struct xdr_stream *xdr,
16049137bdf3SAnna Schumaker 			     struct nfs_pgio_res *result)
1605e4f93234SChuck Lever {
1606e4f93234SChuck Lever 	u32 eof, count, ocount, recvd;
1607e4f93234SChuck Lever 	__be32 *p;
1608e4f93234SChuck Lever 
1609e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 + 4 + 4);
1610eb72f484SChuck Lever 	if (unlikely(!p))
1611eb72f484SChuck Lever 		return -EIO;
1612e4f93234SChuck Lever 	count = be32_to_cpup(p++);
1613e4f93234SChuck Lever 	eof = be32_to_cpup(p++);
1614e4f93234SChuck Lever 	ocount = be32_to_cpup(p++);
1615e4f93234SChuck Lever 	if (unlikely(ocount != count))
1616e4f93234SChuck Lever 		goto out_mismatch;
161764bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
1618e4f93234SChuck Lever 	if (unlikely(count > recvd))
1619e4f93234SChuck Lever 		goto out_cheating;
1620e4f93234SChuck Lever out:
1621e4f93234SChuck Lever 	result->eof = eof;
1622e4f93234SChuck Lever 	result->count = count;
1623e4f93234SChuck Lever 	return count;
1624e4f93234SChuck Lever out_mismatch:
1625e4f93234SChuck Lever 	dprintk("NFS: READ count doesn't match length of opaque: "
1626e4f93234SChuck Lever 		"count %u != ocount %u\n", count, ocount);
1627e4f93234SChuck Lever 	return -EIO;
1628e4f93234SChuck Lever out_cheating:
1629e4f93234SChuck Lever 	dprintk("NFS: server cheating in read result: "
1630e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
1631e4f93234SChuck Lever 	count = recvd;
1632e4f93234SChuck Lever 	eof = 0;
1633e4f93234SChuck Lever 	goto out;
1634e4f93234SChuck Lever }
1635e4f93234SChuck Lever 
1636bf269551SChuck Lever static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1637fc016483SChristoph Hellwig 				 void *data)
1638e4f93234SChuck Lever {
1639fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
16408d8928d8STrond Myklebust 	unsigned int pos;
1641e4f93234SChuck Lever 	enum nfs_stat status;
1642e4f93234SChuck Lever 	int error;
1643e4f93234SChuck Lever 
16448d8928d8STrond Myklebust 	pos = xdr_stream_pos(xdr);
1645bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1646e4f93234SChuck Lever 	if (unlikely(error))
1647e4f93234SChuck Lever 		goto out;
1648264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
1649e4f93234SChuck Lever 	if (unlikely(error))
1650e4f93234SChuck Lever 		goto out;
1651aabff4ddSPeng Tao 	result->op_status = status;
1652e4f93234SChuck Lever 	if (status != NFS3_OK)
1653e4f93234SChuck Lever 		goto out_status;
1654*9ed5af26STrond Myklebust 	result->replen = 3 + ((xdr_stream_pos(xdr) - pos) >> 2);
1655bf269551SChuck Lever 	error = decode_read3resok(xdr, result);
1656e4f93234SChuck Lever out:
1657e4f93234SChuck Lever 	return error;
1658e4f93234SChuck Lever out_status:
16595e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1660e4f93234SChuck Lever }
1661e4f93234SChuck Lever 
1662e4f93234SChuck Lever /*
1663e4f93234SChuck Lever  * 3.3.7  WRITE3res
1664e4f93234SChuck Lever  *
1665e4f93234SChuck Lever  *	enum stable_how {
1666e4f93234SChuck Lever  *		UNSTABLE  = 0,
1667e4f93234SChuck Lever  *		DATA_SYNC = 1,
1668e4f93234SChuck Lever  *		FILE_SYNC = 2
1669e4f93234SChuck Lever  *	};
1670e4f93234SChuck Lever  *
1671e4f93234SChuck Lever  *	struct WRITE3resok {
1672e4f93234SChuck Lever  *		wcc_data	file_wcc;
1673e4f93234SChuck Lever  *		count3		count;
1674e4f93234SChuck Lever  *		stable_how	committed;
1675e4f93234SChuck Lever  *		writeverf3	verf;
1676e4f93234SChuck Lever  *	};
1677e4f93234SChuck Lever  *
1678e4f93234SChuck Lever  *	struct WRITE3resfail {
1679e4f93234SChuck Lever  *		wcc_data	file_wcc;
1680e4f93234SChuck Lever  *	};
1681e4f93234SChuck Lever  *
1682e4f93234SChuck Lever  *	union WRITE3res switch (nfsstat3 status) {
1683e4f93234SChuck Lever  *	case NFS3_OK:
1684e4f93234SChuck Lever  *		WRITE3resok	resok;
1685e4f93234SChuck Lever  *	default:
1686e4f93234SChuck Lever  *		WRITE3resfail	resfail;
1687e4f93234SChuck Lever  *	};
1688e4f93234SChuck Lever  */
1689e4f93234SChuck Lever static int decode_write3resok(struct xdr_stream *xdr,
16909137bdf3SAnna Schumaker 			      struct nfs_pgio_res *result)
1691e4f93234SChuck Lever {
1692e4f93234SChuck Lever 	__be32 *p;
1693e4f93234SChuck Lever 
16942f2c63bcSTrond Myklebust 	p = xdr_inline_decode(xdr, 4 + 4);
1695eb72f484SChuck Lever 	if (unlikely(!p))
1696eb72f484SChuck Lever 		return -EIO;
1697e4f93234SChuck Lever 	result->count = be32_to_cpup(p++);
1698e4f93234SChuck Lever 	result->verf->committed = be32_to_cpup(p++);
1699e4f93234SChuck Lever 	if (unlikely(result->verf->committed > NFS_FILE_SYNC))
1700e4f93234SChuck Lever 		goto out_badvalue;
17012f2c63bcSTrond Myklebust 	if (decode_writeverf3(xdr, &result->verf->verifier))
1702eb72f484SChuck Lever 		return -EIO;
1703e4f93234SChuck Lever 	return result->count;
1704e4f93234SChuck Lever out_badvalue:
1705e4f93234SChuck Lever 	dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
1706e4f93234SChuck Lever 	return -EIO;
1707e4f93234SChuck Lever }
1708e4f93234SChuck Lever 
1709bf269551SChuck Lever static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1710fc016483SChristoph Hellwig 				  void *data)
1711e4f93234SChuck Lever {
1712fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
1713e4f93234SChuck Lever 	enum nfs_stat status;
1714e4f93234SChuck Lever 	int error;
1715e4f93234SChuck Lever 
1716bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1717e4f93234SChuck Lever 	if (unlikely(error))
1718e4f93234SChuck Lever 		goto out;
1719264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->fattr, rpc_rqst_userns(req));
1720e4f93234SChuck Lever 	if (unlikely(error))
1721e4f93234SChuck Lever 		goto out;
1722aabff4ddSPeng Tao 	result->op_status = status;
1723e4f93234SChuck Lever 	if (status != NFS3_OK)
1724e4f93234SChuck Lever 		goto out_status;
1725bf269551SChuck Lever 	error = decode_write3resok(xdr, result);
1726e4f93234SChuck Lever out:
1727e4f93234SChuck Lever 	return error;
1728e4f93234SChuck Lever out_status:
17295e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1730e4f93234SChuck Lever }
1731e4f93234SChuck Lever 
1732e4f93234SChuck Lever /*
1733e4f93234SChuck Lever  * 3.3.8  CREATE3res
1734e4f93234SChuck Lever  *
1735e4f93234SChuck Lever  *	struct CREATE3resok {
1736e4f93234SChuck Lever  *		post_op_fh3	obj;
1737e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1738e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1739e4f93234SChuck Lever  *	};
1740e4f93234SChuck Lever  *
1741e4f93234SChuck Lever  *	struct CREATE3resfail {
1742e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1743e4f93234SChuck Lever  *	};
1744e4f93234SChuck Lever  *
1745e4f93234SChuck Lever  *	union CREATE3res switch (nfsstat3 status) {
1746e4f93234SChuck Lever  *	case NFS3_OK:
1747e4f93234SChuck Lever  *		CREATE3resok	resok;
1748e4f93234SChuck Lever  *	default:
1749e4f93234SChuck Lever  *		CREATE3resfail	resfail;
1750e4f93234SChuck Lever  *	};
1751e4f93234SChuck Lever  */
1752e4f93234SChuck Lever static int decode_create3resok(struct xdr_stream *xdr,
1753264d948cSTrond Myklebust 			       struct nfs3_diropres *result,
1754264d948cSTrond Myklebust 			       struct user_namespace *userns)
1755e4f93234SChuck Lever {
1756e4f93234SChuck Lever 	int error;
1757e4f93234SChuck Lever 
1758e4f93234SChuck Lever 	error = decode_post_op_fh3(xdr, result->fh);
1759e4f93234SChuck Lever 	if (unlikely(error))
1760e4f93234SChuck Lever 		goto out;
1761264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
1762e4f93234SChuck Lever 	if (unlikely(error))
1763e4f93234SChuck Lever 		goto out;
1764e4f93234SChuck Lever 	/* The server isn't required to return a file handle.
1765e4f93234SChuck Lever 	 * If it didn't, force the client to perform a LOOKUP
1766e4f93234SChuck Lever 	 * to determine the correct file handle and attribute
1767e4f93234SChuck Lever 	 * values for the new object. */
1768e4f93234SChuck Lever 	if (result->fh->size == 0)
1769e4f93234SChuck Lever 		result->fattr->valid = 0;
1770264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, userns);
1771e4f93234SChuck Lever out:
1772e4f93234SChuck Lever 	return error;
1773e4f93234SChuck Lever }
1774e4f93234SChuck Lever 
1775bf269551SChuck Lever static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
1776bf269551SChuck Lever 				   struct xdr_stream *xdr,
1777fc016483SChristoph Hellwig 				   void *data)
1778e4f93234SChuck Lever {
1779264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1780fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1781e4f93234SChuck Lever 	enum nfs_stat status;
1782e4f93234SChuck Lever 	int error;
1783e4f93234SChuck Lever 
1784bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1785e4f93234SChuck Lever 	if (unlikely(error))
1786e4f93234SChuck Lever 		goto out;
1787e4f93234SChuck Lever 	if (status != NFS3_OK)
1788e4f93234SChuck Lever 		goto out_default;
1789264d948cSTrond Myklebust 	error = decode_create3resok(xdr, result, userns);
1790e4f93234SChuck Lever out:
1791e4f93234SChuck Lever 	return error;
1792e4f93234SChuck Lever out_default:
1793264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, userns);
1794e4f93234SChuck Lever 	if (unlikely(error))
1795e4f93234SChuck Lever 		goto out;
17965e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1797e4f93234SChuck Lever }
1798e4f93234SChuck Lever 
1799e4f93234SChuck Lever /*
1800e4f93234SChuck Lever  * 3.3.12  REMOVE3res
1801e4f93234SChuck Lever  *
1802e4f93234SChuck Lever  *	struct REMOVE3resok {
1803e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1804e4f93234SChuck Lever  *	};
1805e4f93234SChuck Lever  *
1806e4f93234SChuck Lever  *	struct REMOVE3resfail {
1807e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1808e4f93234SChuck Lever  *	};
1809e4f93234SChuck Lever  *
1810e4f93234SChuck Lever  *	union REMOVE3res switch (nfsstat3 status) {
1811e4f93234SChuck Lever  *	case NFS3_OK:
1812e4f93234SChuck Lever  *		REMOVE3resok   resok;
1813e4f93234SChuck Lever  *	default:
1814e4f93234SChuck Lever  *		REMOVE3resfail resfail;
1815e4f93234SChuck Lever  *	};
1816e4f93234SChuck Lever  */
1817bf269551SChuck Lever static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
1818bf269551SChuck Lever 				   struct xdr_stream *xdr,
1819fc016483SChristoph Hellwig 				   void *data)
1820e4f93234SChuck Lever {
1821fc016483SChristoph Hellwig 	struct nfs_removeres *result = data;
1822e4f93234SChuck Lever 	enum nfs_stat status;
1823e4f93234SChuck Lever 	int error;
1824e4f93234SChuck Lever 
1825bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1826e4f93234SChuck Lever 	if (unlikely(error))
1827e4f93234SChuck Lever 		goto out;
1828264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, rpc_rqst_userns(req));
1829e4f93234SChuck Lever 	if (unlikely(error))
1830e4f93234SChuck Lever 		goto out;
1831e4f93234SChuck Lever 	if (status != NFS3_OK)
1832e4f93234SChuck Lever 		goto out_status;
1833e4f93234SChuck Lever out:
1834e4f93234SChuck Lever 	return error;
1835e4f93234SChuck Lever out_status:
18365e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1837e4f93234SChuck Lever }
1838e4f93234SChuck Lever 
1839e4f93234SChuck Lever /*
1840e4f93234SChuck Lever  * 3.3.14  RENAME3res
1841e4f93234SChuck Lever  *
1842e4f93234SChuck Lever  *	struct RENAME3resok {
1843e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1844e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1845e4f93234SChuck Lever  *	};
1846e4f93234SChuck Lever  *
1847e4f93234SChuck Lever  *	struct RENAME3resfail {
1848e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1849e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1850e4f93234SChuck Lever  *	};
1851e4f93234SChuck Lever  *
1852e4f93234SChuck Lever  *	union RENAME3res switch (nfsstat3 status) {
1853e4f93234SChuck Lever  *	case NFS3_OK:
1854e4f93234SChuck Lever  *		RENAME3resok   resok;
1855e4f93234SChuck Lever  *	default:
1856e4f93234SChuck Lever  *		RENAME3resfail resfail;
1857e4f93234SChuck Lever  *	};
1858e4f93234SChuck Lever  */
1859bf269551SChuck Lever static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
1860bf269551SChuck Lever 				   struct xdr_stream *xdr,
1861fc016483SChristoph Hellwig 				   void *data)
1862e4f93234SChuck Lever {
1863264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1864fc016483SChristoph Hellwig 	struct nfs_renameres *result = data;
1865e4f93234SChuck Lever 	enum nfs_stat status;
1866e4f93234SChuck Lever 	int error;
1867e4f93234SChuck Lever 
1868bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1869e4f93234SChuck Lever 	if (unlikely(error))
1870e4f93234SChuck Lever 		goto out;
1871264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->old_fattr, userns);
1872e4f93234SChuck Lever 	if (unlikely(error))
1873e4f93234SChuck Lever 		goto out;
1874264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->new_fattr, userns);
1875e4f93234SChuck Lever 	if (unlikely(error))
1876e4f93234SChuck Lever 		goto out;
1877e4f93234SChuck Lever 	if (status != NFS3_OK)
1878e4f93234SChuck Lever 		goto out_status;
1879e4f93234SChuck Lever out:
1880e4f93234SChuck Lever 	return error;
1881e4f93234SChuck Lever out_status:
18825e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1883e4f93234SChuck Lever }
1884e4f93234SChuck Lever 
1885e4f93234SChuck Lever /*
1886e4f93234SChuck Lever  * 3.3.15  LINK3res
1887e4f93234SChuck Lever  *
1888e4f93234SChuck Lever  *	struct LINK3resok {
1889e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1890e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1891e4f93234SChuck Lever  *	};
1892e4f93234SChuck Lever  *
1893e4f93234SChuck Lever  *	struct LINK3resfail {
1894e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1895e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1896e4f93234SChuck Lever  *	};
1897e4f93234SChuck Lever  *
1898e4f93234SChuck Lever  *	union LINK3res switch (nfsstat3 status) {
1899e4f93234SChuck Lever  *	case NFS3_OK:
1900e4f93234SChuck Lever  *		LINK3resok	resok;
1901e4f93234SChuck Lever  *	default:
1902e4f93234SChuck Lever  *		LINK3resfail	resfail;
1903e4f93234SChuck Lever  *	};
1904e4f93234SChuck Lever  */
1905bf269551SChuck Lever static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1906fc016483SChristoph Hellwig 				 void *data)
1907e4f93234SChuck Lever {
1908264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1909fc016483SChristoph Hellwig 	struct nfs3_linkres *result = data;
1910e4f93234SChuck Lever 	enum nfs_stat status;
1911e4f93234SChuck Lever 	int error;
1912e4f93234SChuck Lever 
1913bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1914e4f93234SChuck Lever 	if (unlikely(error))
1915e4f93234SChuck Lever 		goto out;
1916264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
1917e4f93234SChuck Lever 	if (unlikely(error))
1918e4f93234SChuck Lever 		goto out;
1919264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, userns);
1920e4f93234SChuck Lever 	if (unlikely(error))
1921e4f93234SChuck Lever 		goto out;
1922e4f93234SChuck Lever 	if (status != NFS3_OK)
1923e4f93234SChuck Lever 		goto out_status;
1924e4f93234SChuck Lever out:
1925e4f93234SChuck Lever 	return error;
1926e4f93234SChuck Lever out_status:
19275e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1928e4f93234SChuck Lever }
1929e4f93234SChuck Lever 
1930e4f93234SChuck Lever /**
1931e4f93234SChuck Lever  * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
1932e4f93234SChuck Lever  *			the local page cache
1933e4f93234SChuck Lever  * @xdr: XDR stream where entry resides
1934e4f93234SChuck Lever  * @entry: buffer to fill in with entry data
1935e4f93234SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
1936e4f93234SChuck Lever  *
1937573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
1938573c4e1eSChuck Lever  * returned.
1939e4f93234SChuck Lever  *
1940e4f93234SChuck Lever  * This function is not invoked during READDIR reply decoding, but
1941e4f93234SChuck Lever  * rather whenever an application invokes the getdents(2) system call
1942e4f93234SChuck Lever  * on a directory already in our cache.
1943e4f93234SChuck Lever  *
1944e4f93234SChuck Lever  * 3.3.16  entry3
1945e4f93234SChuck Lever  *
1946e4f93234SChuck Lever  *	struct entry3 {
1947e4f93234SChuck Lever  *		fileid3		fileid;
1948e4f93234SChuck Lever  *		filename3	name;
1949e4f93234SChuck Lever  *		cookie3		cookie;
1950e4f93234SChuck Lever  *		fhandle3	filehandle;
1951e4f93234SChuck Lever  *		post_op_attr3	attributes;
1952e4f93234SChuck Lever  *		entry3		*nextentry;
1953e4f93234SChuck Lever  *	};
1954e4f93234SChuck Lever  *
1955e4f93234SChuck Lever  * 3.3.17  entryplus3
1956e4f93234SChuck Lever  *	struct entryplus3 {
1957e4f93234SChuck Lever  *		fileid3		fileid;
1958e4f93234SChuck Lever  *		filename3	name;
1959e4f93234SChuck Lever  *		cookie3		cookie;
1960e4f93234SChuck Lever  *		post_op_attr	name_attributes;
1961e4f93234SChuck Lever  *		post_op_fh3	name_handle;
1962e4f93234SChuck Lever  *		entryplus3	*nextentry;
1963e4f93234SChuck Lever  *	};
1964e4f93234SChuck Lever  */
1965573c4e1eSChuck Lever int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
1966a7a3b1e9SBenjamin Coddington 		       bool plus)
1967e4f93234SChuck Lever {
1968264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_userns(entry->server->client);
1969e4f93234SChuck Lever 	struct nfs_entry old = *entry;
1970e4f93234SChuck Lever 	__be32 *p;
1971e4f93234SChuck Lever 	int error;
197298de9ce6SFrank Sorenson 	u64 new_cookie;
1973e4f93234SChuck Lever 
1974e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
1975eb72f484SChuck Lever 	if (unlikely(!p))
1976eb72f484SChuck Lever 		return -EAGAIN;
1977e4f93234SChuck Lever 	if (*p == xdr_zero) {
1978e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
1979eb72f484SChuck Lever 		if (unlikely(!p))
1980eb72f484SChuck Lever 			return -EAGAIN;
1981e4f93234SChuck Lever 		if (*p == xdr_zero)
1982573c4e1eSChuck Lever 			return -EAGAIN;
1983e4f93234SChuck Lever 		entry->eof = 1;
1984573c4e1eSChuck Lever 		return -EBADCOOKIE;
1985e4f93234SChuck Lever 	}
1986e4f93234SChuck Lever 
1987e4f93234SChuck Lever 	error = decode_fileid3(xdr, &entry->ino);
1988e4f93234SChuck Lever 	if (unlikely(error))
1989573c4e1eSChuck Lever 		return error;
1990e4f93234SChuck Lever 
1991e4f93234SChuck Lever 	error = decode_inline_filename3(xdr, &entry->name, &entry->len);
1992e4f93234SChuck Lever 	if (unlikely(error))
1993573c4e1eSChuck Lever 		return error;
1994e4f93234SChuck Lever 
199598de9ce6SFrank Sorenson 	error = decode_cookie3(xdr, &new_cookie);
1996e4f93234SChuck Lever 	if (unlikely(error))
1997573c4e1eSChuck Lever 		return error;
1998e4f93234SChuck Lever 
1999e4f93234SChuck Lever 	entry->d_type = DT_UNKNOWN;
2000e4f93234SChuck Lever 
2001e4f93234SChuck Lever 	if (plus) {
2002e4f93234SChuck Lever 		entry->fattr->valid = 0;
2003264d948cSTrond Myklebust 		error = decode_post_op_attr(xdr, entry->fattr, userns);
2004e4f93234SChuck Lever 		if (unlikely(error))
2005573c4e1eSChuck Lever 			return error;
2006e4f93234SChuck Lever 		if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
2007e4f93234SChuck Lever 			entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
2008e4f93234SChuck Lever 
20091ae04b25STrond Myklebust 		if (entry->fattr->fileid != entry->ino) {
20101ae04b25STrond Myklebust 			entry->fattr->mounted_on_fileid = entry->ino;
20111ae04b25STrond Myklebust 			entry->fattr->valid |= NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
20121ae04b25STrond Myklebust 		}
20131ae04b25STrond Myklebust 
2014e4f93234SChuck Lever 		/* In fact, a post_op_fh3: */
2015e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
2016eb72f484SChuck Lever 		if (unlikely(!p))
2017eb72f484SChuck Lever 			return -EAGAIN;
2018e4f93234SChuck Lever 		if (*p != xdr_zero) {
2019e4f93234SChuck Lever 			error = decode_nfs_fh3(xdr, entry->fh);
2020e4f93234SChuck Lever 			if (unlikely(error)) {
2021e4f93234SChuck Lever 				if (error == -E2BIG)
2022e4f93234SChuck Lever 					goto out_truncated;
2023573c4e1eSChuck Lever 				return error;
2024e4f93234SChuck Lever 			}
2025e4f93234SChuck Lever 		} else
2026e4f93234SChuck Lever 			zero_nfs_fh3(entry->fh);
2027e4f93234SChuck Lever 	}
2028e4f93234SChuck Lever 
202998de9ce6SFrank Sorenson 	entry->prev_cookie = entry->cookie;
203098de9ce6SFrank Sorenson 	entry->cookie = new_cookie;
203198de9ce6SFrank Sorenson 
2032573c4e1eSChuck Lever 	return 0;
2033e4f93234SChuck Lever 
2034e4f93234SChuck Lever out_truncated:
2035e4f93234SChuck Lever 	dprintk("NFS: directory entry contains invalid file handle\n");
2036e4f93234SChuck Lever 	*entry = old;
2037573c4e1eSChuck Lever 	return -EAGAIN;
2038e4f93234SChuck Lever }
2039e4f93234SChuck Lever 
2040e4f93234SChuck Lever /*
2041e4f93234SChuck Lever  * 3.3.16  READDIR3res
2042e4f93234SChuck Lever  *
2043e4f93234SChuck Lever  *	struct dirlist3 {
2044e4f93234SChuck Lever  *		entry3		*entries;
2045e4f93234SChuck Lever  *		bool		eof;
2046e4f93234SChuck Lever  *	};
2047e4f93234SChuck Lever  *
2048e4f93234SChuck Lever  *	struct READDIR3resok {
2049e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2050e4f93234SChuck Lever  *		cookieverf3	cookieverf;
2051e4f93234SChuck Lever  *		dirlist3	reply;
2052e4f93234SChuck Lever  *	};
2053e4f93234SChuck Lever  *
2054e4f93234SChuck Lever  *	struct READDIR3resfail {
2055e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2056e4f93234SChuck Lever  *	};
2057e4f93234SChuck Lever  *
2058e4f93234SChuck Lever  *	union READDIR3res switch (nfsstat3 status) {
2059e4f93234SChuck Lever  *	case NFS3_OK:
2060e4f93234SChuck Lever  *		READDIR3resok	resok;
2061e4f93234SChuck Lever  *	default:
2062e4f93234SChuck Lever  *		READDIR3resfail	resfail;
2063e4f93234SChuck Lever  *	};
2064e4f93234SChuck Lever  *
2065e4f93234SChuck Lever  * Read the directory contents into the page cache, but otherwise
2066e4f93234SChuck Lever  * don't touch them.  The actual decoding is done by nfs3_decode_entry()
2067e4f93234SChuck Lever  * during subsequent nfs_readdir() calls.
2068e4f93234SChuck Lever  */
2069e4f93234SChuck Lever static int decode_dirlist3(struct xdr_stream *xdr)
2070e4f93234SChuck Lever {
207164bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
2072e4f93234SChuck Lever }
2073e4f93234SChuck Lever 
2074e4f93234SChuck Lever static int decode_readdir3resok(struct xdr_stream *xdr,
2075264d948cSTrond Myklebust 				struct nfs3_readdirres *result,
2076264d948cSTrond Myklebust 				struct user_namespace *userns)
2077e4f93234SChuck Lever {
2078e4f93234SChuck Lever 	int error;
2079e4f93234SChuck Lever 
2080264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, userns);
2081e4f93234SChuck Lever 	if (unlikely(error))
2082e4f93234SChuck Lever 		goto out;
2083e4f93234SChuck Lever 	/* XXX: do we need to check if result->verf != NULL ? */
2084e4f93234SChuck Lever 	error = decode_cookieverf3(xdr, result->verf);
2085e4f93234SChuck Lever 	if (unlikely(error))
2086e4f93234SChuck Lever 		goto out;
2087e4f93234SChuck Lever 	error = decode_dirlist3(xdr);
2088e4f93234SChuck Lever out:
2089e4f93234SChuck Lever 	return error;
2090e4f93234SChuck Lever }
2091e4f93234SChuck Lever 
2092bf269551SChuck Lever static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
2093bf269551SChuck Lever 				    struct xdr_stream *xdr,
2094fc016483SChristoph Hellwig 				    void *data)
2095e4f93234SChuck Lever {
2096fc016483SChristoph Hellwig 	struct nfs3_readdirres *result = data;
2097e4f93234SChuck Lever 	enum nfs_stat status;
2098e4f93234SChuck Lever 	int error;
2099e4f93234SChuck Lever 
2100bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2101e4f93234SChuck Lever 	if (unlikely(error))
2102e4f93234SChuck Lever 		goto out;
2103e4f93234SChuck Lever 	if (status != NFS3_OK)
2104e4f93234SChuck Lever 		goto out_default;
2105264d948cSTrond Myklebust 	error = decode_readdir3resok(xdr, result, rpc_rqst_userns(req));
2106e4f93234SChuck Lever out:
2107e4f93234SChuck Lever 	return error;
2108e4f93234SChuck Lever out_default:
2109264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, rpc_rqst_userns(req));
2110e4f93234SChuck Lever 	if (unlikely(error))
2111e4f93234SChuck Lever 		goto out;
21125e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2113e4f93234SChuck Lever }
2114e4f93234SChuck Lever 
2115e4f93234SChuck Lever /*
2116e4f93234SChuck Lever  * 3.3.18  FSSTAT3res
2117e4f93234SChuck Lever  *
2118e4f93234SChuck Lever  *	struct FSSTAT3resok {
2119e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2120e4f93234SChuck Lever  *		size3		tbytes;
2121e4f93234SChuck Lever  *		size3		fbytes;
2122e4f93234SChuck Lever  *		size3		abytes;
2123e4f93234SChuck Lever  *		size3		tfiles;
2124e4f93234SChuck Lever  *		size3		ffiles;
2125e4f93234SChuck Lever  *		size3		afiles;
2126e4f93234SChuck Lever  *		uint32		invarsec;
2127e4f93234SChuck Lever  *	};
2128e4f93234SChuck Lever  *
2129e4f93234SChuck Lever  *	struct FSSTAT3resfail {
2130e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2131e4f93234SChuck Lever  *	};
2132e4f93234SChuck Lever  *
2133e4f93234SChuck Lever  *	union FSSTAT3res switch (nfsstat3 status) {
2134e4f93234SChuck Lever  *	case NFS3_OK:
2135e4f93234SChuck Lever  *		FSSTAT3resok	resok;
2136e4f93234SChuck Lever  *	default:
2137e4f93234SChuck Lever  *		FSSTAT3resfail	resfail;
2138e4f93234SChuck Lever  *	};
2139e4f93234SChuck Lever  */
2140e4f93234SChuck Lever static int decode_fsstat3resok(struct xdr_stream *xdr,
2141e4f93234SChuck Lever 			       struct nfs_fsstat *result)
2142e4f93234SChuck Lever {
2143e4f93234SChuck Lever 	__be32 *p;
2144e4f93234SChuck Lever 
2145e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8 * 6 + 4);
2146eb72f484SChuck Lever 	if (unlikely(!p))
2147eb72f484SChuck Lever 		return -EIO;
2148e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tbytes);
2149e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->fbytes);
2150e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->abytes);
2151e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tfiles);
2152e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->ffiles);
2153e4f93234SChuck Lever 	xdr_decode_size3(p, &result->afiles);
2154e4f93234SChuck Lever 	/* ignore invarsec */
2155e4f93234SChuck Lever 	return 0;
2156e4f93234SChuck Lever }
2157e4f93234SChuck Lever 
2158bf269551SChuck Lever static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
2159bf269551SChuck Lever 				   struct xdr_stream *xdr,
2160fc016483SChristoph Hellwig 				   void *data)
2161e4f93234SChuck Lever {
2162fc016483SChristoph Hellwig 	struct nfs_fsstat *result = data;
2163e4f93234SChuck Lever 	enum nfs_stat status;
2164e4f93234SChuck Lever 	int error;
2165e4f93234SChuck Lever 
2166bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2167e4f93234SChuck Lever 	if (unlikely(error))
2168e4f93234SChuck Lever 		goto out;
2169264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
2170e4f93234SChuck Lever 	if (unlikely(error))
2171e4f93234SChuck Lever 		goto out;
2172e4f93234SChuck Lever 	if (status != NFS3_OK)
2173e4f93234SChuck Lever 		goto out_status;
2174bf269551SChuck Lever 	error = decode_fsstat3resok(xdr, result);
2175e4f93234SChuck Lever out:
2176e4f93234SChuck Lever 	return error;
2177e4f93234SChuck Lever out_status:
21785e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2179e4f93234SChuck Lever }
2180e4f93234SChuck Lever 
2181e4f93234SChuck Lever /*
2182e4f93234SChuck Lever  * 3.3.19  FSINFO3res
2183e4f93234SChuck Lever  *
2184e4f93234SChuck Lever  *	struct FSINFO3resok {
2185e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2186e4f93234SChuck Lever  *		uint32		rtmax;
2187e4f93234SChuck Lever  *		uint32		rtpref;
2188e4f93234SChuck Lever  *		uint32		rtmult;
2189e4f93234SChuck Lever  *		uint32		wtmax;
2190e4f93234SChuck Lever  *		uint32		wtpref;
2191e4f93234SChuck Lever  *		uint32		wtmult;
2192e4f93234SChuck Lever  *		uint32		dtpref;
2193e4f93234SChuck Lever  *		size3		maxfilesize;
2194e4f93234SChuck Lever  *		nfstime3	time_delta;
2195e4f93234SChuck Lever  *		uint32		properties;
2196e4f93234SChuck Lever  *	};
2197e4f93234SChuck Lever  *
2198e4f93234SChuck Lever  *	struct FSINFO3resfail {
2199e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2200e4f93234SChuck Lever  *	};
2201e4f93234SChuck Lever  *
2202e4f93234SChuck Lever  *	union FSINFO3res switch (nfsstat3 status) {
2203e4f93234SChuck Lever  *	case NFS3_OK:
2204e4f93234SChuck Lever  *		FSINFO3resok	resok;
2205e4f93234SChuck Lever  *	default:
2206e4f93234SChuck Lever  *		FSINFO3resfail	resfail;
2207e4f93234SChuck Lever  *	};
2208e4f93234SChuck Lever  */
2209e4f93234SChuck Lever static int decode_fsinfo3resok(struct xdr_stream *xdr,
2210e4f93234SChuck Lever 			       struct nfs_fsinfo *result)
2211e4f93234SChuck Lever {
2212e4f93234SChuck Lever 	__be32 *p;
2213e4f93234SChuck Lever 
2214e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4);
2215eb72f484SChuck Lever 	if (unlikely(!p))
2216eb72f484SChuck Lever 		return -EIO;
2217e4f93234SChuck Lever 	result->rtmax  = be32_to_cpup(p++);
2218e4f93234SChuck Lever 	result->rtpref = be32_to_cpup(p++);
2219e4f93234SChuck Lever 	result->rtmult = be32_to_cpup(p++);
2220e4f93234SChuck Lever 	result->wtmax  = be32_to_cpup(p++);
2221e4f93234SChuck Lever 	result->wtpref = be32_to_cpup(p++);
2222e4f93234SChuck Lever 	result->wtmult = be32_to_cpup(p++);
2223e4f93234SChuck Lever 	result->dtpref = be32_to_cpup(p++);
2224e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->maxfilesize);
2225f6048709SChuck Lever 	xdr_decode_nfstime3(p, &result->time_delta);
2226e4f93234SChuck Lever 
2227e4f93234SChuck Lever 	/* ignore properties */
2228e4f93234SChuck Lever 	result->lease_time = 0;
2229e4f93234SChuck Lever 	return 0;
2230e4f93234SChuck Lever }
2231e4f93234SChuck Lever 
2232bf269551SChuck Lever static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
2233bf269551SChuck Lever 				   struct xdr_stream *xdr,
2234fc016483SChristoph Hellwig 				   void *data)
2235e4f93234SChuck Lever {
2236fc016483SChristoph Hellwig 	struct nfs_fsinfo *result = data;
2237e4f93234SChuck Lever 	enum nfs_stat status;
2238e4f93234SChuck Lever 	int error;
2239e4f93234SChuck Lever 
2240bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2241e4f93234SChuck Lever 	if (unlikely(error))
2242e4f93234SChuck Lever 		goto out;
2243264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
2244e4f93234SChuck Lever 	if (unlikely(error))
2245e4f93234SChuck Lever 		goto out;
2246e4f93234SChuck Lever 	if (status != NFS3_OK)
2247e4f93234SChuck Lever 		goto out_status;
2248bf269551SChuck Lever 	error = decode_fsinfo3resok(xdr, result);
2249e4f93234SChuck Lever out:
2250e4f93234SChuck Lever 	return error;
2251e4f93234SChuck Lever out_status:
22525e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2253e4f93234SChuck Lever }
2254e4f93234SChuck Lever 
2255e4f93234SChuck Lever /*
2256e4f93234SChuck Lever  * 3.3.20  PATHCONF3res
2257e4f93234SChuck Lever  *
2258e4f93234SChuck Lever  *	struct PATHCONF3resok {
2259e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2260e4f93234SChuck Lever  *		uint32		linkmax;
2261e4f93234SChuck Lever  *		uint32		name_max;
2262e4f93234SChuck Lever  *		bool		no_trunc;
2263e4f93234SChuck Lever  *		bool		chown_restricted;
2264e4f93234SChuck Lever  *		bool		case_insensitive;
2265e4f93234SChuck Lever  *		bool		case_preserving;
2266e4f93234SChuck Lever  *	};
2267e4f93234SChuck Lever  *
2268e4f93234SChuck Lever  *	struct PATHCONF3resfail {
2269e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2270e4f93234SChuck Lever  *	};
2271e4f93234SChuck Lever  *
2272e4f93234SChuck Lever  *	union PATHCONF3res switch (nfsstat3 status) {
2273e4f93234SChuck Lever  *	case NFS3_OK:
2274e4f93234SChuck Lever  *		PATHCONF3resok	resok;
2275e4f93234SChuck Lever  *	default:
2276e4f93234SChuck Lever  *		PATHCONF3resfail resfail;
2277e4f93234SChuck Lever  *	};
2278e4f93234SChuck Lever  */
2279e4f93234SChuck Lever static int decode_pathconf3resok(struct xdr_stream *xdr,
2280e4f93234SChuck Lever 				 struct nfs_pathconf *result)
2281e4f93234SChuck Lever {
2282e4f93234SChuck Lever 	__be32 *p;
2283e4f93234SChuck Lever 
2284e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 6);
2285eb72f484SChuck Lever 	if (unlikely(!p))
2286eb72f484SChuck Lever 		return -EIO;
2287e4f93234SChuck Lever 	result->max_link = be32_to_cpup(p++);
2288e4f93234SChuck Lever 	result->max_namelen = be32_to_cpup(p);
2289e4f93234SChuck Lever 	/* ignore remaining fields */
2290e4f93234SChuck Lever 	return 0;
2291e4f93234SChuck Lever }
2292e4f93234SChuck Lever 
2293bf269551SChuck Lever static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
2294bf269551SChuck Lever 				     struct xdr_stream *xdr,
2295fc016483SChristoph Hellwig 				     void *data)
2296e4f93234SChuck Lever {
2297fc016483SChristoph Hellwig 	struct nfs_pathconf *result = data;
2298e4f93234SChuck Lever 	enum nfs_stat status;
2299e4f93234SChuck Lever 	int error;
2300e4f93234SChuck Lever 
2301bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2302e4f93234SChuck Lever 	if (unlikely(error))
2303e4f93234SChuck Lever 		goto out;
2304264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
2305e4f93234SChuck Lever 	if (unlikely(error))
2306e4f93234SChuck Lever 		goto out;
2307e4f93234SChuck Lever 	if (status != NFS3_OK)
2308e4f93234SChuck Lever 		goto out_status;
2309bf269551SChuck Lever 	error = decode_pathconf3resok(xdr, result);
2310e4f93234SChuck Lever out:
2311e4f93234SChuck Lever 	return error;
2312e4f93234SChuck Lever out_status:
23135e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2314e4f93234SChuck Lever }
2315e4f93234SChuck Lever 
2316e4f93234SChuck Lever /*
2317e4f93234SChuck Lever  * 3.3.21  COMMIT3res
2318e4f93234SChuck Lever  *
2319e4f93234SChuck Lever  *	struct COMMIT3resok {
2320e4f93234SChuck Lever  *		wcc_data	file_wcc;
2321e4f93234SChuck Lever  *		writeverf3	verf;
2322e4f93234SChuck Lever  *	};
2323e4f93234SChuck Lever  *
2324e4f93234SChuck Lever  *	struct COMMIT3resfail {
2325e4f93234SChuck Lever  *		wcc_data	file_wcc;
2326e4f93234SChuck Lever  *	};
2327e4f93234SChuck Lever  *
2328e4f93234SChuck Lever  *	union COMMIT3res switch (nfsstat3 status) {
2329e4f93234SChuck Lever  *	case NFS3_OK:
2330e4f93234SChuck Lever  *		COMMIT3resok	resok;
2331e4f93234SChuck Lever  *	default:
2332e4f93234SChuck Lever  *		COMMIT3resfail	resfail;
2333e4f93234SChuck Lever  *	};
2334e4f93234SChuck Lever  */
2335bf269551SChuck Lever static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
2336bf269551SChuck Lever 				   struct xdr_stream *xdr,
2337fc016483SChristoph Hellwig 				   void *data)
2338e4f93234SChuck Lever {
2339fc016483SChristoph Hellwig 	struct nfs_commitres *result = data;
2340221203ceSTrond Myklebust 	struct nfs_writeverf *verf = result->verf;
2341e4f93234SChuck Lever 	enum nfs_stat status;
2342e4f93234SChuck Lever 	int error;
2343e4f93234SChuck Lever 
2344bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2345e4f93234SChuck Lever 	if (unlikely(error))
2346e4f93234SChuck Lever 		goto out;
2347264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->fattr, rpc_rqst_userns(req));
2348e4f93234SChuck Lever 	if (unlikely(error))
2349e4f93234SChuck Lever 		goto out;
2350aabff4ddSPeng Tao 	result->op_status = status;
2351e4f93234SChuck Lever 	if (status != NFS3_OK)
2352e4f93234SChuck Lever 		goto out_status;
2353221203ceSTrond Myklebust 	error = decode_writeverf3(xdr, &verf->verifier);
2354221203ceSTrond Myklebust 	if (!error)
2355221203ceSTrond Myklebust 		verf->committed = NFS_FILE_SYNC;
2356e4f93234SChuck Lever out:
2357e4f93234SChuck Lever 	return error;
2358e4f93234SChuck Lever out_status:
23595e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2360e4f93234SChuck Lever }
2361e4f93234SChuck Lever 
2362b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2363b7fa0554SAndreas Gruenbacher 
2364e4f93234SChuck Lever static inline int decode_getacl3resok(struct xdr_stream *xdr,
2365264d948cSTrond Myklebust 				      struct nfs3_getaclres *result,
2366264d948cSTrond Myklebust 				      struct user_namespace *userns)
2367e4f93234SChuck Lever {
2368e4f93234SChuck Lever 	struct posix_acl **acl;
2369e4f93234SChuck Lever 	unsigned int *aclcnt;
2370e4f93234SChuck Lever 	size_t hdrlen;
2371e4f93234SChuck Lever 	int error;
2372e4f93234SChuck Lever 
2373264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
2374e4f93234SChuck Lever 	if (unlikely(error))
2375e4f93234SChuck Lever 		goto out;
2376e4f93234SChuck Lever 	error = decode_uint32(xdr, &result->mask);
2377e4f93234SChuck Lever 	if (unlikely(error))
2378e4f93234SChuck Lever 		goto out;
2379e4f93234SChuck Lever 	error = -EINVAL;
2380e4f93234SChuck Lever 	if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
2381e4f93234SChuck Lever 		goto out;
2382e4f93234SChuck Lever 
23831aecca3eSTrond Myklebust 	hdrlen = xdr_stream_pos(xdr);
2384e4f93234SChuck Lever 
2385e4f93234SChuck Lever 	acl = NULL;
2386e4f93234SChuck Lever 	if (result->mask & NFS_ACL)
2387e4f93234SChuck Lever 		acl = &result->acl_access;
2388e4f93234SChuck Lever 	aclcnt = NULL;
2389e4f93234SChuck Lever 	if (result->mask & NFS_ACLCNT)
2390e4f93234SChuck Lever 		aclcnt = &result->acl_access_count;
2391e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl);
2392e4f93234SChuck Lever 	if (unlikely(error <= 0))
2393e4f93234SChuck Lever 		goto out;
2394e4f93234SChuck Lever 
2395e4f93234SChuck Lever 	acl = NULL;
2396e4f93234SChuck Lever 	if (result->mask & NFS_DFACL)
2397e4f93234SChuck Lever 		acl = &result->acl_default;
2398e4f93234SChuck Lever 	aclcnt = NULL;
2399e4f93234SChuck Lever 	if (result->mask & NFS_DFACLCNT)
2400e4f93234SChuck Lever 		aclcnt = &result->acl_default_count;
2401e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl);
2402e4f93234SChuck Lever 	if (unlikely(error <= 0))
2403e4f93234SChuck Lever 		return error;
2404e4f93234SChuck Lever 	error = 0;
2405e4f93234SChuck Lever out:
2406e4f93234SChuck Lever 	return error;
2407e4f93234SChuck Lever }
2408e4f93234SChuck Lever 
2409bf269551SChuck Lever static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
2410bf269551SChuck Lever 				   struct xdr_stream *xdr,
2411fc016483SChristoph Hellwig 				   void *result)
2412e4f93234SChuck Lever {
2413e4f93234SChuck Lever 	enum nfs_stat status;
2414e4f93234SChuck Lever 	int error;
2415e4f93234SChuck Lever 
2416bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2417e4f93234SChuck Lever 	if (unlikely(error))
2418e4f93234SChuck Lever 		goto out;
2419e4f93234SChuck Lever 	if (status != NFS3_OK)
2420e4f93234SChuck Lever 		goto out_default;
2421264d948cSTrond Myklebust 	error = decode_getacl3resok(xdr, result, rpc_rqst_userns(req));
2422e4f93234SChuck Lever out:
2423e4f93234SChuck Lever 	return error;
2424e4f93234SChuck Lever out_default:
24255e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2426e4f93234SChuck Lever }
2427e4f93234SChuck Lever 
2428bf269551SChuck Lever static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
2429bf269551SChuck Lever 				   struct xdr_stream *xdr,
2430fc016483SChristoph Hellwig 				   void *result)
2431e4f93234SChuck Lever {
2432e4f93234SChuck Lever 	enum nfs_stat status;
2433e4f93234SChuck Lever 	int error;
2434e4f93234SChuck Lever 
2435bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2436e4f93234SChuck Lever 	if (unlikely(error))
2437e4f93234SChuck Lever 		goto out;
2438e4f93234SChuck Lever 	if (status != NFS3_OK)
2439e4f93234SChuck Lever 		goto out_default;
2440264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result, rpc_rqst_userns(req));
2441e4f93234SChuck Lever out:
2442e4f93234SChuck Lever 	return error;
2443e4f93234SChuck Lever out_default:
24445e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2445e4f93234SChuck Lever }
2446e4f93234SChuck Lever 
2447b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2448b7fa0554SAndreas Gruenbacher 
24495e7e5a0dSBryan Schumaker 
24505e7e5a0dSBryan Schumaker /*
24515e7e5a0dSBryan Schumaker  * We need to translate between nfs status return values and
24525e7e5a0dSBryan Schumaker  * the local errno values which may not be the same.
24535e7e5a0dSBryan Schumaker  */
24545e7e5a0dSBryan Schumaker static const struct {
24555e7e5a0dSBryan Schumaker 	int stat;
24565e7e5a0dSBryan Schumaker 	int errno;
24575e7e5a0dSBryan Schumaker } nfs_errtbl[] = {
24585e7e5a0dSBryan Schumaker 	{ NFS_OK,		0		},
24595e7e5a0dSBryan Schumaker 	{ NFSERR_PERM,		-EPERM		},
24605e7e5a0dSBryan Schumaker 	{ NFSERR_NOENT,		-ENOENT		},
24615e7e5a0dSBryan Schumaker 	{ NFSERR_IO,		-errno_NFSERR_IO},
24625e7e5a0dSBryan Schumaker 	{ NFSERR_NXIO,		-ENXIO		},
24635e7e5a0dSBryan Schumaker /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
24645e7e5a0dSBryan Schumaker 	{ NFSERR_ACCES,		-EACCES		},
24655e7e5a0dSBryan Schumaker 	{ NFSERR_EXIST,		-EEXIST		},
24665e7e5a0dSBryan Schumaker 	{ NFSERR_XDEV,		-EXDEV		},
24675e7e5a0dSBryan Schumaker 	{ NFSERR_NODEV,		-ENODEV		},
24685e7e5a0dSBryan Schumaker 	{ NFSERR_NOTDIR,	-ENOTDIR	},
24695e7e5a0dSBryan Schumaker 	{ NFSERR_ISDIR,		-EISDIR		},
24705e7e5a0dSBryan Schumaker 	{ NFSERR_INVAL,		-EINVAL		},
24715e7e5a0dSBryan Schumaker 	{ NFSERR_FBIG,		-EFBIG		},
24725e7e5a0dSBryan Schumaker 	{ NFSERR_NOSPC,		-ENOSPC		},
24735e7e5a0dSBryan Schumaker 	{ NFSERR_ROFS,		-EROFS		},
24745e7e5a0dSBryan Schumaker 	{ NFSERR_MLINK,		-EMLINK		},
24755e7e5a0dSBryan Schumaker 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
24765e7e5a0dSBryan Schumaker 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
24775e7e5a0dSBryan Schumaker 	{ NFSERR_DQUOT,		-EDQUOT		},
24785e7e5a0dSBryan Schumaker 	{ NFSERR_STALE,		-ESTALE		},
24795e7e5a0dSBryan Schumaker 	{ NFSERR_REMOTE,	-EREMOTE	},
24805e7e5a0dSBryan Schumaker #ifdef EWFLUSH
24815e7e5a0dSBryan Schumaker 	{ NFSERR_WFLUSH,	-EWFLUSH	},
24825e7e5a0dSBryan Schumaker #endif
24835e7e5a0dSBryan Schumaker 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
24845e7e5a0dSBryan Schumaker 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
24855e7e5a0dSBryan Schumaker 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
24865e7e5a0dSBryan Schumaker 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
24875e7e5a0dSBryan Schumaker 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
24885e7e5a0dSBryan Schumaker 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
24895e7e5a0dSBryan Schumaker 	{ NFSERR_BADTYPE,	-EBADTYPE	},
24905e7e5a0dSBryan Schumaker 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
24915e7e5a0dSBryan Schumaker 	{ -1,			-EIO		}
24925e7e5a0dSBryan Schumaker };
24935e7e5a0dSBryan Schumaker 
24945e7e5a0dSBryan Schumaker /**
24955e7e5a0dSBryan Schumaker  * nfs3_stat_to_errno - convert an NFS status code to a local errno
24965e7e5a0dSBryan Schumaker  * @status: NFS status code to convert
24975e7e5a0dSBryan Schumaker  *
24985e7e5a0dSBryan Schumaker  * Returns a local errno value, or -EIO if the NFS status code is
24995e7e5a0dSBryan Schumaker  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
25005e7e5a0dSBryan Schumaker  */
25015e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat status)
25025e7e5a0dSBryan Schumaker {
25035e7e5a0dSBryan Schumaker 	int i;
25045e7e5a0dSBryan Schumaker 
25055e7e5a0dSBryan Schumaker 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
25065e7e5a0dSBryan Schumaker 		if (nfs_errtbl[i].stat == (int)status)
25075e7e5a0dSBryan Schumaker 			return nfs_errtbl[i].errno;
25085e7e5a0dSBryan Schumaker 	}
25095e7e5a0dSBryan Schumaker 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
25105e7e5a0dSBryan Schumaker 	return nfs_errtbl[i].errno;
25115e7e5a0dSBryan Schumaker }
25125e7e5a0dSBryan Schumaker 
25135e7e5a0dSBryan Schumaker 
25141da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
25151da177e4SLinus Torvalds [NFS3PROC_##proc] = {							\
25161da177e4SLinus Torvalds 	.p_proc      = NFS3PROC_##proc,					\
2517fcc85819SChristoph Hellwig 	.p_encode    = nfs3_xdr_enc_##argtype##3args,			\
2518fc016483SChristoph Hellwig 	.p_decode    = nfs3_xdr_dec_##restype##3res,			\
2519ad96b5b5SChuck Lever 	.p_arglen    = NFS3_##argtype##args_sz,				\
2520f5fc3c50SChuck Lever 	.p_replen    = NFS3_##restype##res_sz,				\
2521cc0175c1SChuck Lever 	.p_timer     = timer,						\
2522cc0175c1SChuck Lever 	.p_statidx   = NFS3PROC_##proc,					\
2523cc0175c1SChuck Lever 	.p_name      = #proc,						\
25241da177e4SLinus Torvalds 	}
25251da177e4SLinus Torvalds 
2526511e936bSChristoph Hellwig const struct rpc_procinfo nfs3_procedures[] = {
2527f5fc3c50SChuck Lever 	PROC(GETATTR,		getattr,	getattr,	1),
2528f5fc3c50SChuck Lever 	PROC(SETATTR,		setattr,	setattr,	0),
2529f5fc3c50SChuck Lever 	PROC(LOOKUP,		lookup,		lookup,		2),
2530f5fc3c50SChuck Lever 	PROC(ACCESS,		access,		access,		1),
2531f5fc3c50SChuck Lever 	PROC(READLINK,		readlink,	readlink,	3),
2532f5fc3c50SChuck Lever 	PROC(READ,		read,		read,		3),
2533f5fc3c50SChuck Lever 	PROC(WRITE,		write,		write,		4),
2534f5fc3c50SChuck Lever 	PROC(CREATE,		create,		create,		0),
2535f5fc3c50SChuck Lever 	PROC(MKDIR,		mkdir,		create,		0),
2536f5fc3c50SChuck Lever 	PROC(SYMLINK,		symlink,	create,		0),
2537f5fc3c50SChuck Lever 	PROC(MKNOD,		mknod,		create,		0),
2538f5fc3c50SChuck Lever 	PROC(REMOVE,		remove,		remove,		0),
2539f5fc3c50SChuck Lever 	PROC(RMDIR,		lookup,		setattr,	0),
2540f5fc3c50SChuck Lever 	PROC(RENAME,		rename,		rename,		0),
2541f5fc3c50SChuck Lever 	PROC(LINK,		link,		link,		0),
2542f5fc3c50SChuck Lever 	PROC(READDIR,		readdir,	readdir,	3),
2543f5fc3c50SChuck Lever 	PROC(READDIRPLUS,	readdirplus,	readdir,	3),
2544f5fc3c50SChuck Lever 	PROC(FSSTAT,		getattr,	fsstat,		0),
2545f5fc3c50SChuck Lever 	PROC(FSINFO,		getattr,	fsinfo,		0),
2546f5fc3c50SChuck Lever 	PROC(PATHCONF,		getattr,	pathconf,	0),
2547f5fc3c50SChuck Lever 	PROC(COMMIT,		commit,		commit,		5),
25481da177e4SLinus Torvalds };
25491da177e4SLinus Torvalds 
2550c551858aSChristoph Hellwig static unsigned int nfs_version3_counts[ARRAY_SIZE(nfs3_procedures)];
2551a613fa16STrond Myklebust const struct rpc_version nfs_version3 = {
25521da177e4SLinus Torvalds 	.number			= 3,
2553e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
2554c551858aSChristoph Hellwig 	.procs			= nfs3_procedures,
2555c551858aSChristoph Hellwig 	.counts			= nfs_version3_counts,
25561da177e4SLinus Torvalds };
25571da177e4SLinus Torvalds 
2558b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2559511e936bSChristoph Hellwig static const struct rpc_procinfo nfs3_acl_procedures[] = {
2560b7fa0554SAndreas Gruenbacher 	[ACLPROC3_GETACL] = {
2561b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_GETACL,
2562fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_getacl3args,
2563fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_getacl3res,
25642bea90d4SChuck Lever 		.p_arglen = ACL3_getaclargs_sz,
25652bea90d4SChuck Lever 		.p_replen = ACL3_getaclres_sz,
2566b7fa0554SAndreas Gruenbacher 		.p_timer = 1,
2567cc0175c1SChuck Lever 		.p_name = "GETACL",
2568b7fa0554SAndreas Gruenbacher 	},
2569b7fa0554SAndreas Gruenbacher 	[ACLPROC3_SETACL] = {
2570b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_SETACL,
2571fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_setacl3args,
2572fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_setacl3res,
25732bea90d4SChuck Lever 		.p_arglen = ACL3_setaclargs_sz,
25742bea90d4SChuck Lever 		.p_replen = ACL3_setaclres_sz,
2575b7fa0554SAndreas Gruenbacher 		.p_timer = 0,
2576cc0175c1SChuck Lever 		.p_name = "SETACL",
2577b7fa0554SAndreas Gruenbacher 	},
2578b7fa0554SAndreas Gruenbacher };
2579b7fa0554SAndreas Gruenbacher 
2580c551858aSChristoph Hellwig static unsigned int nfs3_acl_counts[ARRAY_SIZE(nfs3_acl_procedures)];
2581a613fa16STrond Myklebust const struct rpc_version nfsacl_version3 = {
2582b7fa0554SAndreas Gruenbacher 	.number			= 3,
25839ae7d8ffSChristoph Hellwig 	.nrprocs		= ARRAY_SIZE(nfs3_acl_procedures),
2584b7fa0554SAndreas Gruenbacher 	.procs			= nfs3_acl_procedures,
2585c551858aSChristoph Hellwig 	.counts			= nfs3_acl_counts,
2586b7fa0554SAndreas Gruenbacher };
2587b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2588