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