xref: /openbmc/linux/fs/nfs/nfs3xdr.c (revision 9ae7d8ff)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfs/nfs3xdr.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * XDR functions to encode/decode NFSv3 RPC arguments and results.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1996, 1997 Olaf Kirch
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/param.h>
101da177e4SLinus Torvalds #include <linux/time.h>
111da177e4SLinus Torvalds #include <linux/mm.h>
121da177e4SLinus Torvalds #include <linux/errno.h>
131da177e4SLinus Torvalds #include <linux/string.h>
141da177e4SLinus Torvalds #include <linux/in.h>
151da177e4SLinus Torvalds #include <linux/pagemap.h>
161da177e4SLinus Torvalds #include <linux/proc_fs.h>
171da177e4SLinus Torvalds #include <linux/kdev_t.h>
181da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
191da177e4SLinus Torvalds #include <linux/nfs.h>
201da177e4SLinus Torvalds #include <linux/nfs3.h>
211da177e4SLinus Torvalds #include <linux/nfs_fs.h>
22b7fa0554SAndreas Gruenbacher #include <linux/nfsacl.h>
23f7b422b1SDavid Howells #include "internal.h"
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
281da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds /*
311da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
321da177e4SLinus Torvalds  * number of 32bit-words
331da177e4SLinus Torvalds  */
341da177e4SLinus Torvalds #define NFS3_fhandle_sz		(1+16)
351da177e4SLinus Torvalds #define NFS3_fh_sz		(NFS3_fhandle_sz)	/* shorthand */
361da177e4SLinus Torvalds #define NFS3_sattr_sz		(15)
371da177e4SLinus Torvalds #define NFS3_filename_sz	(1+(NFS3_MAXNAMLEN>>2))
381da177e4SLinus Torvalds #define NFS3_path_sz		(1+(NFS3_MAXPATHLEN>>2))
391da177e4SLinus Torvalds #define NFS3_fattr_sz		(21)
40d9c407b1SChuck Lever #define NFS3_cookieverf_sz	(NFS3_COOKIEVERFSIZE>>2)
411da177e4SLinus Torvalds #define NFS3_wcc_attr_sz	(6)
421da177e4SLinus Torvalds #define NFS3_pre_op_attr_sz	(1+NFS3_wcc_attr_sz)
431da177e4SLinus Torvalds #define NFS3_post_op_attr_sz	(1+NFS3_fattr_sz)
441da177e4SLinus Torvalds #define NFS3_wcc_data_sz	(NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
451da177e4SLinus Torvalds #define NFS3_diropargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
46ad96b5b5SChuck Lever 
47ad96b5b5SChuck Lever #define NFS3_getattrargs_sz	(NFS3_fh_sz)
48ad96b5b5SChuck Lever #define NFS3_setattrargs_sz	(NFS3_fh_sz+NFS3_sattr_sz+3)
49ad96b5b5SChuck Lever #define NFS3_lookupargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
501da177e4SLinus Torvalds #define NFS3_accessargs_sz	(NFS3_fh_sz+1)
511da177e4SLinus Torvalds #define NFS3_readlinkargs_sz	(NFS3_fh_sz)
521da177e4SLinus Torvalds #define NFS3_readargs_sz	(NFS3_fh_sz+3)
531da177e4SLinus Torvalds #define NFS3_writeargs_sz	(NFS3_fh_sz+5)
541da177e4SLinus Torvalds #define NFS3_createargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
551da177e4SLinus Torvalds #define NFS3_mkdirargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
5694a6d753SChuck Lever #define NFS3_symlinkargs_sz	(NFS3_diropargs_sz+1+NFS3_sattr_sz)
571da177e4SLinus Torvalds #define NFS3_mknodargs_sz	(NFS3_diropargs_sz+2+NFS3_sattr_sz)
58ad96b5b5SChuck Lever #define NFS3_removeargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
591da177e4SLinus Torvalds #define NFS3_renameargs_sz	(NFS3_diropargs_sz+NFS3_diropargs_sz)
601da177e4SLinus Torvalds #define NFS3_linkargs_sz		(NFS3_fh_sz+NFS3_diropargs_sz)
61d9c407b1SChuck Lever #define NFS3_readdirargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+3)
62d9c407b1SChuck Lever #define NFS3_readdirplusargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+4)
631da177e4SLinus Torvalds #define NFS3_commitargs_sz	(NFS3_fh_sz+3)
641da177e4SLinus Torvalds 
65f5fc3c50SChuck Lever #define NFS3_getattrres_sz	(1+NFS3_fattr_sz)
66f5fc3c50SChuck Lever #define NFS3_setattrres_sz	(1+NFS3_wcc_data_sz)
67f5fc3c50SChuck Lever #define NFS3_removeres_sz	(NFS3_setattrres_sz)
681da177e4SLinus Torvalds #define NFS3_lookupres_sz	(1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
691da177e4SLinus Torvalds #define NFS3_accessres_sz	(1+NFS3_post_op_attr_sz+1)
701da177e4SLinus Torvalds #define NFS3_readlinkres_sz	(1+NFS3_post_op_attr_sz+1)
711da177e4SLinus Torvalds #define NFS3_readres_sz		(1+NFS3_post_op_attr_sz+3)
721da177e4SLinus Torvalds #define NFS3_writeres_sz	(1+NFS3_wcc_data_sz+4)
731da177e4SLinus Torvalds #define NFS3_createres_sz	(1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
741da177e4SLinus Torvalds #define NFS3_renameres_sz	(1+(2 * NFS3_wcc_data_sz))
751da177e4SLinus Torvalds #define NFS3_linkres_sz		(1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
761da177e4SLinus Torvalds #define NFS3_readdirres_sz	(1+NFS3_post_op_attr_sz+2)
771da177e4SLinus Torvalds #define NFS3_fsstatres_sz	(1+NFS3_post_op_attr_sz+13)
781da177e4SLinus Torvalds #define NFS3_fsinfores_sz	(1+NFS3_post_op_attr_sz+12)
791da177e4SLinus Torvalds #define NFS3_pathconfres_sz	(1+NFS3_post_op_attr_sz+6)
801da177e4SLinus Torvalds #define NFS3_commitres_sz	(1+NFS3_wcc_data_sz+2)
811da177e4SLinus Torvalds 
82b7fa0554SAndreas Gruenbacher #define ACL3_getaclargs_sz	(NFS3_fh_sz+1)
83ae46141fSTrond Myklebust #define ACL3_setaclargs_sz	(NFS3_fh_sz+1+ \
84ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
85ae46141fSTrond Myklebust #define ACL3_getaclres_sz	(1+NFS3_post_op_attr_sz+1+ \
86ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
87b7fa0554SAndreas Gruenbacher #define ACL3_setaclres_sz	(1+NFS3_post_op_attr_sz)
88b7fa0554SAndreas Gruenbacher 
895e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat);
905e7e5a0dSBryan Schumaker 
911da177e4SLinus Torvalds /*
921da177e4SLinus Torvalds  * Map file type to S_IFMT bits
931da177e4SLinus Torvalds  */
94bca79478STrond Myklebust static const umode_t nfs_type2fmt[] = {
95bca79478STrond Myklebust 	[NF3BAD] = 0,
96bca79478STrond Myklebust 	[NF3REG] = S_IFREG,
97bca79478STrond Myklebust 	[NF3DIR] = S_IFDIR,
98bca79478STrond Myklebust 	[NF3BLK] = S_IFBLK,
99bca79478STrond Myklebust 	[NF3CHR] = S_IFCHR,
100bca79478STrond Myklebust 	[NF3LNK] = S_IFLNK,
101bca79478STrond Myklebust 	[NF3SOCK] = S_IFSOCK,
102bca79478STrond Myklebust 	[NF3FIFO] = S_IFIFO,
1031da177e4SLinus Torvalds };
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds /*
106d9c407b1SChuck Lever  * While encoding arguments, set up the reply buffer in advance to
107d9c407b1SChuck Lever  * receive reply data directly into the page cache.
108d9c407b1SChuck Lever  */
109d9c407b1SChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
110d9c407b1SChuck Lever 				 unsigned int base, unsigned int len,
111d9c407b1SChuck Lever 				 unsigned int bufsize)
112d9c407b1SChuck Lever {
113d9c407b1SChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
114d9c407b1SChuck Lever 	unsigned int replen;
115d9c407b1SChuck Lever 
116d9c407b1SChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
117d9c407b1SChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
118d9c407b1SChuck Lever }
119d9c407b1SChuck Lever 
120e4f93234SChuck Lever /*
121e4f93234SChuck Lever  * Handle decode buffer overflows out-of-line.
122e4f93234SChuck Lever  */
123e4f93234SChuck Lever static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
124e4f93234SChuck Lever {
125e4f93234SChuck Lever 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
126e4f93234SChuck Lever 		"Remaining buffer length is %tu words.\n",
127e4f93234SChuck Lever 		func, xdr->end - xdr->p);
128e4f93234SChuck Lever }
129e4f93234SChuck Lever 
130d9c407b1SChuck Lever 
131d9c407b1SChuck Lever /*
132d9c407b1SChuck Lever  * Encode/decode NFSv3 basic data types
133d9c407b1SChuck Lever  *
134d9c407b1SChuck Lever  * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
135d9c407b1SChuck Lever  * "NFS Version 3 Protocol Specification".
136d9c407b1SChuck Lever  *
137d9c407b1SChuck Lever  * Not all basic data types have their own encoding and decoding
138d9c407b1SChuck Lever  * functions.  For run-time efficiency, some data types are encoded
139d9c407b1SChuck Lever  * or decoded inline.
140d9c407b1SChuck Lever  */
141d9c407b1SChuck Lever 
142d9c407b1SChuck Lever static void encode_uint32(struct xdr_stream *xdr, u32 value)
143d9c407b1SChuck Lever {
144d9c407b1SChuck Lever 	__be32 *p = xdr_reserve_space(xdr, 4);
145d9c407b1SChuck Lever 	*p = cpu_to_be32(value);
146d9c407b1SChuck Lever }
147d9c407b1SChuck Lever 
148e4f93234SChuck Lever static int decode_uint32(struct xdr_stream *xdr, u32 *value)
149e4f93234SChuck Lever {
150e4f93234SChuck Lever 	__be32 *p;
151e4f93234SChuck Lever 
152e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
153e4f93234SChuck Lever 	if (unlikely(p == NULL))
154e4f93234SChuck Lever 		goto out_overflow;
155e4f93234SChuck Lever 	*value = be32_to_cpup(p);
156e4f93234SChuck Lever 	return 0;
157e4f93234SChuck Lever out_overflow:
158e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
159e4f93234SChuck Lever 	return -EIO;
160e4f93234SChuck Lever }
161e4f93234SChuck Lever 
162e4f93234SChuck Lever static int decode_uint64(struct xdr_stream *xdr, u64 *value)
163e4f93234SChuck Lever {
164e4f93234SChuck Lever 	__be32 *p;
165e4f93234SChuck Lever 
166e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8);
167e4f93234SChuck Lever 	if (unlikely(p == NULL))
168e4f93234SChuck Lever 		goto out_overflow;
169e4f93234SChuck Lever 	xdr_decode_hyper(p, value);
170e4f93234SChuck Lever 	return 0;
171e4f93234SChuck Lever out_overflow:
172e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
173e4f93234SChuck Lever 	return -EIO;
174e4f93234SChuck Lever }
175e4f93234SChuck Lever 
176e4f93234SChuck Lever /*
177e4f93234SChuck Lever  * fileid3
178e4f93234SChuck Lever  *
179e4f93234SChuck Lever  *	typedef uint64 fileid3;
180e4f93234SChuck Lever  */
181f6048709SChuck Lever static __be32 *xdr_decode_fileid3(__be32 *p, u64 *fileid)
182f6048709SChuck Lever {
183f6048709SChuck Lever 	return xdr_decode_hyper(p, fileid);
184f6048709SChuck Lever }
185f6048709SChuck Lever 
186e4f93234SChuck Lever static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid)
187e4f93234SChuck Lever {
188e4f93234SChuck Lever 	return decode_uint64(xdr, fileid);
189e4f93234SChuck Lever }
190e4f93234SChuck Lever 
191d9c407b1SChuck Lever /*
192d9c407b1SChuck Lever  * filename3
193d9c407b1SChuck Lever  *
194d9c407b1SChuck Lever  *	typedef string filename3<>;
195d9c407b1SChuck Lever  */
196d9c407b1SChuck Lever static void encode_filename3(struct xdr_stream *xdr,
197d9c407b1SChuck Lever 			     const char *name, u32 length)
198d9c407b1SChuck Lever {
199d9c407b1SChuck Lever 	__be32 *p;
200d9c407b1SChuck Lever 
2017fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS3_MAXNAMLEN);
202d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
203d9c407b1SChuck Lever 	xdr_encode_opaque(p, name, length);
204d9c407b1SChuck Lever }
205d9c407b1SChuck Lever 
206e4f93234SChuck Lever static int decode_inline_filename3(struct xdr_stream *xdr,
207e4f93234SChuck Lever 				   const char **name, u32 *length)
208e4f93234SChuck Lever {
209e4f93234SChuck Lever 	__be32 *p;
210e4f93234SChuck Lever 	u32 count;
211e4f93234SChuck Lever 
212e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
213e4f93234SChuck Lever 	if (unlikely(p == NULL))
214e4f93234SChuck Lever 		goto out_overflow;
215e4f93234SChuck Lever 	count = be32_to_cpup(p);
216e4f93234SChuck Lever 	if (count > NFS3_MAXNAMLEN)
217e4f93234SChuck Lever 		goto out_nametoolong;
218e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, count);
219e4f93234SChuck Lever 	if (unlikely(p == NULL))
220e4f93234SChuck Lever 		goto out_overflow;
221e4f93234SChuck Lever 	*name = (const char *)p;
222e4f93234SChuck Lever 	*length = count;
223e4f93234SChuck Lever 	return 0;
224e4f93234SChuck Lever 
225e4f93234SChuck Lever out_nametoolong:
226e4f93234SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
227e4f93234SChuck Lever 	return -ENAMETOOLONG;
228e4f93234SChuck Lever out_overflow:
229e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
230e4f93234SChuck Lever 	return -EIO;
231e4f93234SChuck Lever }
232e4f93234SChuck Lever 
233d9c407b1SChuck Lever /*
234d9c407b1SChuck Lever  * nfspath3
235d9c407b1SChuck Lever  *
236d9c407b1SChuck Lever  *	typedef string nfspath3<>;
237d9c407b1SChuck Lever  */
238d9c407b1SChuck Lever static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
239d9c407b1SChuck Lever 			    const u32 length)
240d9c407b1SChuck Lever {
241d9c407b1SChuck Lever 	encode_uint32(xdr, length);
242d9c407b1SChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
243d9c407b1SChuck Lever }
244d9c407b1SChuck Lever 
245e4f93234SChuck Lever static int decode_nfspath3(struct xdr_stream *xdr)
246e4f93234SChuck Lever {
247e4f93234SChuck Lever 	u32 recvd, count;
248e4f93234SChuck Lever 	__be32 *p;
249e4f93234SChuck Lever 
250e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
251e4f93234SChuck Lever 	if (unlikely(p == NULL))
252e4f93234SChuck Lever 		goto out_overflow;
253e4f93234SChuck Lever 	count = be32_to_cpup(p);
254e4f93234SChuck Lever 	if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
255e4f93234SChuck Lever 		goto out_nametoolong;
25664bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
257e4f93234SChuck Lever 	if (unlikely(count > recvd))
258e4f93234SChuck Lever 		goto out_cheating;
259e4f93234SChuck Lever 	xdr_terminate_string(xdr->buf, count);
260e4f93234SChuck Lever 	return 0;
261e4f93234SChuck Lever 
262e4f93234SChuck Lever out_nametoolong:
263e4f93234SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", count);
264e4f93234SChuck Lever 	return -ENAMETOOLONG;
265e4f93234SChuck Lever out_cheating:
266e4f93234SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
267e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
268e4f93234SChuck Lever 	return -EIO;
269e4f93234SChuck Lever out_overflow:
270e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
271e4f93234SChuck Lever 	return -EIO;
272e4f93234SChuck Lever }
273e4f93234SChuck Lever 
274d9c407b1SChuck Lever /*
275d9c407b1SChuck Lever  * cookie3
276d9c407b1SChuck Lever  *
277d9c407b1SChuck Lever  *	typedef uint64 cookie3
278d9c407b1SChuck Lever  */
279d9c407b1SChuck Lever static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
280d9c407b1SChuck Lever {
281d9c407b1SChuck Lever 	return xdr_encode_hyper(p, cookie);
282d9c407b1SChuck Lever }
283d9c407b1SChuck Lever 
284e4f93234SChuck Lever static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie)
285e4f93234SChuck Lever {
286e4f93234SChuck Lever 	return decode_uint64(xdr, cookie);
287e4f93234SChuck Lever }
288e4f93234SChuck Lever 
289d9c407b1SChuck Lever /*
290d9c407b1SChuck Lever  * cookieverf3
291d9c407b1SChuck Lever  *
292d9c407b1SChuck Lever  *	typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
293d9c407b1SChuck Lever  */
294d9c407b1SChuck Lever static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
295d9c407b1SChuck Lever {
296d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
297d9c407b1SChuck Lever 	return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
298d9c407b1SChuck Lever }
299d9c407b1SChuck Lever 
300e4f93234SChuck Lever static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier)
301e4f93234SChuck Lever {
302e4f93234SChuck Lever 	__be32 *p;
303e4f93234SChuck Lever 
304e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
305e4f93234SChuck Lever 	if (unlikely(p == NULL))
306e4f93234SChuck Lever 		goto out_overflow;
307e4f93234SChuck Lever 	memcpy(verifier, p, NFS3_COOKIEVERFSIZE);
308e4f93234SChuck Lever 	return 0;
309e4f93234SChuck Lever out_overflow:
310e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
311e4f93234SChuck Lever 	return -EIO;
312e4f93234SChuck Lever }
313e4f93234SChuck Lever 
314d9c407b1SChuck Lever /*
315d9c407b1SChuck Lever  * createverf3
316d9c407b1SChuck Lever  *
317d9c407b1SChuck Lever  *	typedef opaque createverf3[NFS3_CREATEVERFSIZE];
318d9c407b1SChuck Lever  */
319d9c407b1SChuck Lever static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
320d9c407b1SChuck Lever {
321d9c407b1SChuck Lever 	__be32 *p;
322d9c407b1SChuck Lever 
323d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
324d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_CREATEVERFSIZE);
325d9c407b1SChuck Lever }
326d9c407b1SChuck Lever 
3272f2c63bcSTrond Myklebust static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
328e4f93234SChuck Lever {
329e4f93234SChuck Lever 	__be32 *p;
330e4f93234SChuck Lever 
331e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
332e4f93234SChuck Lever 	if (unlikely(p == NULL))
333e4f93234SChuck Lever 		goto out_overflow;
3342f2c63bcSTrond Myklebust 	memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
335e4f93234SChuck Lever 	return 0;
336e4f93234SChuck Lever out_overflow:
337e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
338e4f93234SChuck Lever 	return -EIO;
339e4f93234SChuck Lever }
340e4f93234SChuck Lever 
341e4f93234SChuck Lever /*
342e4f93234SChuck Lever  * size3
343e4f93234SChuck Lever  *
344e4f93234SChuck Lever  *	typedef uint64 size3;
345e4f93234SChuck Lever  */
346e4f93234SChuck Lever static __be32 *xdr_decode_size3(__be32 *p, u64 *size)
347e4f93234SChuck Lever {
348e4f93234SChuck Lever 	return xdr_decode_hyper(p, size);
349e4f93234SChuck Lever }
350e4f93234SChuck Lever 
351e4f93234SChuck Lever /*
352e4f93234SChuck Lever  * nfsstat3
353e4f93234SChuck Lever  *
354e4f93234SChuck Lever  *	enum nfsstat3 {
355e4f93234SChuck Lever  *		NFS3_OK = 0,
356e4f93234SChuck Lever  *		...
357e4f93234SChuck Lever  *	}
358e4f93234SChuck Lever  */
359e4f93234SChuck Lever #define NFS3_OK		NFS_OK
360e4f93234SChuck Lever 
361e4f93234SChuck Lever static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status)
362e4f93234SChuck Lever {
363e4f93234SChuck Lever 	__be32 *p;
364e4f93234SChuck Lever 
365e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
366e4f93234SChuck Lever 	if (unlikely(p == NULL))
367e4f93234SChuck Lever 		goto out_overflow;
368e4f93234SChuck Lever 	*status = be32_to_cpup(p);
369e4f93234SChuck Lever 	return 0;
370e4f93234SChuck Lever out_overflow:
371e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
372e4f93234SChuck Lever 	return -EIO;
373e4f93234SChuck Lever }
374e4f93234SChuck Lever 
375d9c407b1SChuck Lever /*
376d9c407b1SChuck Lever  * ftype3
377d9c407b1SChuck Lever  *
378d9c407b1SChuck Lever  *	enum ftype3 {
379d9c407b1SChuck Lever  *		NF3REG	= 1,
380d9c407b1SChuck Lever  *		NF3DIR	= 2,
381d9c407b1SChuck Lever  *		NF3BLK	= 3,
382d9c407b1SChuck Lever  *		NF3CHR	= 4,
383d9c407b1SChuck Lever  *		NF3LNK	= 5,
384d9c407b1SChuck Lever  *		NF3SOCK	= 6,
385d9c407b1SChuck Lever  *		NF3FIFO	= 7
386d9c407b1SChuck Lever  *	};
387d9c407b1SChuck Lever  */
388d9c407b1SChuck Lever static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
389d9c407b1SChuck Lever {
390d9c407b1SChuck Lever 	encode_uint32(xdr, type);
391d9c407b1SChuck Lever }
392d9c407b1SChuck Lever 
393f6048709SChuck Lever static __be32 *xdr_decode_ftype3(__be32 *p, umode_t *mode)
394f6048709SChuck Lever {
395f6048709SChuck Lever 	u32 type;
396f6048709SChuck Lever 
397f6048709SChuck Lever 	type = be32_to_cpup(p++);
398f6048709SChuck Lever 	if (type > NF3FIFO)
399f6048709SChuck Lever 		type = NF3NON;
400f6048709SChuck Lever 	*mode = nfs_type2fmt[type];
401f6048709SChuck Lever 	return p;
402f6048709SChuck Lever }
403f6048709SChuck Lever 
404d9c407b1SChuck Lever /*
405d9c407b1SChuck Lever  * specdata3
406d9c407b1SChuck Lever  *
407d9c407b1SChuck Lever  *     struct specdata3 {
408d9c407b1SChuck Lever  *             uint32  specdata1;
409d9c407b1SChuck Lever  *             uint32  specdata2;
410d9c407b1SChuck Lever  *     };
411d9c407b1SChuck Lever  */
412d9c407b1SChuck Lever static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
413d9c407b1SChuck Lever {
414d9c407b1SChuck Lever 	__be32 *p;
415d9c407b1SChuck Lever 
416d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8);
417d9c407b1SChuck Lever 	*p++ = cpu_to_be32(MAJOR(rdev));
418d9c407b1SChuck Lever 	*p = cpu_to_be32(MINOR(rdev));
419d9c407b1SChuck Lever }
420d9c407b1SChuck Lever 
421f6048709SChuck Lever static __be32 *xdr_decode_specdata3(__be32 *p, dev_t *rdev)
422f6048709SChuck Lever {
423f6048709SChuck Lever 	unsigned int major, minor;
424f6048709SChuck Lever 
425f6048709SChuck Lever 	major = be32_to_cpup(p++);
426f6048709SChuck Lever 	minor = be32_to_cpup(p++);
427f6048709SChuck Lever 	*rdev = MKDEV(major, minor);
428f6048709SChuck Lever 	if (MAJOR(*rdev) != major || MINOR(*rdev) != minor)
429f6048709SChuck Lever 		*rdev = 0;
430f6048709SChuck Lever 	return p;
431f6048709SChuck Lever }
432f6048709SChuck Lever 
433d9c407b1SChuck Lever /*
434d9c407b1SChuck Lever  * nfs_fh3
435d9c407b1SChuck Lever  *
436d9c407b1SChuck Lever  *	struct nfs_fh3 {
437d9c407b1SChuck Lever  *		opaque       data<NFS3_FHSIZE>;
438d9c407b1SChuck Lever  *	};
439d9c407b1SChuck Lever  */
440d9c407b1SChuck Lever static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
441d9c407b1SChuck Lever {
442d9c407b1SChuck Lever 	__be32 *p;
443d9c407b1SChuck Lever 
4447fc38846STrond Myklebust 	WARN_ON_ONCE(fh->size > NFS3_FHSIZE);
445d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + fh->size);
446d9c407b1SChuck Lever 	xdr_encode_opaque(p, fh->data, fh->size);
447d9c407b1SChuck Lever }
448d9c407b1SChuck Lever 
449e4f93234SChuck Lever static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
450e4f93234SChuck Lever {
451e4f93234SChuck Lever 	u32 length;
452e4f93234SChuck Lever 	__be32 *p;
453e4f93234SChuck Lever 
454e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
455e4f93234SChuck Lever 	if (unlikely(p == NULL))
456e4f93234SChuck Lever 		goto out_overflow;
457e4f93234SChuck Lever 	length = be32_to_cpup(p++);
458e4f93234SChuck Lever 	if (unlikely(length > NFS3_FHSIZE))
459e4f93234SChuck Lever 		goto out_toobig;
460e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, length);
461e4f93234SChuck Lever 	if (unlikely(p == NULL))
462e4f93234SChuck Lever 		goto out_overflow;
463e4f93234SChuck Lever 	fh->size = length;
464e4f93234SChuck Lever 	memcpy(fh->data, p, length);
465e4f93234SChuck Lever 	return 0;
466e4f93234SChuck Lever out_toobig:
467e4f93234SChuck Lever 	dprintk("NFS: file handle size (%u) too big\n", length);
468e4f93234SChuck Lever 	return -E2BIG;
469e4f93234SChuck Lever out_overflow:
470e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
471e4f93234SChuck Lever 	return -EIO;
472e4f93234SChuck Lever }
473e4f93234SChuck Lever 
474e4f93234SChuck Lever static void zero_nfs_fh3(struct nfs_fh *fh)
475e4f93234SChuck Lever {
476e4f93234SChuck Lever 	memset(fh, 0, sizeof(*fh));
477e4f93234SChuck Lever }
478e4f93234SChuck Lever 
479d9c407b1SChuck Lever /*
4809d5a6434SChuck Lever  * nfstime3
4819d5a6434SChuck Lever  *
4829d5a6434SChuck Lever  *	struct nfstime3 {
4839d5a6434SChuck Lever  *		uint32	seconds;
4849d5a6434SChuck Lever  *		uint32	nseconds;
4859d5a6434SChuck Lever  *	};
4869d5a6434SChuck Lever  */
4879d5a6434SChuck Lever static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec *timep)
4889d5a6434SChuck Lever {
4899d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
4909d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_nsec);
4919d5a6434SChuck Lever 	return p;
4929d5a6434SChuck Lever }
4939d5a6434SChuck Lever 
494f6048709SChuck Lever static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec *timep)
495f6048709SChuck Lever {
496f6048709SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
497f6048709SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++);
498f6048709SChuck Lever 	return p;
499f6048709SChuck Lever }
500f6048709SChuck Lever 
5019d5a6434SChuck Lever /*
502d9c407b1SChuck Lever  * sattr3
503d9c407b1SChuck Lever  *
504d9c407b1SChuck Lever  *	enum time_how {
505d9c407b1SChuck Lever  *		DONT_CHANGE		= 0,
506d9c407b1SChuck Lever  *		SET_TO_SERVER_TIME	= 1,
507d9c407b1SChuck Lever  *		SET_TO_CLIENT_TIME	= 2
508d9c407b1SChuck Lever  *	};
509d9c407b1SChuck Lever  *
510d9c407b1SChuck Lever  *	union set_mode3 switch (bool set_it) {
511d9c407b1SChuck Lever  *	case TRUE:
512d9c407b1SChuck Lever  *		mode3	mode;
513d9c407b1SChuck Lever  *	default:
514d9c407b1SChuck Lever  *		void;
515d9c407b1SChuck Lever  *	};
516d9c407b1SChuck Lever  *
517d9c407b1SChuck Lever  *	union set_uid3 switch (bool set_it) {
518d9c407b1SChuck Lever  *	case TRUE:
519d9c407b1SChuck Lever  *		uid3	uid;
520d9c407b1SChuck Lever  *	default:
521d9c407b1SChuck Lever  *		void;
522d9c407b1SChuck Lever  *	};
523d9c407b1SChuck Lever  *
524d9c407b1SChuck Lever  *	union set_gid3 switch (bool set_it) {
525d9c407b1SChuck Lever  *	case TRUE:
526d9c407b1SChuck Lever  *		gid3	gid;
527d9c407b1SChuck Lever  *	default:
528d9c407b1SChuck Lever  *		void;
529d9c407b1SChuck Lever  *	};
530d9c407b1SChuck Lever  *
531d9c407b1SChuck Lever  *	union set_size3 switch (bool set_it) {
532d9c407b1SChuck Lever  *	case TRUE:
533d9c407b1SChuck Lever  *		size3	size;
534d9c407b1SChuck Lever  *	default:
535d9c407b1SChuck Lever  *		void;
536d9c407b1SChuck Lever  *	};
537d9c407b1SChuck Lever  *
538d9c407b1SChuck Lever  *	union set_atime switch (time_how set_it) {
539d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
540d9c407b1SChuck Lever  *		nfstime3	atime;
541d9c407b1SChuck Lever  *	default:
542d9c407b1SChuck Lever  *		void;
543d9c407b1SChuck Lever  *	};
544d9c407b1SChuck Lever  *
545d9c407b1SChuck Lever  *	union set_mtime switch (time_how set_it) {
546d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
547d9c407b1SChuck Lever  *		nfstime3  mtime;
548d9c407b1SChuck Lever  *	default:
549d9c407b1SChuck Lever  *		void;
550d9c407b1SChuck Lever  *	};
551d9c407b1SChuck Lever  *
552d9c407b1SChuck Lever  *	struct sattr3 {
553d9c407b1SChuck Lever  *		set_mode3	mode;
554d9c407b1SChuck Lever  *		set_uid3	uid;
555d9c407b1SChuck Lever  *		set_gid3	gid;
556d9c407b1SChuck Lever  *		set_size3	size;
557d9c407b1SChuck Lever  *		set_atime	atime;
558d9c407b1SChuck Lever  *		set_mtime	mtime;
559d9c407b1SChuck Lever  *	};
560d9c407b1SChuck Lever  */
561d9c407b1SChuck Lever static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
562d9c407b1SChuck Lever {
563d9c407b1SChuck Lever 	u32 nbytes;
564d9c407b1SChuck Lever 	__be32 *p;
565d9c407b1SChuck Lever 
566d9c407b1SChuck Lever 	/*
567d9c407b1SChuck Lever 	 * In order to make only a single xdr_reserve_space() call,
568d9c407b1SChuck Lever 	 * pre-compute the total number of bytes to be reserved.
569d9c407b1SChuck Lever 	 * Six boolean values, one for each set_foo field, are always
570d9c407b1SChuck Lever 	 * present in the encoded result, so start there.
571d9c407b1SChuck Lever 	 */
572d9c407b1SChuck Lever 	nbytes = 6 * 4;
573d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MODE)
574d9c407b1SChuck Lever 		nbytes += 4;
575d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_UID)
576d9c407b1SChuck Lever 		nbytes += 4;
577d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_GID)
578d9c407b1SChuck Lever 		nbytes += 4;
579d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
580d9c407b1SChuck Lever 		nbytes += 8;
581d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
582d9c407b1SChuck Lever 		nbytes += 8;
583d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
584d9c407b1SChuck Lever 		nbytes += 8;
585d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, nbytes);
586d9c407b1SChuck Lever 
5879d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MODE) {
5889d5a6434SChuck Lever 		*p++ = xdr_one;
5899d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
5909d5a6434SChuck Lever 	} else
5919d5a6434SChuck Lever 		*p++ = xdr_zero;
5929d5a6434SChuck Lever 
5939d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_UID) {
5949d5a6434SChuck Lever 		*p++ = xdr_one;
59557a38daeSEric W. Biederman 		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid));
5969d5a6434SChuck Lever 	} else
5979d5a6434SChuck Lever 		*p++ = xdr_zero;
5989d5a6434SChuck Lever 
5999d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_GID) {
6009d5a6434SChuck Lever 		*p++ = xdr_one;
60157a38daeSEric W. Biederman 		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid));
6029d5a6434SChuck Lever 	} else
6039d5a6434SChuck Lever 		*p++ = xdr_zero;
6049d5a6434SChuck Lever 
6059d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_SIZE) {
6069d5a6434SChuck Lever 		*p++ = xdr_one;
6079d5a6434SChuck Lever 		p = xdr_encode_hyper(p, (u64)attr->ia_size);
6089d5a6434SChuck Lever 	} else
6099d5a6434SChuck Lever 		*p++ = xdr_zero;
6109d5a6434SChuck Lever 
6119d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET) {
6129d5a6434SChuck Lever 		*p++ = xdr_two;
6139d5a6434SChuck Lever 		p = xdr_encode_nfstime3(p, &attr->ia_atime);
6149d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_ATIME) {
6159d5a6434SChuck Lever 		*p++ = xdr_one;
6169d5a6434SChuck Lever 	} else
6179d5a6434SChuck Lever 		*p++ = xdr_zero;
6189d5a6434SChuck Lever 
6199d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET) {
6209d5a6434SChuck Lever 		*p++ = xdr_two;
6219d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &attr->ia_mtime);
6229d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_MTIME) {
6239d5a6434SChuck Lever 		*p = xdr_one;
6249d5a6434SChuck Lever 	} else
6259d5a6434SChuck Lever 		*p = xdr_zero;
626d9c407b1SChuck Lever }
627d9c407b1SChuck Lever 
628d9c407b1SChuck Lever /*
629e4f93234SChuck Lever  * fattr3
630e4f93234SChuck Lever  *
631e4f93234SChuck Lever  *	struct fattr3 {
632e4f93234SChuck Lever  *		ftype3		type;
633e4f93234SChuck Lever  *		mode3		mode;
634e4f93234SChuck Lever  *		uint32		nlink;
635e4f93234SChuck Lever  *		uid3		uid;
636e4f93234SChuck Lever  *		gid3		gid;
637e4f93234SChuck Lever  *		size3		size;
638e4f93234SChuck Lever  *		size3		used;
639e4f93234SChuck Lever  *		specdata3	rdev;
640e4f93234SChuck Lever  *		uint64		fsid;
641e4f93234SChuck Lever  *		fileid3		fileid;
642e4f93234SChuck Lever  *		nfstime3	atime;
643e4f93234SChuck Lever  *		nfstime3	mtime;
644e4f93234SChuck Lever  *		nfstime3	ctime;
645e4f93234SChuck Lever  *	};
646e4f93234SChuck Lever  */
647e4f93234SChuck Lever static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
648e4f93234SChuck Lever {
649f6048709SChuck Lever 	umode_t fmode;
650e4f93234SChuck Lever 	__be32 *p;
651e4f93234SChuck Lever 
652e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2);
653e4f93234SChuck Lever 	if (unlikely(p == NULL))
654e4f93234SChuck Lever 		goto out_overflow;
655f6048709SChuck Lever 
656f6048709SChuck Lever 	p = xdr_decode_ftype3(p, &fmode);
657f6048709SChuck Lever 
658f6048709SChuck Lever 	fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;
659f6048709SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
66057a38daeSEric W. Biederman 	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++));
66157a38daeSEric W. Biederman 	if (!uid_valid(fattr->uid))
66257a38daeSEric W. Biederman 		goto out_uid;
66357a38daeSEric W. Biederman 	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++));
66457a38daeSEric W. Biederman 	if (!gid_valid(fattr->gid))
66557a38daeSEric W. Biederman 		goto out_gid;
666f6048709SChuck Lever 
667f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->size);
668f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->du.nfs3.used);
669f6048709SChuck Lever 	p = xdr_decode_specdata3(p, &fattr->rdev);
670f6048709SChuck Lever 
671f6048709SChuck Lever 	p = xdr_decode_hyper(p, &fattr->fsid.major);
672f6048709SChuck Lever 	fattr->fsid.minor = 0;
673f6048709SChuck Lever 
674f6048709SChuck Lever 	p = xdr_decode_fileid3(p, &fattr->fileid);
675f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->atime);
676f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->mtime);
677f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->ctime);
6783a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
679f6048709SChuck Lever 
680f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V3;
681e4f93234SChuck Lever 	return 0;
68257a38daeSEric W. Biederman out_uid:
68357a38daeSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
68457a38daeSEric W. Biederman 	return -EINVAL;
68557a38daeSEric W. Biederman out_gid:
68657a38daeSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
68757a38daeSEric W. Biederman 	return -EINVAL;
688e4f93234SChuck Lever out_overflow:
689e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
690e4f93234SChuck Lever 	return -EIO;
691e4f93234SChuck Lever }
692e4f93234SChuck Lever 
693e4f93234SChuck Lever /*
694e4f93234SChuck Lever  * post_op_attr
695e4f93234SChuck Lever  *
696e4f93234SChuck Lever  *	union post_op_attr switch (bool attributes_follow) {
697e4f93234SChuck Lever  *	case TRUE:
698e4f93234SChuck Lever  *		fattr3	attributes;
699e4f93234SChuck Lever  *	case FALSE:
700e4f93234SChuck Lever  *		void;
701e4f93234SChuck Lever  *	};
702e4f93234SChuck Lever  */
703e4f93234SChuck Lever static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
704e4f93234SChuck Lever {
705e4f93234SChuck Lever 	__be32 *p;
706e4f93234SChuck Lever 
707e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
708e4f93234SChuck Lever 	if (unlikely(p == NULL))
709e4f93234SChuck Lever 		goto out_overflow;
710e4f93234SChuck Lever 	if (*p != xdr_zero)
711e4f93234SChuck Lever 		return decode_fattr3(xdr, fattr);
712e4f93234SChuck Lever 	return 0;
713e4f93234SChuck Lever out_overflow:
714e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
715e4f93234SChuck Lever 	return -EIO;
716e4f93234SChuck Lever }
717e4f93234SChuck Lever 
718e4f93234SChuck Lever /*
719e4f93234SChuck Lever  * wcc_attr
720e4f93234SChuck Lever  *	struct wcc_attr {
721e4f93234SChuck Lever  *		size3		size;
722e4f93234SChuck Lever  *		nfstime3	mtime;
723e4f93234SChuck Lever  *		nfstime3	ctime;
724e4f93234SChuck Lever  *	};
725e4f93234SChuck Lever  */
726e4f93234SChuck Lever static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
727e4f93234SChuck Lever {
728e4f93234SChuck Lever 	__be32 *p;
729e4f93234SChuck Lever 
730e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2);
731e4f93234SChuck Lever 	if (unlikely(p == NULL))
732e4f93234SChuck Lever 		goto out_overflow;
733f6048709SChuck Lever 
734f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_PRESIZE
7353a1556e8STrond Myklebust 		| NFS_ATTR_FATTR_PRECHANGE
736f6048709SChuck Lever 		| NFS_ATTR_FATTR_PREMTIME
737f6048709SChuck Lever 		| NFS_ATTR_FATTR_PRECTIME;
738f6048709SChuck Lever 
739f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->pre_size);
740f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
741f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->pre_ctime);
7423a1556e8STrond Myklebust 	fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime);
743f6048709SChuck Lever 
744e4f93234SChuck Lever 	return 0;
745e4f93234SChuck Lever out_overflow:
746e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
747e4f93234SChuck Lever 	return -EIO;
748e4f93234SChuck Lever }
749e4f93234SChuck Lever 
750e4f93234SChuck Lever /*
751e4f93234SChuck Lever  * pre_op_attr
752e4f93234SChuck Lever  *	union pre_op_attr switch (bool attributes_follow) {
753e4f93234SChuck Lever  *	case TRUE:
754e4f93234SChuck Lever  *		wcc_attr	attributes;
755e4f93234SChuck Lever  *	case FALSE:
756e4f93234SChuck Lever  *		void;
757e4f93234SChuck Lever  *	};
758e4f93234SChuck Lever  *
759e4f93234SChuck Lever  * wcc_data
760e4f93234SChuck Lever  *
761e4f93234SChuck Lever  *	struct wcc_data {
762e4f93234SChuck Lever  *		pre_op_attr	before;
763e4f93234SChuck Lever  *		post_op_attr	after;
764e4f93234SChuck Lever  *	};
765e4f93234SChuck Lever  */
766e4f93234SChuck Lever static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
767e4f93234SChuck Lever {
768e4f93234SChuck Lever 	__be32 *p;
769e4f93234SChuck Lever 
770e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
771e4f93234SChuck Lever 	if (unlikely(p == NULL))
772e4f93234SChuck Lever 		goto out_overflow;
773e4f93234SChuck Lever 	if (*p != xdr_zero)
774e4f93234SChuck Lever 		return decode_wcc_attr(xdr, fattr);
775e4f93234SChuck Lever 	return 0;
776e4f93234SChuck Lever out_overflow:
777e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
778e4f93234SChuck Lever 	return -EIO;
779e4f93234SChuck Lever }
780e4f93234SChuck Lever 
781e4f93234SChuck Lever static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr)
782e4f93234SChuck Lever {
783e4f93234SChuck Lever 	int error;
784e4f93234SChuck Lever 
785e4f93234SChuck Lever 	error = decode_pre_op_attr(xdr, fattr);
786e4f93234SChuck Lever 	if (unlikely(error))
787e4f93234SChuck Lever 		goto out;
788e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, fattr);
789e4f93234SChuck Lever out:
790e4f93234SChuck Lever 	return error;
791e4f93234SChuck Lever }
792e4f93234SChuck Lever 
793e4f93234SChuck Lever /*
794e4f93234SChuck Lever  * post_op_fh3
795e4f93234SChuck Lever  *
796e4f93234SChuck Lever  *	union post_op_fh3 switch (bool handle_follows) {
797e4f93234SChuck Lever  *	case TRUE:
798e4f93234SChuck Lever  *		nfs_fh3  handle;
799e4f93234SChuck Lever  *	case FALSE:
800e4f93234SChuck Lever  *		void;
801e4f93234SChuck Lever  *	};
802e4f93234SChuck Lever  */
803e4f93234SChuck Lever static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
804e4f93234SChuck Lever {
805e4f93234SChuck Lever 	__be32 *p = xdr_inline_decode(xdr, 4);
806e4f93234SChuck Lever 	if (unlikely(p == NULL))
807e4f93234SChuck Lever 		goto out_overflow;
808e4f93234SChuck Lever 	if (*p != xdr_zero)
809e4f93234SChuck Lever 		return decode_nfs_fh3(xdr, fh);
810e4f93234SChuck Lever 	zero_nfs_fh3(fh);
811e4f93234SChuck Lever 	return 0;
812e4f93234SChuck Lever out_overflow:
813e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
814e4f93234SChuck Lever 	return -EIO;
815e4f93234SChuck Lever }
816e4f93234SChuck Lever 
817e4f93234SChuck Lever /*
818d9c407b1SChuck Lever  * diropargs3
819d9c407b1SChuck Lever  *
820d9c407b1SChuck Lever  *	struct diropargs3 {
821d9c407b1SChuck Lever  *		nfs_fh3		dir;
822d9c407b1SChuck Lever  *		filename3	name;
823d9c407b1SChuck Lever  *	};
824d9c407b1SChuck Lever  */
825d9c407b1SChuck Lever static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
826d9c407b1SChuck Lever 			      const char *name, u32 length)
827d9c407b1SChuck Lever {
828d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, fh);
829d9c407b1SChuck Lever 	encode_filename3(xdr, name, length);
830d9c407b1SChuck Lever }
831d9c407b1SChuck Lever 
832d9c407b1SChuck Lever 
8331da177e4SLinus Torvalds /*
834499ff710SChuck Lever  * NFSv3 XDR encode functions
835499ff710SChuck Lever  *
836499ff710SChuck Lever  * NFSv3 argument types are defined in section 3.3 of RFC 1813:
837499ff710SChuck Lever  * "NFS Version 3 Protocol Specification".
8381da177e4SLinus Torvalds  */
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds /*
841d9c407b1SChuck Lever  * 3.3.1  GETATTR3args
842d9c407b1SChuck Lever  *
843d9c407b1SChuck Lever  *	struct GETATTR3args {
844d9c407b1SChuck Lever  *		nfs_fh3  object;
845d9c407b1SChuck Lever  *	};
846d9c407b1SChuck Lever  */
8479f06c719SChuck Lever static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
8489f06c719SChuck Lever 				      struct xdr_stream *xdr,
849fcc85819SChristoph Hellwig 				      const void *data)
850d9c407b1SChuck Lever {
851fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
852fcc85819SChristoph Hellwig 
8539f06c719SChuck Lever 	encode_nfs_fh3(xdr, fh);
854d9c407b1SChuck Lever }
855d9c407b1SChuck Lever 
856d9c407b1SChuck Lever /*
857d9c407b1SChuck Lever  * 3.3.2  SETATTR3args
858d9c407b1SChuck Lever  *
859d9c407b1SChuck Lever  *	union sattrguard3 switch (bool check) {
860d9c407b1SChuck Lever  *	case TRUE:
861d9c407b1SChuck Lever  *		nfstime3  obj_ctime;
862d9c407b1SChuck Lever  *	case FALSE:
863d9c407b1SChuck Lever  *		void;
864d9c407b1SChuck Lever  *	};
865d9c407b1SChuck Lever  *
866d9c407b1SChuck Lever  *	struct SETATTR3args {
867d9c407b1SChuck Lever  *		nfs_fh3		object;
868d9c407b1SChuck Lever  *		sattr3		new_attributes;
869d9c407b1SChuck Lever  *		sattrguard3	guard;
870d9c407b1SChuck Lever  *	};
871d9c407b1SChuck Lever  */
872d9c407b1SChuck Lever static void encode_sattrguard3(struct xdr_stream *xdr,
873d9c407b1SChuck Lever 			       const struct nfs3_sattrargs *args)
874d9c407b1SChuck Lever {
875d9c407b1SChuck Lever 	__be32 *p;
876d9c407b1SChuck Lever 
877d9c407b1SChuck Lever 	if (args->guard) {
878d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4 + 8);
879d9c407b1SChuck Lever 		*p++ = xdr_one;
8809d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &args->guardtime);
881d9c407b1SChuck Lever 	} else {
882d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4);
883d9c407b1SChuck Lever 		*p = xdr_zero;
884d9c407b1SChuck Lever 	}
885d9c407b1SChuck Lever }
886d9c407b1SChuck Lever 
8879f06c719SChuck Lever static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
8889f06c719SChuck Lever 				      struct xdr_stream *xdr,
889fcc85819SChristoph Hellwig 				      const void *data)
890d9c407b1SChuck Lever {
891fcc85819SChristoph Hellwig 	const struct nfs3_sattrargs *args = data;
8929f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
8939f06c719SChuck Lever 	encode_sattr3(xdr, args->sattr);
8949f06c719SChuck Lever 	encode_sattrguard3(xdr, args);
895d9c407b1SChuck Lever }
896d9c407b1SChuck Lever 
897d9c407b1SChuck Lever /*
898d9c407b1SChuck Lever  * 3.3.3  LOOKUP3args
899d9c407b1SChuck Lever  *
900d9c407b1SChuck Lever  *	struct LOOKUP3args {
901d9c407b1SChuck Lever  *		diropargs3  what;
902d9c407b1SChuck Lever  *	};
903d9c407b1SChuck Lever  */
9049f06c719SChuck Lever static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
9059f06c719SChuck Lever 				     struct xdr_stream *xdr,
906fcc85819SChristoph Hellwig 				     const void *data)
907d9c407b1SChuck Lever {
908fcc85819SChristoph Hellwig 	const struct nfs3_diropargs *args = data;
909fcc85819SChristoph Hellwig 
9109f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
911d9c407b1SChuck Lever }
912d9c407b1SChuck Lever 
913d9c407b1SChuck Lever /*
914d9c407b1SChuck Lever  * 3.3.4  ACCESS3args
915d9c407b1SChuck Lever  *
916d9c407b1SChuck Lever  *	struct ACCESS3args {
917d9c407b1SChuck Lever  *		nfs_fh3		object;
918d9c407b1SChuck Lever  *		uint32		access;
919d9c407b1SChuck Lever  *	};
920d9c407b1SChuck Lever  */
921d9c407b1SChuck Lever static void encode_access3args(struct xdr_stream *xdr,
922d9c407b1SChuck Lever 			       const struct nfs3_accessargs *args)
923d9c407b1SChuck Lever {
924d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
925d9c407b1SChuck Lever 	encode_uint32(xdr, args->access);
926d9c407b1SChuck Lever }
927d9c407b1SChuck Lever 
9289f06c719SChuck Lever static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
9299f06c719SChuck Lever 				     struct xdr_stream *xdr,
930fcc85819SChristoph Hellwig 				     const void *data)
931d9c407b1SChuck Lever {
932fcc85819SChristoph Hellwig 	const struct nfs3_accessargs *args = data;
933fcc85819SChristoph Hellwig 
9349f06c719SChuck Lever 	encode_access3args(xdr, args);
935d9c407b1SChuck Lever }
936d9c407b1SChuck Lever 
937d9c407b1SChuck Lever /*
938d9c407b1SChuck Lever  * 3.3.5  READLINK3args
939d9c407b1SChuck Lever  *
940d9c407b1SChuck Lever  *	struct READLINK3args {
941d9c407b1SChuck Lever  *		nfs_fh3	symlink;
942d9c407b1SChuck Lever  *	};
943d9c407b1SChuck Lever  */
9449f06c719SChuck Lever static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
9459f06c719SChuck Lever 				       struct xdr_stream *xdr,
946fcc85819SChristoph Hellwig 				       const void *data)
947d9c407b1SChuck Lever {
948fcc85819SChristoph Hellwig 	const struct nfs3_readlinkargs *args = data;
949fcc85819SChristoph Hellwig 
9509f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
951d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
952d9c407b1SChuck Lever 					args->pglen, NFS3_readlinkres_sz);
953d9c407b1SChuck Lever }
954d9c407b1SChuck Lever 
955d9c407b1SChuck Lever /*
956d9c407b1SChuck Lever  * 3.3.6  READ3args
957d9c407b1SChuck Lever  *
958d9c407b1SChuck Lever  *	struct READ3args {
959d9c407b1SChuck Lever  *		nfs_fh3		file;
960d9c407b1SChuck Lever  *		offset3		offset;
961d9c407b1SChuck Lever  *		count3		count;
962d9c407b1SChuck Lever  *	};
963d9c407b1SChuck Lever  */
964d9c407b1SChuck Lever static void encode_read3args(struct xdr_stream *xdr,
9653c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
966d9c407b1SChuck Lever {
967d9c407b1SChuck Lever 	__be32 *p;
968d9c407b1SChuck Lever 
969d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
970d9c407b1SChuck Lever 
971d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
972d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
973d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
974d9c407b1SChuck Lever }
975d9c407b1SChuck Lever 
9769f06c719SChuck Lever static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
9779f06c719SChuck Lever 				   struct xdr_stream *xdr,
978fcc85819SChristoph Hellwig 				   const void *data)
979d9c407b1SChuck Lever {
980fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
981fcc85819SChristoph Hellwig 
9829f06c719SChuck Lever 	encode_read3args(xdr, args);
983d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
984d9c407b1SChuck Lever 					args->count, NFS3_readres_sz);
985d9c407b1SChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
986d9c407b1SChuck Lever }
987d9c407b1SChuck Lever 
988d9c407b1SChuck Lever /*
989d9c407b1SChuck Lever  * 3.3.7  WRITE3args
990d9c407b1SChuck Lever  *
991d9c407b1SChuck Lever  *	enum stable_how {
992d9c407b1SChuck Lever  *		UNSTABLE  = 0,
993d9c407b1SChuck Lever  *		DATA_SYNC = 1,
994d9c407b1SChuck Lever  *		FILE_SYNC = 2
995d9c407b1SChuck Lever  *	};
996d9c407b1SChuck Lever  *
997d9c407b1SChuck Lever  *	struct WRITE3args {
998d9c407b1SChuck Lever  *		nfs_fh3		file;
999d9c407b1SChuck Lever  *		offset3		offset;
1000d9c407b1SChuck Lever  *		count3		count;
1001d9c407b1SChuck Lever  *		stable_how	stable;
1002d9c407b1SChuck Lever  *		opaque		data<>;
1003d9c407b1SChuck Lever  *	};
1004d9c407b1SChuck Lever  */
1005d9c407b1SChuck Lever static void encode_write3args(struct xdr_stream *xdr,
10063c6b899cSAnna Schumaker 			      const struct nfs_pgio_args *args)
1007d9c407b1SChuck Lever {
1008d9c407b1SChuck Lever 	__be32 *p;
1009d9c407b1SChuck Lever 
1010d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1011d9c407b1SChuck Lever 
1012d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
1013d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1014d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count);
1015d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->stable);
1016d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1017d9c407b1SChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
1018d9c407b1SChuck Lever }
1019d9c407b1SChuck Lever 
10209f06c719SChuck Lever static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
10219f06c719SChuck Lever 				    struct xdr_stream *xdr,
1022fcc85819SChristoph Hellwig 				    const void *data)
1023d9c407b1SChuck Lever {
1024fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
1025fcc85819SChristoph Hellwig 
10269f06c719SChuck Lever 	encode_write3args(xdr, args);
10279f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
1028d9c407b1SChuck Lever }
1029d9c407b1SChuck Lever 
1030d9c407b1SChuck Lever /*
1031d9c407b1SChuck Lever  * 3.3.8  CREATE3args
1032d9c407b1SChuck Lever  *
1033d9c407b1SChuck Lever  *	enum createmode3 {
1034d9c407b1SChuck Lever  *		UNCHECKED = 0,
1035d9c407b1SChuck Lever  *		GUARDED   = 1,
1036d9c407b1SChuck Lever  *		EXCLUSIVE = 2
1037d9c407b1SChuck Lever  *	};
1038d9c407b1SChuck Lever  *
1039d9c407b1SChuck Lever  *	union createhow3 switch (createmode3 mode) {
1040d9c407b1SChuck Lever  *	case UNCHECKED:
1041d9c407b1SChuck Lever  *	case GUARDED:
1042d9c407b1SChuck Lever  *		sattr3       obj_attributes;
1043d9c407b1SChuck Lever  *	case EXCLUSIVE:
1044d9c407b1SChuck Lever  *		createverf3  verf;
1045d9c407b1SChuck Lever  *	};
1046d9c407b1SChuck Lever  *
1047d9c407b1SChuck Lever  *	struct CREATE3args {
1048d9c407b1SChuck Lever  *		diropargs3	where;
1049d9c407b1SChuck Lever  *		createhow3	how;
1050d9c407b1SChuck Lever  *	};
1051d9c407b1SChuck Lever  */
1052d9c407b1SChuck Lever static void encode_createhow3(struct xdr_stream *xdr,
1053d9c407b1SChuck Lever 			      const struct nfs3_createargs *args)
1054d9c407b1SChuck Lever {
1055d9c407b1SChuck Lever 	encode_uint32(xdr, args->createmode);
1056d9c407b1SChuck Lever 	switch (args->createmode) {
1057d9c407b1SChuck Lever 	case NFS3_CREATE_UNCHECKED:
1058d9c407b1SChuck Lever 	case NFS3_CREATE_GUARDED:
1059d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
1060d9c407b1SChuck Lever 		break;
1061d9c407b1SChuck Lever 	case NFS3_CREATE_EXCLUSIVE:
1062d9c407b1SChuck Lever 		encode_createverf3(xdr, args->verifier);
1063d9c407b1SChuck Lever 		break;
1064d9c407b1SChuck Lever 	default:
1065d9c407b1SChuck Lever 		BUG();
1066d9c407b1SChuck Lever 	}
1067d9c407b1SChuck Lever }
1068d9c407b1SChuck Lever 
10699f06c719SChuck Lever static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
10709f06c719SChuck Lever 				     struct xdr_stream *xdr,
1071fcc85819SChristoph Hellwig 				     const void *data)
1072d9c407b1SChuck Lever {
1073fcc85819SChristoph Hellwig 	const struct nfs3_createargs *args = data;
1074fcc85819SChristoph Hellwig 
10759f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
10769f06c719SChuck Lever 	encode_createhow3(xdr, args);
1077d9c407b1SChuck Lever }
1078d9c407b1SChuck Lever 
1079d9c407b1SChuck Lever /*
1080d9c407b1SChuck Lever  * 3.3.9  MKDIR3args
1081d9c407b1SChuck Lever  *
1082d9c407b1SChuck Lever  *	struct MKDIR3args {
1083d9c407b1SChuck Lever  *		diropargs3	where;
1084d9c407b1SChuck Lever  *		sattr3		attributes;
1085d9c407b1SChuck Lever  *	};
1086d9c407b1SChuck Lever  */
10879f06c719SChuck Lever static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
10889f06c719SChuck Lever 				    struct xdr_stream *xdr,
1089fcc85819SChristoph Hellwig 				    const void *data)
1090d9c407b1SChuck Lever {
1091fcc85819SChristoph Hellwig 	const struct nfs3_mkdirargs *args = data;
1092fcc85819SChristoph Hellwig 
10939f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
10949f06c719SChuck Lever 	encode_sattr3(xdr, args->sattr);
1095d9c407b1SChuck Lever }
1096d9c407b1SChuck Lever 
1097d9c407b1SChuck Lever /*
1098d9c407b1SChuck Lever  * 3.3.10  SYMLINK3args
1099d9c407b1SChuck Lever  *
1100d9c407b1SChuck Lever  *	struct symlinkdata3 {
1101d9c407b1SChuck Lever  *		sattr3		symlink_attributes;
1102d9c407b1SChuck Lever  *		nfspath3	symlink_data;
1103d9c407b1SChuck Lever  *	};
1104d9c407b1SChuck Lever  *
1105d9c407b1SChuck Lever  *	struct SYMLINK3args {
1106d9c407b1SChuck Lever  *		diropargs3	where;
1107d9c407b1SChuck Lever  *		symlinkdata3	symlink;
1108d9c407b1SChuck Lever  *	};
1109d9c407b1SChuck Lever  */
1110d9c407b1SChuck Lever static void encode_symlinkdata3(struct xdr_stream *xdr,
1111fcc85819SChristoph Hellwig 				const void *data)
1112d9c407b1SChuck Lever {
1113fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1114fcc85819SChristoph Hellwig 
1115d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
1116d9c407b1SChuck Lever 	encode_nfspath3(xdr, args->pages, args->pathlen);
1117d9c407b1SChuck Lever }
1118d9c407b1SChuck Lever 
11199f06c719SChuck Lever static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
11209f06c719SChuck Lever 				      struct xdr_stream *xdr,
1121fcc85819SChristoph Hellwig 				      const void *data)
1122d9c407b1SChuck Lever {
1123fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1124fcc85819SChristoph Hellwig 
11259f06c719SChuck Lever 	encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
11269f06c719SChuck Lever 	encode_symlinkdata3(xdr, args);
11272fcc213aSChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
1128d9c407b1SChuck Lever }
1129d9c407b1SChuck Lever 
1130d9c407b1SChuck Lever /*
1131d9c407b1SChuck Lever  * 3.3.11  MKNOD3args
1132d9c407b1SChuck Lever  *
1133d9c407b1SChuck Lever  *	struct devicedata3 {
1134d9c407b1SChuck Lever  *		sattr3		dev_attributes;
1135d9c407b1SChuck Lever  *		specdata3	spec;
1136d9c407b1SChuck Lever  *	};
1137d9c407b1SChuck Lever  *
1138d9c407b1SChuck Lever  *	union mknoddata3 switch (ftype3 type) {
1139d9c407b1SChuck Lever  *	case NF3CHR:
1140d9c407b1SChuck Lever  *	case NF3BLK:
1141d9c407b1SChuck Lever  *		devicedata3	device;
1142d9c407b1SChuck Lever  *	case NF3SOCK:
1143d9c407b1SChuck Lever  *	case NF3FIFO:
1144d9c407b1SChuck Lever  *		sattr3		pipe_attributes;
1145d9c407b1SChuck Lever  *	default:
1146d9c407b1SChuck Lever  *		void;
1147d9c407b1SChuck Lever  *	};
1148d9c407b1SChuck Lever  *
1149d9c407b1SChuck Lever  *	struct MKNOD3args {
1150d9c407b1SChuck Lever  *		diropargs3	where;
1151d9c407b1SChuck Lever  *		mknoddata3	what;
1152d9c407b1SChuck Lever  *	};
1153d9c407b1SChuck Lever  */
1154d9c407b1SChuck Lever static void encode_devicedata3(struct xdr_stream *xdr,
1155d9c407b1SChuck Lever 			       const struct nfs3_mknodargs *args)
1156d9c407b1SChuck Lever {
1157d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
1158d9c407b1SChuck Lever 	encode_specdata3(xdr, args->rdev);
1159d9c407b1SChuck Lever }
1160d9c407b1SChuck Lever 
1161d9c407b1SChuck Lever static void encode_mknoddata3(struct xdr_stream *xdr,
1162d9c407b1SChuck Lever 			      const struct nfs3_mknodargs *args)
1163d9c407b1SChuck Lever {
1164d9c407b1SChuck Lever 	encode_ftype3(xdr, args->type);
1165d9c407b1SChuck Lever 	switch (args->type) {
1166d9c407b1SChuck Lever 	case NF3CHR:
1167d9c407b1SChuck Lever 	case NF3BLK:
1168d9c407b1SChuck Lever 		encode_devicedata3(xdr, args);
1169d9c407b1SChuck Lever 		break;
1170d9c407b1SChuck Lever 	case NF3SOCK:
1171d9c407b1SChuck Lever 	case NF3FIFO:
1172d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
1173d9c407b1SChuck Lever 		break;
1174d9c407b1SChuck Lever 	case NF3REG:
1175d9c407b1SChuck Lever 	case NF3DIR:
1176d9c407b1SChuck Lever 		break;
1177d9c407b1SChuck Lever 	default:
1178d9c407b1SChuck Lever 		BUG();
1179d9c407b1SChuck Lever 	}
1180d9c407b1SChuck Lever }
1181d9c407b1SChuck Lever 
11829f06c719SChuck Lever static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
11839f06c719SChuck Lever 				    struct xdr_stream *xdr,
1184fcc85819SChristoph Hellwig 				    const void *data)
1185d9c407b1SChuck Lever {
1186fcc85819SChristoph Hellwig 	const struct nfs3_mknodargs *args = data;
1187fcc85819SChristoph Hellwig 
11889f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
11899f06c719SChuck Lever 	encode_mknoddata3(xdr, args);
1190d9c407b1SChuck Lever }
1191d9c407b1SChuck Lever 
1192d9c407b1SChuck Lever /*
1193d9c407b1SChuck Lever  * 3.3.12  REMOVE3args
1194d9c407b1SChuck Lever  *
1195d9c407b1SChuck Lever  *	struct REMOVE3args {
1196d9c407b1SChuck Lever  *		diropargs3  object;
1197d9c407b1SChuck Lever  *	};
1198d9c407b1SChuck Lever  */
11999f06c719SChuck Lever static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
12009f06c719SChuck Lever 				     struct xdr_stream *xdr,
1201fcc85819SChristoph Hellwig 				     const void *data)
1202d9c407b1SChuck Lever {
1203fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
1204fcc85819SChristoph Hellwig 
12059f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
1206d9c407b1SChuck Lever }
1207d9c407b1SChuck Lever 
1208d9c407b1SChuck Lever /*
1209d9c407b1SChuck Lever  * 3.3.14  RENAME3args
1210d9c407b1SChuck Lever  *
1211d9c407b1SChuck Lever  *	struct RENAME3args {
1212d9c407b1SChuck Lever  *		diropargs3	from;
1213d9c407b1SChuck Lever  *		diropargs3	to;
1214d9c407b1SChuck Lever  *	};
1215d9c407b1SChuck Lever  */
12169f06c719SChuck Lever static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
12179f06c719SChuck Lever 				     struct xdr_stream *xdr,
1218fcc85819SChristoph Hellwig 				     const void *data)
1219d9c407b1SChuck Lever {
1220fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
1221d9c407b1SChuck Lever 	const struct qstr *old = args->old_name;
1222d9c407b1SChuck Lever 	const struct qstr *new = args->new_name;
1223d9c407b1SChuck Lever 
12249f06c719SChuck Lever 	encode_diropargs3(xdr, args->old_dir, old->name, old->len);
12259f06c719SChuck Lever 	encode_diropargs3(xdr, args->new_dir, new->name, new->len);
1226d9c407b1SChuck Lever }
1227d9c407b1SChuck Lever 
1228d9c407b1SChuck Lever /*
1229d9c407b1SChuck Lever  * 3.3.15  LINK3args
1230d9c407b1SChuck Lever  *
1231d9c407b1SChuck Lever  *	struct LINK3args {
1232d9c407b1SChuck Lever  *		nfs_fh3		file;
1233d9c407b1SChuck Lever  *		diropargs3	link;
1234d9c407b1SChuck Lever  *	};
1235d9c407b1SChuck Lever  */
12369f06c719SChuck Lever static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
12379f06c719SChuck Lever 				   struct xdr_stream *xdr,
1238fcc85819SChristoph Hellwig 				   const void *data)
1239d9c407b1SChuck Lever {
1240fcc85819SChristoph Hellwig 	const struct nfs3_linkargs *args = data;
1241fcc85819SChristoph Hellwig 
12429f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fromfh);
12439f06c719SChuck Lever 	encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
1244d9c407b1SChuck Lever }
1245d9c407b1SChuck Lever 
1246d9c407b1SChuck Lever /*
1247d9c407b1SChuck Lever  * 3.3.16  READDIR3args
1248d9c407b1SChuck Lever  *
1249d9c407b1SChuck Lever  *	struct READDIR3args {
1250d9c407b1SChuck Lever  *		nfs_fh3		dir;
1251d9c407b1SChuck Lever  *		cookie3		cookie;
1252d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1253d9c407b1SChuck Lever  *		count3		count;
1254d9c407b1SChuck Lever  *	};
1255d9c407b1SChuck Lever  */
1256d9c407b1SChuck Lever static void encode_readdir3args(struct xdr_stream *xdr,
1257d9c407b1SChuck Lever 				const struct nfs3_readdirargs *args)
1258d9c407b1SChuck Lever {
1259d9c407b1SChuck Lever 	__be32 *p;
1260d9c407b1SChuck Lever 
1261d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1262d9c407b1SChuck Lever 
1263d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
1264d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1265d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1266d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1267d9c407b1SChuck Lever }
1268d9c407b1SChuck Lever 
12699f06c719SChuck Lever static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
12709f06c719SChuck Lever 				      struct xdr_stream *xdr,
1271fcc85819SChristoph Hellwig 				      const void *data)
1272d9c407b1SChuck Lever {
1273fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1274fcc85819SChristoph Hellwig 
12759f06c719SChuck Lever 	encode_readdir3args(xdr, args);
1276d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
1277d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1278d9c407b1SChuck Lever }
1279d9c407b1SChuck Lever 
1280d9c407b1SChuck Lever /*
1281d9c407b1SChuck Lever  * 3.3.17  READDIRPLUS3args
1282d9c407b1SChuck Lever  *
1283d9c407b1SChuck Lever  *	struct READDIRPLUS3args {
1284d9c407b1SChuck Lever  *		nfs_fh3		dir;
1285d9c407b1SChuck Lever  *		cookie3		cookie;
1286d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1287d9c407b1SChuck Lever  *		count3		dircount;
1288d9c407b1SChuck Lever  *		count3		maxcount;
1289d9c407b1SChuck Lever  *	};
1290d9c407b1SChuck Lever  */
1291d9c407b1SChuck Lever static void encode_readdirplus3args(struct xdr_stream *xdr,
1292d9c407b1SChuck Lever 				    const struct nfs3_readdirargs *args)
1293d9c407b1SChuck Lever {
1294d9c407b1SChuck Lever 	__be32 *p;
1295d9c407b1SChuck Lever 
1296d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1297d9c407b1SChuck Lever 
1298d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
1299d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1300d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1301d9c407b1SChuck Lever 
1302d9c407b1SChuck Lever 	/*
1303d9c407b1SChuck Lever 	 * readdirplus: need dircount + buffer size.
1304d9c407b1SChuck Lever 	 * We just make sure we make dircount big enough
1305d9c407b1SChuck Lever 	 */
1306d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count >> 3);
1307d9c407b1SChuck Lever 
1308d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1309d9c407b1SChuck Lever }
1310d9c407b1SChuck Lever 
13119f06c719SChuck Lever static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
13129f06c719SChuck Lever 					  struct xdr_stream *xdr,
1313fcc85819SChristoph Hellwig 					  const void *data)
1314d9c407b1SChuck Lever {
1315fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1316fcc85819SChristoph Hellwig 
13179f06c719SChuck Lever 	encode_readdirplus3args(xdr, args);
1318d9c407b1SChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
1319d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1320d9c407b1SChuck Lever }
1321d9c407b1SChuck Lever 
1322d9c407b1SChuck Lever /*
1323d9c407b1SChuck Lever  * 3.3.21  COMMIT3args
1324d9c407b1SChuck Lever  *
1325d9c407b1SChuck Lever  *	struct COMMIT3args {
1326d9c407b1SChuck Lever  *		nfs_fh3		file;
1327d9c407b1SChuck Lever  *		offset3		offset;
1328d9c407b1SChuck Lever  *		count3		count;
1329d9c407b1SChuck Lever  *	};
1330d9c407b1SChuck Lever  */
1331d9c407b1SChuck Lever static void encode_commit3args(struct xdr_stream *xdr,
13320b7c0153SFred Isaman 			       const struct nfs_commitargs *args)
1333d9c407b1SChuck Lever {
1334d9c407b1SChuck Lever 	__be32 *p;
1335d9c407b1SChuck Lever 
1336d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1337d9c407b1SChuck Lever 
1338d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1339d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1340d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1341d9c407b1SChuck Lever }
1342d9c407b1SChuck Lever 
13439f06c719SChuck Lever static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
13449f06c719SChuck Lever 				     struct xdr_stream *xdr,
1345fcc85819SChristoph Hellwig 				     const void *data)
1346d9c407b1SChuck Lever {
1347fcc85819SChristoph Hellwig 	const struct nfs_commitargs *args = data;
1348fcc85819SChristoph Hellwig 
13499f06c719SChuck Lever 	encode_commit3args(xdr, args);
1350d9c407b1SChuck Lever }
1351d9c407b1SChuck Lever 
1352b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1353b7fa0554SAndreas Gruenbacher 
13549f06c719SChuck Lever static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
13559f06c719SChuck Lever 				     struct xdr_stream *xdr,
1356fcc85819SChristoph Hellwig 				     const void *data)
1357d9c407b1SChuck Lever {
1358fcc85819SChristoph Hellwig 	const struct nfs3_getaclargs *args = data;
1359fcc85819SChristoph Hellwig 
13609f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
13619f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1362d9c407b1SChuck Lever 	if (args->mask & (NFS_ACL | NFS_DFACL))
1363d9c407b1SChuck Lever 		prepare_reply_buffer(req, args->pages, 0,
1364d9c407b1SChuck Lever 					NFSACL_MAXPAGES << PAGE_SHIFT,
1365d9c407b1SChuck Lever 					ACL3_getaclres_sz);
1366d9c407b1SChuck Lever }
1367d9c407b1SChuck Lever 
13689f06c719SChuck Lever static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
13699f06c719SChuck Lever 				     struct xdr_stream *xdr,
1370fcc85819SChristoph Hellwig 				     const void *data)
1371d9c407b1SChuck Lever {
1372fcc85819SChristoph Hellwig 	const struct nfs3_setaclargs *args = data;
1373d9c407b1SChuck Lever 	unsigned int base;
1374d9c407b1SChuck Lever 	int error;
1375d9c407b1SChuck Lever 
13769f06c719SChuck Lever 	encode_nfs_fh3(xdr, NFS_FH(args->inode));
13779f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1378d9c407b1SChuck Lever 
1379d9c407b1SChuck Lever 	base = req->rq_slen;
1380ee5dc773SChuck Lever 	if (args->npages != 0)
1381ee5dc773SChuck Lever 		xdr_write_pages(xdr, args->pages, 0, args->len);
1382ee5dc773SChuck Lever 	else
1383d683cc49SChuck Lever 		xdr_reserve_space(xdr, args->len);
1384ee5dc773SChuck Lever 
13859f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base, args->inode,
1386d9c407b1SChuck Lever 			    (args->mask & NFS_ACL) ?
1387d9c407b1SChuck Lever 			    args->acl_access : NULL, 1, 0);
13887fc38846STrond Myklebust 	/* FIXME: this is just broken */
1389d9c407b1SChuck Lever 	BUG_ON(error < 0);
13909f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base + error, args->inode,
1391d9c407b1SChuck Lever 			    (args->mask & NFS_DFACL) ?
1392d9c407b1SChuck Lever 			    args->acl_default : NULL, 1,
1393d9c407b1SChuck Lever 			    NFS_ACL_DEFAULT);
1394d9c407b1SChuck Lever 	BUG_ON(error < 0);
1395d9c407b1SChuck Lever }
1396d9c407b1SChuck Lever 
1397b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1398b7fa0554SAndreas Gruenbacher 
13991da177e4SLinus Torvalds /*
1400b2cdd9c9SChuck Lever  * NFSv3 XDR decode functions
1401b2cdd9c9SChuck Lever  *
1402b2cdd9c9SChuck Lever  * NFSv3 result types are defined in section 3.3 of RFC 1813:
1403b2cdd9c9SChuck Lever  * "NFS Version 3 Protocol Specification".
14041da177e4SLinus Torvalds  */
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds /*
1407e4f93234SChuck Lever  * 3.3.1  GETATTR3res
1408e4f93234SChuck Lever  *
1409e4f93234SChuck Lever  *	struct GETATTR3resok {
1410e4f93234SChuck Lever  *		fattr3		obj_attributes;
1411e4f93234SChuck Lever  *	};
1412e4f93234SChuck Lever  *
1413e4f93234SChuck Lever  *	union GETATTR3res switch (nfsstat3 status) {
1414e4f93234SChuck Lever  *	case NFS3_OK:
1415e4f93234SChuck Lever  *		GETATTR3resok  resok;
1416e4f93234SChuck Lever  *	default:
1417e4f93234SChuck Lever  *		void;
1418e4f93234SChuck Lever  *	};
1419e4f93234SChuck Lever  */
1420bf269551SChuck Lever static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
1421bf269551SChuck Lever 				    struct xdr_stream *xdr,
1422fc016483SChristoph Hellwig 				    void *result)
1423e4f93234SChuck Lever {
1424e4f93234SChuck Lever 	enum nfs_stat status;
1425e4f93234SChuck Lever 	int error;
1426e4f93234SChuck Lever 
1427bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1428e4f93234SChuck Lever 	if (unlikely(error))
1429e4f93234SChuck Lever 		goto out;
1430e4f93234SChuck Lever 	if (status != NFS3_OK)
1431e4f93234SChuck Lever 		goto out_default;
1432bf269551SChuck Lever 	error = decode_fattr3(xdr, result);
1433e4f93234SChuck Lever out:
1434e4f93234SChuck Lever 	return error;
1435e4f93234SChuck Lever out_default:
14365e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1437e4f93234SChuck Lever }
1438e4f93234SChuck Lever 
1439e4f93234SChuck Lever /*
1440e4f93234SChuck Lever  * 3.3.2  SETATTR3res
1441e4f93234SChuck Lever  *
1442e4f93234SChuck Lever  *	struct SETATTR3resok {
1443e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1444e4f93234SChuck Lever  *	};
1445e4f93234SChuck Lever  *
1446e4f93234SChuck Lever  *	struct SETATTR3resfail {
1447e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1448e4f93234SChuck Lever  *	};
1449e4f93234SChuck Lever  *
1450e4f93234SChuck Lever  *	union SETATTR3res switch (nfsstat3 status) {
1451e4f93234SChuck Lever  *	case NFS3_OK:
1452e4f93234SChuck Lever  *		SETATTR3resok   resok;
1453e4f93234SChuck Lever  *	default:
1454e4f93234SChuck Lever  *		SETATTR3resfail resfail;
1455e4f93234SChuck Lever  *	};
1456e4f93234SChuck Lever  */
1457bf269551SChuck Lever static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
1458bf269551SChuck Lever 				    struct xdr_stream *xdr,
1459fc016483SChristoph Hellwig 				    void *result)
1460e4f93234SChuck Lever {
1461e4f93234SChuck Lever 	enum nfs_stat status;
1462e4f93234SChuck Lever 	int error;
1463e4f93234SChuck Lever 
1464bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1465e4f93234SChuck Lever 	if (unlikely(error))
1466e4f93234SChuck Lever 		goto out;
1467bf269551SChuck Lever 	error = decode_wcc_data(xdr, result);
1468e4f93234SChuck Lever 	if (unlikely(error))
1469e4f93234SChuck Lever 		goto out;
1470e4f93234SChuck Lever 	if (status != NFS3_OK)
1471e4f93234SChuck Lever 		goto out_status;
1472e4f93234SChuck Lever out:
1473e4f93234SChuck Lever 	return error;
1474e4f93234SChuck Lever out_status:
14755e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1476e4f93234SChuck Lever }
1477e4f93234SChuck Lever 
14781da177e4SLinus Torvalds /*
1479e4f93234SChuck Lever  * 3.3.3  LOOKUP3res
1480e4f93234SChuck Lever  *
1481e4f93234SChuck Lever  *	struct LOOKUP3resok {
1482e4f93234SChuck Lever  *		nfs_fh3		object;
1483e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1484e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1485e4f93234SChuck Lever  *	};
1486e4f93234SChuck Lever  *
1487e4f93234SChuck Lever  *	struct LOOKUP3resfail {
1488e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1489e4f93234SChuck Lever  *	};
1490e4f93234SChuck Lever  *
1491e4f93234SChuck Lever  *	union LOOKUP3res switch (nfsstat3 status) {
1492e4f93234SChuck Lever  *	case NFS3_OK:
1493e4f93234SChuck Lever  *		LOOKUP3resok	resok;
1494e4f93234SChuck Lever  *	default:
1495e4f93234SChuck Lever  *		LOOKUP3resfail	resfail;
1496e4f93234SChuck Lever  *	};
1497e4f93234SChuck Lever  */
1498bf269551SChuck Lever static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
1499bf269551SChuck Lever 				   struct xdr_stream *xdr,
1500fc016483SChristoph Hellwig 				   void *data)
1501e4f93234SChuck Lever {
1502fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1503e4f93234SChuck Lever 	enum nfs_stat status;
1504e4f93234SChuck Lever 	int error;
1505e4f93234SChuck Lever 
1506bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1507e4f93234SChuck Lever 	if (unlikely(error))
1508e4f93234SChuck Lever 		goto out;
1509e4f93234SChuck Lever 	if (status != NFS3_OK)
1510e4f93234SChuck Lever 		goto out_default;
1511bf269551SChuck Lever 	error = decode_nfs_fh3(xdr, result->fh);
1512e4f93234SChuck Lever 	if (unlikely(error))
1513e4f93234SChuck Lever 		goto out;
1514bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1515e4f93234SChuck Lever 	if (unlikely(error))
1516e4f93234SChuck Lever 		goto out;
1517bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
1518e4f93234SChuck Lever out:
1519e4f93234SChuck Lever 	return error;
1520e4f93234SChuck Lever out_default:
1521bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
1522e4f93234SChuck Lever 	if (unlikely(error))
1523e4f93234SChuck Lever 		goto out;
15245e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1525e4f93234SChuck Lever }
1526e4f93234SChuck Lever 
1527e4f93234SChuck Lever /*
1528e4f93234SChuck Lever  * 3.3.4  ACCESS3res
1529e4f93234SChuck Lever  *
1530e4f93234SChuck Lever  *	struct ACCESS3resok {
1531e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1532e4f93234SChuck Lever  *		uint32		access;
1533e4f93234SChuck Lever  *	};
1534e4f93234SChuck Lever  *
1535e4f93234SChuck Lever  *	struct ACCESS3resfail {
1536e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1537e4f93234SChuck Lever  *	};
1538e4f93234SChuck Lever  *
1539e4f93234SChuck Lever  *	union ACCESS3res switch (nfsstat3 status) {
1540e4f93234SChuck Lever  *	case NFS3_OK:
1541e4f93234SChuck Lever  *		ACCESS3resok	resok;
1542e4f93234SChuck Lever  *	default:
1543e4f93234SChuck Lever  *		ACCESS3resfail	resfail;
1544e4f93234SChuck Lever  *	};
1545e4f93234SChuck Lever  */
1546bf269551SChuck Lever static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
1547bf269551SChuck Lever 				   struct xdr_stream *xdr,
1548fc016483SChristoph Hellwig 				   void *data)
1549e4f93234SChuck Lever {
1550fc016483SChristoph Hellwig 	struct nfs3_accessres *result = data;
1551e4f93234SChuck Lever 	enum nfs_stat status;
1552e4f93234SChuck Lever 	int error;
1553e4f93234SChuck Lever 
1554bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1555e4f93234SChuck Lever 	if (unlikely(error))
1556e4f93234SChuck Lever 		goto out;
1557bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1558e4f93234SChuck Lever 	if (unlikely(error))
1559e4f93234SChuck Lever 		goto out;
1560e4f93234SChuck Lever 	if (status != NFS3_OK)
1561e4f93234SChuck Lever 		goto out_default;
1562bf269551SChuck Lever 	error = decode_uint32(xdr, &result->access);
1563e4f93234SChuck Lever out:
1564e4f93234SChuck Lever 	return error;
1565e4f93234SChuck Lever out_default:
15665e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1567e4f93234SChuck Lever }
1568e4f93234SChuck Lever 
1569e4f93234SChuck Lever /*
1570e4f93234SChuck Lever  * 3.3.5  READLINK3res
1571e4f93234SChuck Lever  *
1572e4f93234SChuck Lever  *	struct READLINK3resok {
1573e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1574e4f93234SChuck Lever  *		nfspath3	data;
1575e4f93234SChuck Lever  *	};
1576e4f93234SChuck Lever  *
1577e4f93234SChuck Lever  *	struct READLINK3resfail {
1578e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1579e4f93234SChuck Lever  *	};
1580e4f93234SChuck Lever  *
1581e4f93234SChuck Lever  *	union READLINK3res switch (nfsstat3 status) {
1582e4f93234SChuck Lever  *	case NFS3_OK:
1583e4f93234SChuck Lever  *		READLINK3resok	resok;
1584e4f93234SChuck Lever  *	default:
1585e4f93234SChuck Lever  *		READLINK3resfail resfail;
1586e4f93234SChuck Lever  *	};
1587e4f93234SChuck Lever  */
1588bf269551SChuck Lever static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
1589bf269551SChuck Lever 				     struct xdr_stream *xdr,
1590fc016483SChristoph Hellwig 				     void *result)
1591e4f93234SChuck Lever {
1592e4f93234SChuck Lever 	enum nfs_stat status;
1593e4f93234SChuck Lever 	int error;
1594e4f93234SChuck Lever 
1595bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1596e4f93234SChuck Lever 	if (unlikely(error))
1597e4f93234SChuck Lever 		goto out;
1598bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result);
1599e4f93234SChuck Lever 	if (unlikely(error))
1600e4f93234SChuck Lever 		goto out;
1601e4f93234SChuck Lever 	if (status != NFS3_OK)
1602e4f93234SChuck Lever 		goto out_default;
1603bf269551SChuck Lever 	error = decode_nfspath3(xdr);
1604e4f93234SChuck Lever out:
1605e4f93234SChuck Lever 	return error;
1606e4f93234SChuck Lever out_default:
16075e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1608e4f93234SChuck Lever }
1609e4f93234SChuck Lever 
1610e4f93234SChuck Lever /*
1611e4f93234SChuck Lever  * 3.3.6  READ3res
1612e4f93234SChuck Lever  *
1613e4f93234SChuck Lever  *	struct READ3resok {
1614e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1615e4f93234SChuck Lever  *		count3		count;
1616e4f93234SChuck Lever  *		bool		eof;
1617e4f93234SChuck Lever  *		opaque		data<>;
1618e4f93234SChuck Lever  *	};
1619e4f93234SChuck Lever  *
1620e4f93234SChuck Lever  *	struct READ3resfail {
1621e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1622e4f93234SChuck Lever  *	};
1623e4f93234SChuck Lever  *
1624e4f93234SChuck Lever  *	union READ3res switch (nfsstat3 status) {
1625e4f93234SChuck Lever  *	case NFS3_OK:
1626e4f93234SChuck Lever  *		READ3resok	resok;
1627e4f93234SChuck Lever  *	default:
1628e4f93234SChuck Lever  *		READ3resfail	resfail;
1629e4f93234SChuck Lever  *	};
1630e4f93234SChuck Lever  */
1631e4f93234SChuck Lever static int decode_read3resok(struct xdr_stream *xdr,
16329137bdf3SAnna Schumaker 			     struct nfs_pgio_res *result)
1633e4f93234SChuck Lever {
1634e4f93234SChuck Lever 	u32 eof, count, ocount, recvd;
1635e4f93234SChuck Lever 	__be32 *p;
1636e4f93234SChuck Lever 
1637e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 + 4 + 4);
1638e4f93234SChuck Lever 	if (unlikely(p == NULL))
1639e4f93234SChuck Lever 		goto out_overflow;
1640e4f93234SChuck Lever 	count = be32_to_cpup(p++);
1641e4f93234SChuck Lever 	eof = be32_to_cpup(p++);
1642e4f93234SChuck Lever 	ocount = be32_to_cpup(p++);
1643e4f93234SChuck Lever 	if (unlikely(ocount != count))
1644e4f93234SChuck Lever 		goto out_mismatch;
164564bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
1646e4f93234SChuck Lever 	if (unlikely(count > recvd))
1647e4f93234SChuck Lever 		goto out_cheating;
1648e4f93234SChuck Lever out:
1649e4f93234SChuck Lever 	result->eof = eof;
1650e4f93234SChuck Lever 	result->count = count;
1651e4f93234SChuck Lever 	return count;
1652e4f93234SChuck Lever out_mismatch:
1653e4f93234SChuck Lever 	dprintk("NFS: READ count doesn't match length of opaque: "
1654e4f93234SChuck Lever 		"count %u != ocount %u\n", count, ocount);
1655e4f93234SChuck Lever 	return -EIO;
1656e4f93234SChuck Lever out_cheating:
1657e4f93234SChuck Lever 	dprintk("NFS: server cheating in read result: "
1658e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
1659e4f93234SChuck Lever 	count = recvd;
1660e4f93234SChuck Lever 	eof = 0;
1661e4f93234SChuck Lever 	goto out;
1662e4f93234SChuck Lever out_overflow:
1663e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
1664e4f93234SChuck Lever 	return -EIO;
1665e4f93234SChuck Lever }
1666e4f93234SChuck Lever 
1667bf269551SChuck Lever static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1668fc016483SChristoph Hellwig 				 void *data)
1669e4f93234SChuck Lever {
1670fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
1671e4f93234SChuck Lever 	enum nfs_stat status;
1672e4f93234SChuck Lever 	int error;
1673e4f93234SChuck Lever 
1674bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1675e4f93234SChuck Lever 	if (unlikely(error))
1676e4f93234SChuck Lever 		goto out;
1677bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1678e4f93234SChuck Lever 	if (unlikely(error))
1679e4f93234SChuck Lever 		goto out;
1680aabff4ddSPeng Tao 	result->op_status = status;
1681e4f93234SChuck Lever 	if (status != NFS3_OK)
1682e4f93234SChuck Lever 		goto out_status;
1683bf269551SChuck Lever 	error = decode_read3resok(xdr, result);
1684e4f93234SChuck Lever out:
1685e4f93234SChuck Lever 	return error;
1686e4f93234SChuck Lever out_status:
16875e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1688e4f93234SChuck Lever }
1689e4f93234SChuck Lever 
1690e4f93234SChuck Lever /*
1691e4f93234SChuck Lever  * 3.3.7  WRITE3res
1692e4f93234SChuck Lever  *
1693e4f93234SChuck Lever  *	enum stable_how {
1694e4f93234SChuck Lever  *		UNSTABLE  = 0,
1695e4f93234SChuck Lever  *		DATA_SYNC = 1,
1696e4f93234SChuck Lever  *		FILE_SYNC = 2
1697e4f93234SChuck Lever  *	};
1698e4f93234SChuck Lever  *
1699e4f93234SChuck Lever  *	struct WRITE3resok {
1700e4f93234SChuck Lever  *		wcc_data	file_wcc;
1701e4f93234SChuck Lever  *		count3		count;
1702e4f93234SChuck Lever  *		stable_how	committed;
1703e4f93234SChuck Lever  *		writeverf3	verf;
1704e4f93234SChuck Lever  *	};
1705e4f93234SChuck Lever  *
1706e4f93234SChuck Lever  *	struct WRITE3resfail {
1707e4f93234SChuck Lever  *		wcc_data	file_wcc;
1708e4f93234SChuck Lever  *	};
1709e4f93234SChuck Lever  *
1710e4f93234SChuck Lever  *	union WRITE3res switch (nfsstat3 status) {
1711e4f93234SChuck Lever  *	case NFS3_OK:
1712e4f93234SChuck Lever  *		WRITE3resok	resok;
1713e4f93234SChuck Lever  *	default:
1714e4f93234SChuck Lever  *		WRITE3resfail	resfail;
1715e4f93234SChuck Lever  *	};
1716e4f93234SChuck Lever  */
1717e4f93234SChuck Lever static int decode_write3resok(struct xdr_stream *xdr,
17189137bdf3SAnna Schumaker 			      struct nfs_pgio_res *result)
1719e4f93234SChuck Lever {
1720e4f93234SChuck Lever 	__be32 *p;
1721e4f93234SChuck Lever 
17222f2c63bcSTrond Myklebust 	p = xdr_inline_decode(xdr, 4 + 4);
1723e4f93234SChuck Lever 	if (unlikely(p == NULL))
1724e4f93234SChuck Lever 		goto out_overflow;
1725e4f93234SChuck Lever 	result->count = be32_to_cpup(p++);
1726e4f93234SChuck Lever 	result->verf->committed = be32_to_cpup(p++);
1727e4f93234SChuck Lever 	if (unlikely(result->verf->committed > NFS_FILE_SYNC))
1728e4f93234SChuck Lever 		goto out_badvalue;
17292f2c63bcSTrond Myklebust 	if (decode_writeverf3(xdr, &result->verf->verifier))
17302f2c63bcSTrond Myklebust 		goto out_eio;
1731e4f93234SChuck Lever 	return result->count;
1732e4f93234SChuck Lever out_badvalue:
1733e4f93234SChuck Lever 	dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
1734e4f93234SChuck Lever 	return -EIO;
1735e4f93234SChuck Lever out_overflow:
1736e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
17372f2c63bcSTrond Myklebust out_eio:
1738e4f93234SChuck Lever 	return -EIO;
1739e4f93234SChuck Lever }
1740e4f93234SChuck Lever 
1741bf269551SChuck Lever static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1742fc016483SChristoph Hellwig 				  void *data)
1743e4f93234SChuck Lever {
1744fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
1745e4f93234SChuck Lever 	enum nfs_stat status;
1746e4f93234SChuck Lever 	int error;
1747e4f93234SChuck Lever 
1748bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1749e4f93234SChuck Lever 	if (unlikely(error))
1750e4f93234SChuck Lever 		goto out;
1751bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->fattr);
1752e4f93234SChuck Lever 	if (unlikely(error))
1753e4f93234SChuck Lever 		goto out;
1754aabff4ddSPeng Tao 	result->op_status = status;
1755e4f93234SChuck Lever 	if (status != NFS3_OK)
1756e4f93234SChuck Lever 		goto out_status;
1757bf269551SChuck Lever 	error = decode_write3resok(xdr, result);
1758e4f93234SChuck Lever out:
1759e4f93234SChuck Lever 	return error;
1760e4f93234SChuck Lever out_status:
17615e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1762e4f93234SChuck Lever }
1763e4f93234SChuck Lever 
1764e4f93234SChuck Lever /*
1765e4f93234SChuck Lever  * 3.3.8  CREATE3res
1766e4f93234SChuck Lever  *
1767e4f93234SChuck Lever  *	struct CREATE3resok {
1768e4f93234SChuck Lever  *		post_op_fh3	obj;
1769e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1770e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1771e4f93234SChuck Lever  *	};
1772e4f93234SChuck Lever  *
1773e4f93234SChuck Lever  *	struct CREATE3resfail {
1774e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1775e4f93234SChuck Lever  *	};
1776e4f93234SChuck Lever  *
1777e4f93234SChuck Lever  *	union CREATE3res switch (nfsstat3 status) {
1778e4f93234SChuck Lever  *	case NFS3_OK:
1779e4f93234SChuck Lever  *		CREATE3resok	resok;
1780e4f93234SChuck Lever  *	default:
1781e4f93234SChuck Lever  *		CREATE3resfail	resfail;
1782e4f93234SChuck Lever  *	};
1783e4f93234SChuck Lever  */
1784e4f93234SChuck Lever static int decode_create3resok(struct xdr_stream *xdr,
1785e4f93234SChuck Lever 			       struct nfs3_diropres *result)
1786e4f93234SChuck Lever {
1787e4f93234SChuck Lever 	int error;
1788e4f93234SChuck Lever 
1789e4f93234SChuck Lever 	error = decode_post_op_fh3(xdr, result->fh);
1790e4f93234SChuck Lever 	if (unlikely(error))
1791e4f93234SChuck Lever 		goto out;
1792e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1793e4f93234SChuck Lever 	if (unlikely(error))
1794e4f93234SChuck Lever 		goto out;
1795e4f93234SChuck Lever 	/* The server isn't required to return a file handle.
1796e4f93234SChuck Lever 	 * If it didn't, force the client to perform a LOOKUP
1797e4f93234SChuck Lever 	 * to determine the correct file handle and attribute
1798e4f93234SChuck Lever 	 * values for the new object. */
1799e4f93234SChuck Lever 	if (result->fh->size == 0)
1800e4f93234SChuck Lever 		result->fattr->valid = 0;
1801e4f93234SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1802e4f93234SChuck Lever out:
1803e4f93234SChuck Lever 	return error;
1804e4f93234SChuck Lever }
1805e4f93234SChuck Lever 
1806bf269551SChuck Lever static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
1807bf269551SChuck Lever 				   struct xdr_stream *xdr,
1808fc016483SChristoph Hellwig 				   void *data)
1809e4f93234SChuck Lever {
1810fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1811e4f93234SChuck Lever 	enum nfs_stat status;
1812e4f93234SChuck Lever 	int error;
1813e4f93234SChuck Lever 
1814bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1815e4f93234SChuck Lever 	if (unlikely(error))
1816e4f93234SChuck Lever 		goto out;
1817e4f93234SChuck Lever 	if (status != NFS3_OK)
1818e4f93234SChuck Lever 		goto out_default;
1819bf269551SChuck Lever 	error = decode_create3resok(xdr, result);
1820e4f93234SChuck Lever out:
1821e4f93234SChuck Lever 	return error;
1822e4f93234SChuck Lever out_default:
1823bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1824e4f93234SChuck Lever 	if (unlikely(error))
1825e4f93234SChuck Lever 		goto out;
18265e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1827e4f93234SChuck Lever }
1828e4f93234SChuck Lever 
1829e4f93234SChuck Lever /*
1830e4f93234SChuck Lever  * 3.3.12  REMOVE3res
1831e4f93234SChuck Lever  *
1832e4f93234SChuck Lever  *	struct REMOVE3resok {
1833e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1834e4f93234SChuck Lever  *	};
1835e4f93234SChuck Lever  *
1836e4f93234SChuck Lever  *	struct REMOVE3resfail {
1837e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1838e4f93234SChuck Lever  *	};
1839e4f93234SChuck Lever  *
1840e4f93234SChuck Lever  *	union REMOVE3res switch (nfsstat3 status) {
1841e4f93234SChuck Lever  *	case NFS3_OK:
1842e4f93234SChuck Lever  *		REMOVE3resok   resok;
1843e4f93234SChuck Lever  *	default:
1844e4f93234SChuck Lever  *		REMOVE3resfail resfail;
1845e4f93234SChuck Lever  *	};
1846e4f93234SChuck Lever  */
1847bf269551SChuck Lever static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
1848bf269551SChuck Lever 				   struct xdr_stream *xdr,
1849fc016483SChristoph Hellwig 				   void *data)
1850e4f93234SChuck Lever {
1851fc016483SChristoph Hellwig 	struct nfs_removeres *result = data;
1852e4f93234SChuck Lever 	enum nfs_stat status;
1853e4f93234SChuck Lever 	int error;
1854e4f93234SChuck Lever 
1855bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1856e4f93234SChuck Lever 	if (unlikely(error))
1857e4f93234SChuck Lever 		goto out;
1858bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1859e4f93234SChuck Lever 	if (unlikely(error))
1860e4f93234SChuck Lever 		goto out;
1861e4f93234SChuck Lever 	if (status != NFS3_OK)
1862e4f93234SChuck Lever 		goto out_status;
1863e4f93234SChuck Lever out:
1864e4f93234SChuck Lever 	return error;
1865e4f93234SChuck Lever out_status:
18665e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1867e4f93234SChuck Lever }
1868e4f93234SChuck Lever 
1869e4f93234SChuck Lever /*
1870e4f93234SChuck Lever  * 3.3.14  RENAME3res
1871e4f93234SChuck Lever  *
1872e4f93234SChuck Lever  *	struct RENAME3resok {
1873e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1874e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1875e4f93234SChuck Lever  *	};
1876e4f93234SChuck Lever  *
1877e4f93234SChuck Lever  *	struct RENAME3resfail {
1878e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1879e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1880e4f93234SChuck Lever  *	};
1881e4f93234SChuck Lever  *
1882e4f93234SChuck Lever  *	union RENAME3res switch (nfsstat3 status) {
1883e4f93234SChuck Lever  *	case NFS3_OK:
1884e4f93234SChuck Lever  *		RENAME3resok   resok;
1885e4f93234SChuck Lever  *	default:
1886e4f93234SChuck Lever  *		RENAME3resfail resfail;
1887e4f93234SChuck Lever  *	};
1888e4f93234SChuck Lever  */
1889bf269551SChuck Lever static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
1890bf269551SChuck Lever 				   struct xdr_stream *xdr,
1891fc016483SChristoph Hellwig 				   void *data)
1892e4f93234SChuck Lever {
1893fc016483SChristoph Hellwig 	struct nfs_renameres *result = data;
1894e4f93234SChuck Lever 	enum nfs_stat status;
1895e4f93234SChuck Lever 	int error;
1896e4f93234SChuck Lever 
1897bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1898e4f93234SChuck Lever 	if (unlikely(error))
1899e4f93234SChuck Lever 		goto out;
1900bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->old_fattr);
1901e4f93234SChuck Lever 	if (unlikely(error))
1902e4f93234SChuck Lever 		goto out;
1903bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->new_fattr);
1904e4f93234SChuck Lever 	if (unlikely(error))
1905e4f93234SChuck Lever 		goto out;
1906e4f93234SChuck Lever 	if (status != NFS3_OK)
1907e4f93234SChuck Lever 		goto out_status;
1908e4f93234SChuck Lever out:
1909e4f93234SChuck Lever 	return error;
1910e4f93234SChuck Lever out_status:
19115e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1912e4f93234SChuck Lever }
1913e4f93234SChuck Lever 
1914e4f93234SChuck Lever /*
1915e4f93234SChuck Lever  * 3.3.15  LINK3res
1916e4f93234SChuck Lever  *
1917e4f93234SChuck Lever  *	struct LINK3resok {
1918e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1919e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1920e4f93234SChuck Lever  *	};
1921e4f93234SChuck Lever  *
1922e4f93234SChuck Lever  *	struct LINK3resfail {
1923e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1924e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1925e4f93234SChuck Lever  *	};
1926e4f93234SChuck Lever  *
1927e4f93234SChuck Lever  *	union LINK3res switch (nfsstat3 status) {
1928e4f93234SChuck Lever  *	case NFS3_OK:
1929e4f93234SChuck Lever  *		LINK3resok	resok;
1930e4f93234SChuck Lever  *	default:
1931e4f93234SChuck Lever  *		LINK3resfail	resfail;
1932e4f93234SChuck Lever  *	};
1933e4f93234SChuck Lever  */
1934bf269551SChuck Lever static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1935fc016483SChristoph Hellwig 				 void *data)
1936e4f93234SChuck Lever {
1937fc016483SChristoph Hellwig 	struct nfs3_linkres *result = data;
1938e4f93234SChuck Lever 	enum nfs_stat status;
1939e4f93234SChuck Lever 	int error;
1940e4f93234SChuck Lever 
1941bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1942e4f93234SChuck Lever 	if (unlikely(error))
1943e4f93234SChuck Lever 		goto out;
1944bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1945e4f93234SChuck Lever 	if (unlikely(error))
1946e4f93234SChuck Lever 		goto out;
1947bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1948e4f93234SChuck Lever 	if (unlikely(error))
1949e4f93234SChuck Lever 		goto out;
1950e4f93234SChuck Lever 	if (status != NFS3_OK)
1951e4f93234SChuck Lever 		goto out_status;
1952e4f93234SChuck Lever out:
1953e4f93234SChuck Lever 	return error;
1954e4f93234SChuck Lever out_status:
19555e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1956e4f93234SChuck Lever }
1957e4f93234SChuck Lever 
1958e4f93234SChuck Lever /**
1959e4f93234SChuck Lever  * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
1960e4f93234SChuck Lever  *			the local page cache
1961e4f93234SChuck Lever  * @xdr: XDR stream where entry resides
1962e4f93234SChuck Lever  * @entry: buffer to fill in with entry data
1963e4f93234SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
1964e4f93234SChuck Lever  *
1965573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
1966573c4e1eSChuck Lever  * returned.
1967e4f93234SChuck Lever  *
1968e4f93234SChuck Lever  * This function is not invoked during READDIR reply decoding, but
1969e4f93234SChuck Lever  * rather whenever an application invokes the getdents(2) system call
1970e4f93234SChuck Lever  * on a directory already in our cache.
1971e4f93234SChuck Lever  *
1972e4f93234SChuck Lever  * 3.3.16  entry3
1973e4f93234SChuck Lever  *
1974e4f93234SChuck Lever  *	struct entry3 {
1975e4f93234SChuck Lever  *		fileid3		fileid;
1976e4f93234SChuck Lever  *		filename3	name;
1977e4f93234SChuck Lever  *		cookie3		cookie;
1978e4f93234SChuck Lever  *		fhandle3	filehandle;
1979e4f93234SChuck Lever  *		post_op_attr3	attributes;
1980e4f93234SChuck Lever  *		entry3		*nextentry;
1981e4f93234SChuck Lever  *	};
1982e4f93234SChuck Lever  *
1983e4f93234SChuck Lever  * 3.3.17  entryplus3
1984e4f93234SChuck Lever  *	struct entryplus3 {
1985e4f93234SChuck Lever  *		fileid3		fileid;
1986e4f93234SChuck Lever  *		filename3	name;
1987e4f93234SChuck Lever  *		cookie3		cookie;
1988e4f93234SChuck Lever  *		post_op_attr	name_attributes;
1989e4f93234SChuck Lever  *		post_op_fh3	name_handle;
1990e4f93234SChuck Lever  *		entryplus3	*nextentry;
1991e4f93234SChuck Lever  *	};
1992e4f93234SChuck Lever  */
1993573c4e1eSChuck Lever int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
1994573c4e1eSChuck Lever 		       int plus)
1995e4f93234SChuck Lever {
1996e4f93234SChuck Lever 	struct nfs_entry old = *entry;
1997e4f93234SChuck Lever 	__be32 *p;
1998e4f93234SChuck Lever 	int error;
1999e4f93234SChuck Lever 
2000e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
2001e4f93234SChuck Lever 	if (unlikely(p == NULL))
2002e4f93234SChuck Lever 		goto out_overflow;
2003e4f93234SChuck Lever 	if (*p == xdr_zero) {
2004e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
2005e4f93234SChuck Lever 		if (unlikely(p == NULL))
2006e4f93234SChuck Lever 			goto out_overflow;
2007e4f93234SChuck Lever 		if (*p == xdr_zero)
2008573c4e1eSChuck Lever 			return -EAGAIN;
2009e4f93234SChuck Lever 		entry->eof = 1;
2010573c4e1eSChuck Lever 		return -EBADCOOKIE;
2011e4f93234SChuck Lever 	}
2012e4f93234SChuck Lever 
2013e4f93234SChuck Lever 	error = decode_fileid3(xdr, &entry->ino);
2014e4f93234SChuck Lever 	if (unlikely(error))
2015573c4e1eSChuck Lever 		return error;
2016e4f93234SChuck Lever 
2017e4f93234SChuck Lever 	error = decode_inline_filename3(xdr, &entry->name, &entry->len);
2018e4f93234SChuck Lever 	if (unlikely(error))
2019573c4e1eSChuck Lever 		return error;
2020e4f93234SChuck Lever 
2021e4f93234SChuck Lever 	entry->prev_cookie = entry->cookie;
2022e4f93234SChuck Lever 	error = decode_cookie3(xdr, &entry->cookie);
2023e4f93234SChuck Lever 	if (unlikely(error))
2024573c4e1eSChuck Lever 		return error;
2025e4f93234SChuck Lever 
2026e4f93234SChuck Lever 	entry->d_type = DT_UNKNOWN;
2027e4f93234SChuck Lever 
2028e4f93234SChuck Lever 	if (plus) {
2029e4f93234SChuck Lever 		entry->fattr->valid = 0;
2030e4f93234SChuck Lever 		error = decode_post_op_attr(xdr, entry->fattr);
2031e4f93234SChuck Lever 		if (unlikely(error))
2032573c4e1eSChuck Lever 			return error;
2033e4f93234SChuck Lever 		if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
2034e4f93234SChuck Lever 			entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
2035e4f93234SChuck Lever 
20361ae04b25STrond Myklebust 		if (entry->fattr->fileid != entry->ino) {
20371ae04b25STrond Myklebust 			entry->fattr->mounted_on_fileid = entry->ino;
20381ae04b25STrond Myklebust 			entry->fattr->valid |= NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
20391ae04b25STrond Myklebust 		}
20401ae04b25STrond Myklebust 
2041e4f93234SChuck Lever 		/* In fact, a post_op_fh3: */
2042e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
2043e4f93234SChuck Lever 		if (unlikely(p == NULL))
2044e4f93234SChuck Lever 			goto out_overflow;
2045e4f93234SChuck Lever 		if (*p != xdr_zero) {
2046e4f93234SChuck Lever 			error = decode_nfs_fh3(xdr, entry->fh);
2047e4f93234SChuck Lever 			if (unlikely(error)) {
2048e4f93234SChuck Lever 				if (error == -E2BIG)
2049e4f93234SChuck Lever 					goto out_truncated;
2050573c4e1eSChuck Lever 				return error;
2051e4f93234SChuck Lever 			}
2052e4f93234SChuck Lever 		} else
2053e4f93234SChuck Lever 			zero_nfs_fh3(entry->fh);
2054e4f93234SChuck Lever 	}
2055e4f93234SChuck Lever 
2056573c4e1eSChuck Lever 	return 0;
2057e4f93234SChuck Lever 
2058e4f93234SChuck Lever out_overflow:
2059e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2060573c4e1eSChuck Lever 	return -EAGAIN;
2061e4f93234SChuck Lever out_truncated:
2062e4f93234SChuck Lever 	dprintk("NFS: directory entry contains invalid file handle\n");
2063e4f93234SChuck Lever 	*entry = old;
2064573c4e1eSChuck Lever 	return -EAGAIN;
2065e4f93234SChuck Lever }
2066e4f93234SChuck Lever 
2067e4f93234SChuck Lever /*
2068e4f93234SChuck Lever  * 3.3.16  READDIR3res
2069e4f93234SChuck Lever  *
2070e4f93234SChuck Lever  *	struct dirlist3 {
2071e4f93234SChuck Lever  *		entry3		*entries;
2072e4f93234SChuck Lever  *		bool		eof;
2073e4f93234SChuck Lever  *	};
2074e4f93234SChuck Lever  *
2075e4f93234SChuck Lever  *	struct READDIR3resok {
2076e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2077e4f93234SChuck Lever  *		cookieverf3	cookieverf;
2078e4f93234SChuck Lever  *		dirlist3	reply;
2079e4f93234SChuck Lever  *	};
2080e4f93234SChuck Lever  *
2081e4f93234SChuck Lever  *	struct READDIR3resfail {
2082e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2083e4f93234SChuck Lever  *	};
2084e4f93234SChuck Lever  *
2085e4f93234SChuck Lever  *	union READDIR3res switch (nfsstat3 status) {
2086e4f93234SChuck Lever  *	case NFS3_OK:
2087e4f93234SChuck Lever  *		READDIR3resok	resok;
2088e4f93234SChuck Lever  *	default:
2089e4f93234SChuck Lever  *		READDIR3resfail	resfail;
2090e4f93234SChuck Lever  *	};
2091e4f93234SChuck Lever  *
2092e4f93234SChuck Lever  * Read the directory contents into the page cache, but otherwise
2093e4f93234SChuck Lever  * don't touch them.  The actual decoding is done by nfs3_decode_entry()
2094e4f93234SChuck Lever  * during subsequent nfs_readdir() calls.
2095e4f93234SChuck Lever  */
2096e4f93234SChuck Lever static int decode_dirlist3(struct xdr_stream *xdr)
2097e4f93234SChuck Lever {
209864bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
2099e4f93234SChuck Lever }
2100e4f93234SChuck Lever 
2101e4f93234SChuck Lever static int decode_readdir3resok(struct xdr_stream *xdr,
2102e4f93234SChuck Lever 				struct nfs3_readdirres *result)
2103e4f93234SChuck Lever {
2104e4f93234SChuck Lever 	int error;
2105e4f93234SChuck Lever 
2106e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
2107e4f93234SChuck Lever 	if (unlikely(error))
2108e4f93234SChuck Lever 		goto out;
2109e4f93234SChuck Lever 	/* XXX: do we need to check if result->verf != NULL ? */
2110e4f93234SChuck Lever 	error = decode_cookieverf3(xdr, result->verf);
2111e4f93234SChuck Lever 	if (unlikely(error))
2112e4f93234SChuck Lever 		goto out;
2113e4f93234SChuck Lever 	error = decode_dirlist3(xdr);
2114e4f93234SChuck Lever out:
2115e4f93234SChuck Lever 	return error;
2116e4f93234SChuck Lever }
2117e4f93234SChuck Lever 
2118bf269551SChuck Lever static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
2119bf269551SChuck Lever 				    struct xdr_stream *xdr,
2120fc016483SChristoph Hellwig 				    void *data)
2121e4f93234SChuck Lever {
2122fc016483SChristoph Hellwig 	struct nfs3_readdirres *result = data;
2123e4f93234SChuck Lever 	enum nfs_stat status;
2124e4f93234SChuck Lever 	int error;
2125e4f93234SChuck Lever 
2126bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2127e4f93234SChuck Lever 	if (unlikely(error))
2128e4f93234SChuck Lever 		goto out;
2129e4f93234SChuck Lever 	if (status != NFS3_OK)
2130e4f93234SChuck Lever 		goto out_default;
2131bf269551SChuck Lever 	error = decode_readdir3resok(xdr, result);
2132e4f93234SChuck Lever out:
2133e4f93234SChuck Lever 	return error;
2134e4f93234SChuck Lever out_default:
2135bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
2136e4f93234SChuck Lever 	if (unlikely(error))
2137e4f93234SChuck Lever 		goto out;
21385e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2139e4f93234SChuck Lever }
2140e4f93234SChuck Lever 
2141e4f93234SChuck Lever /*
2142e4f93234SChuck Lever  * 3.3.18  FSSTAT3res
2143e4f93234SChuck Lever  *
2144e4f93234SChuck Lever  *	struct FSSTAT3resok {
2145e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2146e4f93234SChuck Lever  *		size3		tbytes;
2147e4f93234SChuck Lever  *		size3		fbytes;
2148e4f93234SChuck Lever  *		size3		abytes;
2149e4f93234SChuck Lever  *		size3		tfiles;
2150e4f93234SChuck Lever  *		size3		ffiles;
2151e4f93234SChuck Lever  *		size3		afiles;
2152e4f93234SChuck Lever  *		uint32		invarsec;
2153e4f93234SChuck Lever  *	};
2154e4f93234SChuck Lever  *
2155e4f93234SChuck Lever  *	struct FSSTAT3resfail {
2156e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2157e4f93234SChuck Lever  *	};
2158e4f93234SChuck Lever  *
2159e4f93234SChuck Lever  *	union FSSTAT3res switch (nfsstat3 status) {
2160e4f93234SChuck Lever  *	case NFS3_OK:
2161e4f93234SChuck Lever  *		FSSTAT3resok	resok;
2162e4f93234SChuck Lever  *	default:
2163e4f93234SChuck Lever  *		FSSTAT3resfail	resfail;
2164e4f93234SChuck Lever  *	};
2165e4f93234SChuck Lever  */
2166e4f93234SChuck Lever static int decode_fsstat3resok(struct xdr_stream *xdr,
2167e4f93234SChuck Lever 			       struct nfs_fsstat *result)
2168e4f93234SChuck Lever {
2169e4f93234SChuck Lever 	__be32 *p;
2170e4f93234SChuck Lever 
2171e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8 * 6 + 4);
2172e4f93234SChuck Lever 	if (unlikely(p == NULL))
2173e4f93234SChuck Lever 		goto out_overflow;
2174e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tbytes);
2175e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->fbytes);
2176e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->abytes);
2177e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tfiles);
2178e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->ffiles);
2179e4f93234SChuck Lever 	xdr_decode_size3(p, &result->afiles);
2180e4f93234SChuck Lever 	/* ignore invarsec */
2181e4f93234SChuck Lever 	return 0;
2182e4f93234SChuck Lever out_overflow:
2183e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2184e4f93234SChuck Lever 	return -EIO;
2185e4f93234SChuck Lever }
2186e4f93234SChuck Lever 
2187bf269551SChuck Lever static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
2188bf269551SChuck Lever 				   struct xdr_stream *xdr,
2189fc016483SChristoph Hellwig 				   void *data)
2190e4f93234SChuck Lever {
2191fc016483SChristoph Hellwig 	struct nfs_fsstat *result = data;
2192e4f93234SChuck Lever 	enum nfs_stat status;
2193e4f93234SChuck Lever 	int error;
2194e4f93234SChuck Lever 
2195bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2196e4f93234SChuck Lever 	if (unlikely(error))
2197e4f93234SChuck Lever 		goto out;
2198bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2199e4f93234SChuck Lever 	if (unlikely(error))
2200e4f93234SChuck Lever 		goto out;
2201e4f93234SChuck Lever 	if (status != NFS3_OK)
2202e4f93234SChuck Lever 		goto out_status;
2203bf269551SChuck Lever 	error = decode_fsstat3resok(xdr, result);
2204e4f93234SChuck Lever out:
2205e4f93234SChuck Lever 	return error;
2206e4f93234SChuck Lever out_status:
22075e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2208e4f93234SChuck Lever }
2209e4f93234SChuck Lever 
2210e4f93234SChuck Lever /*
2211e4f93234SChuck Lever  * 3.3.19  FSINFO3res
2212e4f93234SChuck Lever  *
2213e4f93234SChuck Lever  *	struct FSINFO3resok {
2214e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2215e4f93234SChuck Lever  *		uint32		rtmax;
2216e4f93234SChuck Lever  *		uint32		rtpref;
2217e4f93234SChuck Lever  *		uint32		rtmult;
2218e4f93234SChuck Lever  *		uint32		wtmax;
2219e4f93234SChuck Lever  *		uint32		wtpref;
2220e4f93234SChuck Lever  *		uint32		wtmult;
2221e4f93234SChuck Lever  *		uint32		dtpref;
2222e4f93234SChuck Lever  *		size3		maxfilesize;
2223e4f93234SChuck Lever  *		nfstime3	time_delta;
2224e4f93234SChuck Lever  *		uint32		properties;
2225e4f93234SChuck Lever  *	};
2226e4f93234SChuck Lever  *
2227e4f93234SChuck Lever  *	struct FSINFO3resfail {
2228e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2229e4f93234SChuck Lever  *	};
2230e4f93234SChuck Lever  *
2231e4f93234SChuck Lever  *	union FSINFO3res switch (nfsstat3 status) {
2232e4f93234SChuck Lever  *	case NFS3_OK:
2233e4f93234SChuck Lever  *		FSINFO3resok	resok;
2234e4f93234SChuck Lever  *	default:
2235e4f93234SChuck Lever  *		FSINFO3resfail	resfail;
2236e4f93234SChuck Lever  *	};
2237e4f93234SChuck Lever  */
2238e4f93234SChuck Lever static int decode_fsinfo3resok(struct xdr_stream *xdr,
2239e4f93234SChuck Lever 			       struct nfs_fsinfo *result)
2240e4f93234SChuck Lever {
2241e4f93234SChuck Lever 	__be32 *p;
2242e4f93234SChuck Lever 
2243e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4);
2244e4f93234SChuck Lever 	if (unlikely(p == NULL))
2245e4f93234SChuck Lever 		goto out_overflow;
2246e4f93234SChuck Lever 	result->rtmax  = be32_to_cpup(p++);
2247e4f93234SChuck Lever 	result->rtpref = be32_to_cpup(p++);
2248e4f93234SChuck Lever 	result->rtmult = be32_to_cpup(p++);
2249e4f93234SChuck Lever 	result->wtmax  = be32_to_cpup(p++);
2250e4f93234SChuck Lever 	result->wtpref = be32_to_cpup(p++);
2251e4f93234SChuck Lever 	result->wtmult = be32_to_cpup(p++);
2252e4f93234SChuck Lever 	result->dtpref = be32_to_cpup(p++);
2253e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->maxfilesize);
2254f6048709SChuck Lever 	xdr_decode_nfstime3(p, &result->time_delta);
2255e4f93234SChuck Lever 
2256e4f93234SChuck Lever 	/* ignore properties */
2257e4f93234SChuck Lever 	result->lease_time = 0;
2258e4f93234SChuck Lever 	return 0;
2259e4f93234SChuck Lever out_overflow:
2260e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2261e4f93234SChuck Lever 	return -EIO;
2262e4f93234SChuck Lever }
2263e4f93234SChuck Lever 
2264bf269551SChuck Lever static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
2265bf269551SChuck Lever 				   struct xdr_stream *xdr,
2266fc016483SChristoph Hellwig 				   void *data)
2267e4f93234SChuck Lever {
2268fc016483SChristoph Hellwig 	struct nfs_fsinfo *result = data;
2269e4f93234SChuck Lever 	enum nfs_stat status;
2270e4f93234SChuck Lever 	int error;
2271e4f93234SChuck Lever 
2272bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2273e4f93234SChuck Lever 	if (unlikely(error))
2274e4f93234SChuck Lever 		goto out;
2275bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2276e4f93234SChuck Lever 	if (unlikely(error))
2277e4f93234SChuck Lever 		goto out;
2278e4f93234SChuck Lever 	if (status != NFS3_OK)
2279e4f93234SChuck Lever 		goto out_status;
2280bf269551SChuck Lever 	error = decode_fsinfo3resok(xdr, result);
2281e4f93234SChuck Lever out:
2282e4f93234SChuck Lever 	return error;
2283e4f93234SChuck Lever out_status:
22845e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2285e4f93234SChuck Lever }
2286e4f93234SChuck Lever 
2287e4f93234SChuck Lever /*
2288e4f93234SChuck Lever  * 3.3.20  PATHCONF3res
2289e4f93234SChuck Lever  *
2290e4f93234SChuck Lever  *	struct PATHCONF3resok {
2291e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2292e4f93234SChuck Lever  *		uint32		linkmax;
2293e4f93234SChuck Lever  *		uint32		name_max;
2294e4f93234SChuck Lever  *		bool		no_trunc;
2295e4f93234SChuck Lever  *		bool		chown_restricted;
2296e4f93234SChuck Lever  *		bool		case_insensitive;
2297e4f93234SChuck Lever  *		bool		case_preserving;
2298e4f93234SChuck Lever  *	};
2299e4f93234SChuck Lever  *
2300e4f93234SChuck Lever  *	struct PATHCONF3resfail {
2301e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2302e4f93234SChuck Lever  *	};
2303e4f93234SChuck Lever  *
2304e4f93234SChuck Lever  *	union PATHCONF3res switch (nfsstat3 status) {
2305e4f93234SChuck Lever  *	case NFS3_OK:
2306e4f93234SChuck Lever  *		PATHCONF3resok	resok;
2307e4f93234SChuck Lever  *	default:
2308e4f93234SChuck Lever  *		PATHCONF3resfail resfail;
2309e4f93234SChuck Lever  *	};
2310e4f93234SChuck Lever  */
2311e4f93234SChuck Lever static int decode_pathconf3resok(struct xdr_stream *xdr,
2312e4f93234SChuck Lever 				 struct nfs_pathconf *result)
2313e4f93234SChuck Lever {
2314e4f93234SChuck Lever 	__be32 *p;
2315e4f93234SChuck Lever 
2316e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 6);
2317e4f93234SChuck Lever 	if (unlikely(p == NULL))
2318e4f93234SChuck Lever 		goto out_overflow;
2319e4f93234SChuck Lever 	result->max_link = be32_to_cpup(p++);
2320e4f93234SChuck Lever 	result->max_namelen = be32_to_cpup(p);
2321e4f93234SChuck Lever 	/* ignore remaining fields */
2322e4f93234SChuck Lever 	return 0;
2323e4f93234SChuck Lever out_overflow:
2324e4f93234SChuck Lever 	print_overflow_msg(__func__, xdr);
2325e4f93234SChuck Lever 	return -EIO;
2326e4f93234SChuck Lever }
2327e4f93234SChuck Lever 
2328bf269551SChuck Lever static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
2329bf269551SChuck Lever 				     struct xdr_stream *xdr,
2330fc016483SChristoph Hellwig 				     void *data)
2331e4f93234SChuck Lever {
2332fc016483SChristoph Hellwig 	struct nfs_pathconf *result = data;
2333e4f93234SChuck Lever 	enum nfs_stat status;
2334e4f93234SChuck Lever 	int error;
2335e4f93234SChuck Lever 
2336bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2337e4f93234SChuck Lever 	if (unlikely(error))
2338e4f93234SChuck Lever 		goto out;
2339bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2340e4f93234SChuck Lever 	if (unlikely(error))
2341e4f93234SChuck Lever 		goto out;
2342e4f93234SChuck Lever 	if (status != NFS3_OK)
2343e4f93234SChuck Lever 		goto out_status;
2344bf269551SChuck Lever 	error = decode_pathconf3resok(xdr, result);
2345e4f93234SChuck Lever out:
2346e4f93234SChuck Lever 	return error;
2347e4f93234SChuck Lever out_status:
23485e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2349e4f93234SChuck Lever }
2350e4f93234SChuck Lever 
2351e4f93234SChuck Lever /*
2352e4f93234SChuck Lever  * 3.3.21  COMMIT3res
2353e4f93234SChuck Lever  *
2354e4f93234SChuck Lever  *	struct COMMIT3resok {
2355e4f93234SChuck Lever  *		wcc_data	file_wcc;
2356e4f93234SChuck Lever  *		writeverf3	verf;
2357e4f93234SChuck Lever  *	};
2358e4f93234SChuck Lever  *
2359e4f93234SChuck Lever  *	struct COMMIT3resfail {
2360e4f93234SChuck Lever  *		wcc_data	file_wcc;
2361e4f93234SChuck Lever  *	};
2362e4f93234SChuck Lever  *
2363e4f93234SChuck Lever  *	union COMMIT3res switch (nfsstat3 status) {
2364e4f93234SChuck Lever  *	case NFS3_OK:
2365e4f93234SChuck Lever  *		COMMIT3resok	resok;
2366e4f93234SChuck Lever  *	default:
2367e4f93234SChuck Lever  *		COMMIT3resfail	resfail;
2368e4f93234SChuck Lever  *	};
2369e4f93234SChuck Lever  */
2370bf269551SChuck Lever static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
2371bf269551SChuck Lever 				   struct xdr_stream *xdr,
2372fc016483SChristoph Hellwig 				   void *data)
2373e4f93234SChuck Lever {
2374fc016483SChristoph Hellwig 	struct nfs_commitres *result = data;
2375e4f93234SChuck Lever 	enum nfs_stat status;
2376e4f93234SChuck Lever 	int error;
2377e4f93234SChuck Lever 
2378bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2379e4f93234SChuck Lever 	if (unlikely(error))
2380e4f93234SChuck Lever 		goto out;
2381bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->fattr);
2382e4f93234SChuck Lever 	if (unlikely(error))
2383e4f93234SChuck Lever 		goto out;
2384aabff4ddSPeng Tao 	result->op_status = status;
2385e4f93234SChuck Lever 	if (status != NFS3_OK)
2386e4f93234SChuck Lever 		goto out_status;
23872f2c63bcSTrond Myklebust 	error = decode_writeverf3(xdr, &result->verf->verifier);
2388e4f93234SChuck Lever out:
2389e4f93234SChuck Lever 	return error;
2390e4f93234SChuck Lever out_status:
23915e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2392e4f93234SChuck Lever }
2393e4f93234SChuck Lever 
2394b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2395b7fa0554SAndreas Gruenbacher 
2396e4f93234SChuck Lever static inline int decode_getacl3resok(struct xdr_stream *xdr,
2397e4f93234SChuck Lever 				      struct nfs3_getaclres *result)
2398e4f93234SChuck Lever {
2399e4f93234SChuck Lever 	struct posix_acl **acl;
2400e4f93234SChuck Lever 	unsigned int *aclcnt;
2401e4f93234SChuck Lever 	size_t hdrlen;
2402e4f93234SChuck Lever 	int error;
2403e4f93234SChuck Lever 
2404e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2405e4f93234SChuck Lever 	if (unlikely(error))
2406e4f93234SChuck Lever 		goto out;
2407e4f93234SChuck Lever 	error = decode_uint32(xdr, &result->mask);
2408e4f93234SChuck Lever 	if (unlikely(error))
2409e4f93234SChuck Lever 		goto out;
2410e4f93234SChuck Lever 	error = -EINVAL;
2411e4f93234SChuck Lever 	if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
2412e4f93234SChuck Lever 		goto out;
2413e4f93234SChuck Lever 
24141aecca3eSTrond Myklebust 	hdrlen = xdr_stream_pos(xdr);
2415e4f93234SChuck Lever 
2416e4f93234SChuck Lever 	acl = NULL;
2417e4f93234SChuck Lever 	if (result->mask & NFS_ACL)
2418e4f93234SChuck Lever 		acl = &result->acl_access;
2419e4f93234SChuck Lever 	aclcnt = NULL;
2420e4f93234SChuck Lever 	if (result->mask & NFS_ACLCNT)
2421e4f93234SChuck Lever 		aclcnt = &result->acl_access_count;
2422e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl);
2423e4f93234SChuck Lever 	if (unlikely(error <= 0))
2424e4f93234SChuck Lever 		goto out;
2425e4f93234SChuck Lever 
2426e4f93234SChuck Lever 	acl = NULL;
2427e4f93234SChuck Lever 	if (result->mask & NFS_DFACL)
2428e4f93234SChuck Lever 		acl = &result->acl_default;
2429e4f93234SChuck Lever 	aclcnt = NULL;
2430e4f93234SChuck Lever 	if (result->mask & NFS_DFACLCNT)
2431e4f93234SChuck Lever 		aclcnt = &result->acl_default_count;
2432e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl);
2433e4f93234SChuck Lever 	if (unlikely(error <= 0))
2434e4f93234SChuck Lever 		return error;
2435e4f93234SChuck Lever 	error = 0;
2436e4f93234SChuck Lever out:
2437e4f93234SChuck Lever 	return error;
2438e4f93234SChuck Lever }
2439e4f93234SChuck Lever 
2440bf269551SChuck Lever static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
2441bf269551SChuck Lever 				   struct xdr_stream *xdr,
2442fc016483SChristoph Hellwig 				   void *result)
2443e4f93234SChuck Lever {
2444e4f93234SChuck Lever 	enum nfs_stat status;
2445e4f93234SChuck Lever 	int error;
2446e4f93234SChuck Lever 
2447bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2448e4f93234SChuck Lever 	if (unlikely(error))
2449e4f93234SChuck Lever 		goto out;
2450e4f93234SChuck Lever 	if (status != NFS3_OK)
2451e4f93234SChuck Lever 		goto out_default;
2452bf269551SChuck Lever 	error = decode_getacl3resok(xdr, result);
2453e4f93234SChuck Lever out:
2454e4f93234SChuck Lever 	return error;
2455e4f93234SChuck Lever out_default:
24565e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2457e4f93234SChuck Lever }
2458e4f93234SChuck Lever 
2459bf269551SChuck Lever static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
2460bf269551SChuck Lever 				   struct xdr_stream *xdr,
2461fc016483SChristoph Hellwig 				   void *result)
2462e4f93234SChuck Lever {
2463e4f93234SChuck Lever 	enum nfs_stat status;
2464e4f93234SChuck Lever 	int error;
2465e4f93234SChuck Lever 
2466bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2467e4f93234SChuck Lever 	if (unlikely(error))
2468e4f93234SChuck Lever 		goto out;
2469e4f93234SChuck Lever 	if (status != NFS3_OK)
2470e4f93234SChuck Lever 		goto out_default;
2471bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result);
2472e4f93234SChuck Lever out:
2473e4f93234SChuck Lever 	return error;
2474e4f93234SChuck Lever out_default:
24755e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2476e4f93234SChuck Lever }
2477e4f93234SChuck Lever 
2478b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2479b7fa0554SAndreas Gruenbacher 
24805e7e5a0dSBryan Schumaker 
24815e7e5a0dSBryan Schumaker /*
24825e7e5a0dSBryan Schumaker  * We need to translate between nfs status return values and
24835e7e5a0dSBryan Schumaker  * the local errno values which may not be the same.
24845e7e5a0dSBryan Schumaker  */
24855e7e5a0dSBryan Schumaker static const struct {
24865e7e5a0dSBryan Schumaker 	int stat;
24875e7e5a0dSBryan Schumaker 	int errno;
24885e7e5a0dSBryan Schumaker } nfs_errtbl[] = {
24895e7e5a0dSBryan Schumaker 	{ NFS_OK,		0		},
24905e7e5a0dSBryan Schumaker 	{ NFSERR_PERM,		-EPERM		},
24915e7e5a0dSBryan Schumaker 	{ NFSERR_NOENT,		-ENOENT		},
24925e7e5a0dSBryan Schumaker 	{ NFSERR_IO,		-errno_NFSERR_IO},
24935e7e5a0dSBryan Schumaker 	{ NFSERR_NXIO,		-ENXIO		},
24945e7e5a0dSBryan Schumaker /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
24955e7e5a0dSBryan Schumaker 	{ NFSERR_ACCES,		-EACCES		},
24965e7e5a0dSBryan Schumaker 	{ NFSERR_EXIST,		-EEXIST		},
24975e7e5a0dSBryan Schumaker 	{ NFSERR_XDEV,		-EXDEV		},
24985e7e5a0dSBryan Schumaker 	{ NFSERR_NODEV,		-ENODEV		},
24995e7e5a0dSBryan Schumaker 	{ NFSERR_NOTDIR,	-ENOTDIR	},
25005e7e5a0dSBryan Schumaker 	{ NFSERR_ISDIR,		-EISDIR		},
25015e7e5a0dSBryan Schumaker 	{ NFSERR_INVAL,		-EINVAL		},
25025e7e5a0dSBryan Schumaker 	{ NFSERR_FBIG,		-EFBIG		},
25035e7e5a0dSBryan Schumaker 	{ NFSERR_NOSPC,		-ENOSPC		},
25045e7e5a0dSBryan Schumaker 	{ NFSERR_ROFS,		-EROFS		},
25055e7e5a0dSBryan Schumaker 	{ NFSERR_MLINK,		-EMLINK		},
25065e7e5a0dSBryan Schumaker 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
25075e7e5a0dSBryan Schumaker 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
25085e7e5a0dSBryan Schumaker 	{ NFSERR_DQUOT,		-EDQUOT		},
25095e7e5a0dSBryan Schumaker 	{ NFSERR_STALE,		-ESTALE		},
25105e7e5a0dSBryan Schumaker 	{ NFSERR_REMOTE,	-EREMOTE	},
25115e7e5a0dSBryan Schumaker #ifdef EWFLUSH
25125e7e5a0dSBryan Schumaker 	{ NFSERR_WFLUSH,	-EWFLUSH	},
25135e7e5a0dSBryan Schumaker #endif
25145e7e5a0dSBryan Schumaker 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
25155e7e5a0dSBryan Schumaker 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
25165e7e5a0dSBryan Schumaker 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
25175e7e5a0dSBryan Schumaker 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
25185e7e5a0dSBryan Schumaker 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
25195e7e5a0dSBryan Schumaker 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
25205e7e5a0dSBryan Schumaker 	{ NFSERR_BADTYPE,	-EBADTYPE	},
25215e7e5a0dSBryan Schumaker 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
25225e7e5a0dSBryan Schumaker 	{ -1,			-EIO		}
25235e7e5a0dSBryan Schumaker };
25245e7e5a0dSBryan Schumaker 
25255e7e5a0dSBryan Schumaker /**
25265e7e5a0dSBryan Schumaker  * nfs3_stat_to_errno - convert an NFS status code to a local errno
25275e7e5a0dSBryan Schumaker  * @status: NFS status code to convert
25285e7e5a0dSBryan Schumaker  *
25295e7e5a0dSBryan Schumaker  * Returns a local errno value, or -EIO if the NFS status code is
25305e7e5a0dSBryan Schumaker  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
25315e7e5a0dSBryan Schumaker  */
25325e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat status)
25335e7e5a0dSBryan Schumaker {
25345e7e5a0dSBryan Schumaker 	int i;
25355e7e5a0dSBryan Schumaker 
25365e7e5a0dSBryan Schumaker 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
25375e7e5a0dSBryan Schumaker 		if (nfs_errtbl[i].stat == (int)status)
25385e7e5a0dSBryan Schumaker 			return nfs_errtbl[i].errno;
25395e7e5a0dSBryan Schumaker 	}
25405e7e5a0dSBryan Schumaker 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
25415e7e5a0dSBryan Schumaker 	return nfs_errtbl[i].errno;
25425e7e5a0dSBryan Schumaker }
25435e7e5a0dSBryan Schumaker 
25445e7e5a0dSBryan Schumaker 
25451da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
25461da177e4SLinus Torvalds [NFS3PROC_##proc] = {							\
25471da177e4SLinus Torvalds 	.p_proc      = NFS3PROC_##proc,					\
2548fcc85819SChristoph Hellwig 	.p_encode    = nfs3_xdr_enc_##argtype##3args,			\
2549fc016483SChristoph Hellwig 	.p_decode    = nfs3_xdr_dec_##restype##3res,			\
2550ad96b5b5SChuck Lever 	.p_arglen    = NFS3_##argtype##args_sz,				\
2551f5fc3c50SChuck Lever 	.p_replen    = NFS3_##restype##res_sz,				\
2552cc0175c1SChuck Lever 	.p_timer     = timer,						\
2553cc0175c1SChuck Lever 	.p_statidx   = NFS3PROC_##proc,					\
2554cc0175c1SChuck Lever 	.p_name      = #proc,						\
25551da177e4SLinus Torvalds 	}
25561da177e4SLinus Torvalds 
25571da177e4SLinus Torvalds struct rpc_procinfo	nfs3_procedures[] = {
2558f5fc3c50SChuck Lever 	PROC(GETATTR,		getattr,	getattr,	1),
2559f5fc3c50SChuck Lever 	PROC(SETATTR,		setattr,	setattr,	0),
2560f5fc3c50SChuck Lever 	PROC(LOOKUP,		lookup,		lookup,		2),
2561f5fc3c50SChuck Lever 	PROC(ACCESS,		access,		access,		1),
2562f5fc3c50SChuck Lever 	PROC(READLINK,		readlink,	readlink,	3),
2563f5fc3c50SChuck Lever 	PROC(READ,		read,		read,		3),
2564f5fc3c50SChuck Lever 	PROC(WRITE,		write,		write,		4),
2565f5fc3c50SChuck Lever 	PROC(CREATE,		create,		create,		0),
2566f5fc3c50SChuck Lever 	PROC(MKDIR,		mkdir,		create,		0),
2567f5fc3c50SChuck Lever 	PROC(SYMLINK,		symlink,	create,		0),
2568f5fc3c50SChuck Lever 	PROC(MKNOD,		mknod,		create,		0),
2569f5fc3c50SChuck Lever 	PROC(REMOVE,		remove,		remove,		0),
2570f5fc3c50SChuck Lever 	PROC(RMDIR,		lookup,		setattr,	0),
2571f5fc3c50SChuck Lever 	PROC(RENAME,		rename,		rename,		0),
2572f5fc3c50SChuck Lever 	PROC(LINK,		link,		link,		0),
2573f5fc3c50SChuck Lever 	PROC(READDIR,		readdir,	readdir,	3),
2574f5fc3c50SChuck Lever 	PROC(READDIRPLUS,	readdirplus,	readdir,	3),
2575f5fc3c50SChuck Lever 	PROC(FSSTAT,		getattr,	fsstat,		0),
2576f5fc3c50SChuck Lever 	PROC(FSINFO,		getattr,	fsinfo,		0),
2577f5fc3c50SChuck Lever 	PROC(PATHCONF,		getattr,	pathconf,	0),
2578f5fc3c50SChuck Lever 	PROC(COMMIT,		commit,		commit,		5),
25791da177e4SLinus Torvalds };
25801da177e4SLinus Torvalds 
2581c551858aSChristoph Hellwig static unsigned int nfs_version3_counts[ARRAY_SIZE(nfs3_procedures)];
2582a613fa16STrond Myklebust const struct rpc_version nfs_version3 = {
25831da177e4SLinus Torvalds 	.number			= 3,
2584e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
2585c551858aSChristoph Hellwig 	.procs			= nfs3_procedures,
2586c551858aSChristoph Hellwig 	.counts			= nfs_version3_counts,
25871da177e4SLinus Torvalds };
25881da177e4SLinus Torvalds 
2589b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2590b7fa0554SAndreas Gruenbacher static struct rpc_procinfo	nfs3_acl_procedures[] = {
2591b7fa0554SAndreas Gruenbacher 	[ACLPROC3_GETACL] = {
2592b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_GETACL,
2593fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_getacl3args,
2594fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_getacl3res,
25952bea90d4SChuck Lever 		.p_arglen = ACL3_getaclargs_sz,
25962bea90d4SChuck Lever 		.p_replen = ACL3_getaclres_sz,
2597b7fa0554SAndreas Gruenbacher 		.p_timer = 1,
2598cc0175c1SChuck Lever 		.p_name = "GETACL",
2599b7fa0554SAndreas Gruenbacher 	},
2600b7fa0554SAndreas Gruenbacher 	[ACLPROC3_SETACL] = {
2601b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_SETACL,
2602fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_setacl3args,
2603fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_setacl3res,
26042bea90d4SChuck Lever 		.p_arglen = ACL3_setaclargs_sz,
26052bea90d4SChuck Lever 		.p_replen = ACL3_setaclres_sz,
2606b7fa0554SAndreas Gruenbacher 		.p_timer = 0,
2607cc0175c1SChuck Lever 		.p_name = "SETACL",
2608b7fa0554SAndreas Gruenbacher 	},
2609b7fa0554SAndreas Gruenbacher };
2610b7fa0554SAndreas Gruenbacher 
2611c551858aSChristoph Hellwig static unsigned int nfs3_acl_counts[ARRAY_SIZE(nfs3_acl_procedures)];
2612a613fa16STrond Myklebust const struct rpc_version nfsacl_version3 = {
2613b7fa0554SAndreas Gruenbacher 	.number			= 3,
26149ae7d8ffSChristoph Hellwig 	.nrprocs		= ARRAY_SIZE(nfs3_acl_procedures),
2615b7fa0554SAndreas Gruenbacher 	.procs			= nfs3_acl_procedures,
2616c551858aSChristoph Hellwig 	.counts			= nfs3_acl_counts,
2617b7fa0554SAndreas Gruenbacher };
2618b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2619