xref: /openbmc/linux/fs/nfs/nfs3xdr.c (revision cf500bac)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/nfs/nfs3xdr.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * XDR functions to encode/decode NFSv3 RPC arguments and results.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1996, 1997 Olaf Kirch
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/param.h>
111da177e4SLinus Torvalds #include <linux/time.h>
121da177e4SLinus Torvalds #include <linux/mm.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/string.h>
151da177e4SLinus Torvalds #include <linux/in.h>
161da177e4SLinus Torvalds #include <linux/pagemap.h>
171da177e4SLinus Torvalds #include <linux/proc_fs.h>
181da177e4SLinus Torvalds #include <linux/kdev_t.h>
191da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
201da177e4SLinus Torvalds #include <linux/nfs.h>
211da177e4SLinus Torvalds #include <linux/nfs3.h>
221da177e4SLinus Torvalds #include <linux/nfs_fs.h>
23b7fa0554SAndreas Gruenbacher #include <linux/nfsacl.h>
24f23f6584SChuck Lever #include "nfstrace.h"
25f7b422b1SDavid Howells #include "internal.h"
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
301da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /*
331da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
341da177e4SLinus Torvalds  * number of 32bit-words
351da177e4SLinus Torvalds  */
361da177e4SLinus Torvalds #define NFS3_fhandle_sz		(1+16)
371da177e4SLinus Torvalds #define NFS3_fh_sz		(NFS3_fhandle_sz)	/* shorthand */
381da177e4SLinus Torvalds #define NFS3_sattr_sz		(15)
391da177e4SLinus Torvalds #define NFS3_filename_sz	(1+(NFS3_MAXNAMLEN>>2))
401da177e4SLinus Torvalds #define NFS3_path_sz		(1+(NFS3_MAXPATHLEN>>2))
411da177e4SLinus Torvalds #define NFS3_fattr_sz		(21)
42d9c407b1SChuck Lever #define NFS3_cookieverf_sz	(NFS3_COOKIEVERFSIZE>>2)
431da177e4SLinus Torvalds #define NFS3_wcc_attr_sz	(6)
441da177e4SLinus Torvalds #define NFS3_pre_op_attr_sz	(1+NFS3_wcc_attr_sz)
451da177e4SLinus Torvalds #define NFS3_post_op_attr_sz	(1+NFS3_fattr_sz)
461da177e4SLinus Torvalds #define NFS3_wcc_data_sz	(NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
471da177e4SLinus Torvalds #define NFS3_diropargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
48ad96b5b5SChuck Lever 
49ad96b5b5SChuck Lever #define NFS3_getattrargs_sz	(NFS3_fh_sz)
50ad96b5b5SChuck Lever #define NFS3_setattrargs_sz	(NFS3_fh_sz+NFS3_sattr_sz+3)
51ad96b5b5SChuck Lever #define NFS3_lookupargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
521da177e4SLinus Torvalds #define NFS3_accessargs_sz	(NFS3_fh_sz+1)
531da177e4SLinus Torvalds #define NFS3_readlinkargs_sz	(NFS3_fh_sz)
541da177e4SLinus Torvalds #define NFS3_readargs_sz	(NFS3_fh_sz+3)
551da177e4SLinus Torvalds #define NFS3_writeargs_sz	(NFS3_fh_sz+5)
561da177e4SLinus Torvalds #define NFS3_createargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
571da177e4SLinus Torvalds #define NFS3_mkdirargs_sz	(NFS3_diropargs_sz+NFS3_sattr_sz)
5894a6d753SChuck Lever #define NFS3_symlinkargs_sz	(NFS3_diropargs_sz+1+NFS3_sattr_sz)
591da177e4SLinus Torvalds #define NFS3_mknodargs_sz	(NFS3_diropargs_sz+2+NFS3_sattr_sz)
60ad96b5b5SChuck Lever #define NFS3_removeargs_sz	(NFS3_fh_sz+NFS3_filename_sz)
611da177e4SLinus Torvalds #define NFS3_renameargs_sz	(NFS3_diropargs_sz+NFS3_diropargs_sz)
621da177e4SLinus Torvalds #define NFS3_linkargs_sz		(NFS3_fh_sz+NFS3_diropargs_sz)
63d9c407b1SChuck Lever #define NFS3_readdirargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+3)
64d9c407b1SChuck Lever #define NFS3_readdirplusargs_sz	(NFS3_fh_sz+NFS3_cookieverf_sz+4)
651da177e4SLinus Torvalds #define NFS3_commitargs_sz	(NFS3_fh_sz+3)
661da177e4SLinus Torvalds 
67f5fc3c50SChuck Lever #define NFS3_getattrres_sz	(1+NFS3_fattr_sz)
68f5fc3c50SChuck Lever #define NFS3_setattrres_sz	(1+NFS3_wcc_data_sz)
69f5fc3c50SChuck Lever #define NFS3_removeres_sz	(NFS3_setattrres_sz)
701da177e4SLinus Torvalds #define NFS3_lookupres_sz	(1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
711da177e4SLinus Torvalds #define NFS3_accessres_sz	(1+NFS3_post_op_attr_sz+1)
721da177e4SLinus Torvalds #define NFS3_readlinkres_sz	(1+NFS3_post_op_attr_sz+1)
731da177e4SLinus Torvalds #define NFS3_readres_sz		(1+NFS3_post_op_attr_sz+3)
741da177e4SLinus Torvalds #define NFS3_writeres_sz	(1+NFS3_wcc_data_sz+4)
751da177e4SLinus Torvalds #define NFS3_createres_sz	(1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
761da177e4SLinus Torvalds #define NFS3_renameres_sz	(1+(2 * NFS3_wcc_data_sz))
771da177e4SLinus Torvalds #define NFS3_linkres_sz		(1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
781da177e4SLinus Torvalds #define NFS3_readdirres_sz	(1+NFS3_post_op_attr_sz+2)
791da177e4SLinus Torvalds #define NFS3_fsstatres_sz	(1+NFS3_post_op_attr_sz+13)
801da177e4SLinus Torvalds #define NFS3_fsinfores_sz	(1+NFS3_post_op_attr_sz+12)
811da177e4SLinus Torvalds #define NFS3_pathconfres_sz	(1+NFS3_post_op_attr_sz+6)
821da177e4SLinus Torvalds #define NFS3_commitres_sz	(1+NFS3_wcc_data_sz+2)
831da177e4SLinus Torvalds 
84b7fa0554SAndreas Gruenbacher #define ACL3_getaclargs_sz	(NFS3_fh_sz+1)
85ae46141fSTrond Myklebust #define ACL3_setaclargs_sz	(NFS3_fh_sz+1+ \
86ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
87ae46141fSTrond Myklebust #define ACL3_getaclres_sz	(1+NFS3_post_op_attr_sz+1+ \
88ae46141fSTrond Myklebust 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
89b7fa0554SAndreas Gruenbacher #define ACL3_setaclres_sz	(1+NFS3_post_op_attr_sz)
90b7fa0554SAndreas Gruenbacher 
915e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat);
925e7e5a0dSBryan Schumaker 
931da177e4SLinus Torvalds /*
941da177e4SLinus Torvalds  * Map file type to S_IFMT bits
951da177e4SLinus Torvalds  */
96bca79478STrond Myklebust static const umode_t nfs_type2fmt[] = {
97bca79478STrond Myklebust 	[NF3BAD] = 0,
98bca79478STrond Myklebust 	[NF3REG] = S_IFREG,
99bca79478STrond Myklebust 	[NF3DIR] = S_IFDIR,
100bca79478STrond Myklebust 	[NF3BLK] = S_IFBLK,
101bca79478STrond Myklebust 	[NF3CHR] = S_IFCHR,
102bca79478STrond Myklebust 	[NF3LNK] = S_IFLNK,
103bca79478STrond Myklebust 	[NF3SOCK] = S_IFSOCK,
104bca79478STrond Myklebust 	[NF3FIFO] = S_IFIFO,
1051da177e4SLinus Torvalds };
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /*
108d9c407b1SChuck Lever  * Encode/decode NFSv3 basic data types
109d9c407b1SChuck Lever  *
110d9c407b1SChuck Lever  * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
111d9c407b1SChuck Lever  * "NFS Version 3 Protocol Specification".
112d9c407b1SChuck Lever  *
113d9c407b1SChuck Lever  * Not all basic data types have their own encoding and decoding
114d9c407b1SChuck Lever  * functions.  For run-time efficiency, some data types are encoded
115d9c407b1SChuck Lever  * or decoded inline.
116d9c407b1SChuck Lever  */
117d9c407b1SChuck Lever 
118d9c407b1SChuck Lever static void encode_uint32(struct xdr_stream *xdr, u32 value)
119d9c407b1SChuck Lever {
120d9c407b1SChuck Lever 	__be32 *p = xdr_reserve_space(xdr, 4);
121d9c407b1SChuck Lever 	*p = cpu_to_be32(value);
122d9c407b1SChuck Lever }
123d9c407b1SChuck Lever 
124e4f93234SChuck Lever static int decode_uint32(struct xdr_stream *xdr, u32 *value)
125e4f93234SChuck Lever {
126e4f93234SChuck Lever 	__be32 *p;
127e4f93234SChuck Lever 
128e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
129eb72f484SChuck Lever 	if (unlikely(!p))
130eb72f484SChuck Lever 		return -EIO;
131e4f93234SChuck Lever 	*value = be32_to_cpup(p);
132e4f93234SChuck Lever 	return 0;
133e4f93234SChuck Lever }
134e4f93234SChuck Lever 
135e4f93234SChuck Lever static int decode_uint64(struct xdr_stream *xdr, u64 *value)
136e4f93234SChuck Lever {
137e4f93234SChuck Lever 	__be32 *p;
138e4f93234SChuck Lever 
139e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8);
140eb72f484SChuck Lever 	if (unlikely(!p))
141eb72f484SChuck Lever 		return -EIO;
142e4f93234SChuck Lever 	xdr_decode_hyper(p, value);
143e4f93234SChuck Lever 	return 0;
144e4f93234SChuck Lever }
145e4f93234SChuck Lever 
146e4f93234SChuck Lever /*
147e4f93234SChuck Lever  * fileid3
148e4f93234SChuck Lever  *
149e4f93234SChuck Lever  *	typedef uint64 fileid3;
150e4f93234SChuck Lever  */
151f6048709SChuck Lever static __be32 *xdr_decode_fileid3(__be32 *p, u64 *fileid)
152f6048709SChuck Lever {
153f6048709SChuck Lever 	return xdr_decode_hyper(p, fileid);
154f6048709SChuck Lever }
155f6048709SChuck Lever 
156e4f93234SChuck Lever static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid)
157e4f93234SChuck Lever {
158e4f93234SChuck Lever 	return decode_uint64(xdr, fileid);
159e4f93234SChuck Lever }
160e4f93234SChuck Lever 
161d9c407b1SChuck Lever /*
162d9c407b1SChuck Lever  * filename3
163d9c407b1SChuck Lever  *
164d9c407b1SChuck Lever  *	typedef string filename3<>;
165d9c407b1SChuck Lever  */
166d9c407b1SChuck Lever static void encode_filename3(struct xdr_stream *xdr,
167d9c407b1SChuck Lever 			     const char *name, u32 length)
168d9c407b1SChuck Lever {
169d9c407b1SChuck Lever 	__be32 *p;
170d9c407b1SChuck Lever 
1717fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS3_MAXNAMLEN);
172d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
173d9c407b1SChuck Lever 	xdr_encode_opaque(p, name, length);
174d9c407b1SChuck Lever }
175d9c407b1SChuck Lever 
176e4f93234SChuck Lever static int decode_inline_filename3(struct xdr_stream *xdr,
177e4f93234SChuck Lever 				   const char **name, u32 *length)
178e4f93234SChuck Lever {
179e4f93234SChuck Lever 	__be32 *p;
180e4f93234SChuck Lever 	u32 count;
181e4f93234SChuck Lever 
182e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
183eb72f484SChuck Lever 	if (unlikely(!p))
184eb72f484SChuck Lever 		return -EIO;
185e4f93234SChuck Lever 	count = be32_to_cpup(p);
186e4f93234SChuck Lever 	if (count > NFS3_MAXNAMLEN)
187e4f93234SChuck Lever 		goto out_nametoolong;
188e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, count);
189eb72f484SChuck Lever 	if (unlikely(!p))
190eb72f484SChuck Lever 		return -EIO;
191e4f93234SChuck Lever 	*name = (const char *)p;
192e4f93234SChuck Lever 	*length = count;
193e4f93234SChuck Lever 	return 0;
194e4f93234SChuck Lever 
195e4f93234SChuck Lever out_nametoolong:
196e4f93234SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
197e4f93234SChuck Lever 	return -ENAMETOOLONG;
198e4f93234SChuck Lever }
199e4f93234SChuck Lever 
200d9c407b1SChuck Lever /*
201d9c407b1SChuck Lever  * nfspath3
202d9c407b1SChuck Lever  *
203d9c407b1SChuck Lever  *	typedef string nfspath3<>;
204d9c407b1SChuck Lever  */
205d9c407b1SChuck Lever static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
206d9c407b1SChuck Lever 			    const u32 length)
207d9c407b1SChuck Lever {
208d9c407b1SChuck Lever 	encode_uint32(xdr, length);
209d9c407b1SChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
210d9c407b1SChuck Lever }
211d9c407b1SChuck Lever 
212e4f93234SChuck Lever static int decode_nfspath3(struct xdr_stream *xdr)
213e4f93234SChuck Lever {
214e4f93234SChuck Lever 	u32 recvd, count;
215e4f93234SChuck Lever 	__be32 *p;
216e4f93234SChuck Lever 
217e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
218eb72f484SChuck Lever 	if (unlikely(!p))
219eb72f484SChuck Lever 		return -EIO;
220e4f93234SChuck Lever 	count = be32_to_cpup(p);
221e4f93234SChuck Lever 	if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
222e4f93234SChuck Lever 		goto out_nametoolong;
22364bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
224e4f93234SChuck Lever 	if (unlikely(count > recvd))
225e4f93234SChuck Lever 		goto out_cheating;
226e4f93234SChuck Lever 	xdr_terminate_string(xdr->buf, count);
227e4f93234SChuck Lever 	return 0;
228e4f93234SChuck Lever 
229e4f93234SChuck Lever out_nametoolong:
230e4f93234SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", count);
231e4f93234SChuck Lever 	return -ENAMETOOLONG;
232e4f93234SChuck Lever out_cheating:
233e4f93234SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
234e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
235e4f93234SChuck Lever 	return -EIO;
236e4f93234SChuck Lever }
237e4f93234SChuck Lever 
238d9c407b1SChuck Lever /*
239d9c407b1SChuck Lever  * cookie3
240d9c407b1SChuck Lever  *
241d9c407b1SChuck Lever  *	typedef uint64 cookie3
242d9c407b1SChuck Lever  */
243d9c407b1SChuck Lever static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
244d9c407b1SChuck Lever {
245d9c407b1SChuck Lever 	return xdr_encode_hyper(p, cookie);
246d9c407b1SChuck Lever }
247d9c407b1SChuck Lever 
248e4f93234SChuck Lever static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie)
249e4f93234SChuck Lever {
250e4f93234SChuck Lever 	return decode_uint64(xdr, cookie);
251e4f93234SChuck Lever }
252e4f93234SChuck Lever 
253d9c407b1SChuck Lever /*
254d9c407b1SChuck Lever  * cookieverf3
255d9c407b1SChuck Lever  *
256d9c407b1SChuck Lever  *	typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
257d9c407b1SChuck Lever  */
258d9c407b1SChuck Lever static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
259d9c407b1SChuck Lever {
260d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
261d9c407b1SChuck Lever 	return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
262d9c407b1SChuck Lever }
263d9c407b1SChuck Lever 
264e4f93234SChuck Lever static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier)
265e4f93234SChuck Lever {
266e4f93234SChuck Lever 	__be32 *p;
267e4f93234SChuck Lever 
268e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
269eb72f484SChuck Lever 	if (unlikely(!p))
270eb72f484SChuck Lever 		return -EIO;
271e4f93234SChuck Lever 	memcpy(verifier, p, NFS3_COOKIEVERFSIZE);
272e4f93234SChuck Lever 	return 0;
273e4f93234SChuck Lever }
274e4f93234SChuck Lever 
275d9c407b1SChuck Lever /*
276d9c407b1SChuck Lever  * createverf3
277d9c407b1SChuck Lever  *
278d9c407b1SChuck Lever  *	typedef opaque createverf3[NFS3_CREATEVERFSIZE];
279d9c407b1SChuck Lever  */
280d9c407b1SChuck Lever static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
281d9c407b1SChuck Lever {
282d9c407b1SChuck Lever 	__be32 *p;
283d9c407b1SChuck Lever 
284d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
285d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_CREATEVERFSIZE);
286d9c407b1SChuck Lever }
287d9c407b1SChuck Lever 
2882f2c63bcSTrond Myklebust static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
289e4f93234SChuck Lever {
290e4f93234SChuck Lever 	__be32 *p;
291e4f93234SChuck Lever 
292e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
293eb72f484SChuck Lever 	if (unlikely(!p))
294eb72f484SChuck Lever 		return -EIO;
2952f2c63bcSTrond Myklebust 	memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
296e4f93234SChuck Lever 	return 0;
297e4f93234SChuck Lever }
298e4f93234SChuck Lever 
299e4f93234SChuck Lever /*
300e4f93234SChuck Lever  * size3
301e4f93234SChuck Lever  *
302e4f93234SChuck Lever  *	typedef uint64 size3;
303e4f93234SChuck Lever  */
304e4f93234SChuck Lever static __be32 *xdr_decode_size3(__be32 *p, u64 *size)
305e4f93234SChuck Lever {
306e4f93234SChuck Lever 	return xdr_decode_hyper(p, size);
307e4f93234SChuck Lever }
308e4f93234SChuck Lever 
309e4f93234SChuck Lever /*
310e4f93234SChuck Lever  * nfsstat3
311e4f93234SChuck Lever  *
312e4f93234SChuck Lever  *	enum nfsstat3 {
313e4f93234SChuck Lever  *		NFS3_OK = 0,
314e4f93234SChuck Lever  *		...
315e4f93234SChuck Lever  *	}
316e4f93234SChuck Lever  */
317e4f93234SChuck Lever #define NFS3_OK		NFS_OK
318e4f93234SChuck Lever 
319e4f93234SChuck Lever static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status)
320e4f93234SChuck Lever {
321e4f93234SChuck Lever 	__be32 *p;
322e4f93234SChuck Lever 
323e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
324eb72f484SChuck Lever 	if (unlikely(!p))
325eb72f484SChuck Lever 		return -EIO;
326f23f6584SChuck Lever 	if (unlikely(*p != cpu_to_be32(NFS3_OK)))
327f23f6584SChuck Lever 		goto out_status;
328f23f6584SChuck Lever 	*status = 0;
329f23f6584SChuck Lever 	return 0;
330f23f6584SChuck Lever out_status:
331e4f93234SChuck Lever 	*status = be32_to_cpup(p);
332f23f6584SChuck Lever 	trace_nfs_xdr_status((int)*status);
333e4f93234SChuck Lever 	return 0;
334e4f93234SChuck Lever }
335e4f93234SChuck Lever 
336d9c407b1SChuck Lever /*
337d9c407b1SChuck Lever  * ftype3
338d9c407b1SChuck Lever  *
339d9c407b1SChuck Lever  *	enum ftype3 {
340d9c407b1SChuck Lever  *		NF3REG	= 1,
341d9c407b1SChuck Lever  *		NF3DIR	= 2,
342d9c407b1SChuck Lever  *		NF3BLK	= 3,
343d9c407b1SChuck Lever  *		NF3CHR	= 4,
344d9c407b1SChuck Lever  *		NF3LNK	= 5,
345d9c407b1SChuck Lever  *		NF3SOCK	= 6,
346d9c407b1SChuck Lever  *		NF3FIFO	= 7
347d9c407b1SChuck Lever  *	};
348d9c407b1SChuck Lever  */
349d9c407b1SChuck Lever static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
350d9c407b1SChuck Lever {
351d9c407b1SChuck Lever 	encode_uint32(xdr, type);
352d9c407b1SChuck Lever }
353d9c407b1SChuck Lever 
354f6048709SChuck Lever static __be32 *xdr_decode_ftype3(__be32 *p, umode_t *mode)
355f6048709SChuck Lever {
356f6048709SChuck Lever 	u32 type;
357f6048709SChuck Lever 
358f6048709SChuck Lever 	type = be32_to_cpup(p++);
359f6048709SChuck Lever 	if (type > NF3FIFO)
360f6048709SChuck Lever 		type = NF3NON;
361f6048709SChuck Lever 	*mode = nfs_type2fmt[type];
362f6048709SChuck Lever 	return p;
363f6048709SChuck Lever }
364f6048709SChuck Lever 
365d9c407b1SChuck Lever /*
366d9c407b1SChuck Lever  * specdata3
367d9c407b1SChuck Lever  *
368d9c407b1SChuck Lever  *     struct specdata3 {
369d9c407b1SChuck Lever  *             uint32  specdata1;
370d9c407b1SChuck Lever  *             uint32  specdata2;
371d9c407b1SChuck Lever  *     };
372d9c407b1SChuck Lever  */
373d9c407b1SChuck Lever static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
374d9c407b1SChuck Lever {
375d9c407b1SChuck Lever 	__be32 *p;
376d9c407b1SChuck Lever 
377d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8);
378d9c407b1SChuck Lever 	*p++ = cpu_to_be32(MAJOR(rdev));
379d9c407b1SChuck Lever 	*p = cpu_to_be32(MINOR(rdev));
380d9c407b1SChuck Lever }
381d9c407b1SChuck Lever 
382f6048709SChuck Lever static __be32 *xdr_decode_specdata3(__be32 *p, dev_t *rdev)
383f6048709SChuck Lever {
384f6048709SChuck Lever 	unsigned int major, minor;
385f6048709SChuck Lever 
386f6048709SChuck Lever 	major = be32_to_cpup(p++);
387f6048709SChuck Lever 	minor = be32_to_cpup(p++);
388f6048709SChuck Lever 	*rdev = MKDEV(major, minor);
389f6048709SChuck Lever 	if (MAJOR(*rdev) != major || MINOR(*rdev) != minor)
390f6048709SChuck Lever 		*rdev = 0;
391f6048709SChuck Lever 	return p;
392f6048709SChuck Lever }
393f6048709SChuck Lever 
394d9c407b1SChuck Lever /*
395d9c407b1SChuck Lever  * nfs_fh3
396d9c407b1SChuck Lever  *
397d9c407b1SChuck Lever  *	struct nfs_fh3 {
398d9c407b1SChuck Lever  *		opaque       data<NFS3_FHSIZE>;
399d9c407b1SChuck Lever  *	};
400d9c407b1SChuck Lever  */
401d9c407b1SChuck Lever static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
402d9c407b1SChuck Lever {
403d9c407b1SChuck Lever 	__be32 *p;
404d9c407b1SChuck Lever 
4057fc38846STrond Myklebust 	WARN_ON_ONCE(fh->size > NFS3_FHSIZE);
406d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + fh->size);
407d9c407b1SChuck Lever 	xdr_encode_opaque(p, fh->data, fh->size);
408d9c407b1SChuck Lever }
409d9c407b1SChuck Lever 
410e4f93234SChuck Lever static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
411e4f93234SChuck Lever {
412e4f93234SChuck Lever 	u32 length;
413e4f93234SChuck Lever 	__be32 *p;
414e4f93234SChuck Lever 
415e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
416eb72f484SChuck Lever 	if (unlikely(!p))
417eb72f484SChuck Lever 		return -EIO;
418e4f93234SChuck Lever 	length = be32_to_cpup(p++);
419e4f93234SChuck Lever 	if (unlikely(length > NFS3_FHSIZE))
420e4f93234SChuck Lever 		goto out_toobig;
421e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, length);
422eb72f484SChuck Lever 	if (unlikely(!p))
423eb72f484SChuck Lever 		return -EIO;
424e4f93234SChuck Lever 	fh->size = length;
425e4f93234SChuck Lever 	memcpy(fh->data, p, length);
426e4f93234SChuck Lever 	return 0;
427e4f93234SChuck Lever out_toobig:
428e4f93234SChuck Lever 	dprintk("NFS: file handle size (%u) too big\n", length);
429e4f93234SChuck Lever 	return -E2BIG;
430e4f93234SChuck Lever }
431e4f93234SChuck Lever 
432e4f93234SChuck Lever static void zero_nfs_fh3(struct nfs_fh *fh)
433e4f93234SChuck Lever {
434e4f93234SChuck Lever 	memset(fh, 0, sizeof(*fh));
435e4f93234SChuck Lever }
436e4f93234SChuck Lever 
437d9c407b1SChuck Lever /*
4389d5a6434SChuck Lever  * nfstime3
4399d5a6434SChuck Lever  *
4409d5a6434SChuck Lever  *	struct nfstime3 {
4419d5a6434SChuck Lever  *		uint32	seconds;
4429d5a6434SChuck Lever  *		uint32	nseconds;
4439d5a6434SChuck Lever  *	};
4449d5a6434SChuck Lever  */
4459d5a6434SChuck Lever static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec *timep)
4469d5a6434SChuck Lever {
4479d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
4489d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_nsec);
4499d5a6434SChuck Lever 	return p;
4509d5a6434SChuck Lever }
4519d5a6434SChuck Lever 
452f6048709SChuck Lever static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec *timep)
453f6048709SChuck Lever {
454f6048709SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
455f6048709SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++);
456f6048709SChuck Lever 	return p;
457f6048709SChuck Lever }
458f6048709SChuck Lever 
4599d5a6434SChuck Lever /*
460d9c407b1SChuck Lever  * sattr3
461d9c407b1SChuck Lever  *
462d9c407b1SChuck Lever  *	enum time_how {
463d9c407b1SChuck Lever  *		DONT_CHANGE		= 0,
464d9c407b1SChuck Lever  *		SET_TO_SERVER_TIME	= 1,
465d9c407b1SChuck Lever  *		SET_TO_CLIENT_TIME	= 2
466d9c407b1SChuck Lever  *	};
467d9c407b1SChuck Lever  *
468d9c407b1SChuck Lever  *	union set_mode3 switch (bool set_it) {
469d9c407b1SChuck Lever  *	case TRUE:
470d9c407b1SChuck Lever  *		mode3	mode;
471d9c407b1SChuck Lever  *	default:
472d9c407b1SChuck Lever  *		void;
473d9c407b1SChuck Lever  *	};
474d9c407b1SChuck Lever  *
475d9c407b1SChuck Lever  *	union set_uid3 switch (bool set_it) {
476d9c407b1SChuck Lever  *	case TRUE:
477d9c407b1SChuck Lever  *		uid3	uid;
478d9c407b1SChuck Lever  *	default:
479d9c407b1SChuck Lever  *		void;
480d9c407b1SChuck Lever  *	};
481d9c407b1SChuck Lever  *
482d9c407b1SChuck Lever  *	union set_gid3 switch (bool set_it) {
483d9c407b1SChuck Lever  *	case TRUE:
484d9c407b1SChuck Lever  *		gid3	gid;
485d9c407b1SChuck Lever  *	default:
486d9c407b1SChuck Lever  *		void;
487d9c407b1SChuck Lever  *	};
488d9c407b1SChuck Lever  *
489d9c407b1SChuck Lever  *	union set_size3 switch (bool set_it) {
490d9c407b1SChuck Lever  *	case TRUE:
491d9c407b1SChuck Lever  *		size3	size;
492d9c407b1SChuck Lever  *	default:
493d9c407b1SChuck Lever  *		void;
494d9c407b1SChuck Lever  *	};
495d9c407b1SChuck Lever  *
496d9c407b1SChuck Lever  *	union set_atime switch (time_how set_it) {
497d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
498d9c407b1SChuck Lever  *		nfstime3	atime;
499d9c407b1SChuck Lever  *	default:
500d9c407b1SChuck Lever  *		void;
501d9c407b1SChuck Lever  *	};
502d9c407b1SChuck Lever  *
503d9c407b1SChuck Lever  *	union set_mtime switch (time_how set_it) {
504d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
505d9c407b1SChuck Lever  *		nfstime3  mtime;
506d9c407b1SChuck Lever  *	default:
507d9c407b1SChuck Lever  *		void;
508d9c407b1SChuck Lever  *	};
509d9c407b1SChuck Lever  *
510d9c407b1SChuck Lever  *	struct sattr3 {
511d9c407b1SChuck Lever  *		set_mode3	mode;
512d9c407b1SChuck Lever  *		set_uid3	uid;
513d9c407b1SChuck Lever  *		set_gid3	gid;
514d9c407b1SChuck Lever  *		set_size3	size;
515d9c407b1SChuck Lever  *		set_atime	atime;
516d9c407b1SChuck Lever  *		set_mtime	mtime;
517d9c407b1SChuck Lever  *	};
518d9c407b1SChuck Lever  */
519d9c407b1SChuck Lever static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
520d9c407b1SChuck Lever {
52195582b00SDeepa Dinamani 	struct timespec ts;
522d9c407b1SChuck Lever 	u32 nbytes;
523d9c407b1SChuck Lever 	__be32 *p;
524d9c407b1SChuck Lever 
525d9c407b1SChuck Lever 	/*
526d9c407b1SChuck Lever 	 * In order to make only a single xdr_reserve_space() call,
527d9c407b1SChuck Lever 	 * pre-compute the total number of bytes to be reserved.
528d9c407b1SChuck Lever 	 * Six boolean values, one for each set_foo field, are always
529d9c407b1SChuck Lever 	 * present in the encoded result, so start there.
530d9c407b1SChuck Lever 	 */
531d9c407b1SChuck Lever 	nbytes = 6 * 4;
532d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MODE)
533d9c407b1SChuck Lever 		nbytes += 4;
534d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_UID)
535d9c407b1SChuck Lever 		nbytes += 4;
536d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_GID)
537d9c407b1SChuck Lever 		nbytes += 4;
538d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
539d9c407b1SChuck Lever 		nbytes += 8;
540d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
541d9c407b1SChuck Lever 		nbytes += 8;
542d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
543d9c407b1SChuck Lever 		nbytes += 8;
544d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, nbytes);
545d9c407b1SChuck Lever 
5469d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MODE) {
5479d5a6434SChuck Lever 		*p++ = xdr_one;
5489d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
5499d5a6434SChuck Lever 	} else
5509d5a6434SChuck Lever 		*p++ = xdr_zero;
5519d5a6434SChuck Lever 
5529d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_UID) {
5539d5a6434SChuck Lever 		*p++ = xdr_one;
55457a38daeSEric W. Biederman 		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid));
5559d5a6434SChuck Lever 	} else
5569d5a6434SChuck Lever 		*p++ = xdr_zero;
5579d5a6434SChuck Lever 
5589d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_GID) {
5599d5a6434SChuck Lever 		*p++ = xdr_one;
56057a38daeSEric W. Biederman 		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid));
5619d5a6434SChuck Lever 	} else
5629d5a6434SChuck Lever 		*p++ = xdr_zero;
5639d5a6434SChuck Lever 
5649d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_SIZE) {
5659d5a6434SChuck Lever 		*p++ = xdr_one;
5669d5a6434SChuck Lever 		p = xdr_encode_hyper(p, (u64)attr->ia_size);
5679d5a6434SChuck Lever 	} else
5689d5a6434SChuck Lever 		*p++ = xdr_zero;
5699d5a6434SChuck Lever 
5709d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET) {
57195582b00SDeepa Dinamani 		struct timespec ts;
5729d5a6434SChuck Lever 		*p++ = xdr_two;
57395582b00SDeepa Dinamani 		ts = timespec64_to_timespec(attr->ia_atime);
57495582b00SDeepa Dinamani 		p = xdr_encode_nfstime3(p, &ts);
5759d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_ATIME) {
5769d5a6434SChuck Lever 		*p++ = xdr_one;
5779d5a6434SChuck Lever 	} else
5789d5a6434SChuck Lever 		*p++ = xdr_zero;
5799d5a6434SChuck Lever 
5809d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET) {
5819d5a6434SChuck Lever 		*p++ = xdr_two;
58295582b00SDeepa Dinamani 		ts = timespec64_to_timespec(attr->ia_mtime);
58395582b00SDeepa Dinamani 		xdr_encode_nfstime3(p, &ts);
5849d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_MTIME) {
5859d5a6434SChuck Lever 		*p = xdr_one;
5869d5a6434SChuck Lever 	} else
5879d5a6434SChuck Lever 		*p = xdr_zero;
588d9c407b1SChuck Lever }
589d9c407b1SChuck Lever 
590d9c407b1SChuck Lever /*
591e4f93234SChuck Lever  * fattr3
592e4f93234SChuck Lever  *
593e4f93234SChuck Lever  *	struct fattr3 {
594e4f93234SChuck Lever  *		ftype3		type;
595e4f93234SChuck Lever  *		mode3		mode;
596e4f93234SChuck Lever  *		uint32		nlink;
597e4f93234SChuck Lever  *		uid3		uid;
598e4f93234SChuck Lever  *		gid3		gid;
599e4f93234SChuck Lever  *		size3		size;
600e4f93234SChuck Lever  *		size3		used;
601e4f93234SChuck Lever  *		specdata3	rdev;
602e4f93234SChuck Lever  *		uint64		fsid;
603e4f93234SChuck Lever  *		fileid3		fileid;
604e4f93234SChuck Lever  *		nfstime3	atime;
605e4f93234SChuck Lever  *		nfstime3	mtime;
606e4f93234SChuck Lever  *		nfstime3	ctime;
607e4f93234SChuck Lever  *	};
608e4f93234SChuck Lever  */
609e4f93234SChuck Lever static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
610e4f93234SChuck Lever {
611f6048709SChuck Lever 	umode_t fmode;
612e4f93234SChuck Lever 	__be32 *p;
613e4f93234SChuck Lever 
614e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2);
615eb72f484SChuck Lever 	if (unlikely(!p))
616eb72f484SChuck Lever 		return -EIO;
617f6048709SChuck Lever 
618f6048709SChuck Lever 	p = xdr_decode_ftype3(p, &fmode);
619f6048709SChuck Lever 
620f6048709SChuck Lever 	fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;
621f6048709SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
62257a38daeSEric W. Biederman 	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++));
62357a38daeSEric W. Biederman 	if (!uid_valid(fattr->uid))
62457a38daeSEric W. Biederman 		goto out_uid;
62557a38daeSEric W. Biederman 	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++));
62657a38daeSEric W. Biederman 	if (!gid_valid(fattr->gid))
62757a38daeSEric W. Biederman 		goto out_gid;
628f6048709SChuck Lever 
629f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->size);
630f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->du.nfs3.used);
631f6048709SChuck Lever 	p = xdr_decode_specdata3(p, &fattr->rdev);
632f6048709SChuck Lever 
633f6048709SChuck Lever 	p = xdr_decode_hyper(p, &fattr->fsid.major);
634f6048709SChuck Lever 	fattr->fsid.minor = 0;
635f6048709SChuck Lever 
636f6048709SChuck Lever 	p = xdr_decode_fileid3(p, &fattr->fileid);
637f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->atime);
638f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->mtime);
639f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->ctime);
6403a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
641f6048709SChuck Lever 
642f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V3;
643e4f93234SChuck Lever 	return 0;
64457a38daeSEric W. Biederman out_uid:
64557a38daeSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
64657a38daeSEric W. Biederman 	return -EINVAL;
64757a38daeSEric W. Biederman out_gid:
64857a38daeSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
64957a38daeSEric W. Biederman 	return -EINVAL;
650e4f93234SChuck Lever }
651e4f93234SChuck Lever 
652e4f93234SChuck Lever /*
653e4f93234SChuck Lever  * post_op_attr
654e4f93234SChuck Lever  *
655e4f93234SChuck Lever  *	union post_op_attr switch (bool attributes_follow) {
656e4f93234SChuck Lever  *	case TRUE:
657e4f93234SChuck Lever  *		fattr3	attributes;
658e4f93234SChuck Lever  *	case FALSE:
659e4f93234SChuck Lever  *		void;
660e4f93234SChuck Lever  *	};
661e4f93234SChuck Lever  */
662e4f93234SChuck Lever static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
663e4f93234SChuck Lever {
664e4f93234SChuck Lever 	__be32 *p;
665e4f93234SChuck Lever 
666e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
667eb72f484SChuck Lever 	if (unlikely(!p))
668eb72f484SChuck Lever 		return -EIO;
669e4f93234SChuck Lever 	if (*p != xdr_zero)
670e4f93234SChuck Lever 		return decode_fattr3(xdr, fattr);
671e4f93234SChuck Lever 	return 0;
672e4f93234SChuck Lever }
673e4f93234SChuck Lever 
674e4f93234SChuck Lever /*
675e4f93234SChuck Lever  * wcc_attr
676e4f93234SChuck Lever  *	struct wcc_attr {
677e4f93234SChuck Lever  *		size3		size;
678e4f93234SChuck Lever  *		nfstime3	mtime;
679e4f93234SChuck Lever  *		nfstime3	ctime;
680e4f93234SChuck Lever  *	};
681e4f93234SChuck Lever  */
682e4f93234SChuck Lever static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
683e4f93234SChuck Lever {
684e4f93234SChuck Lever 	__be32 *p;
685e4f93234SChuck Lever 
686e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2);
687eb72f484SChuck Lever 	if (unlikely(!p))
688eb72f484SChuck Lever 		return -EIO;
689f6048709SChuck Lever 
690f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_PRESIZE
6913a1556e8STrond Myklebust 		| NFS_ATTR_FATTR_PRECHANGE
692f6048709SChuck Lever 		| NFS_ATTR_FATTR_PREMTIME
693f6048709SChuck Lever 		| NFS_ATTR_FATTR_PRECTIME;
694f6048709SChuck Lever 
695f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->pre_size);
696f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
697f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->pre_ctime);
6983a1556e8STrond Myklebust 	fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime);
699f6048709SChuck Lever 
700e4f93234SChuck Lever 	return 0;
701e4f93234SChuck Lever }
702e4f93234SChuck Lever 
703e4f93234SChuck Lever /*
704e4f93234SChuck Lever  * pre_op_attr
705e4f93234SChuck Lever  *	union pre_op_attr switch (bool attributes_follow) {
706e4f93234SChuck Lever  *	case TRUE:
707e4f93234SChuck Lever  *		wcc_attr	attributes;
708e4f93234SChuck Lever  *	case FALSE:
709e4f93234SChuck Lever  *		void;
710e4f93234SChuck Lever  *	};
711e4f93234SChuck Lever  *
712e4f93234SChuck Lever  * wcc_data
713e4f93234SChuck Lever  *
714e4f93234SChuck Lever  *	struct wcc_data {
715e4f93234SChuck Lever  *		pre_op_attr	before;
716e4f93234SChuck Lever  *		post_op_attr	after;
717e4f93234SChuck Lever  *	};
718e4f93234SChuck Lever  */
719e4f93234SChuck Lever static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
720e4f93234SChuck Lever {
721e4f93234SChuck Lever 	__be32 *p;
722e4f93234SChuck Lever 
723e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
724eb72f484SChuck Lever 	if (unlikely(!p))
725eb72f484SChuck Lever 		return -EIO;
726e4f93234SChuck Lever 	if (*p != xdr_zero)
727e4f93234SChuck Lever 		return decode_wcc_attr(xdr, fattr);
728e4f93234SChuck Lever 	return 0;
729e4f93234SChuck Lever }
730e4f93234SChuck Lever 
731e4f93234SChuck Lever static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr)
732e4f93234SChuck Lever {
733e4f93234SChuck Lever 	int error;
734e4f93234SChuck Lever 
735e4f93234SChuck Lever 	error = decode_pre_op_attr(xdr, fattr);
736e4f93234SChuck Lever 	if (unlikely(error))
737e4f93234SChuck Lever 		goto out;
738e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, fattr);
739e4f93234SChuck Lever out:
740e4f93234SChuck Lever 	return error;
741e4f93234SChuck Lever }
742e4f93234SChuck Lever 
743e4f93234SChuck Lever /*
744e4f93234SChuck Lever  * post_op_fh3
745e4f93234SChuck Lever  *
746e4f93234SChuck Lever  *	union post_op_fh3 switch (bool handle_follows) {
747e4f93234SChuck Lever  *	case TRUE:
748e4f93234SChuck Lever  *		nfs_fh3  handle;
749e4f93234SChuck Lever  *	case FALSE:
750e4f93234SChuck Lever  *		void;
751e4f93234SChuck Lever  *	};
752e4f93234SChuck Lever  */
753e4f93234SChuck Lever static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
754e4f93234SChuck Lever {
755e4f93234SChuck Lever 	__be32 *p = xdr_inline_decode(xdr, 4);
756eb72f484SChuck Lever 	if (unlikely(!p))
757eb72f484SChuck Lever 		return -EIO;
758e4f93234SChuck Lever 	if (*p != xdr_zero)
759e4f93234SChuck Lever 		return decode_nfs_fh3(xdr, fh);
760e4f93234SChuck Lever 	zero_nfs_fh3(fh);
761e4f93234SChuck Lever 	return 0;
762e4f93234SChuck Lever }
763e4f93234SChuck Lever 
764e4f93234SChuck Lever /*
765d9c407b1SChuck Lever  * diropargs3
766d9c407b1SChuck Lever  *
767d9c407b1SChuck Lever  *	struct diropargs3 {
768d9c407b1SChuck Lever  *		nfs_fh3		dir;
769d9c407b1SChuck Lever  *		filename3	name;
770d9c407b1SChuck Lever  *	};
771d9c407b1SChuck Lever  */
772d9c407b1SChuck Lever static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
773d9c407b1SChuck Lever 			      const char *name, u32 length)
774d9c407b1SChuck Lever {
775d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, fh);
776d9c407b1SChuck Lever 	encode_filename3(xdr, name, length);
777d9c407b1SChuck Lever }
778d9c407b1SChuck Lever 
779d9c407b1SChuck Lever 
7801da177e4SLinus Torvalds /*
781499ff710SChuck Lever  * NFSv3 XDR encode functions
782499ff710SChuck Lever  *
783499ff710SChuck Lever  * NFSv3 argument types are defined in section 3.3 of RFC 1813:
784499ff710SChuck Lever  * "NFS Version 3 Protocol Specification".
7851da177e4SLinus Torvalds  */
7861da177e4SLinus Torvalds 
7871da177e4SLinus Torvalds /*
788d9c407b1SChuck Lever  * 3.3.1  GETATTR3args
789d9c407b1SChuck Lever  *
790d9c407b1SChuck Lever  *	struct GETATTR3args {
791d9c407b1SChuck Lever  *		nfs_fh3  object;
792d9c407b1SChuck Lever  *	};
793d9c407b1SChuck Lever  */
7949f06c719SChuck Lever static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
7959f06c719SChuck Lever 				      struct xdr_stream *xdr,
796fcc85819SChristoph Hellwig 				      const void *data)
797d9c407b1SChuck Lever {
798fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
799fcc85819SChristoph Hellwig 
8009f06c719SChuck Lever 	encode_nfs_fh3(xdr, fh);
801d9c407b1SChuck Lever }
802d9c407b1SChuck Lever 
803d9c407b1SChuck Lever /*
804d9c407b1SChuck Lever  * 3.3.2  SETATTR3args
805d9c407b1SChuck Lever  *
806d9c407b1SChuck Lever  *	union sattrguard3 switch (bool check) {
807d9c407b1SChuck Lever  *	case TRUE:
808d9c407b1SChuck Lever  *		nfstime3  obj_ctime;
809d9c407b1SChuck Lever  *	case FALSE:
810d9c407b1SChuck Lever  *		void;
811d9c407b1SChuck Lever  *	};
812d9c407b1SChuck Lever  *
813d9c407b1SChuck Lever  *	struct SETATTR3args {
814d9c407b1SChuck Lever  *		nfs_fh3		object;
815d9c407b1SChuck Lever  *		sattr3		new_attributes;
816d9c407b1SChuck Lever  *		sattrguard3	guard;
817d9c407b1SChuck Lever  *	};
818d9c407b1SChuck Lever  */
819d9c407b1SChuck Lever static void encode_sattrguard3(struct xdr_stream *xdr,
820d9c407b1SChuck Lever 			       const struct nfs3_sattrargs *args)
821d9c407b1SChuck Lever {
822d9c407b1SChuck Lever 	__be32 *p;
823d9c407b1SChuck Lever 
824d9c407b1SChuck Lever 	if (args->guard) {
825d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4 + 8);
826d9c407b1SChuck Lever 		*p++ = xdr_one;
8279d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &args->guardtime);
828d9c407b1SChuck Lever 	} else {
829d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4);
830d9c407b1SChuck Lever 		*p = xdr_zero;
831d9c407b1SChuck Lever 	}
832d9c407b1SChuck Lever }
833d9c407b1SChuck Lever 
8349f06c719SChuck Lever static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
8359f06c719SChuck Lever 				      struct xdr_stream *xdr,
836fcc85819SChristoph Hellwig 				      const void *data)
837d9c407b1SChuck Lever {
838fcc85819SChristoph Hellwig 	const struct nfs3_sattrargs *args = data;
8399f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
8409f06c719SChuck Lever 	encode_sattr3(xdr, args->sattr);
8419f06c719SChuck Lever 	encode_sattrguard3(xdr, args);
842d9c407b1SChuck Lever }
843d9c407b1SChuck Lever 
844d9c407b1SChuck Lever /*
845d9c407b1SChuck Lever  * 3.3.3  LOOKUP3args
846d9c407b1SChuck Lever  *
847d9c407b1SChuck Lever  *	struct LOOKUP3args {
848d9c407b1SChuck Lever  *		diropargs3  what;
849d9c407b1SChuck Lever  *	};
850d9c407b1SChuck Lever  */
8519f06c719SChuck Lever static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
8529f06c719SChuck Lever 				     struct xdr_stream *xdr,
853fcc85819SChristoph Hellwig 				     const void *data)
854d9c407b1SChuck Lever {
855fcc85819SChristoph Hellwig 	const struct nfs3_diropargs *args = data;
856fcc85819SChristoph Hellwig 
8579f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
858d9c407b1SChuck Lever }
859d9c407b1SChuck Lever 
860d9c407b1SChuck Lever /*
861d9c407b1SChuck Lever  * 3.3.4  ACCESS3args
862d9c407b1SChuck Lever  *
863d9c407b1SChuck Lever  *	struct ACCESS3args {
864d9c407b1SChuck Lever  *		nfs_fh3		object;
865d9c407b1SChuck Lever  *		uint32		access;
866d9c407b1SChuck Lever  *	};
867d9c407b1SChuck Lever  */
868d9c407b1SChuck Lever static void encode_access3args(struct xdr_stream *xdr,
869d9c407b1SChuck Lever 			       const struct nfs3_accessargs *args)
870d9c407b1SChuck Lever {
871d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
872d9c407b1SChuck Lever 	encode_uint32(xdr, args->access);
873d9c407b1SChuck Lever }
874d9c407b1SChuck Lever 
8759f06c719SChuck Lever static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
8769f06c719SChuck Lever 				     struct xdr_stream *xdr,
877fcc85819SChristoph Hellwig 				     const void *data)
878d9c407b1SChuck Lever {
879fcc85819SChristoph Hellwig 	const struct nfs3_accessargs *args = data;
880fcc85819SChristoph Hellwig 
8819f06c719SChuck Lever 	encode_access3args(xdr, args);
882d9c407b1SChuck Lever }
883d9c407b1SChuck Lever 
884d9c407b1SChuck Lever /*
885d9c407b1SChuck Lever  * 3.3.5  READLINK3args
886d9c407b1SChuck Lever  *
887d9c407b1SChuck Lever  *	struct READLINK3args {
888d9c407b1SChuck Lever  *		nfs_fh3	symlink;
889d9c407b1SChuck Lever  *	};
890d9c407b1SChuck Lever  */
8919f06c719SChuck Lever static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
8929f06c719SChuck Lever 				       struct xdr_stream *xdr,
893fcc85819SChristoph Hellwig 				       const void *data)
894d9c407b1SChuck Lever {
895fcc85819SChristoph Hellwig 	const struct nfs3_readlinkargs *args = data;
896fcc85819SChristoph Hellwig 
8979f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
898cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
899d9c407b1SChuck Lever 				args->pglen, NFS3_readlinkres_sz);
900d9c407b1SChuck Lever }
901d9c407b1SChuck Lever 
902d9c407b1SChuck Lever /*
903d9c407b1SChuck Lever  * 3.3.6  READ3args
904d9c407b1SChuck Lever  *
905d9c407b1SChuck Lever  *	struct READ3args {
906d9c407b1SChuck Lever  *		nfs_fh3		file;
907d9c407b1SChuck Lever  *		offset3		offset;
908d9c407b1SChuck Lever  *		count3		count;
909d9c407b1SChuck Lever  *	};
910d9c407b1SChuck Lever  */
911d9c407b1SChuck Lever static void encode_read3args(struct xdr_stream *xdr,
9123c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
913d9c407b1SChuck Lever {
914d9c407b1SChuck Lever 	__be32 *p;
915d9c407b1SChuck Lever 
916d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
917d9c407b1SChuck Lever 
918d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
919d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
920d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
921d9c407b1SChuck Lever }
922d9c407b1SChuck Lever 
9239f06c719SChuck Lever static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
9249f06c719SChuck Lever 				   struct xdr_stream *xdr,
925fcc85819SChristoph Hellwig 				   const void *data)
926d9c407b1SChuck Lever {
927fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
9288d8928d8STrond Myklebust 	unsigned int replen = args->replen ? args->replen : NFS3_readres_sz;
929fcc85819SChristoph Hellwig 
9309f06c719SChuck Lever 	encode_read3args(xdr, args);
931cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
9328d8928d8STrond Myklebust 				args->count, replen);
933d9c407b1SChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
934d9c407b1SChuck Lever }
935d9c407b1SChuck Lever 
936d9c407b1SChuck Lever /*
937d9c407b1SChuck Lever  * 3.3.7  WRITE3args
938d9c407b1SChuck Lever  *
939d9c407b1SChuck Lever  *	enum stable_how {
940d9c407b1SChuck Lever  *		UNSTABLE  = 0,
941d9c407b1SChuck Lever  *		DATA_SYNC = 1,
942d9c407b1SChuck Lever  *		FILE_SYNC = 2
943d9c407b1SChuck Lever  *	};
944d9c407b1SChuck Lever  *
945d9c407b1SChuck Lever  *	struct WRITE3args {
946d9c407b1SChuck Lever  *		nfs_fh3		file;
947d9c407b1SChuck Lever  *		offset3		offset;
948d9c407b1SChuck Lever  *		count3		count;
949d9c407b1SChuck Lever  *		stable_how	stable;
950d9c407b1SChuck Lever  *		opaque		data<>;
951d9c407b1SChuck Lever  *	};
952d9c407b1SChuck Lever  */
953d9c407b1SChuck Lever static void encode_write3args(struct xdr_stream *xdr,
9543c6b899cSAnna Schumaker 			      const struct nfs_pgio_args *args)
955d9c407b1SChuck Lever {
956d9c407b1SChuck Lever 	__be32 *p;
957d9c407b1SChuck Lever 
958d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
959d9c407b1SChuck Lever 
960d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
961d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
962d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count);
963d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->stable);
964d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
965d9c407b1SChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
966d9c407b1SChuck Lever }
967d9c407b1SChuck Lever 
9689f06c719SChuck Lever static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
9699f06c719SChuck Lever 				    struct xdr_stream *xdr,
970fcc85819SChristoph Hellwig 				    const void *data)
971d9c407b1SChuck Lever {
972fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
973fcc85819SChristoph Hellwig 
9749f06c719SChuck Lever 	encode_write3args(xdr, args);
9759f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
976d9c407b1SChuck Lever }
977d9c407b1SChuck Lever 
978d9c407b1SChuck Lever /*
979d9c407b1SChuck Lever  * 3.3.8  CREATE3args
980d9c407b1SChuck Lever  *
981d9c407b1SChuck Lever  *	enum createmode3 {
982d9c407b1SChuck Lever  *		UNCHECKED = 0,
983d9c407b1SChuck Lever  *		GUARDED   = 1,
984d9c407b1SChuck Lever  *		EXCLUSIVE = 2
985d9c407b1SChuck Lever  *	};
986d9c407b1SChuck Lever  *
987d9c407b1SChuck Lever  *	union createhow3 switch (createmode3 mode) {
988d9c407b1SChuck Lever  *	case UNCHECKED:
989d9c407b1SChuck Lever  *	case GUARDED:
990d9c407b1SChuck Lever  *		sattr3       obj_attributes;
991d9c407b1SChuck Lever  *	case EXCLUSIVE:
992d9c407b1SChuck Lever  *		createverf3  verf;
993d9c407b1SChuck Lever  *	};
994d9c407b1SChuck Lever  *
995d9c407b1SChuck Lever  *	struct CREATE3args {
996d9c407b1SChuck Lever  *		diropargs3	where;
997d9c407b1SChuck Lever  *		createhow3	how;
998d9c407b1SChuck Lever  *	};
999d9c407b1SChuck Lever  */
1000d9c407b1SChuck Lever static void encode_createhow3(struct xdr_stream *xdr,
1001d9c407b1SChuck Lever 			      const struct nfs3_createargs *args)
1002d9c407b1SChuck Lever {
1003d9c407b1SChuck Lever 	encode_uint32(xdr, args->createmode);
1004d9c407b1SChuck Lever 	switch (args->createmode) {
1005d9c407b1SChuck Lever 	case NFS3_CREATE_UNCHECKED:
1006d9c407b1SChuck Lever 	case NFS3_CREATE_GUARDED:
1007d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
1008d9c407b1SChuck Lever 		break;
1009d9c407b1SChuck Lever 	case NFS3_CREATE_EXCLUSIVE:
1010d9c407b1SChuck Lever 		encode_createverf3(xdr, args->verifier);
1011d9c407b1SChuck Lever 		break;
1012d9c407b1SChuck Lever 	default:
1013d9c407b1SChuck Lever 		BUG();
1014d9c407b1SChuck Lever 	}
1015d9c407b1SChuck Lever }
1016d9c407b1SChuck Lever 
10179f06c719SChuck Lever static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
10189f06c719SChuck Lever 				     struct xdr_stream *xdr,
1019fcc85819SChristoph Hellwig 				     const void *data)
1020d9c407b1SChuck Lever {
1021fcc85819SChristoph Hellwig 	const struct nfs3_createargs *args = data;
1022fcc85819SChristoph Hellwig 
10239f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
10249f06c719SChuck Lever 	encode_createhow3(xdr, args);
1025d9c407b1SChuck Lever }
1026d9c407b1SChuck Lever 
1027d9c407b1SChuck Lever /*
1028d9c407b1SChuck Lever  * 3.3.9  MKDIR3args
1029d9c407b1SChuck Lever  *
1030d9c407b1SChuck Lever  *	struct MKDIR3args {
1031d9c407b1SChuck Lever  *		diropargs3	where;
1032d9c407b1SChuck Lever  *		sattr3		attributes;
1033d9c407b1SChuck Lever  *	};
1034d9c407b1SChuck Lever  */
10359f06c719SChuck Lever static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
10369f06c719SChuck Lever 				    struct xdr_stream *xdr,
1037fcc85819SChristoph Hellwig 				    const void *data)
1038d9c407b1SChuck Lever {
1039fcc85819SChristoph Hellwig 	const struct nfs3_mkdirargs *args = data;
1040fcc85819SChristoph Hellwig 
10419f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
10429f06c719SChuck Lever 	encode_sattr3(xdr, args->sattr);
1043d9c407b1SChuck Lever }
1044d9c407b1SChuck Lever 
1045d9c407b1SChuck Lever /*
1046d9c407b1SChuck Lever  * 3.3.10  SYMLINK3args
1047d9c407b1SChuck Lever  *
1048d9c407b1SChuck Lever  *	struct symlinkdata3 {
1049d9c407b1SChuck Lever  *		sattr3		symlink_attributes;
1050d9c407b1SChuck Lever  *		nfspath3	symlink_data;
1051d9c407b1SChuck Lever  *	};
1052d9c407b1SChuck Lever  *
1053d9c407b1SChuck Lever  *	struct SYMLINK3args {
1054d9c407b1SChuck Lever  *		diropargs3	where;
1055d9c407b1SChuck Lever  *		symlinkdata3	symlink;
1056d9c407b1SChuck Lever  *	};
1057d9c407b1SChuck Lever  */
1058d9c407b1SChuck Lever static void encode_symlinkdata3(struct xdr_stream *xdr,
1059fcc85819SChristoph Hellwig 				const void *data)
1060d9c407b1SChuck Lever {
1061fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1062fcc85819SChristoph Hellwig 
1063d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
1064d9c407b1SChuck Lever 	encode_nfspath3(xdr, args->pages, args->pathlen);
1065d9c407b1SChuck Lever }
1066d9c407b1SChuck Lever 
10679f06c719SChuck Lever static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
10689f06c719SChuck Lever 				      struct xdr_stream *xdr,
1069fcc85819SChristoph Hellwig 				      const void *data)
1070d9c407b1SChuck Lever {
1071fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1072fcc85819SChristoph Hellwig 
10739f06c719SChuck Lever 	encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
10749f06c719SChuck Lever 	encode_symlinkdata3(xdr, args);
10752fcc213aSChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
1076d9c407b1SChuck Lever }
1077d9c407b1SChuck Lever 
1078d9c407b1SChuck Lever /*
1079d9c407b1SChuck Lever  * 3.3.11  MKNOD3args
1080d9c407b1SChuck Lever  *
1081d9c407b1SChuck Lever  *	struct devicedata3 {
1082d9c407b1SChuck Lever  *		sattr3		dev_attributes;
1083d9c407b1SChuck Lever  *		specdata3	spec;
1084d9c407b1SChuck Lever  *	};
1085d9c407b1SChuck Lever  *
1086d9c407b1SChuck Lever  *	union mknoddata3 switch (ftype3 type) {
1087d9c407b1SChuck Lever  *	case NF3CHR:
1088d9c407b1SChuck Lever  *	case NF3BLK:
1089d9c407b1SChuck Lever  *		devicedata3	device;
1090d9c407b1SChuck Lever  *	case NF3SOCK:
1091d9c407b1SChuck Lever  *	case NF3FIFO:
1092d9c407b1SChuck Lever  *		sattr3		pipe_attributes;
1093d9c407b1SChuck Lever  *	default:
1094d9c407b1SChuck Lever  *		void;
1095d9c407b1SChuck Lever  *	};
1096d9c407b1SChuck Lever  *
1097d9c407b1SChuck Lever  *	struct MKNOD3args {
1098d9c407b1SChuck Lever  *		diropargs3	where;
1099d9c407b1SChuck Lever  *		mknoddata3	what;
1100d9c407b1SChuck Lever  *	};
1101d9c407b1SChuck Lever  */
1102d9c407b1SChuck Lever static void encode_devicedata3(struct xdr_stream *xdr,
1103d9c407b1SChuck Lever 			       const struct nfs3_mknodargs *args)
1104d9c407b1SChuck Lever {
1105d9c407b1SChuck Lever 	encode_sattr3(xdr, args->sattr);
1106d9c407b1SChuck Lever 	encode_specdata3(xdr, args->rdev);
1107d9c407b1SChuck Lever }
1108d9c407b1SChuck Lever 
1109d9c407b1SChuck Lever static void encode_mknoddata3(struct xdr_stream *xdr,
1110d9c407b1SChuck Lever 			      const struct nfs3_mknodargs *args)
1111d9c407b1SChuck Lever {
1112d9c407b1SChuck Lever 	encode_ftype3(xdr, args->type);
1113d9c407b1SChuck Lever 	switch (args->type) {
1114d9c407b1SChuck Lever 	case NF3CHR:
1115d9c407b1SChuck Lever 	case NF3BLK:
1116d9c407b1SChuck Lever 		encode_devicedata3(xdr, args);
1117d9c407b1SChuck Lever 		break;
1118d9c407b1SChuck Lever 	case NF3SOCK:
1119d9c407b1SChuck Lever 	case NF3FIFO:
1120d9c407b1SChuck Lever 		encode_sattr3(xdr, args->sattr);
1121d9c407b1SChuck Lever 		break;
1122d9c407b1SChuck Lever 	case NF3REG:
1123d9c407b1SChuck Lever 	case NF3DIR:
1124d9c407b1SChuck Lever 		break;
1125d9c407b1SChuck Lever 	default:
1126d9c407b1SChuck Lever 		BUG();
1127d9c407b1SChuck Lever 	}
1128d9c407b1SChuck Lever }
1129d9c407b1SChuck Lever 
11309f06c719SChuck Lever static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
11319f06c719SChuck Lever 				    struct xdr_stream *xdr,
1132fcc85819SChristoph Hellwig 				    const void *data)
1133d9c407b1SChuck Lever {
1134fcc85819SChristoph Hellwig 	const struct nfs3_mknodargs *args = data;
1135fcc85819SChristoph Hellwig 
11369f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
11379f06c719SChuck Lever 	encode_mknoddata3(xdr, args);
1138d9c407b1SChuck Lever }
1139d9c407b1SChuck Lever 
1140d9c407b1SChuck Lever /*
1141d9c407b1SChuck Lever  * 3.3.12  REMOVE3args
1142d9c407b1SChuck Lever  *
1143d9c407b1SChuck Lever  *	struct REMOVE3args {
1144d9c407b1SChuck Lever  *		diropargs3  object;
1145d9c407b1SChuck Lever  *	};
1146d9c407b1SChuck Lever  */
11479f06c719SChuck Lever static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
11489f06c719SChuck Lever 				     struct xdr_stream *xdr,
1149fcc85819SChristoph Hellwig 				     const void *data)
1150d9c407b1SChuck Lever {
1151fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
1152fcc85819SChristoph Hellwig 
11539f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
1154d9c407b1SChuck Lever }
1155d9c407b1SChuck Lever 
1156d9c407b1SChuck Lever /*
1157d9c407b1SChuck Lever  * 3.3.14  RENAME3args
1158d9c407b1SChuck Lever  *
1159d9c407b1SChuck Lever  *	struct RENAME3args {
1160d9c407b1SChuck Lever  *		diropargs3	from;
1161d9c407b1SChuck Lever  *		diropargs3	to;
1162d9c407b1SChuck Lever  *	};
1163d9c407b1SChuck Lever  */
11649f06c719SChuck Lever static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
11659f06c719SChuck Lever 				     struct xdr_stream *xdr,
1166fcc85819SChristoph Hellwig 				     const void *data)
1167d9c407b1SChuck Lever {
1168fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
1169d9c407b1SChuck Lever 	const struct qstr *old = args->old_name;
1170d9c407b1SChuck Lever 	const struct qstr *new = args->new_name;
1171d9c407b1SChuck Lever 
11729f06c719SChuck Lever 	encode_diropargs3(xdr, args->old_dir, old->name, old->len);
11739f06c719SChuck Lever 	encode_diropargs3(xdr, args->new_dir, new->name, new->len);
1174d9c407b1SChuck Lever }
1175d9c407b1SChuck Lever 
1176d9c407b1SChuck Lever /*
1177d9c407b1SChuck Lever  * 3.3.15  LINK3args
1178d9c407b1SChuck Lever  *
1179d9c407b1SChuck Lever  *	struct LINK3args {
1180d9c407b1SChuck Lever  *		nfs_fh3		file;
1181d9c407b1SChuck Lever  *		diropargs3	link;
1182d9c407b1SChuck Lever  *	};
1183d9c407b1SChuck Lever  */
11849f06c719SChuck Lever static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
11859f06c719SChuck Lever 				   struct xdr_stream *xdr,
1186fcc85819SChristoph Hellwig 				   const void *data)
1187d9c407b1SChuck Lever {
1188fcc85819SChristoph Hellwig 	const struct nfs3_linkargs *args = data;
1189fcc85819SChristoph Hellwig 
11909f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fromfh);
11919f06c719SChuck Lever 	encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
1192d9c407b1SChuck Lever }
1193d9c407b1SChuck Lever 
1194d9c407b1SChuck Lever /*
1195d9c407b1SChuck Lever  * 3.3.16  READDIR3args
1196d9c407b1SChuck Lever  *
1197d9c407b1SChuck Lever  *	struct READDIR3args {
1198d9c407b1SChuck Lever  *		nfs_fh3		dir;
1199d9c407b1SChuck Lever  *		cookie3		cookie;
1200d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1201d9c407b1SChuck Lever  *		count3		count;
1202d9c407b1SChuck Lever  *	};
1203d9c407b1SChuck Lever  */
1204d9c407b1SChuck Lever static void encode_readdir3args(struct xdr_stream *xdr,
1205d9c407b1SChuck Lever 				const struct nfs3_readdirargs *args)
1206d9c407b1SChuck Lever {
1207d9c407b1SChuck Lever 	__be32 *p;
1208d9c407b1SChuck Lever 
1209d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1210d9c407b1SChuck Lever 
1211d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
1212d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1213d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1214d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1215d9c407b1SChuck Lever }
1216d9c407b1SChuck Lever 
12179f06c719SChuck Lever static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
12189f06c719SChuck Lever 				      struct xdr_stream *xdr,
1219fcc85819SChristoph Hellwig 				      const void *data)
1220d9c407b1SChuck Lever {
1221fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1222fcc85819SChristoph Hellwig 
12239f06c719SChuck Lever 	encode_readdir3args(xdr, args);
1224cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, 0,
1225d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1226d9c407b1SChuck Lever }
1227d9c407b1SChuck Lever 
1228d9c407b1SChuck Lever /*
1229d9c407b1SChuck Lever  * 3.3.17  READDIRPLUS3args
1230d9c407b1SChuck Lever  *
1231d9c407b1SChuck Lever  *	struct READDIRPLUS3args {
1232d9c407b1SChuck Lever  *		nfs_fh3		dir;
1233d9c407b1SChuck Lever  *		cookie3		cookie;
1234d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1235d9c407b1SChuck Lever  *		count3		dircount;
1236d9c407b1SChuck Lever  *		count3		maxcount;
1237d9c407b1SChuck Lever  *	};
1238d9c407b1SChuck Lever  */
1239d9c407b1SChuck Lever static void encode_readdirplus3args(struct xdr_stream *xdr,
1240d9c407b1SChuck Lever 				    const struct nfs3_readdirargs *args)
1241d9c407b1SChuck Lever {
1242d9c407b1SChuck Lever 	__be32 *p;
1243d9c407b1SChuck Lever 
1244d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1245d9c407b1SChuck Lever 
1246d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
1247d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1248d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1249d9c407b1SChuck Lever 
1250d9c407b1SChuck Lever 	/*
1251d9c407b1SChuck Lever 	 * readdirplus: need dircount + buffer size.
1252d9c407b1SChuck Lever 	 * We just make sure we make dircount big enough
1253d9c407b1SChuck Lever 	 */
1254d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count >> 3);
1255d9c407b1SChuck Lever 
1256d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1257d9c407b1SChuck Lever }
1258d9c407b1SChuck Lever 
12599f06c719SChuck Lever static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
12609f06c719SChuck Lever 					  struct xdr_stream *xdr,
1261fcc85819SChristoph Hellwig 					  const void *data)
1262d9c407b1SChuck Lever {
1263fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1264fcc85819SChristoph Hellwig 
12659f06c719SChuck Lever 	encode_readdirplus3args(xdr, args);
1266cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, 0,
1267d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1268d9c407b1SChuck Lever }
1269d9c407b1SChuck Lever 
1270d9c407b1SChuck Lever /*
1271d9c407b1SChuck Lever  * 3.3.21  COMMIT3args
1272d9c407b1SChuck Lever  *
1273d9c407b1SChuck Lever  *	struct COMMIT3args {
1274d9c407b1SChuck Lever  *		nfs_fh3		file;
1275d9c407b1SChuck Lever  *		offset3		offset;
1276d9c407b1SChuck Lever  *		count3		count;
1277d9c407b1SChuck Lever  *	};
1278d9c407b1SChuck Lever  */
1279d9c407b1SChuck Lever static void encode_commit3args(struct xdr_stream *xdr,
12800b7c0153SFred Isaman 			       const struct nfs_commitargs *args)
1281d9c407b1SChuck Lever {
1282d9c407b1SChuck Lever 	__be32 *p;
1283d9c407b1SChuck Lever 
1284d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1285d9c407b1SChuck Lever 
1286d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1287d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1288d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1289d9c407b1SChuck Lever }
1290d9c407b1SChuck Lever 
12919f06c719SChuck Lever static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
12929f06c719SChuck Lever 				     struct xdr_stream *xdr,
1293fcc85819SChristoph Hellwig 				     const void *data)
1294d9c407b1SChuck Lever {
1295fcc85819SChristoph Hellwig 	const struct nfs_commitargs *args = data;
1296fcc85819SChristoph Hellwig 
12979f06c719SChuck Lever 	encode_commit3args(xdr, args);
1298d9c407b1SChuck Lever }
1299d9c407b1SChuck Lever 
1300b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1301b7fa0554SAndreas Gruenbacher 
13029f06c719SChuck Lever static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
13039f06c719SChuck Lever 				     struct xdr_stream *xdr,
1304fcc85819SChristoph Hellwig 				     const void *data)
1305d9c407b1SChuck Lever {
1306fcc85819SChristoph Hellwig 	const struct nfs3_getaclargs *args = data;
1307fcc85819SChristoph Hellwig 
13089f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
13099f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1310431f6eb3STrond Myklebust 	if (args->mask & (NFS_ACL | NFS_DFACL)) {
1311cf500bacSChuck Lever 		rpc_prepare_reply_pages(req, args->pages, 0,
1312d9c407b1SChuck Lever 					NFSACL_MAXPAGES << PAGE_SHIFT,
1313d9c407b1SChuck Lever 					ACL3_getaclres_sz);
1314431f6eb3STrond Myklebust 		req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
1315431f6eb3STrond Myklebust 	}
1316d9c407b1SChuck Lever }
1317d9c407b1SChuck Lever 
13189f06c719SChuck Lever static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
13199f06c719SChuck Lever 				     struct xdr_stream *xdr,
1320fcc85819SChristoph Hellwig 				     const void *data)
1321d9c407b1SChuck Lever {
1322fcc85819SChristoph Hellwig 	const struct nfs3_setaclargs *args = data;
1323d9c407b1SChuck Lever 	unsigned int base;
1324d9c407b1SChuck Lever 	int error;
1325d9c407b1SChuck Lever 
13269f06c719SChuck Lever 	encode_nfs_fh3(xdr, NFS_FH(args->inode));
13279f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1328d9c407b1SChuck Lever 
1329d9c407b1SChuck Lever 	base = req->rq_slen;
1330ee5dc773SChuck Lever 	if (args->npages != 0)
1331ee5dc773SChuck Lever 		xdr_write_pages(xdr, args->pages, 0, args->len);
1332ee5dc773SChuck Lever 	else
1333d683cc49SChuck Lever 		xdr_reserve_space(xdr, args->len);
1334ee5dc773SChuck Lever 
13359f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base, args->inode,
1336d9c407b1SChuck Lever 			    (args->mask & NFS_ACL) ?
1337d9c407b1SChuck Lever 			    args->acl_access : NULL, 1, 0);
13387fc38846STrond Myklebust 	/* FIXME: this is just broken */
1339d9c407b1SChuck Lever 	BUG_ON(error < 0);
13409f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base + error, args->inode,
1341d9c407b1SChuck Lever 			    (args->mask & NFS_DFACL) ?
1342d9c407b1SChuck Lever 			    args->acl_default : NULL, 1,
1343d9c407b1SChuck Lever 			    NFS_ACL_DEFAULT);
1344d9c407b1SChuck Lever 	BUG_ON(error < 0);
1345d9c407b1SChuck Lever }
1346d9c407b1SChuck Lever 
1347b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1348b7fa0554SAndreas Gruenbacher 
13491da177e4SLinus Torvalds /*
1350b2cdd9c9SChuck Lever  * NFSv3 XDR decode functions
1351b2cdd9c9SChuck Lever  *
1352b2cdd9c9SChuck Lever  * NFSv3 result types are defined in section 3.3 of RFC 1813:
1353b2cdd9c9SChuck Lever  * "NFS Version 3 Protocol Specification".
13541da177e4SLinus Torvalds  */
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds /*
1357e4f93234SChuck Lever  * 3.3.1  GETATTR3res
1358e4f93234SChuck Lever  *
1359e4f93234SChuck Lever  *	struct GETATTR3resok {
1360e4f93234SChuck Lever  *		fattr3		obj_attributes;
1361e4f93234SChuck Lever  *	};
1362e4f93234SChuck Lever  *
1363e4f93234SChuck Lever  *	union GETATTR3res switch (nfsstat3 status) {
1364e4f93234SChuck Lever  *	case NFS3_OK:
1365e4f93234SChuck Lever  *		GETATTR3resok  resok;
1366e4f93234SChuck Lever  *	default:
1367e4f93234SChuck Lever  *		void;
1368e4f93234SChuck Lever  *	};
1369e4f93234SChuck Lever  */
1370bf269551SChuck Lever static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
1371bf269551SChuck Lever 				    struct xdr_stream *xdr,
1372fc016483SChristoph Hellwig 				    void *result)
1373e4f93234SChuck Lever {
1374e4f93234SChuck Lever 	enum nfs_stat status;
1375e4f93234SChuck Lever 	int error;
1376e4f93234SChuck Lever 
1377bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1378e4f93234SChuck Lever 	if (unlikely(error))
1379e4f93234SChuck Lever 		goto out;
1380e4f93234SChuck Lever 	if (status != NFS3_OK)
1381e4f93234SChuck Lever 		goto out_default;
1382bf269551SChuck Lever 	error = decode_fattr3(xdr, result);
1383e4f93234SChuck Lever out:
1384e4f93234SChuck Lever 	return error;
1385e4f93234SChuck Lever out_default:
13865e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1387e4f93234SChuck Lever }
1388e4f93234SChuck Lever 
1389e4f93234SChuck Lever /*
1390e4f93234SChuck Lever  * 3.3.2  SETATTR3res
1391e4f93234SChuck Lever  *
1392e4f93234SChuck Lever  *	struct SETATTR3resok {
1393e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1394e4f93234SChuck Lever  *	};
1395e4f93234SChuck Lever  *
1396e4f93234SChuck Lever  *	struct SETATTR3resfail {
1397e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1398e4f93234SChuck Lever  *	};
1399e4f93234SChuck Lever  *
1400e4f93234SChuck Lever  *	union SETATTR3res switch (nfsstat3 status) {
1401e4f93234SChuck Lever  *	case NFS3_OK:
1402e4f93234SChuck Lever  *		SETATTR3resok   resok;
1403e4f93234SChuck Lever  *	default:
1404e4f93234SChuck Lever  *		SETATTR3resfail resfail;
1405e4f93234SChuck Lever  *	};
1406e4f93234SChuck Lever  */
1407bf269551SChuck Lever static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
1408bf269551SChuck Lever 				    struct xdr_stream *xdr,
1409fc016483SChristoph Hellwig 				    void *result)
1410e4f93234SChuck Lever {
1411e4f93234SChuck Lever 	enum nfs_stat status;
1412e4f93234SChuck Lever 	int error;
1413e4f93234SChuck Lever 
1414bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1415e4f93234SChuck Lever 	if (unlikely(error))
1416e4f93234SChuck Lever 		goto out;
1417bf269551SChuck Lever 	error = decode_wcc_data(xdr, result);
1418e4f93234SChuck Lever 	if (unlikely(error))
1419e4f93234SChuck Lever 		goto out;
1420e4f93234SChuck Lever 	if (status != NFS3_OK)
1421e4f93234SChuck Lever 		goto out_status;
1422e4f93234SChuck Lever out:
1423e4f93234SChuck Lever 	return error;
1424e4f93234SChuck Lever out_status:
14255e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1426e4f93234SChuck Lever }
1427e4f93234SChuck Lever 
14281da177e4SLinus Torvalds /*
1429e4f93234SChuck Lever  * 3.3.3  LOOKUP3res
1430e4f93234SChuck Lever  *
1431e4f93234SChuck Lever  *	struct LOOKUP3resok {
1432e4f93234SChuck Lever  *		nfs_fh3		object;
1433e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1434e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1435e4f93234SChuck Lever  *	};
1436e4f93234SChuck Lever  *
1437e4f93234SChuck Lever  *	struct LOOKUP3resfail {
1438e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1439e4f93234SChuck Lever  *	};
1440e4f93234SChuck Lever  *
1441e4f93234SChuck Lever  *	union LOOKUP3res switch (nfsstat3 status) {
1442e4f93234SChuck Lever  *	case NFS3_OK:
1443e4f93234SChuck Lever  *		LOOKUP3resok	resok;
1444e4f93234SChuck Lever  *	default:
1445e4f93234SChuck Lever  *		LOOKUP3resfail	resfail;
1446e4f93234SChuck Lever  *	};
1447e4f93234SChuck Lever  */
1448bf269551SChuck Lever static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
1449bf269551SChuck Lever 				   struct xdr_stream *xdr,
1450fc016483SChristoph Hellwig 				   void *data)
1451e4f93234SChuck Lever {
1452fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1453e4f93234SChuck Lever 	enum nfs_stat status;
1454e4f93234SChuck Lever 	int error;
1455e4f93234SChuck Lever 
1456bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1457e4f93234SChuck Lever 	if (unlikely(error))
1458e4f93234SChuck Lever 		goto out;
1459e4f93234SChuck Lever 	if (status != NFS3_OK)
1460e4f93234SChuck Lever 		goto out_default;
1461bf269551SChuck Lever 	error = decode_nfs_fh3(xdr, result->fh);
1462e4f93234SChuck Lever 	if (unlikely(error))
1463e4f93234SChuck Lever 		goto out;
1464bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1465e4f93234SChuck Lever 	if (unlikely(error))
1466e4f93234SChuck Lever 		goto out;
1467bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
1468e4f93234SChuck Lever out:
1469e4f93234SChuck Lever 	return error;
1470e4f93234SChuck Lever out_default:
1471bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
1472e4f93234SChuck Lever 	if (unlikely(error))
1473e4f93234SChuck Lever 		goto out;
14745e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1475e4f93234SChuck Lever }
1476e4f93234SChuck Lever 
1477e4f93234SChuck Lever /*
1478e4f93234SChuck Lever  * 3.3.4  ACCESS3res
1479e4f93234SChuck Lever  *
1480e4f93234SChuck Lever  *	struct ACCESS3resok {
1481e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1482e4f93234SChuck Lever  *		uint32		access;
1483e4f93234SChuck Lever  *	};
1484e4f93234SChuck Lever  *
1485e4f93234SChuck Lever  *	struct ACCESS3resfail {
1486e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1487e4f93234SChuck Lever  *	};
1488e4f93234SChuck Lever  *
1489e4f93234SChuck Lever  *	union ACCESS3res switch (nfsstat3 status) {
1490e4f93234SChuck Lever  *	case NFS3_OK:
1491e4f93234SChuck Lever  *		ACCESS3resok	resok;
1492e4f93234SChuck Lever  *	default:
1493e4f93234SChuck Lever  *		ACCESS3resfail	resfail;
1494e4f93234SChuck Lever  *	};
1495e4f93234SChuck Lever  */
1496bf269551SChuck Lever static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
1497bf269551SChuck Lever 				   struct xdr_stream *xdr,
1498fc016483SChristoph Hellwig 				   void *data)
1499e4f93234SChuck Lever {
1500fc016483SChristoph Hellwig 	struct nfs3_accessres *result = data;
1501e4f93234SChuck Lever 	enum nfs_stat status;
1502e4f93234SChuck Lever 	int error;
1503e4f93234SChuck Lever 
1504bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1505e4f93234SChuck Lever 	if (unlikely(error))
1506e4f93234SChuck Lever 		goto out;
1507bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1508e4f93234SChuck Lever 	if (unlikely(error))
1509e4f93234SChuck Lever 		goto out;
1510e4f93234SChuck Lever 	if (status != NFS3_OK)
1511e4f93234SChuck Lever 		goto out_default;
1512bf269551SChuck Lever 	error = decode_uint32(xdr, &result->access);
1513e4f93234SChuck Lever out:
1514e4f93234SChuck Lever 	return error;
1515e4f93234SChuck Lever out_default:
15165e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1517e4f93234SChuck Lever }
1518e4f93234SChuck Lever 
1519e4f93234SChuck Lever /*
1520e4f93234SChuck Lever  * 3.3.5  READLINK3res
1521e4f93234SChuck Lever  *
1522e4f93234SChuck Lever  *	struct READLINK3resok {
1523e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1524e4f93234SChuck Lever  *		nfspath3	data;
1525e4f93234SChuck Lever  *	};
1526e4f93234SChuck Lever  *
1527e4f93234SChuck Lever  *	struct READLINK3resfail {
1528e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1529e4f93234SChuck Lever  *	};
1530e4f93234SChuck Lever  *
1531e4f93234SChuck Lever  *	union READLINK3res switch (nfsstat3 status) {
1532e4f93234SChuck Lever  *	case NFS3_OK:
1533e4f93234SChuck Lever  *		READLINK3resok	resok;
1534e4f93234SChuck Lever  *	default:
1535e4f93234SChuck Lever  *		READLINK3resfail resfail;
1536e4f93234SChuck Lever  *	};
1537e4f93234SChuck Lever  */
1538bf269551SChuck Lever static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
1539bf269551SChuck Lever 				     struct xdr_stream *xdr,
1540fc016483SChristoph Hellwig 				     void *result)
1541e4f93234SChuck Lever {
1542e4f93234SChuck Lever 	enum nfs_stat status;
1543e4f93234SChuck Lever 	int error;
1544e4f93234SChuck Lever 
1545bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1546e4f93234SChuck Lever 	if (unlikely(error))
1547e4f93234SChuck Lever 		goto out;
1548bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result);
1549e4f93234SChuck Lever 	if (unlikely(error))
1550e4f93234SChuck Lever 		goto out;
1551e4f93234SChuck Lever 	if (status != NFS3_OK)
1552e4f93234SChuck Lever 		goto out_default;
1553bf269551SChuck Lever 	error = decode_nfspath3(xdr);
1554e4f93234SChuck Lever out:
1555e4f93234SChuck Lever 	return error;
1556e4f93234SChuck Lever out_default:
15575e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1558e4f93234SChuck Lever }
1559e4f93234SChuck Lever 
1560e4f93234SChuck Lever /*
1561e4f93234SChuck Lever  * 3.3.6  READ3res
1562e4f93234SChuck Lever  *
1563e4f93234SChuck Lever  *	struct READ3resok {
1564e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1565e4f93234SChuck Lever  *		count3		count;
1566e4f93234SChuck Lever  *		bool		eof;
1567e4f93234SChuck Lever  *		opaque		data<>;
1568e4f93234SChuck Lever  *	};
1569e4f93234SChuck Lever  *
1570e4f93234SChuck Lever  *	struct READ3resfail {
1571e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1572e4f93234SChuck Lever  *	};
1573e4f93234SChuck Lever  *
1574e4f93234SChuck Lever  *	union READ3res switch (nfsstat3 status) {
1575e4f93234SChuck Lever  *	case NFS3_OK:
1576e4f93234SChuck Lever  *		READ3resok	resok;
1577e4f93234SChuck Lever  *	default:
1578e4f93234SChuck Lever  *		READ3resfail	resfail;
1579e4f93234SChuck Lever  *	};
1580e4f93234SChuck Lever  */
1581e4f93234SChuck Lever static int decode_read3resok(struct xdr_stream *xdr,
15829137bdf3SAnna Schumaker 			     struct nfs_pgio_res *result)
1583e4f93234SChuck Lever {
1584e4f93234SChuck Lever 	u32 eof, count, ocount, recvd;
1585e4f93234SChuck Lever 	__be32 *p;
1586e4f93234SChuck Lever 
1587e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 + 4 + 4);
1588eb72f484SChuck Lever 	if (unlikely(!p))
1589eb72f484SChuck Lever 		return -EIO;
1590e4f93234SChuck Lever 	count = be32_to_cpup(p++);
1591e4f93234SChuck Lever 	eof = be32_to_cpup(p++);
1592e4f93234SChuck Lever 	ocount = be32_to_cpup(p++);
1593e4f93234SChuck Lever 	if (unlikely(ocount != count))
1594e4f93234SChuck Lever 		goto out_mismatch;
159564bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
1596e4f93234SChuck Lever 	if (unlikely(count > recvd))
1597e4f93234SChuck Lever 		goto out_cheating;
1598e4f93234SChuck Lever out:
1599e4f93234SChuck Lever 	result->eof = eof;
1600e4f93234SChuck Lever 	result->count = count;
1601e4f93234SChuck Lever 	return count;
1602e4f93234SChuck Lever out_mismatch:
1603e4f93234SChuck Lever 	dprintk("NFS: READ count doesn't match length of opaque: "
1604e4f93234SChuck Lever 		"count %u != ocount %u\n", count, ocount);
1605e4f93234SChuck Lever 	return -EIO;
1606e4f93234SChuck Lever out_cheating:
1607e4f93234SChuck Lever 	dprintk("NFS: server cheating in read result: "
1608e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
1609e4f93234SChuck Lever 	count = recvd;
1610e4f93234SChuck Lever 	eof = 0;
1611e4f93234SChuck Lever 	goto out;
1612e4f93234SChuck Lever }
1613e4f93234SChuck Lever 
1614bf269551SChuck Lever static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1615fc016483SChristoph Hellwig 				 void *data)
1616e4f93234SChuck Lever {
1617fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
16188d8928d8STrond Myklebust 	unsigned int pos;
1619e4f93234SChuck Lever 	enum nfs_stat status;
1620e4f93234SChuck Lever 	int error;
1621e4f93234SChuck Lever 
16228d8928d8STrond Myklebust 	pos = xdr_stream_pos(xdr);
1623bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1624e4f93234SChuck Lever 	if (unlikely(error))
1625e4f93234SChuck Lever 		goto out;
1626bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1627e4f93234SChuck Lever 	if (unlikely(error))
1628e4f93234SChuck Lever 		goto out;
1629aabff4ddSPeng Tao 	result->op_status = status;
1630e4f93234SChuck Lever 	if (status != NFS3_OK)
1631e4f93234SChuck Lever 		goto out_status;
16328d8928d8STrond Myklebust 	result->replen = 3 + ((xdr_stream_pos(xdr) - pos) >> 2);
1633bf269551SChuck Lever 	error = decode_read3resok(xdr, result);
1634e4f93234SChuck Lever out:
1635e4f93234SChuck Lever 	return error;
1636e4f93234SChuck Lever out_status:
16375e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1638e4f93234SChuck Lever }
1639e4f93234SChuck Lever 
1640e4f93234SChuck Lever /*
1641e4f93234SChuck Lever  * 3.3.7  WRITE3res
1642e4f93234SChuck Lever  *
1643e4f93234SChuck Lever  *	enum stable_how {
1644e4f93234SChuck Lever  *		UNSTABLE  = 0,
1645e4f93234SChuck Lever  *		DATA_SYNC = 1,
1646e4f93234SChuck Lever  *		FILE_SYNC = 2
1647e4f93234SChuck Lever  *	};
1648e4f93234SChuck Lever  *
1649e4f93234SChuck Lever  *	struct WRITE3resok {
1650e4f93234SChuck Lever  *		wcc_data	file_wcc;
1651e4f93234SChuck Lever  *		count3		count;
1652e4f93234SChuck Lever  *		stable_how	committed;
1653e4f93234SChuck Lever  *		writeverf3	verf;
1654e4f93234SChuck Lever  *	};
1655e4f93234SChuck Lever  *
1656e4f93234SChuck Lever  *	struct WRITE3resfail {
1657e4f93234SChuck Lever  *		wcc_data	file_wcc;
1658e4f93234SChuck Lever  *	};
1659e4f93234SChuck Lever  *
1660e4f93234SChuck Lever  *	union WRITE3res switch (nfsstat3 status) {
1661e4f93234SChuck Lever  *	case NFS3_OK:
1662e4f93234SChuck Lever  *		WRITE3resok	resok;
1663e4f93234SChuck Lever  *	default:
1664e4f93234SChuck Lever  *		WRITE3resfail	resfail;
1665e4f93234SChuck Lever  *	};
1666e4f93234SChuck Lever  */
1667e4f93234SChuck Lever static int decode_write3resok(struct xdr_stream *xdr,
16689137bdf3SAnna Schumaker 			      struct nfs_pgio_res *result)
1669e4f93234SChuck Lever {
1670e4f93234SChuck Lever 	__be32 *p;
1671e4f93234SChuck Lever 
16722f2c63bcSTrond Myklebust 	p = xdr_inline_decode(xdr, 4 + 4);
1673eb72f484SChuck Lever 	if (unlikely(!p))
1674eb72f484SChuck Lever 		return -EIO;
1675e4f93234SChuck Lever 	result->count = be32_to_cpup(p++);
1676e4f93234SChuck Lever 	result->verf->committed = be32_to_cpup(p++);
1677e4f93234SChuck Lever 	if (unlikely(result->verf->committed > NFS_FILE_SYNC))
1678e4f93234SChuck Lever 		goto out_badvalue;
16792f2c63bcSTrond Myklebust 	if (decode_writeverf3(xdr, &result->verf->verifier))
1680eb72f484SChuck Lever 		return -EIO;
1681e4f93234SChuck Lever 	return result->count;
1682e4f93234SChuck Lever out_badvalue:
1683e4f93234SChuck Lever 	dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
1684e4f93234SChuck Lever 	return -EIO;
1685e4f93234SChuck Lever }
1686e4f93234SChuck Lever 
1687bf269551SChuck Lever static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1688fc016483SChristoph Hellwig 				  void *data)
1689e4f93234SChuck Lever {
1690fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
1691e4f93234SChuck Lever 	enum nfs_stat status;
1692e4f93234SChuck Lever 	int error;
1693e4f93234SChuck Lever 
1694bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1695e4f93234SChuck Lever 	if (unlikely(error))
1696e4f93234SChuck Lever 		goto out;
1697bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->fattr);
1698e4f93234SChuck Lever 	if (unlikely(error))
1699e4f93234SChuck Lever 		goto out;
1700aabff4ddSPeng Tao 	result->op_status = status;
1701e4f93234SChuck Lever 	if (status != NFS3_OK)
1702e4f93234SChuck Lever 		goto out_status;
1703bf269551SChuck Lever 	error = decode_write3resok(xdr, result);
1704e4f93234SChuck Lever out:
1705e4f93234SChuck Lever 	return error;
1706e4f93234SChuck Lever out_status:
17075e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1708e4f93234SChuck Lever }
1709e4f93234SChuck Lever 
1710e4f93234SChuck Lever /*
1711e4f93234SChuck Lever  * 3.3.8  CREATE3res
1712e4f93234SChuck Lever  *
1713e4f93234SChuck Lever  *	struct CREATE3resok {
1714e4f93234SChuck Lever  *		post_op_fh3	obj;
1715e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1716e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1717e4f93234SChuck Lever  *	};
1718e4f93234SChuck Lever  *
1719e4f93234SChuck Lever  *	struct CREATE3resfail {
1720e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1721e4f93234SChuck Lever  *	};
1722e4f93234SChuck Lever  *
1723e4f93234SChuck Lever  *	union CREATE3res switch (nfsstat3 status) {
1724e4f93234SChuck Lever  *	case NFS3_OK:
1725e4f93234SChuck Lever  *		CREATE3resok	resok;
1726e4f93234SChuck Lever  *	default:
1727e4f93234SChuck Lever  *		CREATE3resfail	resfail;
1728e4f93234SChuck Lever  *	};
1729e4f93234SChuck Lever  */
1730e4f93234SChuck Lever static int decode_create3resok(struct xdr_stream *xdr,
1731e4f93234SChuck Lever 			       struct nfs3_diropres *result)
1732e4f93234SChuck Lever {
1733e4f93234SChuck Lever 	int error;
1734e4f93234SChuck Lever 
1735e4f93234SChuck Lever 	error = decode_post_op_fh3(xdr, result->fh);
1736e4f93234SChuck Lever 	if (unlikely(error))
1737e4f93234SChuck Lever 		goto out;
1738e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1739e4f93234SChuck Lever 	if (unlikely(error))
1740e4f93234SChuck Lever 		goto out;
1741e4f93234SChuck Lever 	/* The server isn't required to return a file handle.
1742e4f93234SChuck Lever 	 * If it didn't, force the client to perform a LOOKUP
1743e4f93234SChuck Lever 	 * to determine the correct file handle and attribute
1744e4f93234SChuck Lever 	 * values for the new object. */
1745e4f93234SChuck Lever 	if (result->fh->size == 0)
1746e4f93234SChuck Lever 		result->fattr->valid = 0;
1747e4f93234SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1748e4f93234SChuck Lever out:
1749e4f93234SChuck Lever 	return error;
1750e4f93234SChuck Lever }
1751e4f93234SChuck Lever 
1752bf269551SChuck Lever static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
1753bf269551SChuck Lever 				   struct xdr_stream *xdr,
1754fc016483SChristoph Hellwig 				   void *data)
1755e4f93234SChuck Lever {
1756fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1757e4f93234SChuck Lever 	enum nfs_stat status;
1758e4f93234SChuck Lever 	int error;
1759e4f93234SChuck Lever 
1760bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1761e4f93234SChuck Lever 	if (unlikely(error))
1762e4f93234SChuck Lever 		goto out;
1763e4f93234SChuck Lever 	if (status != NFS3_OK)
1764e4f93234SChuck Lever 		goto out_default;
1765bf269551SChuck Lever 	error = decode_create3resok(xdr, result);
1766e4f93234SChuck Lever out:
1767e4f93234SChuck Lever 	return error;
1768e4f93234SChuck Lever out_default:
1769bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1770e4f93234SChuck Lever 	if (unlikely(error))
1771e4f93234SChuck Lever 		goto out;
17725e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1773e4f93234SChuck Lever }
1774e4f93234SChuck Lever 
1775e4f93234SChuck Lever /*
1776e4f93234SChuck Lever  * 3.3.12  REMOVE3res
1777e4f93234SChuck Lever  *
1778e4f93234SChuck Lever  *	struct REMOVE3resok {
1779e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1780e4f93234SChuck Lever  *	};
1781e4f93234SChuck Lever  *
1782e4f93234SChuck Lever  *	struct REMOVE3resfail {
1783e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1784e4f93234SChuck Lever  *	};
1785e4f93234SChuck Lever  *
1786e4f93234SChuck Lever  *	union REMOVE3res switch (nfsstat3 status) {
1787e4f93234SChuck Lever  *	case NFS3_OK:
1788e4f93234SChuck Lever  *		REMOVE3resok   resok;
1789e4f93234SChuck Lever  *	default:
1790e4f93234SChuck Lever  *		REMOVE3resfail resfail;
1791e4f93234SChuck Lever  *	};
1792e4f93234SChuck Lever  */
1793bf269551SChuck Lever static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
1794bf269551SChuck Lever 				   struct xdr_stream *xdr,
1795fc016483SChristoph Hellwig 				   void *data)
1796e4f93234SChuck Lever {
1797fc016483SChristoph Hellwig 	struct nfs_removeres *result = data;
1798e4f93234SChuck Lever 	enum nfs_stat status;
1799e4f93234SChuck Lever 	int error;
1800e4f93234SChuck Lever 
1801bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1802e4f93234SChuck Lever 	if (unlikely(error))
1803e4f93234SChuck Lever 		goto out;
1804bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1805e4f93234SChuck Lever 	if (unlikely(error))
1806e4f93234SChuck Lever 		goto out;
1807e4f93234SChuck Lever 	if (status != NFS3_OK)
1808e4f93234SChuck Lever 		goto out_status;
1809e4f93234SChuck Lever out:
1810e4f93234SChuck Lever 	return error;
1811e4f93234SChuck Lever out_status:
18125e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1813e4f93234SChuck Lever }
1814e4f93234SChuck Lever 
1815e4f93234SChuck Lever /*
1816e4f93234SChuck Lever  * 3.3.14  RENAME3res
1817e4f93234SChuck Lever  *
1818e4f93234SChuck Lever  *	struct RENAME3resok {
1819e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1820e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1821e4f93234SChuck Lever  *	};
1822e4f93234SChuck Lever  *
1823e4f93234SChuck Lever  *	struct RENAME3resfail {
1824e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1825e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1826e4f93234SChuck Lever  *	};
1827e4f93234SChuck Lever  *
1828e4f93234SChuck Lever  *	union RENAME3res switch (nfsstat3 status) {
1829e4f93234SChuck Lever  *	case NFS3_OK:
1830e4f93234SChuck Lever  *		RENAME3resok   resok;
1831e4f93234SChuck Lever  *	default:
1832e4f93234SChuck Lever  *		RENAME3resfail resfail;
1833e4f93234SChuck Lever  *	};
1834e4f93234SChuck Lever  */
1835bf269551SChuck Lever static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
1836bf269551SChuck Lever 				   struct xdr_stream *xdr,
1837fc016483SChristoph Hellwig 				   void *data)
1838e4f93234SChuck Lever {
1839fc016483SChristoph Hellwig 	struct nfs_renameres *result = data;
1840e4f93234SChuck Lever 	enum nfs_stat status;
1841e4f93234SChuck Lever 	int error;
1842e4f93234SChuck Lever 
1843bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1844e4f93234SChuck Lever 	if (unlikely(error))
1845e4f93234SChuck Lever 		goto out;
1846bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->old_fattr);
1847e4f93234SChuck Lever 	if (unlikely(error))
1848e4f93234SChuck Lever 		goto out;
1849bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->new_fattr);
1850e4f93234SChuck Lever 	if (unlikely(error))
1851e4f93234SChuck Lever 		goto out;
1852e4f93234SChuck Lever 	if (status != NFS3_OK)
1853e4f93234SChuck Lever 		goto out_status;
1854e4f93234SChuck Lever out:
1855e4f93234SChuck Lever 	return error;
1856e4f93234SChuck Lever out_status:
18575e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1858e4f93234SChuck Lever }
1859e4f93234SChuck Lever 
1860e4f93234SChuck Lever /*
1861e4f93234SChuck Lever  * 3.3.15  LINK3res
1862e4f93234SChuck Lever  *
1863e4f93234SChuck Lever  *	struct LINK3resok {
1864e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1865e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1866e4f93234SChuck Lever  *	};
1867e4f93234SChuck Lever  *
1868e4f93234SChuck Lever  *	struct LINK3resfail {
1869e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1870e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1871e4f93234SChuck Lever  *	};
1872e4f93234SChuck Lever  *
1873e4f93234SChuck Lever  *	union LINK3res switch (nfsstat3 status) {
1874e4f93234SChuck Lever  *	case NFS3_OK:
1875e4f93234SChuck Lever  *		LINK3resok	resok;
1876e4f93234SChuck Lever  *	default:
1877e4f93234SChuck Lever  *		LINK3resfail	resfail;
1878e4f93234SChuck Lever  *	};
1879e4f93234SChuck Lever  */
1880bf269551SChuck Lever static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1881fc016483SChristoph Hellwig 				 void *data)
1882e4f93234SChuck Lever {
1883fc016483SChristoph Hellwig 	struct nfs3_linkres *result = data;
1884e4f93234SChuck Lever 	enum nfs_stat status;
1885e4f93234SChuck Lever 	int error;
1886e4f93234SChuck Lever 
1887bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1888e4f93234SChuck Lever 	if (unlikely(error))
1889e4f93234SChuck Lever 		goto out;
1890bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
1891e4f93234SChuck Lever 	if (unlikely(error))
1892e4f93234SChuck Lever 		goto out;
1893bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->dir_attr);
1894e4f93234SChuck Lever 	if (unlikely(error))
1895e4f93234SChuck Lever 		goto out;
1896e4f93234SChuck Lever 	if (status != NFS3_OK)
1897e4f93234SChuck Lever 		goto out_status;
1898e4f93234SChuck Lever out:
1899e4f93234SChuck Lever 	return error;
1900e4f93234SChuck Lever out_status:
19015e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1902e4f93234SChuck Lever }
1903e4f93234SChuck Lever 
1904e4f93234SChuck Lever /**
1905e4f93234SChuck Lever  * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
1906e4f93234SChuck Lever  *			the local page cache
1907e4f93234SChuck Lever  * @xdr: XDR stream where entry resides
1908e4f93234SChuck Lever  * @entry: buffer to fill in with entry data
1909e4f93234SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
1910e4f93234SChuck Lever  *
1911573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
1912573c4e1eSChuck Lever  * returned.
1913e4f93234SChuck Lever  *
1914e4f93234SChuck Lever  * This function is not invoked during READDIR reply decoding, but
1915e4f93234SChuck Lever  * rather whenever an application invokes the getdents(2) system call
1916e4f93234SChuck Lever  * on a directory already in our cache.
1917e4f93234SChuck Lever  *
1918e4f93234SChuck Lever  * 3.3.16  entry3
1919e4f93234SChuck Lever  *
1920e4f93234SChuck Lever  *	struct entry3 {
1921e4f93234SChuck Lever  *		fileid3		fileid;
1922e4f93234SChuck Lever  *		filename3	name;
1923e4f93234SChuck Lever  *		cookie3		cookie;
1924e4f93234SChuck Lever  *		fhandle3	filehandle;
1925e4f93234SChuck Lever  *		post_op_attr3	attributes;
1926e4f93234SChuck Lever  *		entry3		*nextentry;
1927e4f93234SChuck Lever  *	};
1928e4f93234SChuck Lever  *
1929e4f93234SChuck Lever  * 3.3.17  entryplus3
1930e4f93234SChuck Lever  *	struct entryplus3 {
1931e4f93234SChuck Lever  *		fileid3		fileid;
1932e4f93234SChuck Lever  *		filename3	name;
1933e4f93234SChuck Lever  *		cookie3		cookie;
1934e4f93234SChuck Lever  *		post_op_attr	name_attributes;
1935e4f93234SChuck Lever  *		post_op_fh3	name_handle;
1936e4f93234SChuck Lever  *		entryplus3	*nextentry;
1937e4f93234SChuck Lever  *	};
1938e4f93234SChuck Lever  */
1939573c4e1eSChuck Lever int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
1940a7a3b1e9SBenjamin Coddington 		       bool plus)
1941e4f93234SChuck Lever {
1942e4f93234SChuck Lever 	struct nfs_entry old = *entry;
1943e4f93234SChuck Lever 	__be32 *p;
1944e4f93234SChuck Lever 	int error;
194598de9ce6SFrank Sorenson 	u64 new_cookie;
1946e4f93234SChuck Lever 
1947e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
1948eb72f484SChuck Lever 	if (unlikely(!p))
1949eb72f484SChuck Lever 		return -EAGAIN;
1950e4f93234SChuck Lever 	if (*p == xdr_zero) {
1951e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
1952eb72f484SChuck Lever 		if (unlikely(!p))
1953eb72f484SChuck Lever 			return -EAGAIN;
1954e4f93234SChuck Lever 		if (*p == xdr_zero)
1955573c4e1eSChuck Lever 			return -EAGAIN;
1956e4f93234SChuck Lever 		entry->eof = 1;
1957573c4e1eSChuck Lever 		return -EBADCOOKIE;
1958e4f93234SChuck Lever 	}
1959e4f93234SChuck Lever 
1960e4f93234SChuck Lever 	error = decode_fileid3(xdr, &entry->ino);
1961e4f93234SChuck Lever 	if (unlikely(error))
1962573c4e1eSChuck Lever 		return error;
1963e4f93234SChuck Lever 
1964e4f93234SChuck Lever 	error = decode_inline_filename3(xdr, &entry->name, &entry->len);
1965e4f93234SChuck Lever 	if (unlikely(error))
1966573c4e1eSChuck Lever 		return error;
1967e4f93234SChuck Lever 
196898de9ce6SFrank Sorenson 	error = decode_cookie3(xdr, &new_cookie);
1969e4f93234SChuck Lever 	if (unlikely(error))
1970573c4e1eSChuck Lever 		return error;
1971e4f93234SChuck Lever 
1972e4f93234SChuck Lever 	entry->d_type = DT_UNKNOWN;
1973e4f93234SChuck Lever 
1974e4f93234SChuck Lever 	if (plus) {
1975e4f93234SChuck Lever 		entry->fattr->valid = 0;
1976e4f93234SChuck Lever 		error = decode_post_op_attr(xdr, entry->fattr);
1977e4f93234SChuck Lever 		if (unlikely(error))
1978573c4e1eSChuck Lever 			return error;
1979e4f93234SChuck Lever 		if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
1980e4f93234SChuck Lever 			entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
1981e4f93234SChuck Lever 
19821ae04b25STrond Myklebust 		if (entry->fattr->fileid != entry->ino) {
19831ae04b25STrond Myklebust 			entry->fattr->mounted_on_fileid = entry->ino;
19841ae04b25STrond Myklebust 			entry->fattr->valid |= NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
19851ae04b25STrond Myklebust 		}
19861ae04b25STrond Myklebust 
1987e4f93234SChuck Lever 		/* In fact, a post_op_fh3: */
1988e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
1989eb72f484SChuck Lever 		if (unlikely(!p))
1990eb72f484SChuck Lever 			return -EAGAIN;
1991e4f93234SChuck Lever 		if (*p != xdr_zero) {
1992e4f93234SChuck Lever 			error = decode_nfs_fh3(xdr, entry->fh);
1993e4f93234SChuck Lever 			if (unlikely(error)) {
1994e4f93234SChuck Lever 				if (error == -E2BIG)
1995e4f93234SChuck Lever 					goto out_truncated;
1996573c4e1eSChuck Lever 				return error;
1997e4f93234SChuck Lever 			}
1998e4f93234SChuck Lever 		} else
1999e4f93234SChuck Lever 			zero_nfs_fh3(entry->fh);
2000e4f93234SChuck Lever 	}
2001e4f93234SChuck Lever 
200298de9ce6SFrank Sorenson 	entry->prev_cookie = entry->cookie;
200398de9ce6SFrank Sorenson 	entry->cookie = new_cookie;
200498de9ce6SFrank Sorenson 
2005573c4e1eSChuck Lever 	return 0;
2006e4f93234SChuck Lever 
2007e4f93234SChuck Lever out_truncated:
2008e4f93234SChuck Lever 	dprintk("NFS: directory entry contains invalid file handle\n");
2009e4f93234SChuck Lever 	*entry = old;
2010573c4e1eSChuck Lever 	return -EAGAIN;
2011e4f93234SChuck Lever }
2012e4f93234SChuck Lever 
2013e4f93234SChuck Lever /*
2014e4f93234SChuck Lever  * 3.3.16  READDIR3res
2015e4f93234SChuck Lever  *
2016e4f93234SChuck Lever  *	struct dirlist3 {
2017e4f93234SChuck Lever  *		entry3		*entries;
2018e4f93234SChuck Lever  *		bool		eof;
2019e4f93234SChuck Lever  *	};
2020e4f93234SChuck Lever  *
2021e4f93234SChuck Lever  *	struct READDIR3resok {
2022e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2023e4f93234SChuck Lever  *		cookieverf3	cookieverf;
2024e4f93234SChuck Lever  *		dirlist3	reply;
2025e4f93234SChuck Lever  *	};
2026e4f93234SChuck Lever  *
2027e4f93234SChuck Lever  *	struct READDIR3resfail {
2028e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2029e4f93234SChuck Lever  *	};
2030e4f93234SChuck Lever  *
2031e4f93234SChuck Lever  *	union READDIR3res switch (nfsstat3 status) {
2032e4f93234SChuck Lever  *	case NFS3_OK:
2033e4f93234SChuck Lever  *		READDIR3resok	resok;
2034e4f93234SChuck Lever  *	default:
2035e4f93234SChuck Lever  *		READDIR3resfail	resfail;
2036e4f93234SChuck Lever  *	};
2037e4f93234SChuck Lever  *
2038e4f93234SChuck Lever  * Read the directory contents into the page cache, but otherwise
2039e4f93234SChuck Lever  * don't touch them.  The actual decoding is done by nfs3_decode_entry()
2040e4f93234SChuck Lever  * during subsequent nfs_readdir() calls.
2041e4f93234SChuck Lever  */
2042e4f93234SChuck Lever static int decode_dirlist3(struct xdr_stream *xdr)
2043e4f93234SChuck Lever {
204464bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
2045e4f93234SChuck Lever }
2046e4f93234SChuck Lever 
2047e4f93234SChuck Lever static int decode_readdir3resok(struct xdr_stream *xdr,
2048e4f93234SChuck Lever 				struct nfs3_readdirres *result)
2049e4f93234SChuck Lever {
2050e4f93234SChuck Lever 	int error;
2051e4f93234SChuck Lever 
2052e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
2053e4f93234SChuck Lever 	if (unlikely(error))
2054e4f93234SChuck Lever 		goto out;
2055e4f93234SChuck Lever 	/* XXX: do we need to check if result->verf != NULL ? */
2056e4f93234SChuck Lever 	error = decode_cookieverf3(xdr, result->verf);
2057e4f93234SChuck Lever 	if (unlikely(error))
2058e4f93234SChuck Lever 		goto out;
2059e4f93234SChuck Lever 	error = decode_dirlist3(xdr);
2060e4f93234SChuck Lever out:
2061e4f93234SChuck Lever 	return error;
2062e4f93234SChuck Lever }
2063e4f93234SChuck Lever 
2064bf269551SChuck Lever static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
2065bf269551SChuck Lever 				    struct xdr_stream *xdr,
2066fc016483SChristoph Hellwig 				    void *data)
2067e4f93234SChuck Lever {
2068fc016483SChristoph Hellwig 	struct nfs3_readdirres *result = data;
2069e4f93234SChuck Lever 	enum nfs_stat status;
2070e4f93234SChuck Lever 	int error;
2071e4f93234SChuck Lever 
2072bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2073e4f93234SChuck Lever 	if (unlikely(error))
2074e4f93234SChuck Lever 		goto out;
2075e4f93234SChuck Lever 	if (status != NFS3_OK)
2076e4f93234SChuck Lever 		goto out_default;
2077bf269551SChuck Lever 	error = decode_readdir3resok(xdr, result);
2078e4f93234SChuck Lever out:
2079e4f93234SChuck Lever 	return error;
2080e4f93234SChuck Lever out_default:
2081bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->dir_attr);
2082e4f93234SChuck Lever 	if (unlikely(error))
2083e4f93234SChuck Lever 		goto out;
20845e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2085e4f93234SChuck Lever }
2086e4f93234SChuck Lever 
2087e4f93234SChuck Lever /*
2088e4f93234SChuck Lever  * 3.3.18  FSSTAT3res
2089e4f93234SChuck Lever  *
2090e4f93234SChuck Lever  *	struct FSSTAT3resok {
2091e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2092e4f93234SChuck Lever  *		size3		tbytes;
2093e4f93234SChuck Lever  *		size3		fbytes;
2094e4f93234SChuck Lever  *		size3		abytes;
2095e4f93234SChuck Lever  *		size3		tfiles;
2096e4f93234SChuck Lever  *		size3		ffiles;
2097e4f93234SChuck Lever  *		size3		afiles;
2098e4f93234SChuck Lever  *		uint32		invarsec;
2099e4f93234SChuck Lever  *	};
2100e4f93234SChuck Lever  *
2101e4f93234SChuck Lever  *	struct FSSTAT3resfail {
2102e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2103e4f93234SChuck Lever  *	};
2104e4f93234SChuck Lever  *
2105e4f93234SChuck Lever  *	union FSSTAT3res switch (nfsstat3 status) {
2106e4f93234SChuck Lever  *	case NFS3_OK:
2107e4f93234SChuck Lever  *		FSSTAT3resok	resok;
2108e4f93234SChuck Lever  *	default:
2109e4f93234SChuck Lever  *		FSSTAT3resfail	resfail;
2110e4f93234SChuck Lever  *	};
2111e4f93234SChuck Lever  */
2112e4f93234SChuck Lever static int decode_fsstat3resok(struct xdr_stream *xdr,
2113e4f93234SChuck Lever 			       struct nfs_fsstat *result)
2114e4f93234SChuck Lever {
2115e4f93234SChuck Lever 	__be32 *p;
2116e4f93234SChuck Lever 
2117e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8 * 6 + 4);
2118eb72f484SChuck Lever 	if (unlikely(!p))
2119eb72f484SChuck Lever 		return -EIO;
2120e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tbytes);
2121e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->fbytes);
2122e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->abytes);
2123e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tfiles);
2124e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->ffiles);
2125e4f93234SChuck Lever 	xdr_decode_size3(p, &result->afiles);
2126e4f93234SChuck Lever 	/* ignore invarsec */
2127e4f93234SChuck Lever 	return 0;
2128e4f93234SChuck Lever }
2129e4f93234SChuck Lever 
2130bf269551SChuck Lever static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
2131bf269551SChuck Lever 				   struct xdr_stream *xdr,
2132fc016483SChristoph Hellwig 				   void *data)
2133e4f93234SChuck Lever {
2134fc016483SChristoph Hellwig 	struct nfs_fsstat *result = data;
2135e4f93234SChuck Lever 	enum nfs_stat status;
2136e4f93234SChuck Lever 	int error;
2137e4f93234SChuck Lever 
2138bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2139e4f93234SChuck Lever 	if (unlikely(error))
2140e4f93234SChuck Lever 		goto out;
2141bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2142e4f93234SChuck Lever 	if (unlikely(error))
2143e4f93234SChuck Lever 		goto out;
2144e4f93234SChuck Lever 	if (status != NFS3_OK)
2145e4f93234SChuck Lever 		goto out_status;
2146bf269551SChuck Lever 	error = decode_fsstat3resok(xdr, result);
2147e4f93234SChuck Lever out:
2148e4f93234SChuck Lever 	return error;
2149e4f93234SChuck Lever out_status:
21505e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2151e4f93234SChuck Lever }
2152e4f93234SChuck Lever 
2153e4f93234SChuck Lever /*
2154e4f93234SChuck Lever  * 3.3.19  FSINFO3res
2155e4f93234SChuck Lever  *
2156e4f93234SChuck Lever  *	struct FSINFO3resok {
2157e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2158e4f93234SChuck Lever  *		uint32		rtmax;
2159e4f93234SChuck Lever  *		uint32		rtpref;
2160e4f93234SChuck Lever  *		uint32		rtmult;
2161e4f93234SChuck Lever  *		uint32		wtmax;
2162e4f93234SChuck Lever  *		uint32		wtpref;
2163e4f93234SChuck Lever  *		uint32		wtmult;
2164e4f93234SChuck Lever  *		uint32		dtpref;
2165e4f93234SChuck Lever  *		size3		maxfilesize;
2166e4f93234SChuck Lever  *		nfstime3	time_delta;
2167e4f93234SChuck Lever  *		uint32		properties;
2168e4f93234SChuck Lever  *	};
2169e4f93234SChuck Lever  *
2170e4f93234SChuck Lever  *	struct FSINFO3resfail {
2171e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2172e4f93234SChuck Lever  *	};
2173e4f93234SChuck Lever  *
2174e4f93234SChuck Lever  *	union FSINFO3res switch (nfsstat3 status) {
2175e4f93234SChuck Lever  *	case NFS3_OK:
2176e4f93234SChuck Lever  *		FSINFO3resok	resok;
2177e4f93234SChuck Lever  *	default:
2178e4f93234SChuck Lever  *		FSINFO3resfail	resfail;
2179e4f93234SChuck Lever  *	};
2180e4f93234SChuck Lever  */
2181e4f93234SChuck Lever static int decode_fsinfo3resok(struct xdr_stream *xdr,
2182e4f93234SChuck Lever 			       struct nfs_fsinfo *result)
2183e4f93234SChuck Lever {
2184e4f93234SChuck Lever 	__be32 *p;
2185e4f93234SChuck Lever 
2186e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4);
2187eb72f484SChuck Lever 	if (unlikely(!p))
2188eb72f484SChuck Lever 		return -EIO;
2189e4f93234SChuck Lever 	result->rtmax  = be32_to_cpup(p++);
2190e4f93234SChuck Lever 	result->rtpref = be32_to_cpup(p++);
2191e4f93234SChuck Lever 	result->rtmult = be32_to_cpup(p++);
2192e4f93234SChuck Lever 	result->wtmax  = be32_to_cpup(p++);
2193e4f93234SChuck Lever 	result->wtpref = be32_to_cpup(p++);
2194e4f93234SChuck Lever 	result->wtmult = be32_to_cpup(p++);
2195e4f93234SChuck Lever 	result->dtpref = be32_to_cpup(p++);
2196e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->maxfilesize);
2197f6048709SChuck Lever 	xdr_decode_nfstime3(p, &result->time_delta);
2198e4f93234SChuck Lever 
2199e4f93234SChuck Lever 	/* ignore properties */
2200e4f93234SChuck Lever 	result->lease_time = 0;
2201e4f93234SChuck Lever 	return 0;
2202e4f93234SChuck Lever }
2203e4f93234SChuck Lever 
2204bf269551SChuck Lever static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
2205bf269551SChuck Lever 				   struct xdr_stream *xdr,
2206fc016483SChristoph Hellwig 				   void *data)
2207e4f93234SChuck Lever {
2208fc016483SChristoph Hellwig 	struct nfs_fsinfo *result = data;
2209e4f93234SChuck Lever 	enum nfs_stat status;
2210e4f93234SChuck Lever 	int error;
2211e4f93234SChuck Lever 
2212bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2213e4f93234SChuck Lever 	if (unlikely(error))
2214e4f93234SChuck Lever 		goto out;
2215bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2216e4f93234SChuck Lever 	if (unlikely(error))
2217e4f93234SChuck Lever 		goto out;
2218e4f93234SChuck Lever 	if (status != NFS3_OK)
2219e4f93234SChuck Lever 		goto out_status;
2220bf269551SChuck Lever 	error = decode_fsinfo3resok(xdr, result);
2221e4f93234SChuck Lever out:
2222e4f93234SChuck Lever 	return error;
2223e4f93234SChuck Lever out_status:
22245e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2225e4f93234SChuck Lever }
2226e4f93234SChuck Lever 
2227e4f93234SChuck Lever /*
2228e4f93234SChuck Lever  * 3.3.20  PATHCONF3res
2229e4f93234SChuck Lever  *
2230e4f93234SChuck Lever  *	struct PATHCONF3resok {
2231e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2232e4f93234SChuck Lever  *		uint32		linkmax;
2233e4f93234SChuck Lever  *		uint32		name_max;
2234e4f93234SChuck Lever  *		bool		no_trunc;
2235e4f93234SChuck Lever  *		bool		chown_restricted;
2236e4f93234SChuck Lever  *		bool		case_insensitive;
2237e4f93234SChuck Lever  *		bool		case_preserving;
2238e4f93234SChuck Lever  *	};
2239e4f93234SChuck Lever  *
2240e4f93234SChuck Lever  *	struct PATHCONF3resfail {
2241e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2242e4f93234SChuck Lever  *	};
2243e4f93234SChuck Lever  *
2244e4f93234SChuck Lever  *	union PATHCONF3res switch (nfsstat3 status) {
2245e4f93234SChuck Lever  *	case NFS3_OK:
2246e4f93234SChuck Lever  *		PATHCONF3resok	resok;
2247e4f93234SChuck Lever  *	default:
2248e4f93234SChuck Lever  *		PATHCONF3resfail resfail;
2249e4f93234SChuck Lever  *	};
2250e4f93234SChuck Lever  */
2251e4f93234SChuck Lever static int decode_pathconf3resok(struct xdr_stream *xdr,
2252e4f93234SChuck Lever 				 struct nfs_pathconf *result)
2253e4f93234SChuck Lever {
2254e4f93234SChuck Lever 	__be32 *p;
2255e4f93234SChuck Lever 
2256e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 6);
2257eb72f484SChuck Lever 	if (unlikely(!p))
2258eb72f484SChuck Lever 		return -EIO;
2259e4f93234SChuck Lever 	result->max_link = be32_to_cpup(p++);
2260e4f93234SChuck Lever 	result->max_namelen = be32_to_cpup(p);
2261e4f93234SChuck Lever 	/* ignore remaining fields */
2262e4f93234SChuck Lever 	return 0;
2263e4f93234SChuck Lever }
2264e4f93234SChuck Lever 
2265bf269551SChuck Lever static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
2266bf269551SChuck Lever 				     struct xdr_stream *xdr,
2267fc016483SChristoph Hellwig 				     void *data)
2268e4f93234SChuck Lever {
2269fc016483SChristoph Hellwig 	struct nfs_pathconf *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_pathconf3resok(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.21  COMMIT3res
2290e4f93234SChuck Lever  *
2291e4f93234SChuck Lever  *	struct COMMIT3resok {
2292e4f93234SChuck Lever  *		wcc_data	file_wcc;
2293e4f93234SChuck Lever  *		writeverf3	verf;
2294e4f93234SChuck Lever  *	};
2295e4f93234SChuck Lever  *
2296e4f93234SChuck Lever  *	struct COMMIT3resfail {
2297e4f93234SChuck Lever  *		wcc_data	file_wcc;
2298e4f93234SChuck Lever  *	};
2299e4f93234SChuck Lever  *
2300e4f93234SChuck Lever  *	union COMMIT3res switch (nfsstat3 status) {
2301e4f93234SChuck Lever  *	case NFS3_OK:
2302e4f93234SChuck Lever  *		COMMIT3resok	resok;
2303e4f93234SChuck Lever  *	default:
2304e4f93234SChuck Lever  *		COMMIT3resfail	resfail;
2305e4f93234SChuck Lever  *	};
2306e4f93234SChuck Lever  */
2307bf269551SChuck Lever static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
2308bf269551SChuck Lever 				   struct xdr_stream *xdr,
2309fc016483SChristoph Hellwig 				   void *data)
2310e4f93234SChuck Lever {
2311fc016483SChristoph Hellwig 	struct nfs_commitres *result = data;
2312e4f93234SChuck Lever 	enum nfs_stat status;
2313e4f93234SChuck Lever 	int error;
2314e4f93234SChuck Lever 
2315bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2316e4f93234SChuck Lever 	if (unlikely(error))
2317e4f93234SChuck Lever 		goto out;
2318bf269551SChuck Lever 	error = decode_wcc_data(xdr, result->fattr);
2319e4f93234SChuck Lever 	if (unlikely(error))
2320e4f93234SChuck Lever 		goto out;
2321aabff4ddSPeng Tao 	result->op_status = status;
2322e4f93234SChuck Lever 	if (status != NFS3_OK)
2323e4f93234SChuck Lever 		goto out_status;
23242f2c63bcSTrond Myklebust 	error = decode_writeverf3(xdr, &result->verf->verifier);
2325e4f93234SChuck Lever out:
2326e4f93234SChuck Lever 	return error;
2327e4f93234SChuck Lever out_status:
23285e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2329e4f93234SChuck Lever }
2330e4f93234SChuck Lever 
2331b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2332b7fa0554SAndreas Gruenbacher 
2333e4f93234SChuck Lever static inline int decode_getacl3resok(struct xdr_stream *xdr,
2334e4f93234SChuck Lever 				      struct nfs3_getaclres *result)
2335e4f93234SChuck Lever {
2336e4f93234SChuck Lever 	struct posix_acl **acl;
2337e4f93234SChuck Lever 	unsigned int *aclcnt;
2338e4f93234SChuck Lever 	size_t hdrlen;
2339e4f93234SChuck Lever 	int error;
2340e4f93234SChuck Lever 
2341e4f93234SChuck Lever 	error = decode_post_op_attr(xdr, result->fattr);
2342e4f93234SChuck Lever 	if (unlikely(error))
2343e4f93234SChuck Lever 		goto out;
2344e4f93234SChuck Lever 	error = decode_uint32(xdr, &result->mask);
2345e4f93234SChuck Lever 	if (unlikely(error))
2346e4f93234SChuck Lever 		goto out;
2347e4f93234SChuck Lever 	error = -EINVAL;
2348e4f93234SChuck Lever 	if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
2349e4f93234SChuck Lever 		goto out;
2350e4f93234SChuck Lever 
23511aecca3eSTrond Myklebust 	hdrlen = xdr_stream_pos(xdr);
2352e4f93234SChuck Lever 
2353e4f93234SChuck Lever 	acl = NULL;
2354e4f93234SChuck Lever 	if (result->mask & NFS_ACL)
2355e4f93234SChuck Lever 		acl = &result->acl_access;
2356e4f93234SChuck Lever 	aclcnt = NULL;
2357e4f93234SChuck Lever 	if (result->mask & NFS_ACLCNT)
2358e4f93234SChuck Lever 		aclcnt = &result->acl_access_count;
2359e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl);
2360e4f93234SChuck Lever 	if (unlikely(error <= 0))
2361e4f93234SChuck Lever 		goto out;
2362e4f93234SChuck Lever 
2363e4f93234SChuck Lever 	acl = NULL;
2364e4f93234SChuck Lever 	if (result->mask & NFS_DFACL)
2365e4f93234SChuck Lever 		acl = &result->acl_default;
2366e4f93234SChuck Lever 	aclcnt = NULL;
2367e4f93234SChuck Lever 	if (result->mask & NFS_DFACLCNT)
2368e4f93234SChuck Lever 		aclcnt = &result->acl_default_count;
2369e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl);
2370e4f93234SChuck Lever 	if (unlikely(error <= 0))
2371e4f93234SChuck Lever 		return error;
2372e4f93234SChuck Lever 	error = 0;
2373e4f93234SChuck Lever out:
2374e4f93234SChuck Lever 	return error;
2375e4f93234SChuck Lever }
2376e4f93234SChuck Lever 
2377bf269551SChuck Lever static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
2378bf269551SChuck Lever 				   struct xdr_stream *xdr,
2379fc016483SChristoph Hellwig 				   void *result)
2380e4f93234SChuck Lever {
2381e4f93234SChuck Lever 	enum nfs_stat status;
2382e4f93234SChuck Lever 	int error;
2383e4f93234SChuck Lever 
2384bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2385e4f93234SChuck Lever 	if (unlikely(error))
2386e4f93234SChuck Lever 		goto out;
2387e4f93234SChuck Lever 	if (status != NFS3_OK)
2388e4f93234SChuck Lever 		goto out_default;
2389bf269551SChuck Lever 	error = decode_getacl3resok(xdr, result);
2390e4f93234SChuck Lever out:
2391e4f93234SChuck Lever 	return error;
2392e4f93234SChuck Lever out_default:
23935e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2394e4f93234SChuck Lever }
2395e4f93234SChuck Lever 
2396bf269551SChuck Lever static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
2397bf269551SChuck Lever 				   struct xdr_stream *xdr,
2398fc016483SChristoph Hellwig 				   void *result)
2399e4f93234SChuck Lever {
2400e4f93234SChuck Lever 	enum nfs_stat status;
2401e4f93234SChuck Lever 	int error;
2402e4f93234SChuck Lever 
2403bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2404e4f93234SChuck Lever 	if (unlikely(error))
2405e4f93234SChuck Lever 		goto out;
2406e4f93234SChuck Lever 	if (status != NFS3_OK)
2407e4f93234SChuck Lever 		goto out_default;
2408bf269551SChuck Lever 	error = decode_post_op_attr(xdr, result);
2409e4f93234SChuck Lever out:
2410e4f93234SChuck Lever 	return error;
2411e4f93234SChuck Lever out_default:
24125e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2413e4f93234SChuck Lever }
2414e4f93234SChuck Lever 
2415b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2416b7fa0554SAndreas Gruenbacher 
24175e7e5a0dSBryan Schumaker 
24185e7e5a0dSBryan Schumaker /*
24195e7e5a0dSBryan Schumaker  * We need to translate between nfs status return values and
24205e7e5a0dSBryan Schumaker  * the local errno values which may not be the same.
24215e7e5a0dSBryan Schumaker  */
24225e7e5a0dSBryan Schumaker static const struct {
24235e7e5a0dSBryan Schumaker 	int stat;
24245e7e5a0dSBryan Schumaker 	int errno;
24255e7e5a0dSBryan Schumaker } nfs_errtbl[] = {
24265e7e5a0dSBryan Schumaker 	{ NFS_OK,		0		},
24275e7e5a0dSBryan Schumaker 	{ NFSERR_PERM,		-EPERM		},
24285e7e5a0dSBryan Schumaker 	{ NFSERR_NOENT,		-ENOENT		},
24295e7e5a0dSBryan Schumaker 	{ NFSERR_IO,		-errno_NFSERR_IO},
24305e7e5a0dSBryan Schumaker 	{ NFSERR_NXIO,		-ENXIO		},
24315e7e5a0dSBryan Schumaker /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
24325e7e5a0dSBryan Schumaker 	{ NFSERR_ACCES,		-EACCES		},
24335e7e5a0dSBryan Schumaker 	{ NFSERR_EXIST,		-EEXIST		},
24345e7e5a0dSBryan Schumaker 	{ NFSERR_XDEV,		-EXDEV		},
24355e7e5a0dSBryan Schumaker 	{ NFSERR_NODEV,		-ENODEV		},
24365e7e5a0dSBryan Schumaker 	{ NFSERR_NOTDIR,	-ENOTDIR	},
24375e7e5a0dSBryan Schumaker 	{ NFSERR_ISDIR,		-EISDIR		},
24385e7e5a0dSBryan Schumaker 	{ NFSERR_INVAL,		-EINVAL		},
24395e7e5a0dSBryan Schumaker 	{ NFSERR_FBIG,		-EFBIG		},
24405e7e5a0dSBryan Schumaker 	{ NFSERR_NOSPC,		-ENOSPC		},
24415e7e5a0dSBryan Schumaker 	{ NFSERR_ROFS,		-EROFS		},
24425e7e5a0dSBryan Schumaker 	{ NFSERR_MLINK,		-EMLINK		},
24435e7e5a0dSBryan Schumaker 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
24445e7e5a0dSBryan Schumaker 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
24455e7e5a0dSBryan Schumaker 	{ NFSERR_DQUOT,		-EDQUOT		},
24465e7e5a0dSBryan Schumaker 	{ NFSERR_STALE,		-ESTALE		},
24475e7e5a0dSBryan Schumaker 	{ NFSERR_REMOTE,	-EREMOTE	},
24485e7e5a0dSBryan Schumaker #ifdef EWFLUSH
24495e7e5a0dSBryan Schumaker 	{ NFSERR_WFLUSH,	-EWFLUSH	},
24505e7e5a0dSBryan Schumaker #endif
24515e7e5a0dSBryan Schumaker 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
24525e7e5a0dSBryan Schumaker 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
24535e7e5a0dSBryan Schumaker 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
24545e7e5a0dSBryan Schumaker 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
24555e7e5a0dSBryan Schumaker 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
24565e7e5a0dSBryan Schumaker 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
24575e7e5a0dSBryan Schumaker 	{ NFSERR_BADTYPE,	-EBADTYPE	},
24585e7e5a0dSBryan Schumaker 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
24595e7e5a0dSBryan Schumaker 	{ -1,			-EIO		}
24605e7e5a0dSBryan Schumaker };
24615e7e5a0dSBryan Schumaker 
24625e7e5a0dSBryan Schumaker /**
24635e7e5a0dSBryan Schumaker  * nfs3_stat_to_errno - convert an NFS status code to a local errno
24645e7e5a0dSBryan Schumaker  * @status: NFS status code to convert
24655e7e5a0dSBryan Schumaker  *
24665e7e5a0dSBryan Schumaker  * Returns a local errno value, or -EIO if the NFS status code is
24675e7e5a0dSBryan Schumaker  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
24685e7e5a0dSBryan Schumaker  */
24695e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat status)
24705e7e5a0dSBryan Schumaker {
24715e7e5a0dSBryan Schumaker 	int i;
24725e7e5a0dSBryan Schumaker 
24735e7e5a0dSBryan Schumaker 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
24745e7e5a0dSBryan Schumaker 		if (nfs_errtbl[i].stat == (int)status)
24755e7e5a0dSBryan Schumaker 			return nfs_errtbl[i].errno;
24765e7e5a0dSBryan Schumaker 	}
24775e7e5a0dSBryan Schumaker 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
24785e7e5a0dSBryan Schumaker 	return nfs_errtbl[i].errno;
24795e7e5a0dSBryan Schumaker }
24805e7e5a0dSBryan Schumaker 
24815e7e5a0dSBryan Schumaker 
24821da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
24831da177e4SLinus Torvalds [NFS3PROC_##proc] = {							\
24841da177e4SLinus Torvalds 	.p_proc      = NFS3PROC_##proc,					\
2485fcc85819SChristoph Hellwig 	.p_encode    = nfs3_xdr_enc_##argtype##3args,			\
2486fc016483SChristoph Hellwig 	.p_decode    = nfs3_xdr_dec_##restype##3res,			\
2487ad96b5b5SChuck Lever 	.p_arglen    = NFS3_##argtype##args_sz,				\
2488f5fc3c50SChuck Lever 	.p_replen    = NFS3_##restype##res_sz,				\
2489cc0175c1SChuck Lever 	.p_timer     = timer,						\
2490cc0175c1SChuck Lever 	.p_statidx   = NFS3PROC_##proc,					\
2491cc0175c1SChuck Lever 	.p_name      = #proc,						\
24921da177e4SLinus Torvalds 	}
24931da177e4SLinus Torvalds 
2494511e936bSChristoph Hellwig const struct rpc_procinfo nfs3_procedures[] = {
2495f5fc3c50SChuck Lever 	PROC(GETATTR,		getattr,	getattr,	1),
2496f5fc3c50SChuck Lever 	PROC(SETATTR,		setattr,	setattr,	0),
2497f5fc3c50SChuck Lever 	PROC(LOOKUP,		lookup,		lookup,		2),
2498f5fc3c50SChuck Lever 	PROC(ACCESS,		access,		access,		1),
2499f5fc3c50SChuck Lever 	PROC(READLINK,		readlink,	readlink,	3),
2500f5fc3c50SChuck Lever 	PROC(READ,		read,		read,		3),
2501f5fc3c50SChuck Lever 	PROC(WRITE,		write,		write,		4),
2502f5fc3c50SChuck Lever 	PROC(CREATE,		create,		create,		0),
2503f5fc3c50SChuck Lever 	PROC(MKDIR,		mkdir,		create,		0),
2504f5fc3c50SChuck Lever 	PROC(SYMLINK,		symlink,	create,		0),
2505f5fc3c50SChuck Lever 	PROC(MKNOD,		mknod,		create,		0),
2506f5fc3c50SChuck Lever 	PROC(REMOVE,		remove,		remove,		0),
2507f5fc3c50SChuck Lever 	PROC(RMDIR,		lookup,		setattr,	0),
2508f5fc3c50SChuck Lever 	PROC(RENAME,		rename,		rename,		0),
2509f5fc3c50SChuck Lever 	PROC(LINK,		link,		link,		0),
2510f5fc3c50SChuck Lever 	PROC(READDIR,		readdir,	readdir,	3),
2511f5fc3c50SChuck Lever 	PROC(READDIRPLUS,	readdirplus,	readdir,	3),
2512f5fc3c50SChuck Lever 	PROC(FSSTAT,		getattr,	fsstat,		0),
2513f5fc3c50SChuck Lever 	PROC(FSINFO,		getattr,	fsinfo,		0),
2514f5fc3c50SChuck Lever 	PROC(PATHCONF,		getattr,	pathconf,	0),
2515f5fc3c50SChuck Lever 	PROC(COMMIT,		commit,		commit,		5),
25161da177e4SLinus Torvalds };
25171da177e4SLinus Torvalds 
2518c551858aSChristoph Hellwig static unsigned int nfs_version3_counts[ARRAY_SIZE(nfs3_procedures)];
2519a613fa16STrond Myklebust const struct rpc_version nfs_version3 = {
25201da177e4SLinus Torvalds 	.number			= 3,
2521e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
2522c551858aSChristoph Hellwig 	.procs			= nfs3_procedures,
2523c551858aSChristoph Hellwig 	.counts			= nfs_version3_counts,
25241da177e4SLinus Torvalds };
25251da177e4SLinus Torvalds 
2526b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2527511e936bSChristoph Hellwig static const struct rpc_procinfo nfs3_acl_procedures[] = {
2528b7fa0554SAndreas Gruenbacher 	[ACLPROC3_GETACL] = {
2529b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_GETACL,
2530fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_getacl3args,
2531fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_getacl3res,
25322bea90d4SChuck Lever 		.p_arglen = ACL3_getaclargs_sz,
25332bea90d4SChuck Lever 		.p_replen = ACL3_getaclres_sz,
2534b7fa0554SAndreas Gruenbacher 		.p_timer = 1,
2535cc0175c1SChuck Lever 		.p_name = "GETACL",
2536b7fa0554SAndreas Gruenbacher 	},
2537b7fa0554SAndreas Gruenbacher 	[ACLPROC3_SETACL] = {
2538b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_SETACL,
2539fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_setacl3args,
2540fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_setacl3res,
25412bea90d4SChuck Lever 		.p_arglen = ACL3_setaclargs_sz,
25422bea90d4SChuck Lever 		.p_replen = ACL3_setaclres_sz,
2543b7fa0554SAndreas Gruenbacher 		.p_timer = 0,
2544cc0175c1SChuck Lever 		.p_name = "SETACL",
2545b7fa0554SAndreas Gruenbacher 	},
2546b7fa0554SAndreas Gruenbacher };
2547b7fa0554SAndreas Gruenbacher 
2548c551858aSChristoph Hellwig static unsigned int nfs3_acl_counts[ARRAY_SIZE(nfs3_acl_procedures)];
2549a613fa16STrond Myklebust const struct rpc_version nfsacl_version3 = {
2550b7fa0554SAndreas Gruenbacher 	.number			= 3,
25519ae7d8ffSChristoph Hellwig 	.nrprocs		= ARRAY_SIZE(nfs3_acl_procedures),
2552b7fa0554SAndreas Gruenbacher 	.procs			= nfs3_acl_procedures,
2553c551858aSChristoph Hellwig 	.counts			= nfs3_acl_counts,
2554b7fa0554SAndreas Gruenbacher };
2555b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2556