xref: /openbmc/linux/fs/nfs/nfs3xdr.c (revision 221203ce)
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)
7202ef04e4SChuck Lever #define NFS3_readlinkres_sz	(1+NFS3_post_op_attr_sz+1+1)
7302ef04e4SChuck Lever #define NFS3_readres_sz		(1+NFS3_post_op_attr_sz+3+1)
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)
7802ef04e4SChuck Lever #define NFS3_readdirres_sz	(1+NFS3_post_op_attr_sz+2+1)
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+ \
8802ef04e4SChuck Lever 				XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)+1)
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 
107264d948cSTrond Myklebust static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt)
108264d948cSTrond Myklebust {
109264d948cSTrond Myklebust 	if (clnt && clnt->cl_cred)
110264d948cSTrond Myklebust 		return clnt->cl_cred->user_ns;
111264d948cSTrond Myklebust 	return &init_user_ns;
112264d948cSTrond Myklebust }
113264d948cSTrond Myklebust 
114264d948cSTrond Myklebust static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp)
115264d948cSTrond Myklebust {
116264d948cSTrond Myklebust 	if (rqstp->rq_task)
117264d948cSTrond Myklebust 		return rpc_userns(rqstp->rq_task->tk_client);
118264d948cSTrond Myklebust 	return &init_user_ns;
119264d948cSTrond Myklebust }
120264d948cSTrond Myklebust 
1211da177e4SLinus Torvalds /*
122d9c407b1SChuck Lever  * Encode/decode NFSv3 basic data types
123d9c407b1SChuck Lever  *
124d9c407b1SChuck Lever  * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
125d9c407b1SChuck Lever  * "NFS Version 3 Protocol Specification".
126d9c407b1SChuck Lever  *
127d9c407b1SChuck Lever  * Not all basic data types have their own encoding and decoding
128d9c407b1SChuck Lever  * functions.  For run-time efficiency, some data types are encoded
129d9c407b1SChuck Lever  * or decoded inline.
130d9c407b1SChuck Lever  */
131d9c407b1SChuck Lever 
132d9c407b1SChuck Lever static void encode_uint32(struct xdr_stream *xdr, u32 value)
133d9c407b1SChuck Lever {
134d9c407b1SChuck Lever 	__be32 *p = xdr_reserve_space(xdr, 4);
135d9c407b1SChuck Lever 	*p = cpu_to_be32(value);
136d9c407b1SChuck Lever }
137d9c407b1SChuck Lever 
138e4f93234SChuck Lever static int decode_uint32(struct xdr_stream *xdr, u32 *value)
139e4f93234SChuck Lever {
140e4f93234SChuck Lever 	__be32 *p;
141e4f93234SChuck Lever 
142e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
143eb72f484SChuck Lever 	if (unlikely(!p))
144eb72f484SChuck Lever 		return -EIO;
145e4f93234SChuck Lever 	*value = be32_to_cpup(p);
146e4f93234SChuck Lever 	return 0;
147e4f93234SChuck Lever }
148e4f93234SChuck Lever 
149e4f93234SChuck Lever static int decode_uint64(struct xdr_stream *xdr, u64 *value)
150e4f93234SChuck Lever {
151e4f93234SChuck Lever 	__be32 *p;
152e4f93234SChuck Lever 
153e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8);
154eb72f484SChuck Lever 	if (unlikely(!p))
155eb72f484SChuck Lever 		return -EIO;
156e4f93234SChuck Lever 	xdr_decode_hyper(p, value);
157e4f93234SChuck Lever 	return 0;
158e4f93234SChuck Lever }
159e4f93234SChuck Lever 
160e4f93234SChuck Lever /*
161e4f93234SChuck Lever  * fileid3
162e4f93234SChuck Lever  *
163e4f93234SChuck Lever  *	typedef uint64 fileid3;
164e4f93234SChuck Lever  */
165f6048709SChuck Lever static __be32 *xdr_decode_fileid3(__be32 *p, u64 *fileid)
166f6048709SChuck Lever {
167f6048709SChuck Lever 	return xdr_decode_hyper(p, fileid);
168f6048709SChuck Lever }
169f6048709SChuck Lever 
170e4f93234SChuck Lever static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid)
171e4f93234SChuck Lever {
172e4f93234SChuck Lever 	return decode_uint64(xdr, fileid);
173e4f93234SChuck Lever }
174e4f93234SChuck Lever 
175d9c407b1SChuck Lever /*
176d9c407b1SChuck Lever  * filename3
177d9c407b1SChuck Lever  *
178d9c407b1SChuck Lever  *	typedef string filename3<>;
179d9c407b1SChuck Lever  */
180d9c407b1SChuck Lever static void encode_filename3(struct xdr_stream *xdr,
181d9c407b1SChuck Lever 			     const char *name, u32 length)
182d9c407b1SChuck Lever {
183d9c407b1SChuck Lever 	__be32 *p;
184d9c407b1SChuck Lever 
1857fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS3_MAXNAMLEN);
186d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
187d9c407b1SChuck Lever 	xdr_encode_opaque(p, name, length);
188d9c407b1SChuck Lever }
189d9c407b1SChuck Lever 
190e4f93234SChuck Lever static int decode_inline_filename3(struct xdr_stream *xdr,
191e4f93234SChuck Lever 				   const char **name, u32 *length)
192e4f93234SChuck Lever {
193e4f93234SChuck Lever 	__be32 *p;
194e4f93234SChuck Lever 	u32 count;
195e4f93234SChuck Lever 
196e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
197eb72f484SChuck Lever 	if (unlikely(!p))
198eb72f484SChuck Lever 		return -EIO;
199e4f93234SChuck Lever 	count = be32_to_cpup(p);
200e4f93234SChuck Lever 	if (count > NFS3_MAXNAMLEN)
201e4f93234SChuck Lever 		goto out_nametoolong;
202e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, count);
203eb72f484SChuck Lever 	if (unlikely(!p))
204eb72f484SChuck Lever 		return -EIO;
205e4f93234SChuck Lever 	*name = (const char *)p;
206e4f93234SChuck Lever 	*length = count;
207e4f93234SChuck Lever 	return 0;
208e4f93234SChuck Lever 
209e4f93234SChuck Lever out_nametoolong:
210e4f93234SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
211e4f93234SChuck Lever 	return -ENAMETOOLONG;
212e4f93234SChuck Lever }
213e4f93234SChuck Lever 
214d9c407b1SChuck Lever /*
215d9c407b1SChuck Lever  * nfspath3
216d9c407b1SChuck Lever  *
217d9c407b1SChuck Lever  *	typedef string nfspath3<>;
218d9c407b1SChuck Lever  */
219d9c407b1SChuck Lever static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
220d9c407b1SChuck Lever 			    const u32 length)
221d9c407b1SChuck Lever {
222d9c407b1SChuck Lever 	encode_uint32(xdr, length);
223d9c407b1SChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
224d9c407b1SChuck Lever }
225d9c407b1SChuck Lever 
226e4f93234SChuck Lever static int decode_nfspath3(struct xdr_stream *xdr)
227e4f93234SChuck Lever {
228e4f93234SChuck Lever 	u32 recvd, count;
229e4f93234SChuck Lever 	__be32 *p;
230e4f93234SChuck Lever 
231e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
232eb72f484SChuck Lever 	if (unlikely(!p))
233eb72f484SChuck Lever 		return -EIO;
234e4f93234SChuck Lever 	count = be32_to_cpup(p);
235e4f93234SChuck Lever 	if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
236e4f93234SChuck Lever 		goto out_nametoolong;
23764bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
238e4f93234SChuck Lever 	if (unlikely(count > recvd))
239e4f93234SChuck Lever 		goto out_cheating;
240e4f93234SChuck Lever 	xdr_terminate_string(xdr->buf, count);
241e4f93234SChuck Lever 	return 0;
242e4f93234SChuck Lever 
243e4f93234SChuck Lever out_nametoolong:
244e4f93234SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", count);
245e4f93234SChuck Lever 	return -ENAMETOOLONG;
246e4f93234SChuck Lever out_cheating:
247e4f93234SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
248e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
249e4f93234SChuck Lever 	return -EIO;
250e4f93234SChuck Lever }
251e4f93234SChuck Lever 
252d9c407b1SChuck Lever /*
253d9c407b1SChuck Lever  * cookie3
254d9c407b1SChuck Lever  *
255d9c407b1SChuck Lever  *	typedef uint64 cookie3
256d9c407b1SChuck Lever  */
257d9c407b1SChuck Lever static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
258d9c407b1SChuck Lever {
259d9c407b1SChuck Lever 	return xdr_encode_hyper(p, cookie);
260d9c407b1SChuck Lever }
261d9c407b1SChuck Lever 
262e4f93234SChuck Lever static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie)
263e4f93234SChuck Lever {
264e4f93234SChuck Lever 	return decode_uint64(xdr, cookie);
265e4f93234SChuck Lever }
266e4f93234SChuck Lever 
267d9c407b1SChuck Lever /*
268d9c407b1SChuck Lever  * cookieverf3
269d9c407b1SChuck Lever  *
270d9c407b1SChuck Lever  *	typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
271d9c407b1SChuck Lever  */
272d9c407b1SChuck Lever static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
273d9c407b1SChuck Lever {
274d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
275d9c407b1SChuck Lever 	return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
276d9c407b1SChuck Lever }
277d9c407b1SChuck Lever 
278e4f93234SChuck Lever static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier)
279e4f93234SChuck Lever {
280e4f93234SChuck Lever 	__be32 *p;
281e4f93234SChuck Lever 
282e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
283eb72f484SChuck Lever 	if (unlikely(!p))
284eb72f484SChuck Lever 		return -EIO;
285e4f93234SChuck Lever 	memcpy(verifier, p, NFS3_COOKIEVERFSIZE);
286e4f93234SChuck Lever 	return 0;
287e4f93234SChuck Lever }
288e4f93234SChuck Lever 
289d9c407b1SChuck Lever /*
290d9c407b1SChuck Lever  * createverf3
291d9c407b1SChuck Lever  *
292d9c407b1SChuck Lever  *	typedef opaque createverf3[NFS3_CREATEVERFSIZE];
293d9c407b1SChuck Lever  */
294d9c407b1SChuck Lever static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
295d9c407b1SChuck Lever {
296d9c407b1SChuck Lever 	__be32 *p;
297d9c407b1SChuck Lever 
298d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
299d9c407b1SChuck Lever 	memcpy(p, verifier, NFS3_CREATEVERFSIZE);
300d9c407b1SChuck Lever }
301d9c407b1SChuck Lever 
3022f2c63bcSTrond Myklebust static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
303e4f93234SChuck Lever {
304e4f93234SChuck Lever 	__be32 *p;
305e4f93234SChuck Lever 
306e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
307eb72f484SChuck Lever 	if (unlikely(!p))
308eb72f484SChuck Lever 		return -EIO;
3092f2c63bcSTrond Myklebust 	memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
310e4f93234SChuck Lever 	return 0;
311e4f93234SChuck Lever }
312e4f93234SChuck Lever 
313e4f93234SChuck Lever /*
314e4f93234SChuck Lever  * size3
315e4f93234SChuck Lever  *
316e4f93234SChuck Lever  *	typedef uint64 size3;
317e4f93234SChuck Lever  */
318e4f93234SChuck Lever static __be32 *xdr_decode_size3(__be32 *p, u64 *size)
319e4f93234SChuck Lever {
320e4f93234SChuck Lever 	return xdr_decode_hyper(p, size);
321e4f93234SChuck Lever }
322e4f93234SChuck Lever 
323e4f93234SChuck Lever /*
324e4f93234SChuck Lever  * nfsstat3
325e4f93234SChuck Lever  *
326e4f93234SChuck Lever  *	enum nfsstat3 {
327e4f93234SChuck Lever  *		NFS3_OK = 0,
328e4f93234SChuck Lever  *		...
329e4f93234SChuck Lever  *	}
330e4f93234SChuck Lever  */
331e4f93234SChuck Lever #define NFS3_OK		NFS_OK
332e4f93234SChuck Lever 
333e4f93234SChuck Lever static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status)
334e4f93234SChuck Lever {
335e4f93234SChuck Lever 	__be32 *p;
336e4f93234SChuck Lever 
337e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
338eb72f484SChuck Lever 	if (unlikely(!p))
339eb72f484SChuck Lever 		return -EIO;
340f23f6584SChuck Lever 	if (unlikely(*p != cpu_to_be32(NFS3_OK)))
341f23f6584SChuck Lever 		goto out_status;
342f23f6584SChuck Lever 	*status = 0;
343f23f6584SChuck Lever 	return 0;
344f23f6584SChuck Lever out_status:
345e4f93234SChuck Lever 	*status = be32_to_cpup(p);
34662a92ba9SChuck Lever 	trace_nfs_xdr_status(xdr, (int)*status);
347e4f93234SChuck Lever 	return 0;
348e4f93234SChuck Lever }
349e4f93234SChuck Lever 
350d9c407b1SChuck Lever /*
351d9c407b1SChuck Lever  * ftype3
352d9c407b1SChuck Lever  *
353d9c407b1SChuck Lever  *	enum ftype3 {
354d9c407b1SChuck Lever  *		NF3REG	= 1,
355d9c407b1SChuck Lever  *		NF3DIR	= 2,
356d9c407b1SChuck Lever  *		NF3BLK	= 3,
357d9c407b1SChuck Lever  *		NF3CHR	= 4,
358d9c407b1SChuck Lever  *		NF3LNK	= 5,
359d9c407b1SChuck Lever  *		NF3SOCK	= 6,
360d9c407b1SChuck Lever  *		NF3FIFO	= 7
361d9c407b1SChuck Lever  *	};
362d9c407b1SChuck Lever  */
363d9c407b1SChuck Lever static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
364d9c407b1SChuck Lever {
365d9c407b1SChuck Lever 	encode_uint32(xdr, type);
366d9c407b1SChuck Lever }
367d9c407b1SChuck Lever 
368f6048709SChuck Lever static __be32 *xdr_decode_ftype3(__be32 *p, umode_t *mode)
369f6048709SChuck Lever {
370f6048709SChuck Lever 	u32 type;
371f6048709SChuck Lever 
372f6048709SChuck Lever 	type = be32_to_cpup(p++);
373f6048709SChuck Lever 	if (type > NF3FIFO)
374f6048709SChuck Lever 		type = NF3NON;
375f6048709SChuck Lever 	*mode = nfs_type2fmt[type];
376f6048709SChuck Lever 	return p;
377f6048709SChuck Lever }
378f6048709SChuck Lever 
379d9c407b1SChuck Lever /*
380d9c407b1SChuck Lever  * specdata3
381d9c407b1SChuck Lever  *
382d9c407b1SChuck Lever  *     struct specdata3 {
383d9c407b1SChuck Lever  *             uint32  specdata1;
384d9c407b1SChuck Lever  *             uint32  specdata2;
385d9c407b1SChuck Lever  *     };
386d9c407b1SChuck Lever  */
387d9c407b1SChuck Lever static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
388d9c407b1SChuck Lever {
389d9c407b1SChuck Lever 	__be32 *p;
390d9c407b1SChuck Lever 
391d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8);
392d9c407b1SChuck Lever 	*p++ = cpu_to_be32(MAJOR(rdev));
393d9c407b1SChuck Lever 	*p = cpu_to_be32(MINOR(rdev));
394d9c407b1SChuck Lever }
395d9c407b1SChuck Lever 
396f6048709SChuck Lever static __be32 *xdr_decode_specdata3(__be32 *p, dev_t *rdev)
397f6048709SChuck Lever {
398f6048709SChuck Lever 	unsigned int major, minor;
399f6048709SChuck Lever 
400f6048709SChuck Lever 	major = be32_to_cpup(p++);
401f6048709SChuck Lever 	minor = be32_to_cpup(p++);
402f6048709SChuck Lever 	*rdev = MKDEV(major, minor);
403f6048709SChuck Lever 	if (MAJOR(*rdev) != major || MINOR(*rdev) != minor)
404f6048709SChuck Lever 		*rdev = 0;
405f6048709SChuck Lever 	return p;
406f6048709SChuck Lever }
407f6048709SChuck Lever 
408d9c407b1SChuck Lever /*
409d9c407b1SChuck Lever  * nfs_fh3
410d9c407b1SChuck Lever  *
411d9c407b1SChuck Lever  *	struct nfs_fh3 {
412d9c407b1SChuck Lever  *		opaque       data<NFS3_FHSIZE>;
413d9c407b1SChuck Lever  *	};
414d9c407b1SChuck Lever  */
415d9c407b1SChuck Lever static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
416d9c407b1SChuck Lever {
417d9c407b1SChuck Lever 	__be32 *p;
418d9c407b1SChuck Lever 
4197fc38846STrond Myklebust 	WARN_ON_ONCE(fh->size > NFS3_FHSIZE);
420d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 4 + fh->size);
421d9c407b1SChuck Lever 	xdr_encode_opaque(p, fh->data, fh->size);
422d9c407b1SChuck Lever }
423d9c407b1SChuck Lever 
424e4f93234SChuck Lever static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
425e4f93234SChuck Lever {
426e4f93234SChuck Lever 	u32 length;
427e4f93234SChuck Lever 	__be32 *p;
428e4f93234SChuck Lever 
429e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
430eb72f484SChuck Lever 	if (unlikely(!p))
431eb72f484SChuck Lever 		return -EIO;
432e4f93234SChuck Lever 	length = be32_to_cpup(p++);
433e4f93234SChuck Lever 	if (unlikely(length > NFS3_FHSIZE))
434e4f93234SChuck Lever 		goto out_toobig;
435e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, length);
436eb72f484SChuck Lever 	if (unlikely(!p))
437eb72f484SChuck Lever 		return -EIO;
438e4f93234SChuck Lever 	fh->size = length;
439e4f93234SChuck Lever 	memcpy(fh->data, p, length);
440e4f93234SChuck Lever 	return 0;
441e4f93234SChuck Lever out_toobig:
442e4f93234SChuck Lever 	dprintk("NFS: file handle size (%u) too big\n", length);
443e4f93234SChuck Lever 	return -E2BIG;
444e4f93234SChuck Lever }
445e4f93234SChuck Lever 
446e4f93234SChuck Lever static void zero_nfs_fh3(struct nfs_fh *fh)
447e4f93234SChuck Lever {
448e4f93234SChuck Lever 	memset(fh, 0, sizeof(*fh));
449e4f93234SChuck Lever }
450e4f93234SChuck Lever 
451d9c407b1SChuck Lever /*
4529d5a6434SChuck Lever  * nfstime3
4539d5a6434SChuck Lever  *
4549d5a6434SChuck Lever  *	struct nfstime3 {
4559d5a6434SChuck Lever  *		uint32	seconds;
4569d5a6434SChuck Lever  *		uint32	nseconds;
4579d5a6434SChuck Lever  *	};
4589d5a6434SChuck Lever  */
4596430b323STrond Myklebust static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec64 *timep)
4609d5a6434SChuck Lever {
4616430b323STrond Myklebust 	*p++ = cpu_to_be32((u32)timep->tv_sec);
4629d5a6434SChuck Lever 	*p++ = cpu_to_be32(timep->tv_nsec);
4639d5a6434SChuck Lever 	return p;
4649d5a6434SChuck Lever }
4659d5a6434SChuck Lever 
466e86d5a02STrond Myklebust static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec64 *timep)
467f6048709SChuck Lever {
468f6048709SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
469f6048709SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++);
470f6048709SChuck Lever 	return p;
471f6048709SChuck Lever }
472f6048709SChuck Lever 
4739d5a6434SChuck Lever /*
474d9c407b1SChuck Lever  * sattr3
475d9c407b1SChuck Lever  *
476d9c407b1SChuck Lever  *	enum time_how {
477d9c407b1SChuck Lever  *		DONT_CHANGE		= 0,
478d9c407b1SChuck Lever  *		SET_TO_SERVER_TIME	= 1,
479d9c407b1SChuck Lever  *		SET_TO_CLIENT_TIME	= 2
480d9c407b1SChuck Lever  *	};
481d9c407b1SChuck Lever  *
482d9c407b1SChuck Lever  *	union set_mode3 switch (bool set_it) {
483d9c407b1SChuck Lever  *	case TRUE:
484d9c407b1SChuck Lever  *		mode3	mode;
485d9c407b1SChuck Lever  *	default:
486d9c407b1SChuck Lever  *		void;
487d9c407b1SChuck Lever  *	};
488d9c407b1SChuck Lever  *
489d9c407b1SChuck Lever  *	union set_uid3 switch (bool set_it) {
490d9c407b1SChuck Lever  *	case TRUE:
491d9c407b1SChuck Lever  *		uid3	uid;
492d9c407b1SChuck Lever  *	default:
493d9c407b1SChuck Lever  *		void;
494d9c407b1SChuck Lever  *	};
495d9c407b1SChuck Lever  *
496d9c407b1SChuck Lever  *	union set_gid3 switch (bool set_it) {
497d9c407b1SChuck Lever  *	case TRUE:
498d9c407b1SChuck Lever  *		gid3	gid;
499d9c407b1SChuck Lever  *	default:
500d9c407b1SChuck Lever  *		void;
501d9c407b1SChuck Lever  *	};
502d9c407b1SChuck Lever  *
503d9c407b1SChuck Lever  *	union set_size3 switch (bool set_it) {
504d9c407b1SChuck Lever  *	case TRUE:
505d9c407b1SChuck Lever  *		size3	size;
506d9c407b1SChuck Lever  *	default:
507d9c407b1SChuck Lever  *		void;
508d9c407b1SChuck Lever  *	};
509d9c407b1SChuck Lever  *
510d9c407b1SChuck Lever  *	union set_atime switch (time_how set_it) {
511d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
512d9c407b1SChuck Lever  *		nfstime3	atime;
513d9c407b1SChuck Lever  *	default:
514d9c407b1SChuck Lever  *		void;
515d9c407b1SChuck Lever  *	};
516d9c407b1SChuck Lever  *
517d9c407b1SChuck Lever  *	union set_mtime switch (time_how set_it) {
518d9c407b1SChuck Lever  *	case SET_TO_CLIENT_TIME:
519d9c407b1SChuck Lever  *		nfstime3  mtime;
520d9c407b1SChuck Lever  *	default:
521d9c407b1SChuck Lever  *		void;
522d9c407b1SChuck Lever  *	};
523d9c407b1SChuck Lever  *
524d9c407b1SChuck Lever  *	struct sattr3 {
525d9c407b1SChuck Lever  *		set_mode3	mode;
526d9c407b1SChuck Lever  *		set_uid3	uid;
527d9c407b1SChuck Lever  *		set_gid3	gid;
528d9c407b1SChuck Lever  *		set_size3	size;
529d9c407b1SChuck Lever  *		set_atime	atime;
530d9c407b1SChuck Lever  *		set_mtime	mtime;
531d9c407b1SChuck Lever  *	};
532d9c407b1SChuck Lever  */
533264d948cSTrond Myklebust static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr,
534264d948cSTrond Myklebust 		struct user_namespace *userns)
535d9c407b1SChuck Lever {
536d9c407b1SChuck Lever 	u32 nbytes;
537d9c407b1SChuck Lever 	__be32 *p;
538d9c407b1SChuck Lever 
539d9c407b1SChuck Lever 	/*
540d9c407b1SChuck Lever 	 * In order to make only a single xdr_reserve_space() call,
541d9c407b1SChuck Lever 	 * pre-compute the total number of bytes to be reserved.
542d9c407b1SChuck Lever 	 * Six boolean values, one for each set_foo field, are always
543d9c407b1SChuck Lever 	 * present in the encoded result, so start there.
544d9c407b1SChuck Lever 	 */
545d9c407b1SChuck Lever 	nbytes = 6 * 4;
546d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MODE)
547d9c407b1SChuck Lever 		nbytes += 4;
548d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_UID)
549d9c407b1SChuck Lever 		nbytes += 4;
550d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_GID)
551d9c407b1SChuck Lever 		nbytes += 4;
552d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
553d9c407b1SChuck Lever 		nbytes += 8;
554d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
555d9c407b1SChuck Lever 		nbytes += 8;
556d9c407b1SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
557d9c407b1SChuck Lever 		nbytes += 8;
558d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, nbytes);
559d9c407b1SChuck Lever 
5609d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MODE) {
5619d5a6434SChuck Lever 		*p++ = xdr_one;
5629d5a6434SChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
5639d5a6434SChuck Lever 	} else
5649d5a6434SChuck Lever 		*p++ = xdr_zero;
5659d5a6434SChuck Lever 
5669d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_UID) {
5679d5a6434SChuck Lever 		*p++ = xdr_one;
568264d948cSTrond Myklebust 		*p++ = cpu_to_be32(from_kuid_munged(userns, attr->ia_uid));
5699d5a6434SChuck Lever 	} else
5709d5a6434SChuck Lever 		*p++ = xdr_zero;
5719d5a6434SChuck Lever 
5729d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_GID) {
5739d5a6434SChuck Lever 		*p++ = xdr_one;
574264d948cSTrond Myklebust 		*p++ = cpu_to_be32(from_kgid_munged(userns, attr->ia_gid));
5759d5a6434SChuck Lever 	} else
5769d5a6434SChuck Lever 		*p++ = xdr_zero;
5779d5a6434SChuck Lever 
5789d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_SIZE) {
5799d5a6434SChuck Lever 		*p++ = xdr_one;
5809d5a6434SChuck Lever 		p = xdr_encode_hyper(p, (u64)attr->ia_size);
5819d5a6434SChuck Lever 	} else
5829d5a6434SChuck Lever 		*p++ = xdr_zero;
5839d5a6434SChuck Lever 
5849d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET) {
5859d5a6434SChuck Lever 		*p++ = xdr_two;
5866430b323STrond Myklebust 		p = xdr_encode_nfstime3(p, &attr->ia_atime);
5879d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_ATIME) {
5889d5a6434SChuck Lever 		*p++ = xdr_one;
5899d5a6434SChuck Lever 	} else
5909d5a6434SChuck Lever 		*p++ = xdr_zero;
5919d5a6434SChuck Lever 
5929d5a6434SChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET) {
5939d5a6434SChuck Lever 		*p++ = xdr_two;
5946430b323STrond Myklebust 		xdr_encode_nfstime3(p, &attr->ia_mtime);
5959d5a6434SChuck Lever 	} else if (attr->ia_valid & ATTR_MTIME) {
5969d5a6434SChuck Lever 		*p = xdr_one;
5979d5a6434SChuck Lever 	} else
5989d5a6434SChuck Lever 		*p = xdr_zero;
599d9c407b1SChuck Lever }
600d9c407b1SChuck Lever 
601d9c407b1SChuck Lever /*
602e4f93234SChuck Lever  * fattr3
603e4f93234SChuck Lever  *
604e4f93234SChuck Lever  *	struct fattr3 {
605e4f93234SChuck Lever  *		ftype3		type;
606e4f93234SChuck Lever  *		mode3		mode;
607e4f93234SChuck Lever  *		uint32		nlink;
608e4f93234SChuck Lever  *		uid3		uid;
609e4f93234SChuck Lever  *		gid3		gid;
610e4f93234SChuck Lever  *		size3		size;
611e4f93234SChuck Lever  *		size3		used;
612e4f93234SChuck Lever  *		specdata3	rdev;
613e4f93234SChuck Lever  *		uint64		fsid;
614e4f93234SChuck Lever  *		fileid3		fileid;
615e4f93234SChuck Lever  *		nfstime3	atime;
616e4f93234SChuck Lever  *		nfstime3	mtime;
617e4f93234SChuck Lever  *		nfstime3	ctime;
618e4f93234SChuck Lever  *	};
619e4f93234SChuck Lever  */
620264d948cSTrond Myklebust static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr,
621264d948cSTrond Myklebust 		struct user_namespace *userns)
622e4f93234SChuck Lever {
623f6048709SChuck Lever 	umode_t fmode;
624e4f93234SChuck Lever 	__be32 *p;
625e4f93234SChuck Lever 
626e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2);
627eb72f484SChuck Lever 	if (unlikely(!p))
628eb72f484SChuck Lever 		return -EIO;
629f6048709SChuck Lever 
630f6048709SChuck Lever 	p = xdr_decode_ftype3(p, &fmode);
631f6048709SChuck Lever 
632f6048709SChuck Lever 	fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;
633f6048709SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
634264d948cSTrond Myklebust 	fattr->uid = make_kuid(userns, be32_to_cpup(p++));
63557a38daeSEric W. Biederman 	if (!uid_valid(fattr->uid))
63657a38daeSEric W. Biederman 		goto out_uid;
637264d948cSTrond Myklebust 	fattr->gid = make_kgid(userns, be32_to_cpup(p++));
63857a38daeSEric W. Biederman 	if (!gid_valid(fattr->gid))
63957a38daeSEric W. Biederman 		goto out_gid;
640f6048709SChuck Lever 
641f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->size);
642f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->du.nfs3.used);
643f6048709SChuck Lever 	p = xdr_decode_specdata3(p, &fattr->rdev);
644f6048709SChuck Lever 
645f6048709SChuck Lever 	p = xdr_decode_hyper(p, &fattr->fsid.major);
646f6048709SChuck Lever 	fattr->fsid.minor = 0;
647f6048709SChuck Lever 
648f6048709SChuck Lever 	p = xdr_decode_fileid3(p, &fattr->fileid);
649f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->atime);
650f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->mtime);
651f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->ctime);
6523a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
653f6048709SChuck Lever 
654f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V3;
655e4f93234SChuck Lever 	return 0;
65657a38daeSEric W. Biederman out_uid:
65757a38daeSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
65857a38daeSEric W. Biederman 	return -EINVAL;
65957a38daeSEric W. Biederman out_gid:
66057a38daeSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
66157a38daeSEric W. Biederman 	return -EINVAL;
662e4f93234SChuck Lever }
663e4f93234SChuck Lever 
664e4f93234SChuck Lever /*
665e4f93234SChuck Lever  * post_op_attr
666e4f93234SChuck Lever  *
667e4f93234SChuck Lever  *	union post_op_attr switch (bool attributes_follow) {
668e4f93234SChuck Lever  *	case TRUE:
669e4f93234SChuck Lever  *		fattr3	attributes;
670e4f93234SChuck Lever  *	case FALSE:
671e4f93234SChuck Lever  *		void;
672e4f93234SChuck Lever  *	};
673e4f93234SChuck Lever  */
674264d948cSTrond Myklebust static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
675264d948cSTrond Myklebust 		struct user_namespace *userns)
676e4f93234SChuck Lever {
677e4f93234SChuck Lever 	__be32 *p;
678e4f93234SChuck Lever 
679e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
680eb72f484SChuck Lever 	if (unlikely(!p))
681eb72f484SChuck Lever 		return -EIO;
682e4f93234SChuck Lever 	if (*p != xdr_zero)
683264d948cSTrond Myklebust 		return decode_fattr3(xdr, fattr, userns);
684e4f93234SChuck Lever 	return 0;
685e4f93234SChuck Lever }
686e4f93234SChuck Lever 
687e4f93234SChuck Lever /*
688e4f93234SChuck Lever  * wcc_attr
689e4f93234SChuck Lever  *	struct wcc_attr {
690e4f93234SChuck Lever  *		size3		size;
691e4f93234SChuck Lever  *		nfstime3	mtime;
692e4f93234SChuck Lever  *		nfstime3	ctime;
693e4f93234SChuck Lever  *	};
694e4f93234SChuck Lever  */
695e4f93234SChuck Lever static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
696e4f93234SChuck Lever {
697e4f93234SChuck Lever 	__be32 *p;
698e4f93234SChuck Lever 
699e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2);
700eb72f484SChuck Lever 	if (unlikely(!p))
701eb72f484SChuck Lever 		return -EIO;
702f6048709SChuck Lever 
703f6048709SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_PRESIZE
7043a1556e8STrond Myklebust 		| NFS_ATTR_FATTR_PRECHANGE
705f6048709SChuck Lever 		| NFS_ATTR_FATTR_PREMTIME
706f6048709SChuck Lever 		| NFS_ATTR_FATTR_PRECTIME;
707f6048709SChuck Lever 
708f6048709SChuck Lever 	p = xdr_decode_size3(p, &fattr->pre_size);
709f6048709SChuck Lever 	p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
710f6048709SChuck Lever 	xdr_decode_nfstime3(p, &fattr->pre_ctime);
7113a1556e8STrond Myklebust 	fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime);
712f6048709SChuck Lever 
713e4f93234SChuck Lever 	return 0;
714e4f93234SChuck Lever }
715e4f93234SChuck Lever 
716e4f93234SChuck Lever /*
717e4f93234SChuck Lever  * pre_op_attr
718e4f93234SChuck Lever  *	union pre_op_attr switch (bool attributes_follow) {
719e4f93234SChuck Lever  *	case TRUE:
720e4f93234SChuck Lever  *		wcc_attr	attributes;
721e4f93234SChuck Lever  *	case FALSE:
722e4f93234SChuck Lever  *		void;
723e4f93234SChuck Lever  *	};
724e4f93234SChuck Lever  *
725e4f93234SChuck Lever  * wcc_data
726e4f93234SChuck Lever  *
727e4f93234SChuck Lever  *	struct wcc_data {
728e4f93234SChuck Lever  *		pre_op_attr	before;
729e4f93234SChuck Lever  *		post_op_attr	after;
730e4f93234SChuck Lever  *	};
731e4f93234SChuck Lever  */
732e4f93234SChuck Lever static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
733e4f93234SChuck Lever {
734e4f93234SChuck Lever 	__be32 *p;
735e4f93234SChuck Lever 
736e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
737eb72f484SChuck Lever 	if (unlikely(!p))
738eb72f484SChuck Lever 		return -EIO;
739e4f93234SChuck Lever 	if (*p != xdr_zero)
740e4f93234SChuck Lever 		return decode_wcc_attr(xdr, fattr);
741e4f93234SChuck Lever 	return 0;
742e4f93234SChuck Lever }
743e4f93234SChuck Lever 
744264d948cSTrond Myklebust static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr,
745264d948cSTrond Myklebust 		struct user_namespace *userns)
746e4f93234SChuck Lever {
747e4f93234SChuck Lever 	int error;
748e4f93234SChuck Lever 
749e4f93234SChuck Lever 	error = decode_pre_op_attr(xdr, fattr);
750e4f93234SChuck Lever 	if (unlikely(error))
751e4f93234SChuck Lever 		goto out;
752264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, fattr, userns);
753e4f93234SChuck Lever out:
754e4f93234SChuck Lever 	return error;
755e4f93234SChuck Lever }
756e4f93234SChuck Lever 
757e4f93234SChuck Lever /*
758e4f93234SChuck Lever  * post_op_fh3
759e4f93234SChuck Lever  *
760e4f93234SChuck Lever  *	union post_op_fh3 switch (bool handle_follows) {
761e4f93234SChuck Lever  *	case TRUE:
762e4f93234SChuck Lever  *		nfs_fh3  handle;
763e4f93234SChuck Lever  *	case FALSE:
764e4f93234SChuck Lever  *		void;
765e4f93234SChuck Lever  *	};
766e4f93234SChuck Lever  */
767e4f93234SChuck Lever static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
768e4f93234SChuck Lever {
769e4f93234SChuck Lever 	__be32 *p = xdr_inline_decode(xdr, 4);
770eb72f484SChuck Lever 	if (unlikely(!p))
771eb72f484SChuck Lever 		return -EIO;
772e4f93234SChuck Lever 	if (*p != xdr_zero)
773e4f93234SChuck Lever 		return decode_nfs_fh3(xdr, fh);
774e4f93234SChuck Lever 	zero_nfs_fh3(fh);
775e4f93234SChuck Lever 	return 0;
776e4f93234SChuck Lever }
777e4f93234SChuck Lever 
778e4f93234SChuck Lever /*
779d9c407b1SChuck Lever  * diropargs3
780d9c407b1SChuck Lever  *
781d9c407b1SChuck Lever  *	struct diropargs3 {
782d9c407b1SChuck Lever  *		nfs_fh3		dir;
783d9c407b1SChuck Lever  *		filename3	name;
784d9c407b1SChuck Lever  *	};
785d9c407b1SChuck Lever  */
786d9c407b1SChuck Lever static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
787d9c407b1SChuck Lever 			      const char *name, u32 length)
788d9c407b1SChuck Lever {
789d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, fh);
790d9c407b1SChuck Lever 	encode_filename3(xdr, name, length);
791d9c407b1SChuck Lever }
792d9c407b1SChuck Lever 
793d9c407b1SChuck Lever 
7941da177e4SLinus Torvalds /*
795499ff710SChuck Lever  * NFSv3 XDR encode functions
796499ff710SChuck Lever  *
797499ff710SChuck Lever  * NFSv3 argument types are defined in section 3.3 of RFC 1813:
798499ff710SChuck Lever  * "NFS Version 3 Protocol Specification".
7991da177e4SLinus Torvalds  */
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds /*
802d9c407b1SChuck Lever  * 3.3.1  GETATTR3args
803d9c407b1SChuck Lever  *
804d9c407b1SChuck Lever  *	struct GETATTR3args {
805d9c407b1SChuck Lever  *		nfs_fh3  object;
806d9c407b1SChuck Lever  *	};
807d9c407b1SChuck Lever  */
8089f06c719SChuck Lever static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
8099f06c719SChuck Lever 				      struct xdr_stream *xdr,
810fcc85819SChristoph Hellwig 				      const void *data)
811d9c407b1SChuck Lever {
812fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
813fcc85819SChristoph Hellwig 
8149f06c719SChuck Lever 	encode_nfs_fh3(xdr, fh);
815d9c407b1SChuck Lever }
816d9c407b1SChuck Lever 
817d9c407b1SChuck Lever /*
818d9c407b1SChuck Lever  * 3.3.2  SETATTR3args
819d9c407b1SChuck Lever  *
820d9c407b1SChuck Lever  *	union sattrguard3 switch (bool check) {
821d9c407b1SChuck Lever  *	case TRUE:
822d9c407b1SChuck Lever  *		nfstime3  obj_ctime;
823d9c407b1SChuck Lever  *	case FALSE:
824d9c407b1SChuck Lever  *		void;
825d9c407b1SChuck Lever  *	};
826d9c407b1SChuck Lever  *
827d9c407b1SChuck Lever  *	struct SETATTR3args {
828d9c407b1SChuck Lever  *		nfs_fh3		object;
829d9c407b1SChuck Lever  *		sattr3		new_attributes;
830d9c407b1SChuck Lever  *		sattrguard3	guard;
831d9c407b1SChuck Lever  *	};
832d9c407b1SChuck Lever  */
833d9c407b1SChuck Lever static void encode_sattrguard3(struct xdr_stream *xdr,
834d9c407b1SChuck Lever 			       const struct nfs3_sattrargs *args)
835d9c407b1SChuck Lever {
836d9c407b1SChuck Lever 	__be32 *p;
837d9c407b1SChuck Lever 
838d9c407b1SChuck Lever 	if (args->guard) {
839d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4 + 8);
840d9c407b1SChuck Lever 		*p++ = xdr_one;
8419d5a6434SChuck Lever 		xdr_encode_nfstime3(p, &args->guardtime);
842d9c407b1SChuck Lever 	} else {
843d9c407b1SChuck Lever 		p = xdr_reserve_space(xdr, 4);
844d9c407b1SChuck Lever 		*p = xdr_zero;
845d9c407b1SChuck Lever 	}
846d9c407b1SChuck Lever }
847d9c407b1SChuck Lever 
8489f06c719SChuck Lever static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
8499f06c719SChuck Lever 				      struct xdr_stream *xdr,
850fcc85819SChristoph Hellwig 				      const void *data)
851d9c407b1SChuck Lever {
852fcc85819SChristoph Hellwig 	const struct nfs3_sattrargs *args = data;
8539f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
854264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, rpc_rqst_userns(req));
8559f06c719SChuck Lever 	encode_sattrguard3(xdr, args);
856d9c407b1SChuck Lever }
857d9c407b1SChuck Lever 
858d9c407b1SChuck Lever /*
859d9c407b1SChuck Lever  * 3.3.3  LOOKUP3args
860d9c407b1SChuck Lever  *
861d9c407b1SChuck Lever  *	struct LOOKUP3args {
862d9c407b1SChuck Lever  *		diropargs3  what;
863d9c407b1SChuck Lever  *	};
864d9c407b1SChuck Lever  */
8659f06c719SChuck Lever static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
8669f06c719SChuck Lever 				     struct xdr_stream *xdr,
867fcc85819SChristoph Hellwig 				     const void *data)
868d9c407b1SChuck Lever {
869fcc85819SChristoph Hellwig 	const struct nfs3_diropargs *args = data;
870fcc85819SChristoph Hellwig 
8719f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
872d9c407b1SChuck Lever }
873d9c407b1SChuck Lever 
874d9c407b1SChuck Lever /*
875d9c407b1SChuck Lever  * 3.3.4  ACCESS3args
876d9c407b1SChuck Lever  *
877d9c407b1SChuck Lever  *	struct ACCESS3args {
878d9c407b1SChuck Lever  *		nfs_fh3		object;
879d9c407b1SChuck Lever  *		uint32		access;
880d9c407b1SChuck Lever  *	};
881d9c407b1SChuck Lever  */
882d9c407b1SChuck Lever static void encode_access3args(struct xdr_stream *xdr,
883d9c407b1SChuck Lever 			       const struct nfs3_accessargs *args)
884d9c407b1SChuck Lever {
885d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
886d9c407b1SChuck Lever 	encode_uint32(xdr, args->access);
887d9c407b1SChuck Lever }
888d9c407b1SChuck Lever 
8899f06c719SChuck Lever static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
8909f06c719SChuck Lever 				     struct xdr_stream *xdr,
891fcc85819SChristoph Hellwig 				     const void *data)
892d9c407b1SChuck Lever {
893fcc85819SChristoph Hellwig 	const struct nfs3_accessargs *args = data;
894fcc85819SChristoph Hellwig 
8959f06c719SChuck Lever 	encode_access3args(xdr, args);
896d9c407b1SChuck Lever }
897d9c407b1SChuck Lever 
898d9c407b1SChuck Lever /*
899d9c407b1SChuck Lever  * 3.3.5  READLINK3args
900d9c407b1SChuck Lever  *
901d9c407b1SChuck Lever  *	struct READLINK3args {
902d9c407b1SChuck Lever  *		nfs_fh3	symlink;
903d9c407b1SChuck Lever  *	};
904d9c407b1SChuck Lever  */
9059f06c719SChuck Lever static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
9069f06c719SChuck Lever 				       struct xdr_stream *xdr,
907fcc85819SChristoph Hellwig 				       const void *data)
908d9c407b1SChuck Lever {
909fcc85819SChristoph Hellwig 	const struct nfs3_readlinkargs *args = data;
910fcc85819SChristoph Hellwig 
9119f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
912cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
913d9c407b1SChuck Lever 				args->pglen, NFS3_readlinkres_sz);
914d9c407b1SChuck Lever }
915d9c407b1SChuck Lever 
916d9c407b1SChuck Lever /*
917d9c407b1SChuck Lever  * 3.3.6  READ3args
918d9c407b1SChuck Lever  *
919d9c407b1SChuck Lever  *	struct READ3args {
920d9c407b1SChuck Lever  *		nfs_fh3		file;
921d9c407b1SChuck Lever  *		offset3		offset;
922d9c407b1SChuck Lever  *		count3		count;
923d9c407b1SChuck Lever  *	};
924d9c407b1SChuck Lever  */
925d9c407b1SChuck Lever static void encode_read3args(struct xdr_stream *xdr,
9263c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
927d9c407b1SChuck Lever {
928d9c407b1SChuck Lever 	__be32 *p;
929d9c407b1SChuck Lever 
930d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
931d9c407b1SChuck Lever 
932d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
933d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
934d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
935d9c407b1SChuck Lever }
936d9c407b1SChuck Lever 
9379f06c719SChuck Lever static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
9389f06c719SChuck Lever 				   struct xdr_stream *xdr,
939fcc85819SChristoph Hellwig 				   const void *data)
940d9c407b1SChuck Lever {
941fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
9428d8928d8STrond Myklebust 	unsigned int replen = args->replen ? args->replen : NFS3_readres_sz;
943fcc85819SChristoph Hellwig 
9449f06c719SChuck Lever 	encode_read3args(xdr, args);
945cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
9468d8928d8STrond Myklebust 				args->count, replen);
947d9c407b1SChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
948d9c407b1SChuck Lever }
949d9c407b1SChuck Lever 
950d9c407b1SChuck Lever /*
951d9c407b1SChuck Lever  * 3.3.7  WRITE3args
952d9c407b1SChuck Lever  *
953d9c407b1SChuck Lever  *	enum stable_how {
954d9c407b1SChuck Lever  *		UNSTABLE  = 0,
955d9c407b1SChuck Lever  *		DATA_SYNC = 1,
956d9c407b1SChuck Lever  *		FILE_SYNC = 2
957d9c407b1SChuck Lever  *	};
958d9c407b1SChuck Lever  *
959d9c407b1SChuck Lever  *	struct WRITE3args {
960d9c407b1SChuck Lever  *		nfs_fh3		file;
961d9c407b1SChuck Lever  *		offset3		offset;
962d9c407b1SChuck Lever  *		count3		count;
963d9c407b1SChuck Lever  *		stable_how	stable;
964d9c407b1SChuck Lever  *		opaque		data<>;
965d9c407b1SChuck Lever  *	};
966d9c407b1SChuck Lever  */
967d9c407b1SChuck Lever static void encode_write3args(struct xdr_stream *xdr,
9683c6b899cSAnna Schumaker 			      const struct nfs_pgio_args *args)
969d9c407b1SChuck Lever {
970d9c407b1SChuck Lever 	__be32 *p;
971d9c407b1SChuck Lever 
972d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
973d9c407b1SChuck Lever 
974d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
975d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
976d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count);
977d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->stable);
978d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
979d9c407b1SChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
980d9c407b1SChuck Lever }
981d9c407b1SChuck Lever 
9829f06c719SChuck Lever static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
9839f06c719SChuck Lever 				    struct xdr_stream *xdr,
984fcc85819SChristoph Hellwig 				    const void *data)
985d9c407b1SChuck Lever {
986fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
987fcc85819SChristoph Hellwig 
9889f06c719SChuck Lever 	encode_write3args(xdr, args);
9899f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
990d9c407b1SChuck Lever }
991d9c407b1SChuck Lever 
992d9c407b1SChuck Lever /*
993d9c407b1SChuck Lever  * 3.3.8  CREATE3args
994d9c407b1SChuck Lever  *
995d9c407b1SChuck Lever  *	enum createmode3 {
996d9c407b1SChuck Lever  *		UNCHECKED = 0,
997d9c407b1SChuck Lever  *		GUARDED   = 1,
998d9c407b1SChuck Lever  *		EXCLUSIVE = 2
999d9c407b1SChuck Lever  *	};
1000d9c407b1SChuck Lever  *
1001d9c407b1SChuck Lever  *	union createhow3 switch (createmode3 mode) {
1002d9c407b1SChuck Lever  *	case UNCHECKED:
1003d9c407b1SChuck Lever  *	case GUARDED:
1004d9c407b1SChuck Lever  *		sattr3       obj_attributes;
1005d9c407b1SChuck Lever  *	case EXCLUSIVE:
1006d9c407b1SChuck Lever  *		createverf3  verf;
1007d9c407b1SChuck Lever  *	};
1008d9c407b1SChuck Lever  *
1009d9c407b1SChuck Lever  *	struct CREATE3args {
1010d9c407b1SChuck Lever  *		diropargs3	where;
1011d9c407b1SChuck Lever  *		createhow3	how;
1012d9c407b1SChuck Lever  *	};
1013d9c407b1SChuck Lever  */
1014d9c407b1SChuck Lever static void encode_createhow3(struct xdr_stream *xdr,
1015264d948cSTrond Myklebust 			      const struct nfs3_createargs *args,
1016264d948cSTrond Myklebust 			      struct user_namespace *userns)
1017d9c407b1SChuck Lever {
1018d9c407b1SChuck Lever 	encode_uint32(xdr, args->createmode);
1019d9c407b1SChuck Lever 	switch (args->createmode) {
1020d9c407b1SChuck Lever 	case NFS3_CREATE_UNCHECKED:
1021d9c407b1SChuck Lever 	case NFS3_CREATE_GUARDED:
1022264d948cSTrond Myklebust 		encode_sattr3(xdr, args->sattr, userns);
1023d9c407b1SChuck Lever 		break;
1024d9c407b1SChuck Lever 	case NFS3_CREATE_EXCLUSIVE:
1025d9c407b1SChuck Lever 		encode_createverf3(xdr, args->verifier);
1026d9c407b1SChuck Lever 		break;
1027d9c407b1SChuck Lever 	default:
1028d9c407b1SChuck Lever 		BUG();
1029d9c407b1SChuck Lever 	}
1030d9c407b1SChuck Lever }
1031d9c407b1SChuck Lever 
10329f06c719SChuck Lever static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
10339f06c719SChuck Lever 				     struct xdr_stream *xdr,
1034fcc85819SChristoph Hellwig 				     const void *data)
1035d9c407b1SChuck Lever {
1036fcc85819SChristoph Hellwig 	const struct nfs3_createargs *args = data;
1037fcc85819SChristoph Hellwig 
10389f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
1039264d948cSTrond Myklebust 	encode_createhow3(xdr, args, rpc_rqst_userns(req));
1040d9c407b1SChuck Lever }
1041d9c407b1SChuck Lever 
1042d9c407b1SChuck Lever /*
1043d9c407b1SChuck Lever  * 3.3.9  MKDIR3args
1044d9c407b1SChuck Lever  *
1045d9c407b1SChuck Lever  *	struct MKDIR3args {
1046d9c407b1SChuck Lever  *		diropargs3	where;
1047d9c407b1SChuck Lever  *		sattr3		attributes;
1048d9c407b1SChuck Lever  *	};
1049d9c407b1SChuck Lever  */
10509f06c719SChuck Lever static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
10519f06c719SChuck Lever 				    struct xdr_stream *xdr,
1052fcc85819SChristoph Hellwig 				    const void *data)
1053d9c407b1SChuck Lever {
1054fcc85819SChristoph Hellwig 	const struct nfs3_mkdirargs *args = data;
1055fcc85819SChristoph Hellwig 
10569f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
1057264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, rpc_rqst_userns(req));
1058d9c407b1SChuck Lever }
1059d9c407b1SChuck Lever 
1060d9c407b1SChuck Lever /*
1061d9c407b1SChuck Lever  * 3.3.10  SYMLINK3args
1062d9c407b1SChuck Lever  *
1063d9c407b1SChuck Lever  *	struct symlinkdata3 {
1064d9c407b1SChuck Lever  *		sattr3		symlink_attributes;
1065d9c407b1SChuck Lever  *		nfspath3	symlink_data;
1066d9c407b1SChuck Lever  *	};
1067d9c407b1SChuck Lever  *
1068d9c407b1SChuck Lever  *	struct SYMLINK3args {
1069d9c407b1SChuck Lever  *		diropargs3	where;
1070d9c407b1SChuck Lever  *		symlinkdata3	symlink;
1071d9c407b1SChuck Lever  *	};
1072d9c407b1SChuck Lever  */
1073d9c407b1SChuck Lever static void encode_symlinkdata3(struct xdr_stream *xdr,
1074264d948cSTrond Myklebust 				const void *data,
1075264d948cSTrond Myklebust 				struct user_namespace *userns)
1076d9c407b1SChuck Lever {
1077fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1078fcc85819SChristoph Hellwig 
1079264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, userns);
1080d9c407b1SChuck Lever 	encode_nfspath3(xdr, args->pages, args->pathlen);
1081d9c407b1SChuck Lever }
1082d9c407b1SChuck Lever 
10839f06c719SChuck Lever static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
10849f06c719SChuck Lever 				      struct xdr_stream *xdr,
1085fcc85819SChristoph Hellwig 				      const void *data)
1086d9c407b1SChuck Lever {
1087fcc85819SChristoph Hellwig 	const struct nfs3_symlinkargs *args = data;
1088fcc85819SChristoph Hellwig 
10899f06c719SChuck Lever 	encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
1090264d948cSTrond Myklebust 	encode_symlinkdata3(xdr, args, rpc_rqst_userns(req));
10912fcc213aSChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
1092d9c407b1SChuck Lever }
1093d9c407b1SChuck Lever 
1094d9c407b1SChuck Lever /*
1095d9c407b1SChuck Lever  * 3.3.11  MKNOD3args
1096d9c407b1SChuck Lever  *
1097d9c407b1SChuck Lever  *	struct devicedata3 {
1098d9c407b1SChuck Lever  *		sattr3		dev_attributes;
1099d9c407b1SChuck Lever  *		specdata3	spec;
1100d9c407b1SChuck Lever  *	};
1101d9c407b1SChuck Lever  *
1102d9c407b1SChuck Lever  *	union mknoddata3 switch (ftype3 type) {
1103d9c407b1SChuck Lever  *	case NF3CHR:
1104d9c407b1SChuck Lever  *	case NF3BLK:
1105d9c407b1SChuck Lever  *		devicedata3	device;
1106d9c407b1SChuck Lever  *	case NF3SOCK:
1107d9c407b1SChuck Lever  *	case NF3FIFO:
1108d9c407b1SChuck Lever  *		sattr3		pipe_attributes;
1109d9c407b1SChuck Lever  *	default:
1110d9c407b1SChuck Lever  *		void;
1111d9c407b1SChuck Lever  *	};
1112d9c407b1SChuck Lever  *
1113d9c407b1SChuck Lever  *	struct MKNOD3args {
1114d9c407b1SChuck Lever  *		diropargs3	where;
1115d9c407b1SChuck Lever  *		mknoddata3	what;
1116d9c407b1SChuck Lever  *	};
1117d9c407b1SChuck Lever  */
1118d9c407b1SChuck Lever static void encode_devicedata3(struct xdr_stream *xdr,
1119264d948cSTrond Myklebust 			       const struct nfs3_mknodargs *args,
1120264d948cSTrond Myklebust 			       struct user_namespace *userns)
1121d9c407b1SChuck Lever {
1122264d948cSTrond Myklebust 	encode_sattr3(xdr, args->sattr, userns);
1123d9c407b1SChuck Lever 	encode_specdata3(xdr, args->rdev);
1124d9c407b1SChuck Lever }
1125d9c407b1SChuck Lever 
1126d9c407b1SChuck Lever static void encode_mknoddata3(struct xdr_stream *xdr,
1127264d948cSTrond Myklebust 			      const struct nfs3_mknodargs *args,
1128264d948cSTrond Myklebust 			      struct user_namespace *userns)
1129d9c407b1SChuck Lever {
1130d9c407b1SChuck Lever 	encode_ftype3(xdr, args->type);
1131d9c407b1SChuck Lever 	switch (args->type) {
1132d9c407b1SChuck Lever 	case NF3CHR:
1133d9c407b1SChuck Lever 	case NF3BLK:
1134264d948cSTrond Myklebust 		encode_devicedata3(xdr, args, userns);
1135d9c407b1SChuck Lever 		break;
1136d9c407b1SChuck Lever 	case NF3SOCK:
1137d9c407b1SChuck Lever 	case NF3FIFO:
1138264d948cSTrond Myklebust 		encode_sattr3(xdr, args->sattr, userns);
1139d9c407b1SChuck Lever 		break;
1140d9c407b1SChuck Lever 	case NF3REG:
1141d9c407b1SChuck Lever 	case NF3DIR:
1142d9c407b1SChuck Lever 		break;
1143d9c407b1SChuck Lever 	default:
1144d9c407b1SChuck Lever 		BUG();
1145d9c407b1SChuck Lever 	}
1146d9c407b1SChuck Lever }
1147d9c407b1SChuck Lever 
11489f06c719SChuck Lever static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
11499f06c719SChuck Lever 				    struct xdr_stream *xdr,
1150fcc85819SChristoph Hellwig 				    const void *data)
1151d9c407b1SChuck Lever {
1152fcc85819SChristoph Hellwig 	const struct nfs3_mknodargs *args = data;
1153fcc85819SChristoph Hellwig 
11549f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name, args->len);
1155264d948cSTrond Myklebust 	encode_mknoddata3(xdr, args, rpc_rqst_userns(req));
1156d9c407b1SChuck Lever }
1157d9c407b1SChuck Lever 
1158d9c407b1SChuck Lever /*
1159d9c407b1SChuck Lever  * 3.3.12  REMOVE3args
1160d9c407b1SChuck Lever  *
1161d9c407b1SChuck Lever  *	struct REMOVE3args {
1162d9c407b1SChuck Lever  *		diropargs3  object;
1163d9c407b1SChuck Lever  *	};
1164d9c407b1SChuck Lever  */
11659f06c719SChuck Lever static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
11669f06c719SChuck Lever 				     struct xdr_stream *xdr,
1167fcc85819SChristoph Hellwig 				     const void *data)
1168d9c407b1SChuck Lever {
1169fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
1170fcc85819SChristoph Hellwig 
11719f06c719SChuck Lever 	encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
1172d9c407b1SChuck Lever }
1173d9c407b1SChuck Lever 
1174d9c407b1SChuck Lever /*
1175d9c407b1SChuck Lever  * 3.3.14  RENAME3args
1176d9c407b1SChuck Lever  *
1177d9c407b1SChuck Lever  *	struct RENAME3args {
1178d9c407b1SChuck Lever  *		diropargs3	from;
1179d9c407b1SChuck Lever  *		diropargs3	to;
1180d9c407b1SChuck Lever  *	};
1181d9c407b1SChuck Lever  */
11829f06c719SChuck Lever static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
11839f06c719SChuck Lever 				     struct xdr_stream *xdr,
1184fcc85819SChristoph Hellwig 				     const void *data)
1185d9c407b1SChuck Lever {
1186fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
1187d9c407b1SChuck Lever 	const struct qstr *old = args->old_name;
1188d9c407b1SChuck Lever 	const struct qstr *new = args->new_name;
1189d9c407b1SChuck Lever 
11909f06c719SChuck Lever 	encode_diropargs3(xdr, args->old_dir, old->name, old->len);
11919f06c719SChuck Lever 	encode_diropargs3(xdr, args->new_dir, new->name, new->len);
1192d9c407b1SChuck Lever }
1193d9c407b1SChuck Lever 
1194d9c407b1SChuck Lever /*
1195d9c407b1SChuck Lever  * 3.3.15  LINK3args
1196d9c407b1SChuck Lever  *
1197d9c407b1SChuck Lever  *	struct LINK3args {
1198d9c407b1SChuck Lever  *		nfs_fh3		file;
1199d9c407b1SChuck Lever  *		diropargs3	link;
1200d9c407b1SChuck Lever  *	};
1201d9c407b1SChuck Lever  */
12029f06c719SChuck Lever static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
12039f06c719SChuck Lever 				   struct xdr_stream *xdr,
1204fcc85819SChristoph Hellwig 				   const void *data)
1205d9c407b1SChuck Lever {
1206fcc85819SChristoph Hellwig 	const struct nfs3_linkargs *args = data;
1207fcc85819SChristoph Hellwig 
12089f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fromfh);
12099f06c719SChuck Lever 	encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
1210d9c407b1SChuck Lever }
1211d9c407b1SChuck Lever 
1212d9c407b1SChuck Lever /*
1213d9c407b1SChuck Lever  * 3.3.16  READDIR3args
1214d9c407b1SChuck Lever  *
1215d9c407b1SChuck Lever  *	struct READDIR3args {
1216d9c407b1SChuck Lever  *		nfs_fh3		dir;
1217d9c407b1SChuck Lever  *		cookie3		cookie;
1218d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1219d9c407b1SChuck Lever  *		count3		count;
1220d9c407b1SChuck Lever  *	};
1221d9c407b1SChuck Lever  */
1222d9c407b1SChuck Lever static void encode_readdir3args(struct xdr_stream *xdr,
1223d9c407b1SChuck Lever 				const struct nfs3_readdirargs *args)
1224d9c407b1SChuck Lever {
1225d9c407b1SChuck Lever 	__be32 *p;
1226d9c407b1SChuck Lever 
1227d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1228d9c407b1SChuck Lever 
1229d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
1230d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1231d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1232d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1233d9c407b1SChuck Lever }
1234d9c407b1SChuck Lever 
12359f06c719SChuck Lever static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
12369f06c719SChuck Lever 				      struct xdr_stream *xdr,
1237fcc85819SChristoph Hellwig 				      const void *data)
1238d9c407b1SChuck Lever {
1239fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1240fcc85819SChristoph Hellwig 
12419f06c719SChuck Lever 	encode_readdir3args(xdr, args);
1242cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, 0,
1243d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1244d9c407b1SChuck Lever }
1245d9c407b1SChuck Lever 
1246d9c407b1SChuck Lever /*
1247d9c407b1SChuck Lever  * 3.3.17  READDIRPLUS3args
1248d9c407b1SChuck Lever  *
1249d9c407b1SChuck Lever  *	struct READDIRPLUS3args {
1250d9c407b1SChuck Lever  *		nfs_fh3		dir;
1251d9c407b1SChuck Lever  *		cookie3		cookie;
1252d9c407b1SChuck Lever  *		cookieverf3	cookieverf;
1253d9c407b1SChuck Lever  *		count3		dircount;
1254d9c407b1SChuck Lever  *		count3		maxcount;
1255d9c407b1SChuck Lever  *	};
1256d9c407b1SChuck Lever  */
1257d9c407b1SChuck Lever static void encode_readdirplus3args(struct xdr_stream *xdr,
1258d9c407b1SChuck Lever 				    const struct nfs3_readdirargs *args)
1259d9c407b1SChuck Lever {
1260d9c407b1SChuck Lever 	__be32 *p;
1261d9c407b1SChuck Lever 
1262d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1263d9c407b1SChuck Lever 
1264d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
1265d9c407b1SChuck Lever 	p = xdr_encode_cookie3(p, args->cookie);
1266d9c407b1SChuck Lever 	p = xdr_encode_cookieverf3(p, args->verf);
1267d9c407b1SChuck Lever 
1268d9c407b1SChuck Lever 	/*
1269d9c407b1SChuck Lever 	 * readdirplus: need dircount + buffer size.
1270d9c407b1SChuck Lever 	 * We just make sure we make dircount big enough
1271d9c407b1SChuck Lever 	 */
1272d9c407b1SChuck Lever 	*p++ = cpu_to_be32(args->count >> 3);
1273d9c407b1SChuck Lever 
1274d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1275d9c407b1SChuck Lever }
1276d9c407b1SChuck Lever 
12779f06c719SChuck Lever static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
12789f06c719SChuck Lever 					  struct xdr_stream *xdr,
1279fcc85819SChristoph Hellwig 					  const void *data)
1280d9c407b1SChuck Lever {
1281fcc85819SChristoph Hellwig 	const struct nfs3_readdirargs *args = data;
1282fcc85819SChristoph Hellwig 
12839f06c719SChuck Lever 	encode_readdirplus3args(xdr, args);
1284cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, 0,
1285d9c407b1SChuck Lever 				args->count, NFS3_readdirres_sz);
1286d9c407b1SChuck Lever }
1287d9c407b1SChuck Lever 
1288d9c407b1SChuck Lever /*
1289d9c407b1SChuck Lever  * 3.3.21  COMMIT3args
1290d9c407b1SChuck Lever  *
1291d9c407b1SChuck Lever  *	struct COMMIT3args {
1292d9c407b1SChuck Lever  *		nfs_fh3		file;
1293d9c407b1SChuck Lever  *		offset3		offset;
1294d9c407b1SChuck Lever  *		count3		count;
1295d9c407b1SChuck Lever  *	};
1296d9c407b1SChuck Lever  */
1297d9c407b1SChuck Lever static void encode_commit3args(struct xdr_stream *xdr,
12980b7c0153SFred Isaman 			       const struct nfs_commitargs *args)
1299d9c407b1SChuck Lever {
1300d9c407b1SChuck Lever 	__be32 *p;
1301d9c407b1SChuck Lever 
1302d9c407b1SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
1303d9c407b1SChuck Lever 
1304d9c407b1SChuck Lever 	p = xdr_reserve_space(xdr, 8 + 4);
1305d9c407b1SChuck Lever 	p = xdr_encode_hyper(p, args->offset);
1306d9c407b1SChuck Lever 	*p = cpu_to_be32(args->count);
1307d9c407b1SChuck Lever }
1308d9c407b1SChuck Lever 
13099f06c719SChuck Lever static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
13109f06c719SChuck Lever 				     struct xdr_stream *xdr,
1311fcc85819SChristoph Hellwig 				     const void *data)
1312d9c407b1SChuck Lever {
1313fcc85819SChristoph Hellwig 	const struct nfs_commitargs *args = data;
1314fcc85819SChristoph Hellwig 
13159f06c719SChuck Lever 	encode_commit3args(xdr, args);
1316d9c407b1SChuck Lever }
1317d9c407b1SChuck Lever 
1318b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
1319b7fa0554SAndreas Gruenbacher 
13209f06c719SChuck Lever static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
13219f06c719SChuck Lever 				     struct xdr_stream *xdr,
1322fcc85819SChristoph Hellwig 				     const void *data)
1323d9c407b1SChuck Lever {
1324fcc85819SChristoph Hellwig 	const struct nfs3_getaclargs *args = data;
1325fcc85819SChristoph Hellwig 
13269f06c719SChuck Lever 	encode_nfs_fh3(xdr, args->fh);
13279f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1328431f6eb3STrond Myklebust 	if (args->mask & (NFS_ACL | NFS_DFACL)) {
1329cf500bacSChuck Lever 		rpc_prepare_reply_pages(req, args->pages, 0,
1330d9c407b1SChuck Lever 					NFSACL_MAXPAGES << PAGE_SHIFT,
1331d9c407b1SChuck Lever 					ACL3_getaclres_sz);
1332431f6eb3STrond Myklebust 		req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES;
1333431f6eb3STrond Myklebust 	}
1334d9c407b1SChuck Lever }
1335d9c407b1SChuck Lever 
13369f06c719SChuck Lever static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
13379f06c719SChuck Lever 				     struct xdr_stream *xdr,
1338fcc85819SChristoph Hellwig 				     const void *data)
1339d9c407b1SChuck Lever {
1340fcc85819SChristoph Hellwig 	const struct nfs3_setaclargs *args = data;
1341d9c407b1SChuck Lever 	unsigned int base;
1342d9c407b1SChuck Lever 	int error;
1343d9c407b1SChuck Lever 
13449f06c719SChuck Lever 	encode_nfs_fh3(xdr, NFS_FH(args->inode));
13459f06c719SChuck Lever 	encode_uint32(xdr, args->mask);
1346d9c407b1SChuck Lever 
1347d9c407b1SChuck Lever 	base = req->rq_slen;
1348ee5dc773SChuck Lever 	if (args->npages != 0)
1349ee5dc773SChuck Lever 		xdr_write_pages(xdr, args->pages, 0, args->len);
1350ee5dc773SChuck Lever 	else
1351d683cc49SChuck Lever 		xdr_reserve_space(xdr, args->len);
1352ee5dc773SChuck Lever 
13539f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base, args->inode,
1354d9c407b1SChuck Lever 			    (args->mask & NFS_ACL) ?
1355d9c407b1SChuck Lever 			    args->acl_access : NULL, 1, 0);
13567fc38846STrond Myklebust 	/* FIXME: this is just broken */
1357d9c407b1SChuck Lever 	BUG_ON(error < 0);
13589f06c719SChuck Lever 	error = nfsacl_encode(xdr->buf, base + error, args->inode,
1359d9c407b1SChuck Lever 			    (args->mask & NFS_DFACL) ?
1360d9c407b1SChuck Lever 			    args->acl_default : NULL, 1,
1361d9c407b1SChuck Lever 			    NFS_ACL_DEFAULT);
1362d9c407b1SChuck Lever 	BUG_ON(error < 0);
1363d9c407b1SChuck Lever }
1364d9c407b1SChuck Lever 
1365b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
1366b7fa0554SAndreas Gruenbacher 
13671da177e4SLinus Torvalds /*
1368b2cdd9c9SChuck Lever  * NFSv3 XDR decode functions
1369b2cdd9c9SChuck Lever  *
1370b2cdd9c9SChuck Lever  * NFSv3 result types are defined in section 3.3 of RFC 1813:
1371b2cdd9c9SChuck Lever  * "NFS Version 3 Protocol Specification".
13721da177e4SLinus Torvalds  */
13731da177e4SLinus Torvalds 
13741da177e4SLinus Torvalds /*
1375e4f93234SChuck Lever  * 3.3.1  GETATTR3res
1376e4f93234SChuck Lever  *
1377e4f93234SChuck Lever  *	struct GETATTR3resok {
1378e4f93234SChuck Lever  *		fattr3		obj_attributes;
1379e4f93234SChuck Lever  *	};
1380e4f93234SChuck Lever  *
1381e4f93234SChuck Lever  *	union GETATTR3res switch (nfsstat3 status) {
1382e4f93234SChuck Lever  *	case NFS3_OK:
1383e4f93234SChuck Lever  *		GETATTR3resok  resok;
1384e4f93234SChuck Lever  *	default:
1385e4f93234SChuck Lever  *		void;
1386e4f93234SChuck Lever  *	};
1387e4f93234SChuck Lever  */
1388bf269551SChuck Lever static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
1389bf269551SChuck Lever 				    struct xdr_stream *xdr,
1390fc016483SChristoph Hellwig 				    void *result)
1391e4f93234SChuck Lever {
1392e4f93234SChuck Lever 	enum nfs_stat status;
1393e4f93234SChuck Lever 	int error;
1394e4f93234SChuck Lever 
1395bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1396e4f93234SChuck Lever 	if (unlikely(error))
1397e4f93234SChuck Lever 		goto out;
1398e4f93234SChuck Lever 	if (status != NFS3_OK)
1399e4f93234SChuck Lever 		goto out_default;
1400264d948cSTrond Myklebust 	error = decode_fattr3(xdr, result, rpc_rqst_userns(req));
1401e4f93234SChuck Lever out:
1402e4f93234SChuck Lever 	return error;
1403e4f93234SChuck Lever out_default:
14045e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1405e4f93234SChuck Lever }
1406e4f93234SChuck Lever 
1407e4f93234SChuck Lever /*
1408e4f93234SChuck Lever  * 3.3.2  SETATTR3res
1409e4f93234SChuck Lever  *
1410e4f93234SChuck Lever  *	struct SETATTR3resok {
1411e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1412e4f93234SChuck Lever  *	};
1413e4f93234SChuck Lever  *
1414e4f93234SChuck Lever  *	struct SETATTR3resfail {
1415e4f93234SChuck Lever  *		wcc_data  obj_wcc;
1416e4f93234SChuck Lever  *	};
1417e4f93234SChuck Lever  *
1418e4f93234SChuck Lever  *	union SETATTR3res switch (nfsstat3 status) {
1419e4f93234SChuck Lever  *	case NFS3_OK:
1420e4f93234SChuck Lever  *		SETATTR3resok   resok;
1421e4f93234SChuck Lever  *	default:
1422e4f93234SChuck Lever  *		SETATTR3resfail resfail;
1423e4f93234SChuck Lever  *	};
1424e4f93234SChuck Lever  */
1425bf269551SChuck Lever static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
1426bf269551SChuck Lever 				    struct xdr_stream *xdr,
1427fc016483SChristoph Hellwig 				    void *result)
1428e4f93234SChuck Lever {
1429e4f93234SChuck Lever 	enum nfs_stat status;
1430e4f93234SChuck Lever 	int error;
1431e4f93234SChuck Lever 
1432bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1433e4f93234SChuck Lever 	if (unlikely(error))
1434e4f93234SChuck Lever 		goto out;
1435264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result, rpc_rqst_userns(req));
1436e4f93234SChuck Lever 	if (unlikely(error))
1437e4f93234SChuck Lever 		goto out;
1438e4f93234SChuck Lever 	if (status != NFS3_OK)
1439e4f93234SChuck Lever 		goto out_status;
1440e4f93234SChuck Lever out:
1441e4f93234SChuck Lever 	return error;
1442e4f93234SChuck Lever out_status:
14435e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1444e4f93234SChuck Lever }
1445e4f93234SChuck Lever 
14461da177e4SLinus Torvalds /*
1447e4f93234SChuck Lever  * 3.3.3  LOOKUP3res
1448e4f93234SChuck Lever  *
1449e4f93234SChuck Lever  *	struct LOOKUP3resok {
1450e4f93234SChuck Lever  *		nfs_fh3		object;
1451e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1452e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1453e4f93234SChuck Lever  *	};
1454e4f93234SChuck Lever  *
1455e4f93234SChuck Lever  *	struct LOOKUP3resfail {
1456e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
1457e4f93234SChuck Lever  *	};
1458e4f93234SChuck Lever  *
1459e4f93234SChuck Lever  *	union LOOKUP3res switch (nfsstat3 status) {
1460e4f93234SChuck Lever  *	case NFS3_OK:
1461e4f93234SChuck Lever  *		LOOKUP3resok	resok;
1462e4f93234SChuck Lever  *	default:
1463e4f93234SChuck Lever  *		LOOKUP3resfail	resfail;
1464e4f93234SChuck Lever  *	};
1465e4f93234SChuck Lever  */
1466bf269551SChuck Lever static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
1467bf269551SChuck Lever 				   struct xdr_stream *xdr,
1468fc016483SChristoph Hellwig 				   void *data)
1469e4f93234SChuck Lever {
1470264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1471fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1472e4f93234SChuck Lever 	enum nfs_stat status;
1473e4f93234SChuck Lever 	int error;
1474e4f93234SChuck Lever 
1475bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1476e4f93234SChuck Lever 	if (unlikely(error))
1477e4f93234SChuck Lever 		goto out;
1478e4f93234SChuck Lever 	if (status != NFS3_OK)
1479e4f93234SChuck Lever 		goto out_default;
1480bf269551SChuck Lever 	error = decode_nfs_fh3(xdr, result->fh);
1481e4f93234SChuck Lever 	if (unlikely(error))
1482e4f93234SChuck Lever 		goto out;
1483264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
1484e4f93234SChuck Lever 	if (unlikely(error))
1485e4f93234SChuck Lever 		goto out;
1486264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, userns);
1487e4f93234SChuck Lever out:
1488e4f93234SChuck Lever 	return error;
1489e4f93234SChuck Lever out_default:
1490264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, userns);
1491e4f93234SChuck Lever 	if (unlikely(error))
1492e4f93234SChuck Lever 		goto out;
14935e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1494e4f93234SChuck Lever }
1495e4f93234SChuck Lever 
1496e4f93234SChuck Lever /*
1497e4f93234SChuck Lever  * 3.3.4  ACCESS3res
1498e4f93234SChuck Lever  *
1499e4f93234SChuck Lever  *	struct ACCESS3resok {
1500e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1501e4f93234SChuck Lever  *		uint32		access;
1502e4f93234SChuck Lever  *	};
1503e4f93234SChuck Lever  *
1504e4f93234SChuck Lever  *	struct ACCESS3resfail {
1505e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1506e4f93234SChuck Lever  *	};
1507e4f93234SChuck Lever  *
1508e4f93234SChuck Lever  *	union ACCESS3res switch (nfsstat3 status) {
1509e4f93234SChuck Lever  *	case NFS3_OK:
1510e4f93234SChuck Lever  *		ACCESS3resok	resok;
1511e4f93234SChuck Lever  *	default:
1512e4f93234SChuck Lever  *		ACCESS3resfail	resfail;
1513e4f93234SChuck Lever  *	};
1514e4f93234SChuck Lever  */
1515bf269551SChuck Lever static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
1516bf269551SChuck Lever 				   struct xdr_stream *xdr,
1517fc016483SChristoph Hellwig 				   void *data)
1518e4f93234SChuck Lever {
1519fc016483SChristoph Hellwig 	struct nfs3_accessres *result = data;
1520e4f93234SChuck Lever 	enum nfs_stat status;
1521e4f93234SChuck Lever 	int error;
1522e4f93234SChuck Lever 
1523bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1524e4f93234SChuck Lever 	if (unlikely(error))
1525e4f93234SChuck Lever 		goto out;
1526264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
1527e4f93234SChuck Lever 	if (unlikely(error))
1528e4f93234SChuck Lever 		goto out;
1529e4f93234SChuck Lever 	if (status != NFS3_OK)
1530e4f93234SChuck Lever 		goto out_default;
1531bf269551SChuck Lever 	error = decode_uint32(xdr, &result->access);
1532e4f93234SChuck Lever out:
1533e4f93234SChuck Lever 	return error;
1534e4f93234SChuck Lever out_default:
15355e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1536e4f93234SChuck Lever }
1537e4f93234SChuck Lever 
1538e4f93234SChuck Lever /*
1539e4f93234SChuck Lever  * 3.3.5  READLINK3res
1540e4f93234SChuck Lever  *
1541e4f93234SChuck Lever  *	struct READLINK3resok {
1542e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1543e4f93234SChuck Lever  *		nfspath3	data;
1544e4f93234SChuck Lever  *	};
1545e4f93234SChuck Lever  *
1546e4f93234SChuck Lever  *	struct READLINK3resfail {
1547e4f93234SChuck Lever  *		post_op_attr	symlink_attributes;
1548e4f93234SChuck Lever  *	};
1549e4f93234SChuck Lever  *
1550e4f93234SChuck Lever  *	union READLINK3res switch (nfsstat3 status) {
1551e4f93234SChuck Lever  *	case NFS3_OK:
1552e4f93234SChuck Lever  *		READLINK3resok	resok;
1553e4f93234SChuck Lever  *	default:
1554e4f93234SChuck Lever  *		READLINK3resfail resfail;
1555e4f93234SChuck Lever  *	};
1556e4f93234SChuck Lever  */
1557bf269551SChuck Lever static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
1558bf269551SChuck Lever 				     struct xdr_stream *xdr,
1559fc016483SChristoph Hellwig 				     void *result)
1560e4f93234SChuck Lever {
1561e4f93234SChuck Lever 	enum nfs_stat status;
1562e4f93234SChuck Lever 	int error;
1563e4f93234SChuck Lever 
1564bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1565e4f93234SChuck Lever 	if (unlikely(error))
1566e4f93234SChuck Lever 		goto out;
1567264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result, rpc_rqst_userns(req));
1568e4f93234SChuck Lever 	if (unlikely(error))
1569e4f93234SChuck Lever 		goto out;
1570e4f93234SChuck Lever 	if (status != NFS3_OK)
1571e4f93234SChuck Lever 		goto out_default;
1572bf269551SChuck Lever 	error = decode_nfspath3(xdr);
1573e4f93234SChuck Lever out:
1574e4f93234SChuck Lever 	return error;
1575e4f93234SChuck Lever out_default:
15765e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1577e4f93234SChuck Lever }
1578e4f93234SChuck Lever 
1579e4f93234SChuck Lever /*
1580e4f93234SChuck Lever  * 3.3.6  READ3res
1581e4f93234SChuck Lever  *
1582e4f93234SChuck Lever  *	struct READ3resok {
1583e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1584e4f93234SChuck Lever  *		count3		count;
1585e4f93234SChuck Lever  *		bool		eof;
1586e4f93234SChuck Lever  *		opaque		data<>;
1587e4f93234SChuck Lever  *	};
1588e4f93234SChuck Lever  *
1589e4f93234SChuck Lever  *	struct READ3resfail {
1590e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1591e4f93234SChuck Lever  *	};
1592e4f93234SChuck Lever  *
1593e4f93234SChuck Lever  *	union READ3res switch (nfsstat3 status) {
1594e4f93234SChuck Lever  *	case NFS3_OK:
1595e4f93234SChuck Lever  *		READ3resok	resok;
1596e4f93234SChuck Lever  *	default:
1597e4f93234SChuck Lever  *		READ3resfail	resfail;
1598e4f93234SChuck Lever  *	};
1599e4f93234SChuck Lever  */
1600e4f93234SChuck Lever static int decode_read3resok(struct xdr_stream *xdr,
16019137bdf3SAnna Schumaker 			     struct nfs_pgio_res *result)
1602e4f93234SChuck Lever {
1603e4f93234SChuck Lever 	u32 eof, count, ocount, recvd;
1604e4f93234SChuck Lever 	__be32 *p;
1605e4f93234SChuck Lever 
1606e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 + 4 + 4);
1607eb72f484SChuck Lever 	if (unlikely(!p))
1608eb72f484SChuck Lever 		return -EIO;
1609e4f93234SChuck Lever 	count = be32_to_cpup(p++);
1610e4f93234SChuck Lever 	eof = be32_to_cpup(p++);
1611e4f93234SChuck Lever 	ocount = be32_to_cpup(p++);
1612e4f93234SChuck Lever 	if (unlikely(ocount != count))
1613e4f93234SChuck Lever 		goto out_mismatch;
161464bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
1615e4f93234SChuck Lever 	if (unlikely(count > recvd))
1616e4f93234SChuck Lever 		goto out_cheating;
1617e4f93234SChuck Lever out:
1618e4f93234SChuck Lever 	result->eof = eof;
1619e4f93234SChuck Lever 	result->count = count;
1620e4f93234SChuck Lever 	return count;
1621e4f93234SChuck Lever out_mismatch:
1622e4f93234SChuck Lever 	dprintk("NFS: READ count doesn't match length of opaque: "
1623e4f93234SChuck Lever 		"count %u != ocount %u\n", count, ocount);
1624e4f93234SChuck Lever 	return -EIO;
1625e4f93234SChuck Lever out_cheating:
1626e4f93234SChuck Lever 	dprintk("NFS: server cheating in read result: "
1627e4f93234SChuck Lever 		"count %u > recvd %u\n", count, recvd);
1628e4f93234SChuck Lever 	count = recvd;
1629e4f93234SChuck Lever 	eof = 0;
1630e4f93234SChuck Lever 	goto out;
1631e4f93234SChuck Lever }
1632e4f93234SChuck Lever 
1633bf269551SChuck Lever static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1634fc016483SChristoph Hellwig 				 void *data)
1635e4f93234SChuck Lever {
1636fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
16378d8928d8STrond Myklebust 	unsigned int pos;
1638e4f93234SChuck Lever 	enum nfs_stat status;
1639e4f93234SChuck Lever 	int error;
1640e4f93234SChuck Lever 
16418d8928d8STrond Myklebust 	pos = xdr_stream_pos(xdr);
1642bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1643e4f93234SChuck Lever 	if (unlikely(error))
1644e4f93234SChuck Lever 		goto out;
1645264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
1646e4f93234SChuck Lever 	if (unlikely(error))
1647e4f93234SChuck Lever 		goto out;
1648aabff4ddSPeng Tao 	result->op_status = status;
1649e4f93234SChuck Lever 	if (status != NFS3_OK)
1650e4f93234SChuck Lever 		goto out_status;
165102ef04e4SChuck Lever 	result->replen = 4 + ((xdr_stream_pos(xdr) - pos) >> 2);
1652bf269551SChuck Lever 	error = decode_read3resok(xdr, result);
1653e4f93234SChuck Lever out:
1654e4f93234SChuck Lever 	return error;
1655e4f93234SChuck Lever out_status:
16565e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1657e4f93234SChuck Lever }
1658e4f93234SChuck Lever 
1659e4f93234SChuck Lever /*
1660e4f93234SChuck Lever  * 3.3.7  WRITE3res
1661e4f93234SChuck Lever  *
1662e4f93234SChuck Lever  *	enum stable_how {
1663e4f93234SChuck Lever  *		UNSTABLE  = 0,
1664e4f93234SChuck Lever  *		DATA_SYNC = 1,
1665e4f93234SChuck Lever  *		FILE_SYNC = 2
1666e4f93234SChuck Lever  *	};
1667e4f93234SChuck Lever  *
1668e4f93234SChuck Lever  *	struct WRITE3resok {
1669e4f93234SChuck Lever  *		wcc_data	file_wcc;
1670e4f93234SChuck Lever  *		count3		count;
1671e4f93234SChuck Lever  *		stable_how	committed;
1672e4f93234SChuck Lever  *		writeverf3	verf;
1673e4f93234SChuck Lever  *	};
1674e4f93234SChuck Lever  *
1675e4f93234SChuck Lever  *	struct WRITE3resfail {
1676e4f93234SChuck Lever  *		wcc_data	file_wcc;
1677e4f93234SChuck Lever  *	};
1678e4f93234SChuck Lever  *
1679e4f93234SChuck Lever  *	union WRITE3res switch (nfsstat3 status) {
1680e4f93234SChuck Lever  *	case NFS3_OK:
1681e4f93234SChuck Lever  *		WRITE3resok	resok;
1682e4f93234SChuck Lever  *	default:
1683e4f93234SChuck Lever  *		WRITE3resfail	resfail;
1684e4f93234SChuck Lever  *	};
1685e4f93234SChuck Lever  */
1686e4f93234SChuck Lever static int decode_write3resok(struct xdr_stream *xdr,
16879137bdf3SAnna Schumaker 			      struct nfs_pgio_res *result)
1688e4f93234SChuck Lever {
1689e4f93234SChuck Lever 	__be32 *p;
1690e4f93234SChuck Lever 
16912f2c63bcSTrond Myklebust 	p = xdr_inline_decode(xdr, 4 + 4);
1692eb72f484SChuck Lever 	if (unlikely(!p))
1693eb72f484SChuck Lever 		return -EIO;
1694e4f93234SChuck Lever 	result->count = be32_to_cpup(p++);
1695e4f93234SChuck Lever 	result->verf->committed = be32_to_cpup(p++);
1696e4f93234SChuck Lever 	if (unlikely(result->verf->committed > NFS_FILE_SYNC))
1697e4f93234SChuck Lever 		goto out_badvalue;
16982f2c63bcSTrond Myklebust 	if (decode_writeverf3(xdr, &result->verf->verifier))
1699eb72f484SChuck Lever 		return -EIO;
1700e4f93234SChuck Lever 	return result->count;
1701e4f93234SChuck Lever out_badvalue:
1702e4f93234SChuck Lever 	dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
1703e4f93234SChuck Lever 	return -EIO;
1704e4f93234SChuck Lever }
1705e4f93234SChuck Lever 
1706bf269551SChuck Lever static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1707fc016483SChristoph Hellwig 				  void *data)
1708e4f93234SChuck Lever {
1709fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
1710e4f93234SChuck Lever 	enum nfs_stat status;
1711e4f93234SChuck Lever 	int error;
1712e4f93234SChuck Lever 
1713bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1714e4f93234SChuck Lever 	if (unlikely(error))
1715e4f93234SChuck Lever 		goto out;
1716264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->fattr, rpc_rqst_userns(req));
1717e4f93234SChuck Lever 	if (unlikely(error))
1718e4f93234SChuck Lever 		goto out;
1719aabff4ddSPeng Tao 	result->op_status = status;
1720e4f93234SChuck Lever 	if (status != NFS3_OK)
1721e4f93234SChuck Lever 		goto out_status;
1722bf269551SChuck Lever 	error = decode_write3resok(xdr, result);
1723e4f93234SChuck Lever out:
1724e4f93234SChuck Lever 	return error;
1725e4f93234SChuck Lever out_status:
17265e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1727e4f93234SChuck Lever }
1728e4f93234SChuck Lever 
1729e4f93234SChuck Lever /*
1730e4f93234SChuck Lever  * 3.3.8  CREATE3res
1731e4f93234SChuck Lever  *
1732e4f93234SChuck Lever  *	struct CREATE3resok {
1733e4f93234SChuck Lever  *		post_op_fh3	obj;
1734e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
1735e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1736e4f93234SChuck Lever  *	};
1737e4f93234SChuck Lever  *
1738e4f93234SChuck Lever  *	struct CREATE3resfail {
1739e4f93234SChuck Lever  *		wcc_data	dir_wcc;
1740e4f93234SChuck Lever  *	};
1741e4f93234SChuck Lever  *
1742e4f93234SChuck Lever  *	union CREATE3res switch (nfsstat3 status) {
1743e4f93234SChuck Lever  *	case NFS3_OK:
1744e4f93234SChuck Lever  *		CREATE3resok	resok;
1745e4f93234SChuck Lever  *	default:
1746e4f93234SChuck Lever  *		CREATE3resfail	resfail;
1747e4f93234SChuck Lever  *	};
1748e4f93234SChuck Lever  */
1749e4f93234SChuck Lever static int decode_create3resok(struct xdr_stream *xdr,
1750264d948cSTrond Myklebust 			       struct nfs3_diropres *result,
1751264d948cSTrond Myklebust 			       struct user_namespace *userns)
1752e4f93234SChuck Lever {
1753e4f93234SChuck Lever 	int error;
1754e4f93234SChuck Lever 
1755e4f93234SChuck Lever 	error = decode_post_op_fh3(xdr, result->fh);
1756e4f93234SChuck Lever 	if (unlikely(error))
1757e4f93234SChuck Lever 		goto out;
1758264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
1759e4f93234SChuck Lever 	if (unlikely(error))
1760e4f93234SChuck Lever 		goto out;
1761e4f93234SChuck Lever 	/* The server isn't required to return a file handle.
1762e4f93234SChuck Lever 	 * If it didn't, force the client to perform a LOOKUP
1763e4f93234SChuck Lever 	 * to determine the correct file handle and attribute
1764e4f93234SChuck Lever 	 * values for the new object. */
1765e4f93234SChuck Lever 	if (result->fh->size == 0)
1766e4f93234SChuck Lever 		result->fattr->valid = 0;
1767264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, userns);
1768e4f93234SChuck Lever out:
1769e4f93234SChuck Lever 	return error;
1770e4f93234SChuck Lever }
1771e4f93234SChuck Lever 
1772bf269551SChuck Lever static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
1773bf269551SChuck Lever 				   struct xdr_stream *xdr,
1774fc016483SChristoph Hellwig 				   void *data)
1775e4f93234SChuck Lever {
1776264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1777fc016483SChristoph Hellwig 	struct nfs3_diropres *result = data;
1778e4f93234SChuck Lever 	enum nfs_stat status;
1779e4f93234SChuck Lever 	int error;
1780e4f93234SChuck Lever 
1781bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1782e4f93234SChuck Lever 	if (unlikely(error))
1783e4f93234SChuck Lever 		goto out;
1784e4f93234SChuck Lever 	if (status != NFS3_OK)
1785e4f93234SChuck Lever 		goto out_default;
1786264d948cSTrond Myklebust 	error = decode_create3resok(xdr, result, userns);
1787e4f93234SChuck Lever out:
1788e4f93234SChuck Lever 	return error;
1789e4f93234SChuck Lever out_default:
1790264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, userns);
1791e4f93234SChuck Lever 	if (unlikely(error))
1792e4f93234SChuck Lever 		goto out;
17935e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1794e4f93234SChuck Lever }
1795e4f93234SChuck Lever 
1796e4f93234SChuck Lever /*
1797e4f93234SChuck Lever  * 3.3.12  REMOVE3res
1798e4f93234SChuck Lever  *
1799e4f93234SChuck Lever  *	struct REMOVE3resok {
1800e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1801e4f93234SChuck Lever  *	};
1802e4f93234SChuck Lever  *
1803e4f93234SChuck Lever  *	struct REMOVE3resfail {
1804e4f93234SChuck Lever  *		wcc_data    dir_wcc;
1805e4f93234SChuck Lever  *	};
1806e4f93234SChuck Lever  *
1807e4f93234SChuck Lever  *	union REMOVE3res switch (nfsstat3 status) {
1808e4f93234SChuck Lever  *	case NFS3_OK:
1809e4f93234SChuck Lever  *		REMOVE3resok   resok;
1810e4f93234SChuck Lever  *	default:
1811e4f93234SChuck Lever  *		REMOVE3resfail resfail;
1812e4f93234SChuck Lever  *	};
1813e4f93234SChuck Lever  */
1814bf269551SChuck Lever static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
1815bf269551SChuck Lever 				   struct xdr_stream *xdr,
1816fc016483SChristoph Hellwig 				   void *data)
1817e4f93234SChuck Lever {
1818fc016483SChristoph Hellwig 	struct nfs_removeres *result = data;
1819e4f93234SChuck Lever 	enum nfs_stat status;
1820e4f93234SChuck Lever 	int error;
1821e4f93234SChuck Lever 
1822bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1823e4f93234SChuck Lever 	if (unlikely(error))
1824e4f93234SChuck Lever 		goto out;
1825264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, rpc_rqst_userns(req));
1826e4f93234SChuck Lever 	if (unlikely(error))
1827e4f93234SChuck Lever 		goto out;
1828e4f93234SChuck Lever 	if (status != NFS3_OK)
1829e4f93234SChuck Lever 		goto out_status;
1830e4f93234SChuck Lever out:
1831e4f93234SChuck Lever 	return error;
1832e4f93234SChuck Lever out_status:
18335e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1834e4f93234SChuck Lever }
1835e4f93234SChuck Lever 
1836e4f93234SChuck Lever /*
1837e4f93234SChuck Lever  * 3.3.14  RENAME3res
1838e4f93234SChuck Lever  *
1839e4f93234SChuck Lever  *	struct RENAME3resok {
1840e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1841e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1842e4f93234SChuck Lever  *	};
1843e4f93234SChuck Lever  *
1844e4f93234SChuck Lever  *	struct RENAME3resfail {
1845e4f93234SChuck Lever  *		wcc_data	fromdir_wcc;
1846e4f93234SChuck Lever  *		wcc_data	todir_wcc;
1847e4f93234SChuck Lever  *	};
1848e4f93234SChuck Lever  *
1849e4f93234SChuck Lever  *	union RENAME3res switch (nfsstat3 status) {
1850e4f93234SChuck Lever  *	case NFS3_OK:
1851e4f93234SChuck Lever  *		RENAME3resok   resok;
1852e4f93234SChuck Lever  *	default:
1853e4f93234SChuck Lever  *		RENAME3resfail resfail;
1854e4f93234SChuck Lever  *	};
1855e4f93234SChuck Lever  */
1856bf269551SChuck Lever static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
1857bf269551SChuck Lever 				   struct xdr_stream *xdr,
1858fc016483SChristoph Hellwig 				   void *data)
1859e4f93234SChuck Lever {
1860264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1861fc016483SChristoph Hellwig 	struct nfs_renameres *result = data;
1862e4f93234SChuck Lever 	enum nfs_stat status;
1863e4f93234SChuck Lever 	int error;
1864e4f93234SChuck Lever 
1865bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1866e4f93234SChuck Lever 	if (unlikely(error))
1867e4f93234SChuck Lever 		goto out;
1868264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->old_fattr, userns);
1869e4f93234SChuck Lever 	if (unlikely(error))
1870e4f93234SChuck Lever 		goto out;
1871264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->new_fattr, userns);
1872e4f93234SChuck Lever 	if (unlikely(error))
1873e4f93234SChuck Lever 		goto out;
1874e4f93234SChuck Lever 	if (status != NFS3_OK)
1875e4f93234SChuck Lever 		goto out_status;
1876e4f93234SChuck Lever out:
1877e4f93234SChuck Lever 	return error;
1878e4f93234SChuck Lever out_status:
18795e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1880e4f93234SChuck Lever }
1881e4f93234SChuck Lever 
1882e4f93234SChuck Lever /*
1883e4f93234SChuck Lever  * 3.3.15  LINK3res
1884e4f93234SChuck Lever  *
1885e4f93234SChuck Lever  *	struct LINK3resok {
1886e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1887e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1888e4f93234SChuck Lever  *	};
1889e4f93234SChuck Lever  *
1890e4f93234SChuck Lever  *	struct LINK3resfail {
1891e4f93234SChuck Lever  *		post_op_attr	file_attributes;
1892e4f93234SChuck Lever  *		wcc_data	linkdir_wcc;
1893e4f93234SChuck Lever  *	};
1894e4f93234SChuck Lever  *
1895e4f93234SChuck Lever  *	union LINK3res switch (nfsstat3 status) {
1896e4f93234SChuck Lever  *	case NFS3_OK:
1897e4f93234SChuck Lever  *		LINK3resok	resok;
1898e4f93234SChuck Lever  *	default:
1899e4f93234SChuck Lever  *		LINK3resfail	resfail;
1900e4f93234SChuck Lever  *	};
1901e4f93234SChuck Lever  */
1902bf269551SChuck Lever static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
1903fc016483SChristoph Hellwig 				 void *data)
1904e4f93234SChuck Lever {
1905264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_rqst_userns(req);
1906fc016483SChristoph Hellwig 	struct nfs3_linkres *result = data;
1907e4f93234SChuck Lever 	enum nfs_stat status;
1908e4f93234SChuck Lever 	int error;
1909e4f93234SChuck Lever 
1910bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
1911e4f93234SChuck Lever 	if (unlikely(error))
1912e4f93234SChuck Lever 		goto out;
1913264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
1914e4f93234SChuck Lever 	if (unlikely(error))
1915e4f93234SChuck Lever 		goto out;
1916264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->dir_attr, userns);
1917e4f93234SChuck Lever 	if (unlikely(error))
1918e4f93234SChuck Lever 		goto out;
1919e4f93234SChuck Lever 	if (status != NFS3_OK)
1920e4f93234SChuck Lever 		goto out_status;
1921e4f93234SChuck Lever out:
1922e4f93234SChuck Lever 	return error;
1923e4f93234SChuck Lever out_status:
19245e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
1925e4f93234SChuck Lever }
1926e4f93234SChuck Lever 
1927e4f93234SChuck Lever /**
1928e4f93234SChuck Lever  * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
1929e4f93234SChuck Lever  *			the local page cache
1930e4f93234SChuck Lever  * @xdr: XDR stream where entry resides
1931e4f93234SChuck Lever  * @entry: buffer to fill in with entry data
1932e4f93234SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
1933e4f93234SChuck Lever  *
1934573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
1935573c4e1eSChuck Lever  * returned.
1936e4f93234SChuck Lever  *
1937e4f93234SChuck Lever  * This function is not invoked during READDIR reply decoding, but
1938e4f93234SChuck Lever  * rather whenever an application invokes the getdents(2) system call
1939e4f93234SChuck Lever  * on a directory already in our cache.
1940e4f93234SChuck Lever  *
1941e4f93234SChuck Lever  * 3.3.16  entry3
1942e4f93234SChuck Lever  *
1943e4f93234SChuck Lever  *	struct entry3 {
1944e4f93234SChuck Lever  *		fileid3		fileid;
1945e4f93234SChuck Lever  *		filename3	name;
1946e4f93234SChuck Lever  *		cookie3		cookie;
1947e4f93234SChuck Lever  *		fhandle3	filehandle;
1948e4f93234SChuck Lever  *		post_op_attr3	attributes;
1949e4f93234SChuck Lever  *		entry3		*nextentry;
1950e4f93234SChuck Lever  *	};
1951e4f93234SChuck Lever  *
1952e4f93234SChuck Lever  * 3.3.17  entryplus3
1953e4f93234SChuck Lever  *	struct entryplus3 {
1954e4f93234SChuck Lever  *		fileid3		fileid;
1955e4f93234SChuck Lever  *		filename3	name;
1956e4f93234SChuck Lever  *		cookie3		cookie;
1957e4f93234SChuck Lever  *		post_op_attr	name_attributes;
1958e4f93234SChuck Lever  *		post_op_fh3	name_handle;
1959e4f93234SChuck Lever  *		entryplus3	*nextentry;
1960e4f93234SChuck Lever  *	};
1961e4f93234SChuck Lever  */
1962573c4e1eSChuck Lever int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
1963a7a3b1e9SBenjamin Coddington 		       bool plus)
1964e4f93234SChuck Lever {
1965264d948cSTrond Myklebust 	struct user_namespace *userns = rpc_userns(entry->server->client);
1966e4f93234SChuck Lever 	struct nfs_entry old = *entry;
1967e4f93234SChuck Lever 	__be32 *p;
1968e4f93234SChuck Lever 	int error;
196998de9ce6SFrank Sorenson 	u64 new_cookie;
1970e4f93234SChuck Lever 
1971e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4);
1972eb72f484SChuck Lever 	if (unlikely(!p))
1973eb72f484SChuck Lever 		return -EAGAIN;
1974e4f93234SChuck Lever 	if (*p == xdr_zero) {
1975e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
1976eb72f484SChuck Lever 		if (unlikely(!p))
1977eb72f484SChuck Lever 			return -EAGAIN;
1978e4f93234SChuck Lever 		if (*p == xdr_zero)
1979573c4e1eSChuck Lever 			return -EAGAIN;
1980e4f93234SChuck Lever 		entry->eof = 1;
1981573c4e1eSChuck Lever 		return -EBADCOOKIE;
1982e4f93234SChuck Lever 	}
1983e4f93234SChuck Lever 
1984e4f93234SChuck Lever 	error = decode_fileid3(xdr, &entry->ino);
1985e4f93234SChuck Lever 	if (unlikely(error))
1986573c4e1eSChuck Lever 		return error;
1987e4f93234SChuck Lever 
1988e4f93234SChuck Lever 	error = decode_inline_filename3(xdr, &entry->name, &entry->len);
1989e4f93234SChuck Lever 	if (unlikely(error))
1990573c4e1eSChuck Lever 		return error;
1991e4f93234SChuck Lever 
199298de9ce6SFrank Sorenson 	error = decode_cookie3(xdr, &new_cookie);
1993e4f93234SChuck Lever 	if (unlikely(error))
1994573c4e1eSChuck Lever 		return error;
1995e4f93234SChuck Lever 
1996e4f93234SChuck Lever 	entry->d_type = DT_UNKNOWN;
1997e4f93234SChuck Lever 
1998e4f93234SChuck Lever 	if (plus) {
1999e4f93234SChuck Lever 		entry->fattr->valid = 0;
2000264d948cSTrond Myklebust 		error = decode_post_op_attr(xdr, entry->fattr, userns);
2001e4f93234SChuck Lever 		if (unlikely(error))
2002573c4e1eSChuck Lever 			return error;
2003e4f93234SChuck Lever 		if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
2004e4f93234SChuck Lever 			entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
2005e4f93234SChuck Lever 
20061ae04b25STrond Myklebust 		if (entry->fattr->fileid != entry->ino) {
20071ae04b25STrond Myklebust 			entry->fattr->mounted_on_fileid = entry->ino;
20081ae04b25STrond Myklebust 			entry->fattr->valid |= NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
20091ae04b25STrond Myklebust 		}
20101ae04b25STrond Myklebust 
2011e4f93234SChuck Lever 		/* In fact, a post_op_fh3: */
2012e4f93234SChuck Lever 		p = xdr_inline_decode(xdr, 4);
2013eb72f484SChuck Lever 		if (unlikely(!p))
2014eb72f484SChuck Lever 			return -EAGAIN;
2015e4f93234SChuck Lever 		if (*p != xdr_zero) {
2016e4f93234SChuck Lever 			error = decode_nfs_fh3(xdr, entry->fh);
2017e4f93234SChuck Lever 			if (unlikely(error)) {
2018e4f93234SChuck Lever 				if (error == -E2BIG)
2019e4f93234SChuck Lever 					goto out_truncated;
2020573c4e1eSChuck Lever 				return error;
2021e4f93234SChuck Lever 			}
2022e4f93234SChuck Lever 		} else
2023e4f93234SChuck Lever 			zero_nfs_fh3(entry->fh);
2024e4f93234SChuck Lever 	}
2025e4f93234SChuck Lever 
202698de9ce6SFrank Sorenson 	entry->prev_cookie = entry->cookie;
202798de9ce6SFrank Sorenson 	entry->cookie = new_cookie;
202898de9ce6SFrank Sorenson 
2029573c4e1eSChuck Lever 	return 0;
2030e4f93234SChuck Lever 
2031e4f93234SChuck Lever out_truncated:
2032e4f93234SChuck Lever 	dprintk("NFS: directory entry contains invalid file handle\n");
2033e4f93234SChuck Lever 	*entry = old;
2034573c4e1eSChuck Lever 	return -EAGAIN;
2035e4f93234SChuck Lever }
2036e4f93234SChuck Lever 
2037e4f93234SChuck Lever /*
2038e4f93234SChuck Lever  * 3.3.16  READDIR3res
2039e4f93234SChuck Lever  *
2040e4f93234SChuck Lever  *	struct dirlist3 {
2041e4f93234SChuck Lever  *		entry3		*entries;
2042e4f93234SChuck Lever  *		bool		eof;
2043e4f93234SChuck Lever  *	};
2044e4f93234SChuck Lever  *
2045e4f93234SChuck Lever  *	struct READDIR3resok {
2046e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2047e4f93234SChuck Lever  *		cookieverf3	cookieverf;
2048e4f93234SChuck Lever  *		dirlist3	reply;
2049e4f93234SChuck Lever  *	};
2050e4f93234SChuck Lever  *
2051e4f93234SChuck Lever  *	struct READDIR3resfail {
2052e4f93234SChuck Lever  *		post_op_attr	dir_attributes;
2053e4f93234SChuck Lever  *	};
2054e4f93234SChuck Lever  *
2055e4f93234SChuck Lever  *	union READDIR3res switch (nfsstat3 status) {
2056e4f93234SChuck Lever  *	case NFS3_OK:
2057e4f93234SChuck Lever  *		READDIR3resok	resok;
2058e4f93234SChuck Lever  *	default:
2059e4f93234SChuck Lever  *		READDIR3resfail	resfail;
2060e4f93234SChuck Lever  *	};
2061e4f93234SChuck Lever  *
2062e4f93234SChuck Lever  * Read the directory contents into the page cache, but otherwise
2063e4f93234SChuck Lever  * don't touch them.  The actual decoding is done by nfs3_decode_entry()
2064e4f93234SChuck Lever  * during subsequent nfs_readdir() calls.
2065e4f93234SChuck Lever  */
2066e4f93234SChuck Lever static int decode_dirlist3(struct xdr_stream *xdr)
2067e4f93234SChuck Lever {
206864bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
2069e4f93234SChuck Lever }
2070e4f93234SChuck Lever 
2071e4f93234SChuck Lever static int decode_readdir3resok(struct xdr_stream *xdr,
2072264d948cSTrond Myklebust 				struct nfs3_readdirres *result,
2073264d948cSTrond Myklebust 				struct user_namespace *userns)
2074e4f93234SChuck Lever {
2075e4f93234SChuck Lever 	int error;
2076e4f93234SChuck Lever 
2077264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, userns);
2078e4f93234SChuck Lever 	if (unlikely(error))
2079e4f93234SChuck Lever 		goto out;
2080e4f93234SChuck Lever 	/* XXX: do we need to check if result->verf != NULL ? */
2081e4f93234SChuck Lever 	error = decode_cookieverf3(xdr, result->verf);
2082e4f93234SChuck Lever 	if (unlikely(error))
2083e4f93234SChuck Lever 		goto out;
2084e4f93234SChuck Lever 	error = decode_dirlist3(xdr);
2085e4f93234SChuck Lever out:
2086e4f93234SChuck Lever 	return error;
2087e4f93234SChuck Lever }
2088e4f93234SChuck Lever 
2089bf269551SChuck Lever static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
2090bf269551SChuck Lever 				    struct xdr_stream *xdr,
2091fc016483SChristoph Hellwig 				    void *data)
2092e4f93234SChuck Lever {
2093fc016483SChristoph Hellwig 	struct nfs3_readdirres *result = data;
2094e4f93234SChuck Lever 	enum nfs_stat status;
2095e4f93234SChuck Lever 	int error;
2096e4f93234SChuck Lever 
2097bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2098e4f93234SChuck Lever 	if (unlikely(error))
2099e4f93234SChuck Lever 		goto out;
2100e4f93234SChuck Lever 	if (status != NFS3_OK)
2101e4f93234SChuck Lever 		goto out_default;
2102264d948cSTrond Myklebust 	error = decode_readdir3resok(xdr, result, rpc_rqst_userns(req));
2103e4f93234SChuck Lever out:
2104e4f93234SChuck Lever 	return error;
2105e4f93234SChuck Lever out_default:
2106264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->dir_attr, rpc_rqst_userns(req));
2107e4f93234SChuck Lever 	if (unlikely(error))
2108e4f93234SChuck Lever 		goto out;
21095e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2110e4f93234SChuck Lever }
2111e4f93234SChuck Lever 
2112e4f93234SChuck Lever /*
2113e4f93234SChuck Lever  * 3.3.18  FSSTAT3res
2114e4f93234SChuck Lever  *
2115e4f93234SChuck Lever  *	struct FSSTAT3resok {
2116e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2117e4f93234SChuck Lever  *		size3		tbytes;
2118e4f93234SChuck Lever  *		size3		fbytes;
2119e4f93234SChuck Lever  *		size3		abytes;
2120e4f93234SChuck Lever  *		size3		tfiles;
2121e4f93234SChuck Lever  *		size3		ffiles;
2122e4f93234SChuck Lever  *		size3		afiles;
2123e4f93234SChuck Lever  *		uint32		invarsec;
2124e4f93234SChuck Lever  *	};
2125e4f93234SChuck Lever  *
2126e4f93234SChuck Lever  *	struct FSSTAT3resfail {
2127e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2128e4f93234SChuck Lever  *	};
2129e4f93234SChuck Lever  *
2130e4f93234SChuck Lever  *	union FSSTAT3res switch (nfsstat3 status) {
2131e4f93234SChuck Lever  *	case NFS3_OK:
2132e4f93234SChuck Lever  *		FSSTAT3resok	resok;
2133e4f93234SChuck Lever  *	default:
2134e4f93234SChuck Lever  *		FSSTAT3resfail	resfail;
2135e4f93234SChuck Lever  *	};
2136e4f93234SChuck Lever  */
2137e4f93234SChuck Lever static int decode_fsstat3resok(struct xdr_stream *xdr,
2138e4f93234SChuck Lever 			       struct nfs_fsstat *result)
2139e4f93234SChuck Lever {
2140e4f93234SChuck Lever 	__be32 *p;
2141e4f93234SChuck Lever 
2142e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 8 * 6 + 4);
2143eb72f484SChuck Lever 	if (unlikely(!p))
2144eb72f484SChuck Lever 		return -EIO;
2145e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tbytes);
2146e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->fbytes);
2147e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->abytes);
2148e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->tfiles);
2149e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->ffiles);
2150e4f93234SChuck Lever 	xdr_decode_size3(p, &result->afiles);
2151e4f93234SChuck Lever 	/* ignore invarsec */
2152e4f93234SChuck Lever 	return 0;
2153e4f93234SChuck Lever }
2154e4f93234SChuck Lever 
2155bf269551SChuck Lever static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
2156bf269551SChuck Lever 				   struct xdr_stream *xdr,
2157fc016483SChristoph Hellwig 				   void *data)
2158e4f93234SChuck Lever {
2159fc016483SChristoph Hellwig 	struct nfs_fsstat *result = data;
2160e4f93234SChuck Lever 	enum nfs_stat status;
2161e4f93234SChuck Lever 	int error;
2162e4f93234SChuck Lever 
2163bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2164e4f93234SChuck Lever 	if (unlikely(error))
2165e4f93234SChuck Lever 		goto out;
2166264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
2167e4f93234SChuck Lever 	if (unlikely(error))
2168e4f93234SChuck Lever 		goto out;
2169e4f93234SChuck Lever 	if (status != NFS3_OK)
2170e4f93234SChuck Lever 		goto out_status;
2171bf269551SChuck Lever 	error = decode_fsstat3resok(xdr, result);
2172e4f93234SChuck Lever out:
2173e4f93234SChuck Lever 	return error;
2174e4f93234SChuck Lever out_status:
21755e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2176e4f93234SChuck Lever }
2177e4f93234SChuck Lever 
2178e4f93234SChuck Lever /*
2179e4f93234SChuck Lever  * 3.3.19  FSINFO3res
2180e4f93234SChuck Lever  *
2181e4f93234SChuck Lever  *	struct FSINFO3resok {
2182e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2183e4f93234SChuck Lever  *		uint32		rtmax;
2184e4f93234SChuck Lever  *		uint32		rtpref;
2185e4f93234SChuck Lever  *		uint32		rtmult;
2186e4f93234SChuck Lever  *		uint32		wtmax;
2187e4f93234SChuck Lever  *		uint32		wtpref;
2188e4f93234SChuck Lever  *		uint32		wtmult;
2189e4f93234SChuck Lever  *		uint32		dtpref;
2190e4f93234SChuck Lever  *		size3		maxfilesize;
2191e4f93234SChuck Lever  *		nfstime3	time_delta;
2192e4f93234SChuck Lever  *		uint32		properties;
2193e4f93234SChuck Lever  *	};
2194e4f93234SChuck Lever  *
2195e4f93234SChuck Lever  *	struct FSINFO3resfail {
2196e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2197e4f93234SChuck Lever  *	};
2198e4f93234SChuck Lever  *
2199e4f93234SChuck Lever  *	union FSINFO3res switch (nfsstat3 status) {
2200e4f93234SChuck Lever  *	case NFS3_OK:
2201e4f93234SChuck Lever  *		FSINFO3resok	resok;
2202e4f93234SChuck Lever  *	default:
2203e4f93234SChuck Lever  *		FSINFO3resfail	resfail;
2204e4f93234SChuck Lever  *	};
2205e4f93234SChuck Lever  */
2206e4f93234SChuck Lever static int decode_fsinfo3resok(struct xdr_stream *xdr,
2207e4f93234SChuck Lever 			       struct nfs_fsinfo *result)
2208e4f93234SChuck Lever {
2209e4f93234SChuck Lever 	__be32 *p;
2210e4f93234SChuck Lever 
2211e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4);
2212eb72f484SChuck Lever 	if (unlikely(!p))
2213eb72f484SChuck Lever 		return -EIO;
2214e4f93234SChuck Lever 	result->rtmax  = be32_to_cpup(p++);
2215e4f93234SChuck Lever 	result->rtpref = be32_to_cpup(p++);
2216e4f93234SChuck Lever 	result->rtmult = be32_to_cpup(p++);
2217e4f93234SChuck Lever 	result->wtmax  = be32_to_cpup(p++);
2218e4f93234SChuck Lever 	result->wtpref = be32_to_cpup(p++);
2219e4f93234SChuck Lever 	result->wtmult = be32_to_cpup(p++);
2220e4f93234SChuck Lever 	result->dtpref = be32_to_cpup(p++);
2221e4f93234SChuck Lever 	p = xdr_decode_size3(p, &result->maxfilesize);
2222f6048709SChuck Lever 	xdr_decode_nfstime3(p, &result->time_delta);
2223e4f93234SChuck Lever 
2224e4f93234SChuck Lever 	/* ignore properties */
2225e4f93234SChuck Lever 	result->lease_time = 0;
2226e4f93234SChuck Lever 	return 0;
2227e4f93234SChuck Lever }
2228e4f93234SChuck Lever 
2229bf269551SChuck Lever static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
2230bf269551SChuck Lever 				   struct xdr_stream *xdr,
2231fc016483SChristoph Hellwig 				   void *data)
2232e4f93234SChuck Lever {
2233fc016483SChristoph Hellwig 	struct nfs_fsinfo *result = data;
2234e4f93234SChuck Lever 	enum nfs_stat status;
2235e4f93234SChuck Lever 	int error;
2236e4f93234SChuck Lever 
2237bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2238e4f93234SChuck Lever 	if (unlikely(error))
2239e4f93234SChuck Lever 		goto out;
2240264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
2241e4f93234SChuck Lever 	if (unlikely(error))
2242e4f93234SChuck Lever 		goto out;
2243e4f93234SChuck Lever 	if (status != NFS3_OK)
2244e4f93234SChuck Lever 		goto out_status;
2245bf269551SChuck Lever 	error = decode_fsinfo3resok(xdr, result);
2246e4f93234SChuck Lever out:
2247e4f93234SChuck Lever 	return error;
2248e4f93234SChuck Lever out_status:
22495e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2250e4f93234SChuck Lever }
2251e4f93234SChuck Lever 
2252e4f93234SChuck Lever /*
2253e4f93234SChuck Lever  * 3.3.20  PATHCONF3res
2254e4f93234SChuck Lever  *
2255e4f93234SChuck Lever  *	struct PATHCONF3resok {
2256e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2257e4f93234SChuck Lever  *		uint32		linkmax;
2258e4f93234SChuck Lever  *		uint32		name_max;
2259e4f93234SChuck Lever  *		bool		no_trunc;
2260e4f93234SChuck Lever  *		bool		chown_restricted;
2261e4f93234SChuck Lever  *		bool		case_insensitive;
2262e4f93234SChuck Lever  *		bool		case_preserving;
2263e4f93234SChuck Lever  *	};
2264e4f93234SChuck Lever  *
2265e4f93234SChuck Lever  *	struct PATHCONF3resfail {
2266e4f93234SChuck Lever  *		post_op_attr	obj_attributes;
2267e4f93234SChuck Lever  *	};
2268e4f93234SChuck Lever  *
2269e4f93234SChuck Lever  *	union PATHCONF3res switch (nfsstat3 status) {
2270e4f93234SChuck Lever  *	case NFS3_OK:
2271e4f93234SChuck Lever  *		PATHCONF3resok	resok;
2272e4f93234SChuck Lever  *	default:
2273e4f93234SChuck Lever  *		PATHCONF3resfail resfail;
2274e4f93234SChuck Lever  *	};
2275e4f93234SChuck Lever  */
2276e4f93234SChuck Lever static int decode_pathconf3resok(struct xdr_stream *xdr,
2277e4f93234SChuck Lever 				 struct nfs_pathconf *result)
2278e4f93234SChuck Lever {
2279e4f93234SChuck Lever 	__be32 *p;
2280e4f93234SChuck Lever 
2281e4f93234SChuck Lever 	p = xdr_inline_decode(xdr, 4 * 6);
2282eb72f484SChuck Lever 	if (unlikely(!p))
2283eb72f484SChuck Lever 		return -EIO;
2284e4f93234SChuck Lever 	result->max_link = be32_to_cpup(p++);
2285e4f93234SChuck Lever 	result->max_namelen = be32_to_cpup(p);
2286e4f93234SChuck Lever 	/* ignore remaining fields */
2287e4f93234SChuck Lever 	return 0;
2288e4f93234SChuck Lever }
2289e4f93234SChuck Lever 
2290bf269551SChuck Lever static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
2291bf269551SChuck Lever 				     struct xdr_stream *xdr,
2292fc016483SChristoph Hellwig 				     void *data)
2293e4f93234SChuck Lever {
2294fc016483SChristoph Hellwig 	struct nfs_pathconf *result = data;
2295e4f93234SChuck Lever 	enum nfs_stat status;
2296e4f93234SChuck Lever 	int error;
2297e4f93234SChuck Lever 
2298bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2299e4f93234SChuck Lever 	if (unlikely(error))
2300e4f93234SChuck Lever 		goto out;
2301264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));
2302e4f93234SChuck Lever 	if (unlikely(error))
2303e4f93234SChuck Lever 		goto out;
2304e4f93234SChuck Lever 	if (status != NFS3_OK)
2305e4f93234SChuck Lever 		goto out_status;
2306bf269551SChuck Lever 	error = decode_pathconf3resok(xdr, result);
2307e4f93234SChuck Lever out:
2308e4f93234SChuck Lever 	return error;
2309e4f93234SChuck Lever out_status:
23105e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2311e4f93234SChuck Lever }
2312e4f93234SChuck Lever 
2313e4f93234SChuck Lever /*
2314e4f93234SChuck Lever  * 3.3.21  COMMIT3res
2315e4f93234SChuck Lever  *
2316e4f93234SChuck Lever  *	struct COMMIT3resok {
2317e4f93234SChuck Lever  *		wcc_data	file_wcc;
2318e4f93234SChuck Lever  *		writeverf3	verf;
2319e4f93234SChuck Lever  *	};
2320e4f93234SChuck Lever  *
2321e4f93234SChuck Lever  *	struct COMMIT3resfail {
2322e4f93234SChuck Lever  *		wcc_data	file_wcc;
2323e4f93234SChuck Lever  *	};
2324e4f93234SChuck Lever  *
2325e4f93234SChuck Lever  *	union COMMIT3res switch (nfsstat3 status) {
2326e4f93234SChuck Lever  *	case NFS3_OK:
2327e4f93234SChuck Lever  *		COMMIT3resok	resok;
2328e4f93234SChuck Lever  *	default:
2329e4f93234SChuck Lever  *		COMMIT3resfail	resfail;
2330e4f93234SChuck Lever  *	};
2331e4f93234SChuck Lever  */
2332bf269551SChuck Lever static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
2333bf269551SChuck Lever 				   struct xdr_stream *xdr,
2334fc016483SChristoph Hellwig 				   void *data)
2335e4f93234SChuck Lever {
2336fc016483SChristoph Hellwig 	struct nfs_commitres *result = data;
2337221203ceSTrond Myklebust 	struct nfs_writeverf *verf = result->verf;
2338e4f93234SChuck Lever 	enum nfs_stat status;
2339e4f93234SChuck Lever 	int error;
2340e4f93234SChuck Lever 
2341bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2342e4f93234SChuck Lever 	if (unlikely(error))
2343e4f93234SChuck Lever 		goto out;
2344264d948cSTrond Myklebust 	error = decode_wcc_data(xdr, result->fattr, rpc_rqst_userns(req));
2345e4f93234SChuck Lever 	if (unlikely(error))
2346e4f93234SChuck Lever 		goto out;
2347aabff4ddSPeng Tao 	result->op_status = status;
2348e4f93234SChuck Lever 	if (status != NFS3_OK)
2349e4f93234SChuck Lever 		goto out_status;
2350221203ceSTrond Myklebust 	error = decode_writeverf3(xdr, &verf->verifier);
2351221203ceSTrond Myklebust 	if (!error)
2352221203ceSTrond Myklebust 		verf->committed = NFS_FILE_SYNC;
2353e4f93234SChuck Lever out:
2354e4f93234SChuck Lever 	return error;
2355e4f93234SChuck Lever out_status:
23565e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2357e4f93234SChuck Lever }
2358e4f93234SChuck Lever 
2359b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2360b7fa0554SAndreas Gruenbacher 
2361e4f93234SChuck Lever static inline int decode_getacl3resok(struct xdr_stream *xdr,
2362264d948cSTrond Myklebust 				      struct nfs3_getaclres *result,
2363264d948cSTrond Myklebust 				      struct user_namespace *userns)
2364e4f93234SChuck Lever {
2365e4f93234SChuck Lever 	struct posix_acl **acl;
2366e4f93234SChuck Lever 	unsigned int *aclcnt;
2367e4f93234SChuck Lever 	size_t hdrlen;
2368e4f93234SChuck Lever 	int error;
2369e4f93234SChuck Lever 
2370264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result->fattr, userns);
2371e4f93234SChuck Lever 	if (unlikely(error))
2372e4f93234SChuck Lever 		goto out;
2373e4f93234SChuck Lever 	error = decode_uint32(xdr, &result->mask);
2374e4f93234SChuck Lever 	if (unlikely(error))
2375e4f93234SChuck Lever 		goto out;
2376e4f93234SChuck Lever 	error = -EINVAL;
2377e4f93234SChuck Lever 	if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
2378e4f93234SChuck Lever 		goto out;
2379e4f93234SChuck Lever 
23801aecca3eSTrond Myklebust 	hdrlen = xdr_stream_pos(xdr);
2381e4f93234SChuck Lever 
2382e4f93234SChuck Lever 	acl = NULL;
2383e4f93234SChuck Lever 	if (result->mask & NFS_ACL)
2384e4f93234SChuck Lever 		acl = &result->acl_access;
2385e4f93234SChuck Lever 	aclcnt = NULL;
2386e4f93234SChuck Lever 	if (result->mask & NFS_ACLCNT)
2387e4f93234SChuck Lever 		aclcnt = &result->acl_access_count;
2388e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl);
2389e4f93234SChuck Lever 	if (unlikely(error <= 0))
2390e4f93234SChuck Lever 		goto out;
2391e4f93234SChuck Lever 
2392e4f93234SChuck Lever 	acl = NULL;
2393e4f93234SChuck Lever 	if (result->mask & NFS_DFACL)
2394e4f93234SChuck Lever 		acl = &result->acl_default;
2395e4f93234SChuck Lever 	aclcnt = NULL;
2396e4f93234SChuck Lever 	if (result->mask & NFS_DFACLCNT)
2397e4f93234SChuck Lever 		aclcnt = &result->acl_default_count;
2398e4f93234SChuck Lever 	error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl);
2399e4f93234SChuck Lever 	if (unlikely(error <= 0))
2400e4f93234SChuck Lever 		return error;
2401e4f93234SChuck Lever 	error = 0;
2402e4f93234SChuck Lever out:
2403e4f93234SChuck Lever 	return error;
2404e4f93234SChuck Lever }
2405e4f93234SChuck Lever 
2406bf269551SChuck Lever static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
2407bf269551SChuck Lever 				   struct xdr_stream *xdr,
2408fc016483SChristoph Hellwig 				   void *result)
2409e4f93234SChuck Lever {
2410e4f93234SChuck Lever 	enum nfs_stat status;
2411e4f93234SChuck Lever 	int error;
2412e4f93234SChuck Lever 
2413bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2414e4f93234SChuck Lever 	if (unlikely(error))
2415e4f93234SChuck Lever 		goto out;
2416e4f93234SChuck Lever 	if (status != NFS3_OK)
2417e4f93234SChuck Lever 		goto out_default;
2418264d948cSTrond Myklebust 	error = decode_getacl3resok(xdr, result, rpc_rqst_userns(req));
2419e4f93234SChuck Lever out:
2420e4f93234SChuck Lever 	return error;
2421e4f93234SChuck Lever out_default:
24225e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2423e4f93234SChuck Lever }
2424e4f93234SChuck Lever 
2425bf269551SChuck Lever static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
2426bf269551SChuck Lever 				   struct xdr_stream *xdr,
2427fc016483SChristoph Hellwig 				   void *result)
2428e4f93234SChuck Lever {
2429e4f93234SChuck Lever 	enum nfs_stat status;
2430e4f93234SChuck Lever 	int error;
2431e4f93234SChuck Lever 
2432bf269551SChuck Lever 	error = decode_nfsstat3(xdr, &status);
2433e4f93234SChuck Lever 	if (unlikely(error))
2434e4f93234SChuck Lever 		goto out;
2435e4f93234SChuck Lever 	if (status != NFS3_OK)
2436e4f93234SChuck Lever 		goto out_default;
2437264d948cSTrond Myklebust 	error = decode_post_op_attr(xdr, result, rpc_rqst_userns(req));
2438e4f93234SChuck Lever out:
2439e4f93234SChuck Lever 	return error;
2440e4f93234SChuck Lever out_default:
24415e7e5a0dSBryan Schumaker 	return nfs3_stat_to_errno(status);
2442e4f93234SChuck Lever }
2443e4f93234SChuck Lever 
2444b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2445b7fa0554SAndreas Gruenbacher 
24465e7e5a0dSBryan Schumaker 
24475e7e5a0dSBryan Schumaker /*
24485e7e5a0dSBryan Schumaker  * We need to translate between nfs status return values and
24495e7e5a0dSBryan Schumaker  * the local errno values which may not be the same.
24505e7e5a0dSBryan Schumaker  */
24515e7e5a0dSBryan Schumaker static const struct {
24525e7e5a0dSBryan Schumaker 	int stat;
24535e7e5a0dSBryan Schumaker 	int errno;
24545e7e5a0dSBryan Schumaker } nfs_errtbl[] = {
24555e7e5a0dSBryan Schumaker 	{ NFS_OK,		0		},
24565e7e5a0dSBryan Schumaker 	{ NFSERR_PERM,		-EPERM		},
24575e7e5a0dSBryan Schumaker 	{ NFSERR_NOENT,		-ENOENT		},
24585e7e5a0dSBryan Schumaker 	{ NFSERR_IO,		-errno_NFSERR_IO},
24595e7e5a0dSBryan Schumaker 	{ NFSERR_NXIO,		-ENXIO		},
24605e7e5a0dSBryan Schumaker /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
24615e7e5a0dSBryan Schumaker 	{ NFSERR_ACCES,		-EACCES		},
24625e7e5a0dSBryan Schumaker 	{ NFSERR_EXIST,		-EEXIST		},
24635e7e5a0dSBryan Schumaker 	{ NFSERR_XDEV,		-EXDEV		},
24645e7e5a0dSBryan Schumaker 	{ NFSERR_NODEV,		-ENODEV		},
24655e7e5a0dSBryan Schumaker 	{ NFSERR_NOTDIR,	-ENOTDIR	},
24665e7e5a0dSBryan Schumaker 	{ NFSERR_ISDIR,		-EISDIR		},
24675e7e5a0dSBryan Schumaker 	{ NFSERR_INVAL,		-EINVAL		},
24685e7e5a0dSBryan Schumaker 	{ NFSERR_FBIG,		-EFBIG		},
24695e7e5a0dSBryan Schumaker 	{ NFSERR_NOSPC,		-ENOSPC		},
24705e7e5a0dSBryan Schumaker 	{ NFSERR_ROFS,		-EROFS		},
24715e7e5a0dSBryan Schumaker 	{ NFSERR_MLINK,		-EMLINK		},
24725e7e5a0dSBryan Schumaker 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
24735e7e5a0dSBryan Schumaker 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
24745e7e5a0dSBryan Schumaker 	{ NFSERR_DQUOT,		-EDQUOT		},
24755e7e5a0dSBryan Schumaker 	{ NFSERR_STALE,		-ESTALE		},
24765e7e5a0dSBryan Schumaker 	{ NFSERR_REMOTE,	-EREMOTE	},
24775e7e5a0dSBryan Schumaker #ifdef EWFLUSH
24785e7e5a0dSBryan Schumaker 	{ NFSERR_WFLUSH,	-EWFLUSH	},
24795e7e5a0dSBryan Schumaker #endif
24805e7e5a0dSBryan Schumaker 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
24815e7e5a0dSBryan Schumaker 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
24825e7e5a0dSBryan Schumaker 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
24835e7e5a0dSBryan Schumaker 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
24845e7e5a0dSBryan Schumaker 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
24855e7e5a0dSBryan Schumaker 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
24865e7e5a0dSBryan Schumaker 	{ NFSERR_BADTYPE,	-EBADTYPE	},
24875e7e5a0dSBryan Schumaker 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
24885e7e5a0dSBryan Schumaker 	{ -1,			-EIO		}
24895e7e5a0dSBryan Schumaker };
24905e7e5a0dSBryan Schumaker 
24915e7e5a0dSBryan Schumaker /**
24925e7e5a0dSBryan Schumaker  * nfs3_stat_to_errno - convert an NFS status code to a local errno
24935e7e5a0dSBryan Schumaker  * @status: NFS status code to convert
24945e7e5a0dSBryan Schumaker  *
24955e7e5a0dSBryan Schumaker  * Returns a local errno value, or -EIO if the NFS status code is
24965e7e5a0dSBryan Schumaker  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
24975e7e5a0dSBryan Schumaker  */
24985e7e5a0dSBryan Schumaker static int nfs3_stat_to_errno(enum nfs_stat status)
24995e7e5a0dSBryan Schumaker {
25005e7e5a0dSBryan Schumaker 	int i;
25015e7e5a0dSBryan Schumaker 
25025e7e5a0dSBryan Schumaker 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
25035e7e5a0dSBryan Schumaker 		if (nfs_errtbl[i].stat == (int)status)
25045e7e5a0dSBryan Schumaker 			return nfs_errtbl[i].errno;
25055e7e5a0dSBryan Schumaker 	}
25065e7e5a0dSBryan Schumaker 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
25075e7e5a0dSBryan Schumaker 	return nfs_errtbl[i].errno;
25085e7e5a0dSBryan Schumaker }
25095e7e5a0dSBryan Schumaker 
25105e7e5a0dSBryan Schumaker 
25111da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
25121da177e4SLinus Torvalds [NFS3PROC_##proc] = {							\
25131da177e4SLinus Torvalds 	.p_proc      = NFS3PROC_##proc,					\
2514fcc85819SChristoph Hellwig 	.p_encode    = nfs3_xdr_enc_##argtype##3args,			\
2515fc016483SChristoph Hellwig 	.p_decode    = nfs3_xdr_dec_##restype##3res,			\
2516ad96b5b5SChuck Lever 	.p_arglen    = NFS3_##argtype##args_sz,				\
2517f5fc3c50SChuck Lever 	.p_replen    = NFS3_##restype##res_sz,				\
2518cc0175c1SChuck Lever 	.p_timer     = timer,						\
2519cc0175c1SChuck Lever 	.p_statidx   = NFS3PROC_##proc,					\
2520cc0175c1SChuck Lever 	.p_name      = #proc,						\
25211da177e4SLinus Torvalds 	}
25221da177e4SLinus Torvalds 
2523511e936bSChristoph Hellwig const struct rpc_procinfo nfs3_procedures[] = {
2524f5fc3c50SChuck Lever 	PROC(GETATTR,		getattr,	getattr,	1),
2525f5fc3c50SChuck Lever 	PROC(SETATTR,		setattr,	setattr,	0),
2526f5fc3c50SChuck Lever 	PROC(LOOKUP,		lookup,		lookup,		2),
2527f5fc3c50SChuck Lever 	PROC(ACCESS,		access,		access,		1),
2528f5fc3c50SChuck Lever 	PROC(READLINK,		readlink,	readlink,	3),
2529f5fc3c50SChuck Lever 	PROC(READ,		read,		read,		3),
2530f5fc3c50SChuck Lever 	PROC(WRITE,		write,		write,		4),
2531f5fc3c50SChuck Lever 	PROC(CREATE,		create,		create,		0),
2532f5fc3c50SChuck Lever 	PROC(MKDIR,		mkdir,		create,		0),
2533f5fc3c50SChuck Lever 	PROC(SYMLINK,		symlink,	create,		0),
2534f5fc3c50SChuck Lever 	PROC(MKNOD,		mknod,		create,		0),
2535f5fc3c50SChuck Lever 	PROC(REMOVE,		remove,		remove,		0),
2536f5fc3c50SChuck Lever 	PROC(RMDIR,		lookup,		setattr,	0),
2537f5fc3c50SChuck Lever 	PROC(RENAME,		rename,		rename,		0),
2538f5fc3c50SChuck Lever 	PROC(LINK,		link,		link,		0),
2539f5fc3c50SChuck Lever 	PROC(READDIR,		readdir,	readdir,	3),
2540f5fc3c50SChuck Lever 	PROC(READDIRPLUS,	readdirplus,	readdir,	3),
2541f5fc3c50SChuck Lever 	PROC(FSSTAT,		getattr,	fsstat,		0),
2542f5fc3c50SChuck Lever 	PROC(FSINFO,		getattr,	fsinfo,		0),
2543f5fc3c50SChuck Lever 	PROC(PATHCONF,		getattr,	pathconf,	0),
2544f5fc3c50SChuck Lever 	PROC(COMMIT,		commit,		commit,		5),
25451da177e4SLinus Torvalds };
25461da177e4SLinus Torvalds 
2547c551858aSChristoph Hellwig static unsigned int nfs_version3_counts[ARRAY_SIZE(nfs3_procedures)];
2548a613fa16STrond Myklebust const struct rpc_version nfs_version3 = {
25491da177e4SLinus Torvalds 	.number			= 3,
2550e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs3_procedures),
2551c551858aSChristoph Hellwig 	.procs			= nfs3_procedures,
2552c551858aSChristoph Hellwig 	.counts			= nfs_version3_counts,
25531da177e4SLinus Torvalds };
25541da177e4SLinus Torvalds 
2555b7fa0554SAndreas Gruenbacher #ifdef CONFIG_NFS_V3_ACL
2556511e936bSChristoph Hellwig static const struct rpc_procinfo nfs3_acl_procedures[] = {
2557b7fa0554SAndreas Gruenbacher 	[ACLPROC3_GETACL] = {
2558b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_GETACL,
2559fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_getacl3args,
2560fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_getacl3res,
25612bea90d4SChuck Lever 		.p_arglen = ACL3_getaclargs_sz,
25622bea90d4SChuck Lever 		.p_replen = ACL3_getaclres_sz,
2563b7fa0554SAndreas Gruenbacher 		.p_timer = 1,
2564cc0175c1SChuck Lever 		.p_name = "GETACL",
2565b7fa0554SAndreas Gruenbacher 	},
2566b7fa0554SAndreas Gruenbacher 	[ACLPROC3_SETACL] = {
2567b7fa0554SAndreas Gruenbacher 		.p_proc = ACLPROC3_SETACL,
2568fcc85819SChristoph Hellwig 		.p_encode = nfs3_xdr_enc_setacl3args,
2569fc016483SChristoph Hellwig 		.p_decode = nfs3_xdr_dec_setacl3res,
25702bea90d4SChuck Lever 		.p_arglen = ACL3_setaclargs_sz,
25712bea90d4SChuck Lever 		.p_replen = ACL3_setaclres_sz,
2572b7fa0554SAndreas Gruenbacher 		.p_timer = 0,
2573cc0175c1SChuck Lever 		.p_name = "SETACL",
2574b7fa0554SAndreas Gruenbacher 	},
2575b7fa0554SAndreas Gruenbacher };
2576b7fa0554SAndreas Gruenbacher 
2577c551858aSChristoph Hellwig static unsigned int nfs3_acl_counts[ARRAY_SIZE(nfs3_acl_procedures)];
2578a613fa16STrond Myklebust const struct rpc_version nfsacl_version3 = {
2579b7fa0554SAndreas Gruenbacher 	.number			= 3,
25809ae7d8ffSChristoph Hellwig 	.nrprocs		= ARRAY_SIZE(nfs3_acl_procedures),
2581b7fa0554SAndreas Gruenbacher 	.procs			= nfs3_acl_procedures,
2582c551858aSChristoph Hellwig 	.counts			= nfs3_acl_counts,
2583b7fa0554SAndreas Gruenbacher };
2584b7fa0554SAndreas Gruenbacher #endif  /* CONFIG_NFS_V3_ACL */
2585