xref: /linux/fs/nfsd/nfs4xdr.c (revision 42b16d3ac371a2fac9b6f08fd75f23f34ba3955a)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Server-side XDR for NFSv4
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (c) 2002 The Regents of the University of Michigan.
51da177e4SLinus Torvalds  *  All rights reserved.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Kendrick Smith <kmsmith@umich.edu>
81da177e4SLinus Torvalds  *  Andy Adamson   <andros@umich.edu>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  Redistribution and use in source and binary forms, with or without
111da177e4SLinus Torvalds  *  modification, are permitted provided that the following conditions
121da177e4SLinus Torvalds  *  are met:
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  1. Redistributions of source code must retain the above copyright
151da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer.
161da177e4SLinus Torvalds  *  2. Redistributions in binary form must reproduce the above copyright
171da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer in the
181da177e4SLinus Torvalds  *     documentation and/or other materials provided with the distribution.
191da177e4SLinus Torvalds  *  3. Neither the name of the University nor the names of its
201da177e4SLinus Torvalds  *     contributors may be used to endorse or promote products derived
211da177e4SLinus Torvalds  *     from this software without specific prior written permission.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
241da177e4SLinus Torvalds  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
251da177e4SLinus Torvalds  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
261da177e4SLinus Torvalds  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271da177e4SLinus Torvalds  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
281da177e4SLinus Torvalds  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
291da177e4SLinus Torvalds  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
301da177e4SLinus Torvalds  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
311da177e4SLinus Torvalds  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
321da177e4SLinus Torvalds  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
331da177e4SLinus Torvalds  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
341da177e4SLinus Torvalds  */
351da177e4SLinus Torvalds 
3696bcad50SChristoph Hellwig #include <linux/file.h>
375a0e3ad6STejun Heo #include <linux/slab.h>
381da177e4SLinus Torvalds #include <linux/namei.h>
39341eb184SBoaz Harrosh #include <linux/statfs.h>
400733d213SAndy Adamson #include <linux/utsname.h>
4117456804SBryan Schumaker #include <linux/pagemap.h>
424796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h>
4384e1b21dSOlga Kornievskaia #include <linux/sunrpc/addr.h>
4423e50fe3SFrank van der Linden #include <linux/xattr.h>
4580e591ceSChuck Lever #include <linux/vmalloc.h>
4680e591ceSChuck Lever 
4723e50fe3SFrank van der Linden #include <uapi/linux/xattr.h>
489a74af21SBoaz Harrosh 
492ca72e17SJ. Bruce Fields #include "idmap.h"
502ca72e17SJ. Bruce Fields #include "acl.h"
519a74af21SBoaz Harrosh #include "xdr4.h"
520a3adadeSJ. Bruce Fields #include "vfs.h"
5317456804SBryan Schumaker #include "state.h"
541091006cSJ. Bruce Fields #include "cache.h"
553d733711SStanislav Kinsbursky #include "netns.h"
569cf514ccSChristoph Hellwig #include "pnfs.h"
575c4583b2SJeff Layton #include "filecache.h"
582ca72e17SJ. Bruce Fields 
5908281341SChuck Lever #include "trace.h"
6008281341SChuck Lever 
6118032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
6218032ca0SDavid Quigley #include <linux/security.h>
6318032ca0SDavid Quigley #endif
6418032ca0SDavid Quigley 
6518032ca0SDavid Quigley 
661da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
671da177e4SLinus Torvalds 
685cf23dbbSJ. Bruce Fields const u32 nfsd_suppattrs[3][3] = {
69916d2d84SJ. Bruce Fields 	{NFSD4_SUPPORTED_ATTRS_WORD0,
70916d2d84SJ. Bruce Fields 	 NFSD4_SUPPORTED_ATTRS_WORD1,
71916d2d84SJ. Bruce Fields 	 NFSD4_SUPPORTED_ATTRS_WORD2},
72916d2d84SJ. Bruce Fields 
73916d2d84SJ. Bruce Fields 	{NFSD4_1_SUPPORTED_ATTRS_WORD0,
74916d2d84SJ. Bruce Fields 	 NFSD4_1_SUPPORTED_ATTRS_WORD1,
75916d2d84SJ. Bruce Fields 	 NFSD4_1_SUPPORTED_ATTRS_WORD2},
76916d2d84SJ. Bruce Fields 
77916d2d84SJ. Bruce Fields 	{NFSD4_1_SUPPORTED_ATTRS_WORD0,
78916d2d84SJ. Bruce Fields 	 NFSD4_1_SUPPORTED_ATTRS_WORD1,
79916d2d84SJ. Bruce Fields 	 NFSD4_2_SUPPORTED_ATTRS_WORD2},
80916d2d84SJ. Bruce Fields };
81916d2d84SJ. Bruce Fields 
8242ca0993SJ.Bruce Fields /*
8342ca0993SJ.Bruce Fields  * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
8442ca0993SJ.Bruce Fields  * directory in order to indicate to the client that a filesystem boundary is present
8542ca0993SJ.Bruce Fields  * We use a fixed fsid for a referral
8642ca0993SJ.Bruce Fields  */
8742ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
8842ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL
8942ca0993SJ.Bruce Fields 
90b37ad28bSAl Viro static __be32
check_filename(char * str,int len)91a36b1725SJ. Bruce Fields check_filename(char *str, int len)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	int i;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	if (len == 0)
961da177e4SLinus Torvalds 		return nfserr_inval;
97000dfa18SChuck Lever 	if (len > NFS4_MAXNAMLEN)
98000dfa18SChuck Lever 		return nfserr_nametoolong;
991da177e4SLinus Torvalds 	if (isdotent(str, len))
100a36b1725SJ. Bruce Fields 		return nfserr_badname;
1011da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
1021da177e4SLinus Torvalds 		if (str[i] == '/')
103a36b1725SJ. Bruce Fields 			return nfserr_badname;
1041da177e4SLinus Torvalds 	return 0;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
zero_clientid(clientid_t * clid)10760adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
10860adfc50SAndy Adamson {
10960adfc50SAndy Adamson 	return (clid->cl_boot == 0) && (clid->cl_id == 0);
11060adfc50SAndy Adamson }
11160adfc50SAndy Adamson 
1122d8498dbSChristoph Hellwig /**
113d5e23383SJ. Bruce Fields  * svcxdr_tmpalloc - allocate memory to be freed after compound processing
114ce043ac8SJ. Bruce Fields  * @argp: NFSv4 compound argument structure
115ed992753STrond Myklebust  * @len: length of buffer to allocate
1162d8498dbSChristoph Hellwig  *
117ed992753STrond Myklebust  * Allocates a buffer of size @len to be freed when processing the compound
118ed992753STrond Myklebust  * operation described in @argp finishes.
1192d8498dbSChristoph Hellwig  */
120d5e23383SJ. Bruce Fields static void *
svcxdr_tmpalloc(struct nfsd4_compoundargs * argp,size_t len)121dbc834e5SDan Carpenter svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, size_t len)
1221da177e4SLinus Torvalds {
123d5e23383SJ. Bruce Fields 	struct svcxdr_tmpbuf *tb;
1241da177e4SLinus Torvalds 
125dbc834e5SDan Carpenter 	tb = kmalloc(struct_size(tb, buf, len), GFP_KERNEL);
1261da177e4SLinus Torvalds 	if (!tb)
127d5e23383SJ. Bruce Fields 		return NULL;
1281da177e4SLinus Torvalds 	tb->next = argp->to_free;
1291da177e4SLinus Torvalds 	argp->to_free = tb;
130d5e23383SJ. Bruce Fields 	return tb->buf;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
13329c353b3SJ. Bruce Fields /*
13429c353b3SJ. Bruce Fields  * For xdr strings that need to be passed to other kernel api's
13529c353b3SJ. Bruce Fields  * as null-terminated strings.
13629c353b3SJ. Bruce Fields  *
13729c353b3SJ. Bruce Fields  * Note null-terminating in place usually isn't safe since the
13829c353b3SJ. Bruce Fields  * buffer might end on a page boundary.
13929c353b3SJ. Bruce Fields  */
14029c353b3SJ. Bruce Fields static char *
svcxdr_dupstr(struct nfsd4_compoundargs * argp,void * buf,size_t len)141dbc834e5SDan Carpenter svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, size_t len)
14229c353b3SJ. Bruce Fields {
143dbc834e5SDan Carpenter 	char *p = svcxdr_tmpalloc(argp, size_add(len, 1));
14429c353b3SJ. Bruce Fields 
14529c353b3SJ. Bruce Fields 	if (!p)
14629c353b3SJ. Bruce Fields 		return NULL;
14729c353b3SJ. Bruce Fields 	memcpy(p, buf, len);
14829c353b3SJ. Bruce Fields 	p[len] = '\0';
14929c353b3SJ. Bruce Fields 	return p;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1527b723008SChuck Lever static void *
svcxdr_savemem(struct nfsd4_compoundargs * argp,__be32 * p,size_t len)153dbc834e5SDan Carpenter svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, size_t len)
1547b723008SChuck Lever {
1557b723008SChuck Lever 	__be32 *tmp;
1567b723008SChuck Lever 
1577b723008SChuck Lever 	/*
1587b723008SChuck Lever 	 * The location of the decoded data item is stable,
1597b723008SChuck Lever 	 * so @p is OK to use. This is the common case.
1607b723008SChuck Lever 	 */
1617b723008SChuck Lever 	if (p != argp->xdr->scratch.iov_base)
1627b723008SChuck Lever 		return p;
1637b723008SChuck Lever 
1647b723008SChuck Lever 	tmp = svcxdr_tmpalloc(argp, len);
1657b723008SChuck Lever 	if (!tmp)
1667b723008SChuck Lever 		return NULL;
1677b723008SChuck Lever 	memcpy(tmp, p, len);
1687b723008SChuck Lever 	return tmp;
1697b723008SChuck Lever }
1707b723008SChuck Lever 
1715dcbfabbSChuck Lever /*
1725dcbfabbSChuck Lever  * NFSv4 basic data type decoders
1735dcbfabbSChuck Lever  */
1745dcbfabbSChuck Lever 
1751a994408SChuck Lever /*
1761a994408SChuck Lever  * This helper handles variable-length opaques which belong to protocol
1771a994408SChuck Lever  * elements that this implementation does not support.
1781a994408SChuck Lever  */
1791a994408SChuck Lever static __be32
nfsd4_decode_ignored_string(struct nfsd4_compoundargs * argp,u32 maxlen)1801a994408SChuck Lever nfsd4_decode_ignored_string(struct nfsd4_compoundargs *argp, u32 maxlen)
1811a994408SChuck Lever {
1821a994408SChuck Lever 	u32 len;
1831a994408SChuck Lever 
1841a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &len) < 0)
1851a994408SChuck Lever 		return nfserr_bad_xdr;
1861a994408SChuck Lever 	if (maxlen && len > maxlen)
1871a994408SChuck Lever 		return nfserr_bad_xdr;
1881a994408SChuck Lever 	if (!xdr_inline_decode(argp->xdr, len))
1891a994408SChuck Lever 		return nfserr_bad_xdr;
1901a994408SChuck Lever 
1911a994408SChuck Lever 	return nfs_ok;
1921a994408SChuck Lever }
1931a994408SChuck Lever 
1945dcbfabbSChuck Lever static __be32
nfsd4_decode_opaque(struct nfsd4_compoundargs * argp,struct xdr_netobj * o)1955dcbfabbSChuck Lever nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
1965dcbfabbSChuck Lever {
1975dcbfabbSChuck Lever 	__be32 *p;
1985dcbfabbSChuck Lever 	u32 len;
1995dcbfabbSChuck Lever 
2005dcbfabbSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &len) < 0)
2015dcbfabbSChuck Lever 		return nfserr_bad_xdr;
2025dcbfabbSChuck Lever 	if (len == 0 || len > NFS4_OPAQUE_LIMIT)
2035dcbfabbSChuck Lever 		return nfserr_bad_xdr;
2045dcbfabbSChuck Lever 	p = xdr_inline_decode(argp->xdr, len);
2055dcbfabbSChuck Lever 	if (!p)
2065dcbfabbSChuck Lever 		return nfserr_bad_xdr;
2077b723008SChuck Lever 	o->data = svcxdr_savemem(argp, p, len);
2085dcbfabbSChuck Lever 	if (!o->data)
2095dcbfabbSChuck Lever 		return nfserr_jukebox;
2105dcbfabbSChuck Lever 	o->len = len;
2115dcbfabbSChuck Lever 
2125dcbfabbSChuck Lever 	return nfs_ok;
2135dcbfabbSChuck Lever }
2145dcbfabbSChuck Lever 
2154c94e13eSChristoph Hellwig static __be32
nfsd4_decode_component4(struct nfsd4_compoundargs * argp,char ** namp,u32 * lenp)216000dfa18SChuck Lever nfsd4_decode_component4(struct nfsd4_compoundargs *argp, char **namp, u32 *lenp)
217000dfa18SChuck Lever {
218000dfa18SChuck Lever 	__be32 *p, status;
219000dfa18SChuck Lever 
220000dfa18SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, lenp) < 0)
221000dfa18SChuck Lever 		return nfserr_bad_xdr;
222000dfa18SChuck Lever 	p = xdr_inline_decode(argp->xdr, *lenp);
223000dfa18SChuck Lever 	if (!p)
224000dfa18SChuck Lever 		return nfserr_bad_xdr;
225000dfa18SChuck Lever 	status = check_filename((char *)p, *lenp);
226000dfa18SChuck Lever 	if (status)
227000dfa18SChuck Lever 		return status;
2287b723008SChuck Lever 	*namp = svcxdr_savemem(argp, p, *lenp);
229000dfa18SChuck Lever 	if (!*namp)
230000dfa18SChuck Lever 		return nfserr_jukebox;
231000dfa18SChuck Lever 
232000dfa18SChuck Lever 	return nfs_ok;
233000dfa18SChuck Lever }
234000dfa18SChuck Lever 
235000dfa18SChuck Lever static __be32
nfsd4_decode_nfstime4(struct nfsd4_compoundargs * argp,struct timespec64 * tv)2361c3eff7eSChuck Lever nfsd4_decode_nfstime4(struct nfsd4_compoundargs *argp, struct timespec64 *tv)
2371c3eff7eSChuck Lever {
2381c3eff7eSChuck Lever 	__be32 *p;
2391c3eff7eSChuck Lever 
2401c3eff7eSChuck Lever 	p = xdr_inline_decode(argp->xdr, XDR_UNIT * 3);
2411c3eff7eSChuck Lever 	if (!p)
2421c3eff7eSChuck Lever 		return nfserr_bad_xdr;
2431c3eff7eSChuck Lever 	p = xdr_decode_hyper(p, &tv->tv_sec);
2441c3eff7eSChuck Lever 	tv->tv_nsec = be32_to_cpup(p++);
2451c3eff7eSChuck Lever 	if (tv->tv_nsec >= (u32)1000000000)
2461c3eff7eSChuck Lever 		return nfserr_inval;
2471c3eff7eSChuck Lever 	return nfs_ok;
2481c3eff7eSChuck Lever }
2491c3eff7eSChuck Lever 
2501c3eff7eSChuck Lever static __be32
nfsd4_decode_verifier4(struct nfsd4_compoundargs * argp,nfs4_verifier * verf)251796dd1c6SChuck Lever nfsd4_decode_verifier4(struct nfsd4_compoundargs *argp, nfs4_verifier *verf)
252796dd1c6SChuck Lever {
253796dd1c6SChuck Lever 	__be32 *p;
254796dd1c6SChuck Lever 
255796dd1c6SChuck Lever 	p = xdr_inline_decode(argp->xdr, NFS4_VERIFIER_SIZE);
256796dd1c6SChuck Lever 	if (!p)
257796dd1c6SChuck Lever 		return nfserr_bad_xdr;
258796dd1c6SChuck Lever 	memcpy(verf->data, p, sizeof(verf->data));
259796dd1c6SChuck Lever 	return nfs_ok;
260796dd1c6SChuck Lever }
261796dd1c6SChuck Lever 
262d1c263a0SChuck Lever /**
263d1c263a0SChuck Lever  * nfsd4_decode_bitmap4 - Decode an NFSv4 bitmap4
264d1c263a0SChuck Lever  * @argp: NFSv4 compound argument structure
265d1c263a0SChuck Lever  * @bmval: pointer to an array of u32's to decode into
266d1c263a0SChuck Lever  * @bmlen: size of the @bmval array
267d1c263a0SChuck Lever  *
268d1c263a0SChuck Lever  * The server needs to return nfs_ok rather than nfserr_bad_xdr when
269d1c263a0SChuck Lever  * encountering bitmaps containing bits it does not recognize. This
270d1c263a0SChuck Lever  * includes bits in bitmap words past WORDn, where WORDn is the last
271d1c263a0SChuck Lever  * bitmap WORD the implementation currently supports. Thus we are
272d1c263a0SChuck Lever  * careful here to simply ignore bits in bitmap words that this
273d1c263a0SChuck Lever  * implementation has yet to support explicitly.
274d1c263a0SChuck Lever  *
275d1c263a0SChuck Lever  * Return values:
276d1c263a0SChuck Lever  *   %nfs_ok: @bmval populated successfully
277d1c263a0SChuck Lever  *   %nfserr_bad_xdr: the encoded bitmap was invalid
278d1c263a0SChuck Lever  */
279d1c263a0SChuck Lever static __be32
nfsd4_decode_bitmap4(struct nfsd4_compoundargs * argp,u32 * bmval,u32 bmlen)280d1c263a0SChuck Lever nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
281d1c263a0SChuck Lever {
282cd2e999cSChuck Lever 	ssize_t status;
283d1c263a0SChuck Lever 
284cd2e999cSChuck Lever 	status = xdr_stream_decode_uint32_array(argp->xdr, bmval, bmlen);
285cd2e999cSChuck Lever 	return status == -EBADMSG ? nfserr_bad_xdr : nfs_ok;
286d1c263a0SChuck Lever }
287d1c263a0SChuck Lever 
288b37ad28bSAl Viro static __be32
nfsd4_decode_nfsace4(struct nfsd4_compoundargs * argp,struct nfs4_ace * ace)289c941a968SChuck Lever nfsd4_decode_nfsace4(struct nfsd4_compoundargs *argp, struct nfs4_ace *ace)
290c941a968SChuck Lever {
291c941a968SChuck Lever 	__be32 *p, status;
292c941a968SChuck Lever 	u32 length;
293c941a968SChuck Lever 
294c941a968SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &ace->type) < 0)
295c941a968SChuck Lever 		return nfserr_bad_xdr;
296c941a968SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &ace->flag) < 0)
297c941a968SChuck Lever 		return nfserr_bad_xdr;
298c941a968SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &ace->access_mask) < 0)
299c941a968SChuck Lever 		return nfserr_bad_xdr;
300c941a968SChuck Lever 
301c941a968SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
302c941a968SChuck Lever 		return nfserr_bad_xdr;
303c941a968SChuck Lever 	p = xdr_inline_decode(argp->xdr, length);
304c941a968SChuck Lever 	if (!p)
305c941a968SChuck Lever 		return nfserr_bad_xdr;
306c941a968SChuck Lever 	ace->whotype = nfs4_acl_get_whotype((char *)p, length);
307c941a968SChuck Lever 	if (ace->whotype != NFS4_ACL_WHO_NAMED)
308c941a968SChuck Lever 		status = nfs_ok;
309c941a968SChuck Lever 	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
310c941a968SChuck Lever 		status = nfsd_map_name_to_gid(argp->rqstp,
311c941a968SChuck Lever 				(char *)p, length, &ace->who_gid);
312c941a968SChuck Lever 	else
313c941a968SChuck Lever 		status = nfsd_map_name_to_uid(argp->rqstp,
314c941a968SChuck Lever 				(char *)p, length, &ace->who_uid);
315c941a968SChuck Lever 
316c941a968SChuck Lever 	return status;
317c941a968SChuck Lever }
318c941a968SChuck Lever 
319c941a968SChuck Lever /* A counted array of nfsace4's */
320c941a968SChuck Lever static noinline __be32
nfsd4_decode_acl(struct nfsd4_compoundargs * argp,struct nfs4_acl ** acl)321c941a968SChuck Lever nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl)
322c941a968SChuck Lever {
323c941a968SChuck Lever 	struct nfs4_ace *ace;
324c941a968SChuck Lever 	__be32 status;
325c941a968SChuck Lever 	u32 count;
326c941a968SChuck Lever 
327c941a968SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
328c941a968SChuck Lever 		return nfserr_bad_xdr;
329c941a968SChuck Lever 
330c941a968SChuck Lever 	if (count > xdr_stream_remaining(argp->xdr) / 20)
331c941a968SChuck Lever 		/*
332c941a968SChuck Lever 		 * Even with 4-byte names there wouldn't be
333c941a968SChuck Lever 		 * space for that many aces; something fishy is
334c941a968SChuck Lever 		 * going on:
335c941a968SChuck Lever 		 */
336c941a968SChuck Lever 		return nfserr_fbig;
337c941a968SChuck Lever 
338c941a968SChuck Lever 	*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(count));
339c941a968SChuck Lever 	if (*acl == NULL)
340c941a968SChuck Lever 		return nfserr_jukebox;
341c941a968SChuck Lever 
342c941a968SChuck Lever 	(*acl)->naces = count;
343c941a968SChuck Lever 	for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) {
344c941a968SChuck Lever 		status = nfsd4_decode_nfsace4(argp, ace);
345c941a968SChuck Lever 		if (status)
346c941a968SChuck Lever 			return status;
347c941a968SChuck Lever 	}
348c941a968SChuck Lever 
349c941a968SChuck Lever 	return nfs_ok;
350c941a968SChuck Lever }
351c941a968SChuck Lever 
352dabe9182SChuck Lever static noinline __be32
nfsd4_decode_security_label(struct nfsd4_compoundargs * argp,struct xdr_netobj * label)353dabe9182SChuck Lever nfsd4_decode_security_label(struct nfsd4_compoundargs *argp,
354dabe9182SChuck Lever 			    struct xdr_netobj *label)
355dabe9182SChuck Lever {
356dabe9182SChuck Lever 	u32 lfs, pi, length;
357dabe9182SChuck Lever 	__be32 *p;
358dabe9182SChuck Lever 
359dabe9182SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lfs) < 0)
360dabe9182SChuck Lever 		return nfserr_bad_xdr;
361dabe9182SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &pi) < 0)
362dabe9182SChuck Lever 		return nfserr_bad_xdr;
363dabe9182SChuck Lever 
364dabe9182SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
365dabe9182SChuck Lever 		return nfserr_bad_xdr;
366dabe9182SChuck Lever 	if (length > NFS4_MAXLABELLEN)
367dabe9182SChuck Lever 		return nfserr_badlabel;
368dabe9182SChuck Lever 	p = xdr_inline_decode(argp->xdr, length);
369dabe9182SChuck Lever 	if (!p)
370dabe9182SChuck Lever 		return nfserr_bad_xdr;
371dabe9182SChuck Lever 	label->len = length;
372dabe9182SChuck Lever 	label->data = svcxdr_dupstr(argp, p, length);
373dabe9182SChuck Lever 	if (!label->data)
374dabe9182SChuck Lever 		return nfserr_jukebox;
375dabe9182SChuck Lever 
376dabe9182SChuck Lever 	return nfs_ok;
377dabe9182SChuck Lever }
378dabe9182SChuck Lever 
379c941a968SChuck Lever static __be32
nfsd4_decode_fattr4(struct nfsd4_compoundargs * argp,u32 * bmval,u32 bmlen,struct iattr * iattr,struct nfs4_acl ** acl,struct xdr_netobj * label,int * umask)380d1c263a0SChuck Lever nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
38118032ca0SDavid Quigley 		    struct iattr *iattr, struct nfs4_acl **acl,
38247057abdSAndreas Gruenbacher 		    struct xdr_netobj *label, int *umask)
3831da177e4SLinus Torvalds {
384081d53feSChuck Lever 	unsigned int starting_pos;
385081d53feSChuck Lever 	u32 attrlist4_count;
386d1c263a0SChuck Lever 	__be32 *p, status;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	iattr->ia_valid = 0;
389d1c263a0SChuck Lever 	status = nfsd4_decode_bitmap4(argp, bmval, bmlen);
390d1c263a0SChuck Lever 	if (status)
391d1c263a0SChuck Lever 		return nfserr_bad_xdr;
3921da177e4SLinus Torvalds 
393e864c189SJ. Bruce Fields 	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
394e864c189SJ. Bruce Fields 	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
395e864c189SJ. Bruce Fields 	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) {
396e864c189SJ. Bruce Fields 		if (nfsd_attrs_supported(argp->minorversion, bmval))
397e864c189SJ. Bruce Fields 			return nfserr_inval;
398e864c189SJ. Bruce Fields 		return nfserr_attrnotsupp;
399e864c189SJ. Bruce Fields 	}
400e864c189SJ. Bruce Fields 
401081d53feSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &attrlist4_count) < 0)
402081d53feSChuck Lever 		return nfserr_bad_xdr;
403081d53feSChuck Lever 	starting_pos = xdr_stream_pos(argp->xdr);
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
4062ac1b9b2SChuck Lever 		u64 size;
4072ac1b9b2SChuck Lever 
4082ac1b9b2SChuck Lever 		if (xdr_stream_decode_u64(argp->xdr, &size) < 0)
4092ac1b9b2SChuck Lever 			return nfserr_bad_xdr;
4102ac1b9b2SChuck Lever 		iattr->ia_size = size;
4111da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
4121da177e4SLinus Torvalds 	}
4131da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
414c941a968SChuck Lever 		status = nfsd4_decode_acl(argp, acl);
4153c726023SJ. Bruce Fields 		if (status)
4163c726023SJ. Bruce Fields 			return status;
4171da177e4SLinus Torvalds 	} else
4181da177e4SLinus Torvalds 		*acl = NULL;
4191da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
4201c8f0ad7SChuck Lever 		u32 mode;
4211c8f0ad7SChuck Lever 
4221c8f0ad7SChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &mode) < 0)
4231c8f0ad7SChuck Lever 			return nfserr_bad_xdr;
4241c8f0ad7SChuck Lever 		iattr->ia_mode = mode;
4251da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
4261da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
4271da177e4SLinus Torvalds 	}
4281da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
4299853a5acSChuck Lever 		u32 length;
4309853a5acSChuck Lever 
4319853a5acSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
4329853a5acSChuck Lever 			return nfserr_bad_xdr;
4339853a5acSChuck Lever 		p = xdr_inline_decode(argp->xdr, length);
4349853a5acSChuck Lever 		if (!p)
4359853a5acSChuck Lever 			return nfserr_bad_xdr;
4369853a5acSChuck Lever 		status = nfsd_map_name_to_uid(argp->rqstp, (char *)p, length,
4379853a5acSChuck Lever 					      &iattr->ia_uid);
4389853a5acSChuck Lever 		if (status)
43947c85291SNeilBrown 			return status;
4401da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
4411da177e4SLinus Torvalds 	}
4421da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
443393c31ddSChuck Lever 		u32 length;
444393c31ddSChuck Lever 
445393c31ddSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
446393c31ddSChuck Lever 			return nfserr_bad_xdr;
447393c31ddSChuck Lever 		p = xdr_inline_decode(argp->xdr, length);
448393c31ddSChuck Lever 		if (!p)
449393c31ddSChuck Lever 			return nfserr_bad_xdr;
450393c31ddSChuck Lever 		status = nfsd_map_name_to_gid(argp->rqstp, (char *)p, length,
451393c31ddSChuck Lever 					      &iattr->ia_gid);
452393c31ddSChuck Lever 		if (status)
45347c85291SNeilBrown 			return status;
4541da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
4551da177e4SLinus Torvalds 	}
4561da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
4571c3eff7eSChuck Lever 		u32 set_it;
4581c3eff7eSChuck Lever 
4591c3eff7eSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0)
4601c3eff7eSChuck Lever 			return nfserr_bad_xdr;
4611c3eff7eSChuck Lever 		switch (set_it) {
4621da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
4631c3eff7eSChuck Lever 			status = nfsd4_decode_nfstime4(argp, &iattr->ia_atime);
4644c94e13eSChristoph Hellwig 			if (status)
4654c94e13eSChristoph Hellwig 				return status;
4661da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
4671da177e4SLinus Torvalds 			break;
4681da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
4691da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
4701da177e4SLinus Torvalds 			break;
4711da177e4SLinus Torvalds 		default:
4721c3eff7eSChuck Lever 			return nfserr_bad_xdr;
4731da177e4SLinus Torvalds 		}
4741da177e4SLinus Torvalds 	}
4755b2f3e07SChuck Lever 	if (bmval[1] & FATTR4_WORD1_TIME_CREATE) {
4765b2f3e07SChuck Lever 		struct timespec64 ts;
4775b2f3e07SChuck Lever 
4785b2f3e07SChuck Lever 		/* No Linux filesystem supports setting this attribute. */
4795b2f3e07SChuck Lever 		bmval[1] &= ~FATTR4_WORD1_TIME_CREATE;
4805b2f3e07SChuck Lever 		status = nfsd4_decode_nfstime4(argp, &ts);
4815b2f3e07SChuck Lever 		if (status)
4825b2f3e07SChuck Lever 			return status;
4835b2f3e07SChuck Lever 	}
4841da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
4851c3eff7eSChuck Lever 		u32 set_it;
4861c3eff7eSChuck Lever 
4871c3eff7eSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0)
4881c3eff7eSChuck Lever 			return nfserr_bad_xdr;
4891c3eff7eSChuck Lever 		switch (set_it) {
4901da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
4911c3eff7eSChuck Lever 			status = nfsd4_decode_nfstime4(argp, &iattr->ia_mtime);
4924c94e13eSChristoph Hellwig 			if (status)
4934c94e13eSChristoph Hellwig 				return status;
4941da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
4951da177e4SLinus Torvalds 			break;
4961da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
4971da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
4981da177e4SLinus Torvalds 			break;
4991da177e4SLinus Torvalds 		default:
5001c3eff7eSChuck Lever 			return nfserr_bad_xdr;
5011da177e4SLinus Torvalds 		}
5021da177e4SLinus Torvalds 	}
50318032ca0SDavid Quigley 	label->len = 0;
5042285ae76SArnd Bergmann 	if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) &&
5052285ae76SArnd Bergmann 	    bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
506dabe9182SChuck Lever 		status = nfsd4_decode_security_label(argp, label);
507dabe9182SChuck Lever 		if (status)
508dabe9182SChuck Lever 			return status;
50918032ca0SDavid Quigley 	}
51047057abdSAndreas Gruenbacher 	if (bmval[2] & FATTR4_WORD2_MODE_UMASK) {
51166f0476cSChuck Lever 		u32 mode, mask;
51266f0476cSChuck Lever 
51347057abdSAndreas Gruenbacher 		if (!umask)
51466f0476cSChuck Lever 			return nfserr_bad_xdr;
51566f0476cSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &mode) < 0)
51666f0476cSChuck Lever 			return nfserr_bad_xdr;
51766f0476cSChuck Lever 		iattr->ia_mode = mode & (S_IFMT | S_IALLUGO);
51866f0476cSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &mask) < 0)
51966f0476cSChuck Lever 			return nfserr_bad_xdr;
52066f0476cSChuck Lever 		*umask = mask & S_IRWXUGO;
52147057abdSAndreas Gruenbacher 		iattr->ia_valid |= ATTR_MODE;
52247057abdSAndreas Gruenbacher 	}
523081d53feSChuck Lever 
524081d53feSChuck Lever 	/* request sanity: did attrlist4 contain the expected number of words? */
525081d53feSChuck Lever 	if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
526081d53feSChuck Lever 		return nfserr_bad_xdr;
5271da177e4SLinus Torvalds 
528d1c263a0SChuck Lever 	return nfs_ok;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds 
531b37ad28bSAl Viro static __be32
nfsd4_decode_stateid4(struct nfsd4_compoundargs * argp,stateid_t * sid)532d3d2f381SChuck Lever nfsd4_decode_stateid4(struct nfsd4_compoundargs *argp, stateid_t *sid)
533d3d2f381SChuck Lever {
534d3d2f381SChuck Lever 	__be32 *p;
535d3d2f381SChuck Lever 
536d3d2f381SChuck Lever 	p = xdr_inline_decode(argp->xdr, NFS4_STATEID_SIZE);
537d3d2f381SChuck Lever 	if (!p)
538d3d2f381SChuck Lever 		return nfserr_bad_xdr;
539d3d2f381SChuck Lever 	sid->si_generation = be32_to_cpup(p++);
540d3d2f381SChuck Lever 	memcpy(&sid->si_opaque, p, sizeof(sid->si_opaque));
541d3d2f381SChuck Lever 	return nfs_ok;
542d3d2f381SChuck Lever }
543d3d2f381SChuck Lever 
544144e8269SChuck Lever static __be32
nfsd4_decode_clientid4(struct nfsd4_compoundargs * argp,clientid_t * clientid)545144e8269SChuck Lever nfsd4_decode_clientid4(struct nfsd4_compoundargs *argp, clientid_t *clientid)
546144e8269SChuck Lever {
547144e8269SChuck Lever 	__be32 *p;
548144e8269SChuck Lever 
549144e8269SChuck Lever 	p = xdr_inline_decode(argp->xdr, sizeof(__be64));
550144e8269SChuck Lever 	if (!p)
551144e8269SChuck Lever 		return nfserr_bad_xdr;
552144e8269SChuck Lever 	memcpy(clientid, p, sizeof(*clientid));
553144e8269SChuck Lever 	return nfs_ok;
554144e8269SChuck Lever }
555144e8269SChuck Lever 
556144e8269SChuck Lever static __be32
nfsd4_decode_state_owner4(struct nfsd4_compoundargs * argp,clientid_t * clientid,struct xdr_netobj * owner)557144e8269SChuck Lever nfsd4_decode_state_owner4(struct nfsd4_compoundargs *argp,
558144e8269SChuck Lever 			  clientid_t *clientid, struct xdr_netobj *owner)
559144e8269SChuck Lever {
560144e8269SChuck Lever 	__be32 status;
561144e8269SChuck Lever 
562144e8269SChuck Lever 	status = nfsd4_decode_clientid4(argp, clientid);
563144e8269SChuck Lever 	if (status)
564144e8269SChuck Lever 		return status;
565144e8269SChuck Lever 	return nfsd4_decode_opaque(argp, owner);
566144e8269SChuck Lever }
567144e8269SChuck Lever 
56804495971SChuck Lever #ifdef CONFIG_NFSD_PNFS
56904495971SChuck Lever static __be32
nfsd4_decode_deviceid4(struct nfsd4_compoundargs * argp,struct nfsd4_deviceid * devid)57004495971SChuck Lever nfsd4_decode_deviceid4(struct nfsd4_compoundargs *argp,
57104495971SChuck Lever 		       struct nfsd4_deviceid *devid)
57204495971SChuck Lever {
57304495971SChuck Lever 	__be32 *p;
57404495971SChuck Lever 
57504495971SChuck Lever 	p = xdr_inline_decode(argp->xdr, NFS4_DEVICEID4_SIZE);
57604495971SChuck Lever 	if (!p)
57704495971SChuck Lever 		return nfserr_bad_xdr;
57804495971SChuck Lever 	memcpy(devid, p, sizeof(*devid));
57904495971SChuck Lever 	return nfs_ok;
58004495971SChuck Lever }
5815185980dSChuck Lever 
5825185980dSChuck Lever static __be32
nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs * argp,struct nfsd4_layoutcommit * lcp)5835185980dSChuck Lever nfsd4_decode_layoutupdate4(struct nfsd4_compoundargs *argp,
5845185980dSChuck Lever 			   struct nfsd4_layoutcommit *lcp)
5855185980dSChuck Lever {
5865185980dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_layout_type) < 0)
5875185980dSChuck Lever 		return nfserr_bad_xdr;
5885185980dSChuck Lever 	if (lcp->lc_layout_type < LAYOUT_NFSV4_1_FILES)
5895185980dSChuck Lever 		return nfserr_bad_xdr;
5905185980dSChuck Lever 	if (lcp->lc_layout_type >= LAYOUT_TYPE_MAX)
5915185980dSChuck Lever 		return nfserr_bad_xdr;
5925185980dSChuck Lever 
5935185980dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_up_len) < 0)
5945185980dSChuck Lever 		return nfserr_bad_xdr;
5955185980dSChuck Lever 	if (lcp->lc_up_len > 0) {
5965185980dSChuck Lever 		lcp->lc_up_layout = xdr_inline_decode(argp->xdr, lcp->lc_up_len);
5975185980dSChuck Lever 		if (!lcp->lc_up_layout)
5985185980dSChuck Lever 			return nfserr_bad_xdr;
5995185980dSChuck Lever 	}
6005185980dSChuck Lever 
6015185980dSChuck Lever 	return nfs_ok;
6025185980dSChuck Lever }
6035185980dSChuck Lever 
604645fcad3SChuck Lever static __be32
nfsd4_decode_layoutreturn4(struct nfsd4_compoundargs * argp,struct nfsd4_layoutreturn * lrp)605645fcad3SChuck Lever nfsd4_decode_layoutreturn4(struct nfsd4_compoundargs *argp,
606645fcad3SChuck Lever 			   struct nfsd4_layoutreturn *lrp)
607645fcad3SChuck Lever {
608645fcad3SChuck Lever 	__be32 status;
609645fcad3SChuck Lever 
610645fcad3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_return_type) < 0)
611645fcad3SChuck Lever 		return nfserr_bad_xdr;
612645fcad3SChuck Lever 	switch (lrp->lr_return_type) {
613645fcad3SChuck Lever 	case RETURN_FILE:
614645fcad3SChuck Lever 		if (xdr_stream_decode_u64(argp->xdr, &lrp->lr_seg.offset) < 0)
615645fcad3SChuck Lever 			return nfserr_bad_xdr;
616645fcad3SChuck Lever 		if (xdr_stream_decode_u64(argp->xdr, &lrp->lr_seg.length) < 0)
617645fcad3SChuck Lever 			return nfserr_bad_xdr;
618645fcad3SChuck Lever 		status = nfsd4_decode_stateid4(argp, &lrp->lr_sid);
619645fcad3SChuck Lever 		if (status)
620645fcad3SChuck Lever 			return status;
621645fcad3SChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &lrp->lrf_body_len) < 0)
622645fcad3SChuck Lever 			return nfserr_bad_xdr;
623645fcad3SChuck Lever 		if (lrp->lrf_body_len > 0) {
624645fcad3SChuck Lever 			lrp->lrf_body = xdr_inline_decode(argp->xdr, lrp->lrf_body_len);
625645fcad3SChuck Lever 			if (!lrp->lrf_body)
626645fcad3SChuck Lever 				return nfserr_bad_xdr;
627645fcad3SChuck Lever 		}
628645fcad3SChuck Lever 		break;
629645fcad3SChuck Lever 	case RETURN_FSID:
630645fcad3SChuck Lever 	case RETURN_ALL:
631645fcad3SChuck Lever 		lrp->lr_seg.offset = 0;
632645fcad3SChuck Lever 		lrp->lr_seg.length = NFS4_MAX_UINT64;
633645fcad3SChuck Lever 		break;
634645fcad3SChuck Lever 	default:
635645fcad3SChuck Lever 		return nfserr_bad_xdr;
636645fcad3SChuck Lever 	}
637645fcad3SChuck Lever 
638645fcad3SChuck Lever 	return nfs_ok;
639645fcad3SChuck Lever }
640645fcad3SChuck Lever 
64104495971SChuck Lever #endif /* CONFIG_NFSD_PNFS */
64204495971SChuck Lever 
643571e0451SChuck Lever static __be32
nfsd4_decode_sessionid4(struct nfsd4_compoundargs * argp,struct nfs4_sessionid * sessionid)644571e0451SChuck Lever nfsd4_decode_sessionid4(struct nfsd4_compoundargs *argp,
645571e0451SChuck Lever 			struct nfs4_sessionid *sessionid)
646571e0451SChuck Lever {
647571e0451SChuck Lever 	__be32 *p;
648571e0451SChuck Lever 
649571e0451SChuck Lever 	p = xdr_inline_decode(argp->xdr, NFS4_MAX_SESSIONID_LEN);
650571e0451SChuck Lever 	if (!p)
651571e0451SChuck Lever 		return nfserr_bad_xdr;
652571e0451SChuck Lever 	memcpy(sessionid->data, p, sizeof(sessionid->data));
653571e0451SChuck Lever 	return nfs_ok;
654571e0451SChuck Lever }
655571e0451SChuck Lever 
6561a994408SChuck Lever /* Defined in Appendix A of RFC 5531 */
6571a994408SChuck Lever static __be32
nfsd4_decode_authsys_parms(struct nfsd4_compoundargs * argp,struct nfsd4_cb_sec * cbs)6581a994408SChuck Lever nfsd4_decode_authsys_parms(struct nfsd4_compoundargs *argp,
6591a994408SChuck Lever 			   struct nfsd4_cb_sec *cbs)
660acb2887eSJ. Bruce Fields {
6611a994408SChuck Lever 	u32 stamp, gidcount, uid, gid;
6621a994408SChuck Lever 	__be32 *p, status;
663acb2887eSJ. Bruce Fields 
6641a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &stamp) < 0)
6651a994408SChuck Lever 		return nfserr_bad_xdr;
666acb2887eSJ. Bruce Fields 	/* machine name */
6671a994408SChuck Lever 	status = nfsd4_decode_ignored_string(argp, 255);
6681a994408SChuck Lever 	if (status)
6691a994408SChuck Lever 		return status;
6701a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &uid) < 0)
6711a994408SChuck Lever 		return nfserr_bad_xdr;
6721a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &gid) < 0)
6731a994408SChuck Lever 		return nfserr_bad_xdr;
6741a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &gidcount) < 0)
6751a994408SChuck Lever 		return nfserr_bad_xdr;
6761a994408SChuck Lever 	if (gidcount > 16)
6771a994408SChuck Lever 		return nfserr_bad_xdr;
6781a994408SChuck Lever 	p = xdr_inline_decode(argp->xdr, gidcount << 2);
6791a994408SChuck Lever 	if (!p)
6801a994408SChuck Lever 		return nfserr_bad_xdr;
68112fc3e92SJ. Bruce Fields 	if (cbs->flavor == (u32)(-1)) {
6821a994408SChuck Lever 		struct user_namespace *userns = nfsd_user_namespace(argp->rqstp);
6831a994408SChuck Lever 
684e45d1a18STrond Myklebust 		kuid_t kuid = make_kuid(userns, uid);
685e45d1a18STrond Myklebust 		kgid_t kgid = make_kgid(userns, gid);
68603bc6d1cSEric W. Biederman 		if (uid_valid(kuid) && gid_valid(kgid)) {
68703bc6d1cSEric W. Biederman 			cbs->uid = kuid;
68803bc6d1cSEric W. Biederman 			cbs->gid = kgid;
68912fc3e92SJ. Bruce Fields 			cbs->flavor = RPC_AUTH_UNIX;
69003bc6d1cSEric W. Biederman 		} else {
6911a994408SChuck Lever 			dprintk("RPC_AUTH_UNIX with invalid uid or gid, ignoring!\n");
69203bc6d1cSEric W. Biederman 		}
69312fc3e92SJ. Bruce Fields 	}
6941a994408SChuck Lever 
6951a994408SChuck Lever 	return nfs_ok;
6961a994408SChuck Lever }
6971a994408SChuck Lever 
6981a994408SChuck Lever static __be32
nfsd4_decode_gss_cb_handles4(struct nfsd4_compoundargs * argp,struct nfsd4_cb_sec * cbs)6991a994408SChuck Lever nfsd4_decode_gss_cb_handles4(struct nfsd4_compoundargs *argp,
7001a994408SChuck Lever 			     struct nfsd4_cb_sec *cbs)
7011a994408SChuck Lever {
7021a994408SChuck Lever 	__be32 status;
7031a994408SChuck Lever 	u32 service;
7041a994408SChuck Lever 
7051a994408SChuck Lever 	dprintk("RPC_AUTH_GSS callback secflavor not supported!\n");
7061a994408SChuck Lever 
7071a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &service) < 0)
7081a994408SChuck Lever 		return nfserr_bad_xdr;
7091a994408SChuck Lever 	if (service < RPC_GSS_SVC_NONE || service > RPC_GSS_SVC_PRIVACY)
7101a994408SChuck Lever 		return nfserr_bad_xdr;
7111a994408SChuck Lever 	/* gcbp_handle_from_server */
7121a994408SChuck Lever 	status = nfsd4_decode_ignored_string(argp, 0);
7131a994408SChuck Lever 	if (status)
7141a994408SChuck Lever 		return status;
7151a994408SChuck Lever 	/* gcbp_handle_from_client */
7161a994408SChuck Lever 	status = nfsd4_decode_ignored_string(argp, 0);
7171a994408SChuck Lever 	if (status)
7181a994408SChuck Lever 		return status;
7191a994408SChuck Lever 
7201a994408SChuck Lever 	return nfs_ok;
7211a994408SChuck Lever }
7221a994408SChuck Lever 
7231a994408SChuck Lever /* a counted array of callback_sec_parms4 items */
7241a994408SChuck Lever static __be32
nfsd4_decode_cb_sec(struct nfsd4_compoundargs * argp,struct nfsd4_cb_sec * cbs)7251a994408SChuck Lever nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
7261a994408SChuck Lever {
7271a994408SChuck Lever 	u32 i, secflavor, nr_secflavs;
7281a994408SChuck Lever 	__be32 status;
7291a994408SChuck Lever 
7301a994408SChuck Lever 	/* callback_sec_params4 */
7311a994408SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &nr_secflavs) < 0)
7321a994408SChuck Lever 		return nfserr_bad_xdr;
7331a994408SChuck Lever 	if (nr_secflavs)
7341a994408SChuck Lever 		cbs->flavor = (u32)(-1);
7351a994408SChuck Lever 	else
7361a994408SChuck Lever 		/* Is this legal? Be generous, take it to mean AUTH_NONE: */
7371a994408SChuck Lever 		cbs->flavor = 0;
7381a994408SChuck Lever 
7391a994408SChuck Lever 	for (i = 0; i < nr_secflavs; ++i) {
7401a994408SChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &secflavor) < 0)
7411a994408SChuck Lever 			return nfserr_bad_xdr;
7421a994408SChuck Lever 		switch (secflavor) {
7431a994408SChuck Lever 		case RPC_AUTH_NULL:
7441a994408SChuck Lever 			/* void */
7451a994408SChuck Lever 			if (cbs->flavor == (u32)(-1))
7461a994408SChuck Lever 				cbs->flavor = RPC_AUTH_NULL;
7471a994408SChuck Lever 			break;
7481a994408SChuck Lever 		case RPC_AUTH_UNIX:
7491a994408SChuck Lever 			status = nfsd4_decode_authsys_parms(argp, cbs);
7501a994408SChuck Lever 			if (status)
7511a994408SChuck Lever 				return status;
752acb2887eSJ. Bruce Fields 			break;
753acb2887eSJ. Bruce Fields 		case RPC_AUTH_GSS:
7541a994408SChuck Lever 			status = nfsd4_decode_gss_cb_handles4(argp, cbs);
7551a994408SChuck Lever 			if (status)
7561a994408SChuck Lever 				return status;
757acb2887eSJ. Bruce Fields 			break;
758acb2887eSJ. Bruce Fields 		default:
759acb2887eSJ. Bruce Fields 			return nfserr_inval;
760acb2887eSJ. Bruce Fields 		}
761acb2887eSJ. Bruce Fields 	}
7621a994408SChuck Lever 
7631a994408SChuck Lever 	return nfs_ok;
764acb2887eSJ. Bruce Fields }
765acb2887eSJ. Bruce Fields 
7661a994408SChuck Lever 
767d169a6a9SChuck Lever /*
768d169a6a9SChuck Lever  * NFSv4 operation argument decoders
769d169a6a9SChuck Lever  */
770d169a6a9SChuck Lever 
771d169a6a9SChuck Lever static __be32
nfsd4_decode_access(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)772d169a6a9SChuck Lever nfsd4_decode_access(struct nfsd4_compoundargs *argp,
773e78e274eSKees Cook 		    union nfsd4_op_u *u)
774d169a6a9SChuck Lever {
775e78e274eSKees Cook 	struct nfsd4_access *access = &u->access;
776d169a6a9SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &access->ac_req_access) < 0)
777d169a6a9SChuck Lever 		return nfserr_bad_xdr;
778d169a6a9SChuck Lever 	return nfs_ok;
779d169a6a9SChuck Lever }
780d169a6a9SChuck Lever 
781b37ad28bSAl Viro static __be32
nfsd4_decode_close(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)782e78e274eSKees Cook nfsd4_decode_close(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
7831da177e4SLinus Torvalds {
784e78e274eSKees Cook 	struct nfsd4_close *close = &u->close;
785d3d2f381SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &close->cl_seqid) < 0)
786d3d2f381SChuck Lever 		return nfserr_bad_xdr;
787d3d2f381SChuck Lever 	return nfsd4_decode_stateid4(argp, &close->cl_stateid);
7881da177e4SLinus Torvalds }
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 
791b37ad28bSAl Viro static __be32
nfsd4_decode_commit(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)792e78e274eSKees Cook nfsd4_decode_commit(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
7931da177e4SLinus Torvalds {
794e78e274eSKees Cook 	struct nfsd4_commit *commit = &u->commit;
795cbd9abb3SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &commit->co_offset) < 0)
796cbd9abb3SChuck Lever 		return nfserr_bad_xdr;
797cbd9abb3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &commit->co_count) < 0)
798cbd9abb3SChuck Lever 		return nfserr_bad_xdr;
7993fdc5464SChuck Lever 	memset(&commit->co_verf, 0, sizeof(commit->co_verf));
800cbd9abb3SChuck Lever 	return nfs_ok;
8011da177e4SLinus Torvalds }
8021da177e4SLinus Torvalds 
803b37ad28bSAl Viro static __be32
nfsd4_decode_create(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)804e78e274eSKees Cook nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8051da177e4SLinus Torvalds {
806e78e274eSKees Cook 	struct nfsd4_create *create = &u->create;
807000dfa18SChuck Lever 	__be32 *p, status;
8081da177e4SLinus Torvalds 
8093fdc5464SChuck Lever 	memset(create, 0, sizeof(*create));
810000dfa18SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &create->cr_type) < 0)
811000dfa18SChuck Lever 		return nfserr_bad_xdr;
8121da177e4SLinus Torvalds 	switch (create->cr_type) {
8131da177e4SLinus Torvalds 	case NF4LNK:
814000dfa18SChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &create->cr_datalen) < 0)
815000dfa18SChuck Lever 			return nfserr_bad_xdr;
816000dfa18SChuck Lever 		p = xdr_inline_decode(argp->xdr, create->cr_datalen);
817000dfa18SChuck Lever 		if (!p)
818000dfa18SChuck Lever 			return nfserr_bad_xdr;
81929c353b3SJ. Bruce Fields 		create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen);
8207fb84306SJ. Bruce Fields 		if (!create->cr_data)
82176f47128SJ. Bruce Fields 			return nfserr_jukebox;
8221da177e4SLinus Torvalds 		break;
8231da177e4SLinus Torvalds 	case NF4BLK:
8241da177e4SLinus Torvalds 	case NF4CHR:
825000dfa18SChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &create->cr_specdata1) < 0)
826000dfa18SChuck Lever 			return nfserr_bad_xdr;
827000dfa18SChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &create->cr_specdata2) < 0)
828000dfa18SChuck Lever 			return nfserr_bad_xdr;
8291da177e4SLinus Torvalds 		break;
8301da177e4SLinus Torvalds 	case NF4SOCK:
8311da177e4SLinus Torvalds 	case NF4FIFO:
8321da177e4SLinus Torvalds 	case NF4DIR:
8331da177e4SLinus Torvalds 	default:
8341da177e4SLinus Torvalds 		break;
8351da177e4SLinus Torvalds 	}
836000dfa18SChuck Lever 	status = nfsd4_decode_component4(argp, &create->cr_name,
837000dfa18SChuck Lever 					 &create->cr_namelen);
838000dfa18SChuck Lever 	if (status)
8391da177e4SLinus Torvalds 		return status;
840d1c263a0SChuck Lever 	status = nfsd4_decode_fattr4(argp, create->cr_bmval,
841d1c263a0SChuck Lever 				    ARRAY_SIZE(create->cr_bmval),
842d1c263a0SChuck Lever 				    &create->cr_iattr, &create->cr_acl,
843d1c263a0SChuck Lever 				    &create->cr_label, &create->cr_umask);
844c0d6fc8aSBenny Halevy 	if (status)
845000dfa18SChuck Lever 		return status;
8461da177e4SLinus Torvalds 
847000dfa18SChuck Lever 	return nfs_ok;
8481da177e4SLinus Torvalds }
8491da177e4SLinus Torvalds 
850b37ad28bSAl Viro static inline __be32
nfsd4_decode_delegreturn(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)851e78e274eSKees Cook nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8521da177e4SLinus Torvalds {
853e78e274eSKees Cook 	struct nfsd4_delegreturn *dr = &u->delegreturn;
85495e6482cSChuck Lever 	return nfsd4_decode_stateid4(argp, &dr->dr_stateid);
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds 
857b37ad28bSAl Viro static inline __be32
nfsd4_decode_getattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)858e78e274eSKees Cook nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8591da177e4SLinus Torvalds {
860e78e274eSKees Cook 	struct nfsd4_getattr *getattr = &u->getattr;
8613fdc5464SChuck Lever 	memset(getattr, 0, sizeof(*getattr));
862f759eff2SChuck Lever 	return nfsd4_decode_bitmap4(argp, getattr->ga_bmval,
863f759eff2SChuck Lever 				    ARRAY_SIZE(getattr->ga_bmval));
8641da177e4SLinus Torvalds }
8651da177e4SLinus Torvalds 
866b37ad28bSAl Viro static __be32
nfsd4_decode_link(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)867e78e274eSKees Cook nfsd4_decode_link(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
8681da177e4SLinus Torvalds {
869e78e274eSKees Cook 	struct nfsd4_link *link = &u->link;
8703fdc5464SChuck Lever 	memset(link, 0, sizeof(*link));
8715c505d12SChuck Lever 	return nfsd4_decode_component4(argp, &link->li_name, &link->li_namelen);
8721da177e4SLinus Torvalds }
8731da177e4SLinus Torvalds 
874b37ad28bSAl Viro static __be32
nfsd4_decode_open_to_lock_owner4(struct nfsd4_compoundargs * argp,struct nfsd4_lock * lock)8758918cc0dSChuck Lever nfsd4_decode_open_to_lock_owner4(struct nfsd4_compoundargs *argp,
8768918cc0dSChuck Lever 				 struct nfsd4_lock *lock)
8778918cc0dSChuck Lever {
8788918cc0dSChuck Lever 	__be32 status;
8798918cc0dSChuck Lever 
8808918cc0dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lock->lk_new_open_seqid) < 0)
8818918cc0dSChuck Lever 		return nfserr_bad_xdr;
8828918cc0dSChuck Lever 	status = nfsd4_decode_stateid4(argp, &lock->lk_new_open_stateid);
8838918cc0dSChuck Lever 	if (status)
8848918cc0dSChuck Lever 		return status;
8858918cc0dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lock->lk_new_lock_seqid) < 0)
8868918cc0dSChuck Lever 		return nfserr_bad_xdr;
8878918cc0dSChuck Lever 	return nfsd4_decode_state_owner4(argp, &lock->lk_new_clientid,
8888918cc0dSChuck Lever 					 &lock->lk_new_owner);
8898918cc0dSChuck Lever }
8908918cc0dSChuck Lever 
8918918cc0dSChuck Lever static __be32
nfsd4_decode_exist_lock_owner4(struct nfsd4_compoundargs * argp,struct nfsd4_lock * lock)8928918cc0dSChuck Lever nfsd4_decode_exist_lock_owner4(struct nfsd4_compoundargs *argp,
8938918cc0dSChuck Lever 			       struct nfsd4_lock *lock)
8948918cc0dSChuck Lever {
8958918cc0dSChuck Lever 	__be32 status;
8968918cc0dSChuck Lever 
8978918cc0dSChuck Lever 	status = nfsd4_decode_stateid4(argp, &lock->lk_old_lock_stateid);
8988918cc0dSChuck Lever 	if (status)
8998918cc0dSChuck Lever 		return status;
9008918cc0dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lock->lk_old_lock_seqid) < 0)
9018918cc0dSChuck Lever 		return nfserr_bad_xdr;
9028918cc0dSChuck Lever 
9038918cc0dSChuck Lever 	return nfs_ok;
9048918cc0dSChuck Lever }
9058918cc0dSChuck Lever 
9068918cc0dSChuck Lever static __be32
nfsd4_decode_locker4(struct nfsd4_compoundargs * argp,struct nfsd4_lock * lock)9078918cc0dSChuck Lever nfsd4_decode_locker4(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
9088918cc0dSChuck Lever {
9098918cc0dSChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &lock->lk_is_new) < 0)
9108918cc0dSChuck Lever 		return nfserr_bad_xdr;
9118918cc0dSChuck Lever 	if (lock->lk_is_new)
9128918cc0dSChuck Lever 		return nfsd4_decode_open_to_lock_owner4(argp, lock);
9138918cc0dSChuck Lever 	return nfsd4_decode_exist_lock_owner4(argp, lock);
9148918cc0dSChuck Lever }
9158918cc0dSChuck Lever 
9168918cc0dSChuck Lever static __be32
nfsd4_decode_lock(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)917e78e274eSKees Cook nfsd4_decode_lock(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9181da177e4SLinus Torvalds {
919e78e274eSKees Cook 	struct nfsd4_lock *lock = &u->lock;
9203fdc5464SChuck Lever 	memset(lock, 0, sizeof(*lock));
9217c59deedSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lock->lk_type) < 0)
9227c59deedSChuck Lever 		return nfserr_bad_xdr;
9231da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
9247c59deedSChuck Lever 		return nfserr_bad_xdr;
9257c59deedSChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &lock->lk_reclaim) < 0)
9267c59deedSChuck Lever 		return nfserr_bad_xdr;
9277c59deedSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lock->lk_offset) < 0)
9287c59deedSChuck Lever 		return nfserr_bad_xdr;
9297c59deedSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lock->lk_length) < 0)
9307c59deedSChuck Lever 		return nfserr_bad_xdr;
9317c59deedSChuck Lever 	return nfsd4_decode_locker4(argp, lock);
9321da177e4SLinus Torvalds }
9331da177e4SLinus Torvalds 
934b37ad28bSAl Viro static __be32
nfsd4_decode_lockt(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)935e78e274eSKees Cook nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9361da177e4SLinus Torvalds {
937e78e274eSKees Cook 	struct nfsd4_lockt *lockt = &u->lockt;
9383fdc5464SChuck Lever 	memset(lockt, 0, sizeof(*lockt));
9390a146f04SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lockt->lt_type) < 0)
9400a146f04SChuck Lever 		return nfserr_bad_xdr;
9411da177e4SLinus Torvalds 	if ((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
9420a146f04SChuck Lever 		return nfserr_bad_xdr;
9430a146f04SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lockt->lt_offset) < 0)
9440a146f04SChuck Lever 		return nfserr_bad_xdr;
9450a146f04SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lockt->lt_length) < 0)
9460a146f04SChuck Lever 		return nfserr_bad_xdr;
9470a146f04SChuck Lever 	return nfsd4_decode_state_owner4(argp, &lockt->lt_clientid,
9480a146f04SChuck Lever 					 &lockt->lt_owner);
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds 
951b37ad28bSAl Viro static __be32
nfsd4_decode_locku(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)952e78e274eSKees Cook nfsd4_decode_locku(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9531da177e4SLinus Torvalds {
954e78e274eSKees Cook 	struct nfsd4_locku *locku = &u->locku;
955ca9cf9fcSChuck Lever 	__be32 status;
9561da177e4SLinus Torvalds 
957ca9cf9fcSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &locku->lu_type) < 0)
958ca9cf9fcSChuck Lever 		return nfserr_bad_xdr;
9591da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
960ca9cf9fcSChuck Lever 		return nfserr_bad_xdr;
961ca9cf9fcSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &locku->lu_seqid) < 0)
962ca9cf9fcSChuck Lever 		return nfserr_bad_xdr;
963ca9cf9fcSChuck Lever 	status = nfsd4_decode_stateid4(argp, &locku->lu_stateid);
964e31a1b66SBenny Halevy 	if (status)
965e31a1b66SBenny Halevy 		return status;
966ca9cf9fcSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &locku->lu_offset) < 0)
967ca9cf9fcSChuck Lever 		return nfserr_bad_xdr;
968ca9cf9fcSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &locku->lu_length) < 0)
969ca9cf9fcSChuck Lever 		return nfserr_bad_xdr;
9701da177e4SLinus Torvalds 
971ca9cf9fcSChuck Lever 	return nfs_ok;
9721da177e4SLinus Torvalds }
9731da177e4SLinus Torvalds 
974b37ad28bSAl Viro static __be32
nfsd4_decode_lookup(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)975e78e274eSKees Cook nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
9761da177e4SLinus Torvalds {
977e78e274eSKees Cook 	struct nfsd4_lookup *lookup = &u->lookup;
9783d5877e8SChuck Lever 	return nfsd4_decode_component4(argp, &lookup->lo_name, &lookup->lo_len);
9791da177e4SLinus Torvalds }
9801da177e4SLinus Torvalds 
981bf33bab3SChuck Lever static __be32
nfsd4_decode_createhow4(struct nfsd4_compoundargs * argp,struct nfsd4_open * open)982bf33bab3SChuck Lever nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
983bf33bab3SChuck Lever {
984bf33bab3SChuck Lever 	__be32 status;
985bf33bab3SChuck Lever 
986bf33bab3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &open->op_createmode) < 0)
987bf33bab3SChuck Lever 		return nfserr_bad_xdr;
988bf33bab3SChuck Lever 	switch (open->op_createmode) {
989bf33bab3SChuck Lever 	case NFS4_CREATE_UNCHECKED:
990bf33bab3SChuck Lever 	case NFS4_CREATE_GUARDED:
991bf33bab3SChuck Lever 		status = nfsd4_decode_fattr4(argp, open->op_bmval,
992bf33bab3SChuck Lever 					     ARRAY_SIZE(open->op_bmval),
993bf33bab3SChuck Lever 					     &open->op_iattr, &open->op_acl,
994bf33bab3SChuck Lever 					     &open->op_label, &open->op_umask);
995bf33bab3SChuck Lever 		if (status)
996bf33bab3SChuck Lever 			return status;
997bf33bab3SChuck Lever 		break;
998bf33bab3SChuck Lever 	case NFS4_CREATE_EXCLUSIVE:
999bf33bab3SChuck Lever 		status = nfsd4_decode_verifier4(argp, &open->op_verf);
1000bf33bab3SChuck Lever 		if (status)
1001bf33bab3SChuck Lever 			return status;
1002bf33bab3SChuck Lever 		break;
1003bf33bab3SChuck Lever 	case NFS4_CREATE_EXCLUSIVE4_1:
1004bf33bab3SChuck Lever 		if (argp->minorversion < 1)
1005bf33bab3SChuck Lever 			return nfserr_bad_xdr;
1006bf33bab3SChuck Lever 		status = nfsd4_decode_verifier4(argp, &open->op_verf);
1007bf33bab3SChuck Lever 		if (status)
1008bf33bab3SChuck Lever 			return status;
1009bf33bab3SChuck Lever 		status = nfsd4_decode_fattr4(argp, open->op_bmval,
1010bf33bab3SChuck Lever 					     ARRAY_SIZE(open->op_bmval),
1011bf33bab3SChuck Lever 					     &open->op_iattr, &open->op_acl,
1012bf33bab3SChuck Lever 					     &open->op_label, &open->op_umask);
1013bf33bab3SChuck Lever 		if (status)
1014bf33bab3SChuck Lever 			return status;
1015bf33bab3SChuck Lever 		break;
1016bf33bab3SChuck Lever 	default:
1017bf33bab3SChuck Lever 		return nfserr_bad_xdr;
1018bf33bab3SChuck Lever 	}
1019bf33bab3SChuck Lever 
1020bf33bab3SChuck Lever 	return nfs_ok;
1021bf33bab3SChuck Lever }
1022bf33bab3SChuck Lever 
1023e6ec04b2SChuck Lever static __be32
nfsd4_decode_openflag4(struct nfsd4_compoundargs * argp,struct nfsd4_open * open)1024e6ec04b2SChuck Lever nfsd4_decode_openflag4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
1025e6ec04b2SChuck Lever {
1026e6ec04b2SChuck Lever 	__be32 status;
1027e6ec04b2SChuck Lever 
1028e6ec04b2SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &open->op_create) < 0)
1029e6ec04b2SChuck Lever 		return nfserr_bad_xdr;
1030e6ec04b2SChuck Lever 	switch (open->op_create) {
1031e6ec04b2SChuck Lever 	case NFS4_OPEN_NOCREATE:
1032e6ec04b2SChuck Lever 		break;
1033e6ec04b2SChuck Lever 	case NFS4_OPEN_CREATE:
1034e6ec04b2SChuck Lever 		status = nfsd4_decode_createhow4(argp, open);
1035e6ec04b2SChuck Lever 		if (status)
1036e6ec04b2SChuck Lever 			return status;
1037e6ec04b2SChuck Lever 		break;
1038e6ec04b2SChuck Lever 	default:
1039e6ec04b2SChuck Lever 		return nfserr_bad_xdr;
1040e6ec04b2SChuck Lever 	}
1041e6ec04b2SChuck Lever 
1042e6ec04b2SChuck Lever 	return nfs_ok;
1043e6ec04b2SChuck Lever }
1044e6ec04b2SChuck Lever 
nfsd4_decode_share_access(struct nfsd4_compoundargs * argp,u32 * share_access,u32 * deleg_want,u32 * deleg_when)10452c8bd7e0SBenny Halevy static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when)
104604f9e664SJ. Bruce Fields {
104704f9e664SJ. Bruce Fields 	u32 w;
104804f9e664SJ. Bruce Fields 
10499aa62f51SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &w) < 0)
10509aa62f51SChuck Lever 		return nfserr_bad_xdr;
10512c8bd7e0SBenny Halevy 	*share_access = w & NFS4_SHARE_ACCESS_MASK;
10522c8bd7e0SBenny Halevy 	*deleg_want = w & NFS4_SHARE_WANT_MASK;
10532c8bd7e0SBenny Halevy 	if (deleg_when)
10542c8bd7e0SBenny Halevy 		*deleg_when = w & NFS4_SHARE_WHEN_MASK;
10552c8bd7e0SBenny Halevy 
105604f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_ACCESS_MASK) {
105704f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_READ:
105804f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_WRITE:
105904f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_BOTH:
106004f9e664SJ. Bruce Fields 		break;
106104f9e664SJ. Bruce Fields 	default:
106204f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
106304f9e664SJ. Bruce Fields 	}
1064fc0d14feSBenny Halevy 	w &= ~NFS4_SHARE_ACCESS_MASK;
106504f9e664SJ. Bruce Fields 	if (!w)
106604f9e664SJ. Bruce Fields 		return nfs_ok;
106704f9e664SJ. Bruce Fields 	if (!argp->minorversion)
106804f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
106904f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_WANT_MASK) {
107004f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_PREFERENCE:
107104f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_READ_DELEG:
107204f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_WRITE_DELEG:
107304f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_ANY_DELEG:
107404f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_DELEG:
107504f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_CANCEL:
107604f9e664SJ. Bruce Fields 		break;
107704f9e664SJ. Bruce Fields 	default:
107804f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
107904f9e664SJ. Bruce Fields 	}
108092bac8c5SBenny Halevy 	w &= ~NFS4_SHARE_WANT_MASK;
108104f9e664SJ. Bruce Fields 	if (!w)
108204f9e664SJ. Bruce Fields 		return nfs_ok;
10832c8bd7e0SBenny Halevy 
10842c8bd7e0SBenny Halevy 	if (!deleg_when)	/* open_downgrade */
10852c8bd7e0SBenny Halevy 		return nfserr_inval;
108604f9e664SJ. Bruce Fields 	switch (w) {
108704f9e664SJ. Bruce Fields 	case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
108804f9e664SJ. Bruce Fields 	case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
1089c668fc6dSBenny Halevy 	case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
1090c668fc6dSBenny Halevy 	      NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
109104f9e664SJ. Bruce Fields 		return nfs_ok;
109204f9e664SJ. Bruce Fields 	}
109304f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
109404f9e664SJ. Bruce Fields }
109504f9e664SJ. Bruce Fields 
nfsd4_decode_share_deny(struct nfsd4_compoundargs * argp,u32 * x)109604f9e664SJ. Bruce Fields static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
109704f9e664SJ. Bruce Fields {
1098b07bebd9SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, x) < 0)
1099b07bebd9SChuck Lever 		return nfserr_bad_xdr;
1100b07bebd9SChuck Lever 	/* Note: unlike access bits, deny bits may be zero. */
110101cd4afaSDan Carpenter 	if (*x & ~NFS4_SHARE_DENY_BOTH)
110204f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
1103b07bebd9SChuck Lever 
110404f9e664SJ. Bruce Fields 	return nfs_ok;
110504f9e664SJ. Bruce Fields }
110604f9e664SJ. Bruce Fields 
1107b37ad28bSAl Viro static __be32
nfsd4_decode_open_claim4(struct nfsd4_compoundargs * argp,struct nfsd4_open * open)11081708e50bSChuck Lever nfsd4_decode_open_claim4(struct nfsd4_compoundargs *argp,
11091708e50bSChuck Lever 			 struct nfsd4_open *open)
11101708e50bSChuck Lever {
11111708e50bSChuck Lever 	__be32 status;
11121708e50bSChuck Lever 
11131708e50bSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &open->op_claim_type) < 0)
11141708e50bSChuck Lever 		return nfserr_bad_xdr;
11151708e50bSChuck Lever 	switch (open->op_claim_type) {
11161708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_NULL:
11171708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
11181708e50bSChuck Lever 		status = nfsd4_decode_component4(argp, &open->op_fname,
11191708e50bSChuck Lever 						 &open->op_fnamelen);
11201708e50bSChuck Lever 		if (status)
11211708e50bSChuck Lever 			return status;
11221708e50bSChuck Lever 		break;
11231708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_PREVIOUS:
11241708e50bSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &open->op_delegate_type) < 0)
11251708e50bSChuck Lever 			return nfserr_bad_xdr;
11261708e50bSChuck Lever 		break;
11271708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
11281708e50bSChuck Lever 		status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid);
11291708e50bSChuck Lever 		if (status)
11301708e50bSChuck Lever 			return status;
11311708e50bSChuck Lever 		status = nfsd4_decode_component4(argp, &open->op_fname,
11321708e50bSChuck Lever 						 &open->op_fnamelen);
11331708e50bSChuck Lever 		if (status)
11341708e50bSChuck Lever 			return status;
11351708e50bSChuck Lever 		break;
11361708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_FH:
11371708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
11381708e50bSChuck Lever 		if (argp->minorversion < 1)
11391708e50bSChuck Lever 			return nfserr_bad_xdr;
11401708e50bSChuck Lever 		/* void */
11411708e50bSChuck Lever 		break;
11421708e50bSChuck Lever 	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
11431708e50bSChuck Lever 		if (argp->minorversion < 1)
11441708e50bSChuck Lever 			return nfserr_bad_xdr;
11451708e50bSChuck Lever 		status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid);
11461708e50bSChuck Lever 		if (status)
11471708e50bSChuck Lever 			return status;
11481708e50bSChuck Lever 		break;
11491708e50bSChuck Lever 	default:
11501708e50bSChuck Lever 		return nfserr_bad_xdr;
11511708e50bSChuck Lever 	}
11521708e50bSChuck Lever 
11531708e50bSChuck Lever 	return nfs_ok;
11541708e50bSChuck Lever }
11551708e50bSChuck Lever 
11561708e50bSChuck Lever static __be32
nfsd4_decode_open(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1157e78e274eSKees Cook nfsd4_decode_open(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
11581da177e4SLinus Torvalds {
1159e78e274eSKees Cook 	struct nfsd4_open *open = &u->open;
116061e5e0b3SChuck Lever 	__be32 status;
11612c8bd7e0SBenny Halevy 	u32 dummy;
11621da177e4SLinus Torvalds 
11633fdc5464SChuck Lever 	memset(open, 0, sizeof(*open));
11641da177e4SLinus Torvalds 
116561e5e0b3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &open->op_seqid) < 0)
116661e5e0b3SChuck Lever 		return nfserr_bad_xdr;
116761e5e0b3SChuck Lever 	/* deleg_want is ignored */
11682c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open->op_share_access,
11692c8bd7e0SBenny Halevy 					   &open->op_deleg_want, &dummy);
117004f9e664SJ. Bruce Fields 	if (status)
117161e5e0b3SChuck Lever 		return status;
117204f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
117304f9e664SJ. Bruce Fields 	if (status)
117461e5e0b3SChuck Lever 		return status;
117561e5e0b3SChuck Lever 	status = nfsd4_decode_state_owner4(argp, &open->op_clientid,
117661e5e0b3SChuck Lever 					   &open->op_owner);
1177a084daf5SJ. Bruce Fields 	if (status)
117861e5e0b3SChuck Lever 		return status;
1179e6ec04b2SChuck Lever 	status = nfsd4_decode_openflag4(argp, open);
1180796dd1c6SChuck Lever 	if (status)
1181796dd1c6SChuck Lever 		return status;
118261e5e0b3SChuck Lever 	return nfsd4_decode_open_claim4(argp, open);
11831da177e4SLinus Torvalds }
11841da177e4SLinus Torvalds 
1185b37ad28bSAl Viro static __be32
nfsd4_decode_open_confirm(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1186e78e274eSKees Cook nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp,
1187e78e274eSKees Cook 			  union nfsd4_op_u *u)
11881da177e4SLinus Torvalds {
1189e78e274eSKees Cook 	struct nfsd4_open_confirm *open_conf = &u->open_confirm;
119006bee693SChuck Lever 	__be32 status;
11911da177e4SLinus Torvalds 
1192e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1193e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1194e1a90ebdSAnna Schumaker 
119506bee693SChuck Lever 	status = nfsd4_decode_stateid4(argp, &open_conf->oc_req_stateid);
1196e31a1b66SBenny Halevy 	if (status)
1197e31a1b66SBenny Halevy 		return status;
119806bee693SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &open_conf->oc_seqid) < 0)
119906bee693SChuck Lever 		return nfserr_bad_xdr;
12001da177e4SLinus Torvalds 
12013fdc5464SChuck Lever 	memset(&open_conf->oc_resp_stateid, 0,
12023fdc5464SChuck Lever 	       sizeof(open_conf->oc_resp_stateid));
120306bee693SChuck Lever 	return nfs_ok;
12041da177e4SLinus Torvalds }
12051da177e4SLinus Torvalds 
1206b37ad28bSAl Viro static __be32
nfsd4_decode_open_downgrade(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1207e78e274eSKees Cook nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp,
1208e78e274eSKees Cook 			    union nfsd4_op_u *u)
12091da177e4SLinus Torvalds {
1210e78e274eSKees Cook 	struct nfsd4_open_downgrade *open_down = &u->open_downgrade;
1211dca71651SChuck Lever 	__be32 status;
12121da177e4SLinus Torvalds 
12133fdc5464SChuck Lever 	memset(open_down, 0, sizeof(*open_down));
1214dca71651SChuck Lever 	status = nfsd4_decode_stateid4(argp, &open_down->od_stateid);
1215e31a1b66SBenny Halevy 	if (status)
1216e31a1b66SBenny Halevy 		return status;
1217dca71651SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &open_down->od_seqid) < 0)
1218dca71651SChuck Lever 		return nfserr_bad_xdr;
1219dca71651SChuck Lever 	/* deleg_want is ignored */
12202c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
12212c8bd7e0SBenny Halevy 					   &open_down->od_deleg_want, NULL);
122204f9e664SJ. Bruce Fields 	if (status)
122304f9e664SJ. Bruce Fields 		return status;
1224dca71651SChuck Lever 	return nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds 
1227b37ad28bSAl Viro static __be32
nfsd4_decode_putfh(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1228e78e274eSKees Cook nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12291da177e4SLinus Torvalds {
1230e78e274eSKees Cook 	struct nfsd4_putfh *putfh = &u->putfh;
1231a73bed98SChuck Lever 	__be32 *p;
12321da177e4SLinus Torvalds 
1233a73bed98SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &putfh->pf_fhlen) < 0)
1234a73bed98SChuck Lever 		return nfserr_bad_xdr;
12351da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
1236a73bed98SChuck Lever 		return nfserr_bad_xdr;
1237a73bed98SChuck Lever 	p = xdr_inline_decode(argp->xdr, putfh->pf_fhlen);
1238a73bed98SChuck Lever 	if (!p)
1239a73bed98SChuck Lever 		return nfserr_bad_xdr;
12407b723008SChuck Lever 	putfh->pf_fhval = svcxdr_savemem(argp, p, putfh->pf_fhlen);
1241a73bed98SChuck Lever 	if (!putfh->pf_fhval)
1242a73bed98SChuck Lever 		return nfserr_jukebox;
12431da177e4SLinus Torvalds 
12443fdc5464SChuck Lever 	putfh->no_verify = false;
1245a73bed98SChuck Lever 	return nfs_ok;
12461da177e4SLinus Torvalds }
12471da177e4SLinus Torvalds 
1248b37ad28bSAl Viro static __be32
nfsd4_decode_read(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1249e78e274eSKees Cook nfsd4_decode_read(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1250e1a90ebdSAnna Schumaker {
1251e1a90ebdSAnna Schumaker 	struct nfsd4_read *read = &u->read;
1252e1a90ebdSAnna Schumaker 	__be32 status;
1253e1a90ebdSAnna Schumaker 
1254e1a90ebdSAnna Schumaker 	memset(read, 0, sizeof(*read));
1255e1a90ebdSAnna Schumaker 	status = nfsd4_decode_stateid4(argp, &read->rd_stateid);
1256e1a90ebdSAnna Schumaker 	if (status)
1257e78e274eSKees Cook 		return status;
12581da177e4SLinus Torvalds 	if (xdr_stream_decode_u64(argp->xdr, &read->rd_offset) < 0)
1259e78e274eSKees Cook 		return nfserr_bad_xdr;
12603909c3bcSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &read->rd_length) < 0)
12611da177e4SLinus Torvalds 		return nfserr_bad_xdr;
12623fdc5464SChuck Lever 
12633909c3bcSChuck Lever 	return nfs_ok;
1264e31a1b66SBenny Halevy }
1265e31a1b66SBenny Halevy 
12663909c3bcSChuck Lever static __be32
nfsd4_decode_readdir(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)12673909c3bcSChuck Lever nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12683909c3bcSChuck Lever {
12693909c3bcSChuck Lever 	struct nfsd4_readdir *readdir = &u->readdir;
12701da177e4SLinus Torvalds 	__be32 status;
12713909c3bcSChuck Lever 
12721da177e4SLinus Torvalds 	memset(readdir, 0, sizeof(*readdir));
12731da177e4SLinus Torvalds 	if (xdr_stream_decode_u64(argp->xdr, &readdir->rd_cookie) < 0)
1274b37ad28bSAl Viro 		return nfserr_bad_xdr;
1275e78e274eSKees Cook 	status = nfsd4_decode_verifier4(argp, &readdir->rd_verf);
12761da177e4SLinus Torvalds 	if (status)
1277e78e274eSKees Cook 		return status;
12780dfaf2a3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_dircount) < 0)
12791da177e4SLinus Torvalds 		return nfserr_bad_xdr;
12803fdc5464SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_maxcount) < 0)
12810dfaf2a3SChuck Lever 		return nfserr_bad_xdr;
12820dfaf2a3SChuck Lever 	if (xdr_stream_decode_uint32_array(argp->xdr, readdir->rd_bmval,
12830dfaf2a3SChuck Lever 					   ARRAY_SIZE(readdir->rd_bmval)) < 0)
12840dfaf2a3SChuck Lever 		return nfserr_bad_xdr;
12850dfaf2a3SChuck Lever 
12860dfaf2a3SChuck Lever 	return nfs_ok;
12870dfaf2a3SChuck Lever }
12880dfaf2a3SChuck Lever 
12890dfaf2a3SChuck Lever static __be32
nfsd4_decode_remove(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)12900dfaf2a3SChuck Lever nfsd4_decode_remove(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12910dfaf2a3SChuck Lever {
12920dfaf2a3SChuck Lever 	struct nfsd4_remove *remove = &u->remove;
12931da177e4SLinus Torvalds 	memset(&remove->rm_cinfo, 0, sizeof(remove->rm_cinfo));
12940dfaf2a3SChuck Lever 	return nfsd4_decode_component4(argp, &remove->rm_name, &remove->rm_namelen);
12951da177e4SLinus Torvalds }
12961da177e4SLinus Torvalds 
1297b37ad28bSAl Viro static __be32
nfsd4_decode_rename(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1298e78e274eSKees Cook nfsd4_decode_rename(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12991da177e4SLinus Torvalds {
1300e78e274eSKees Cook 	struct nfsd4_rename *rename = &u->rename;
13013fdc5464SChuck Lever 	__be32 status;
1302b7f5fbf2SChuck Lever 
13031da177e4SLinus Torvalds 	memset(rename, 0, sizeof(*rename));
13041da177e4SLinus Torvalds 	status = nfsd4_decode_component4(argp, &rename->rn_sname, &rename->rn_snamelen);
1305b37ad28bSAl Viro 	if (status)
1306e78e274eSKees Cook 		return status;
13071da177e4SLinus Torvalds 	return nfsd4_decode_component4(argp, &rename->rn_tname, &rename->rn_tnamelen);
1308e78e274eSKees Cook }
1309ba881a0aSChuck Lever 
13101da177e4SLinus Torvalds static __be32
nfsd4_decode_renew(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)13113fdc5464SChuck Lever nfsd4_decode_renew(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1312ba881a0aSChuck Lever {
1313ba881a0aSChuck Lever 	clientid_t *clientid = &u->renew;
13141da177e4SLinus Torvalds 	return nfsd4_decode_clientid4(argp, clientid);
1315ba881a0aSChuck Lever }
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds static __be32
nfsd4_decode_secinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1318b37ad28bSAl Viro nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1319e78e274eSKees Cook 		     union nfsd4_op_u *u)
13201da177e4SLinus Torvalds {
1321e78e274eSKees Cook 	struct nfsd4_secinfo *secinfo = &u->secinfo;
1322d12f9045SChuck Lever 	secinfo->si_exp = NULL;
13231da177e4SLinus Torvalds 	return nfsd4_decode_component4(argp, &secinfo->si_name, &secinfo->si_namelen);
13241da177e4SLinus Torvalds }
1325b37ad28bSAl Viro 
1326dcb488a3SAndy Adamson static __be32
nfsd4_decode_setattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1327e78e274eSKees Cook nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1328dcb488a3SAndy Adamson {
1329e78e274eSKees Cook 	struct nfsd4_setattr *setattr = &u->setattr;
13303fdc5464SChuck Lever 	__be32 status;
1331d0abdae5SChuck Lever 
1332dcb488a3SAndy Adamson 	memset(setattr, 0, sizeof(*setattr));
1333dcb488a3SAndy Adamson 	status = nfsd4_decode_stateid4(argp, &setattr->sa_stateid);
1334dcb488a3SAndy Adamson 	if (status)
1335e78e274eSKees Cook 		return status;
13361da177e4SLinus Torvalds 	return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
1337e78e274eSKees Cook 				   ARRAY_SIZE(setattr->sa_bmval),
1338e31a1b66SBenny Halevy 				   &setattr->sa_iattr, &setattr->sa_acl,
13391da177e4SLinus Torvalds 				   &setattr->sa_label, NULL);
13403fdc5464SChuck Lever }
134144592fe9SChuck Lever 
1342e31a1b66SBenny Halevy static __be32
nfsd4_decode_setclientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1343e31a1b66SBenny Halevy nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1344d1c263a0SChuck Lever {
1345d1c263a0SChuck Lever 	struct nfsd4_setclientid *setclientid = &u->setclientid;
1346d1c263a0SChuck Lever 	__be32 *p, status;
1347d1c263a0SChuck Lever 
13481da177e4SLinus Torvalds 	memset(setclientid, 0, sizeof(*setclientid));
13491da177e4SLinus Torvalds 
1350b37ad28bSAl Viro 	if (argp->minorversion >= 1)
1351e78e274eSKees Cook 		return nfserr_notsupp;
13521da177e4SLinus Torvalds 
1353e78e274eSKees Cook 	status = nfsd4_decode_verifier4(argp, &setclientid->se_verf);
135492fa6c08SChuck Lever 	if (status)
13551da177e4SLinus Torvalds 		return status;
13563fdc5464SChuck Lever 	status = nfsd4_decode_opaque(argp, &setclientid->se_name);
13573fdc5464SChuck Lever 	if (status)
1358e1a90ebdSAnna Schumaker 		return status;
1359e1a90ebdSAnna Schumaker 	if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_prog) < 0)
1360e1a90ebdSAnna Schumaker 		return nfserr_bad_xdr;
136192fa6c08SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_netid_len) < 0)
136292fa6c08SChuck Lever 		return nfserr_bad_xdr;
136392fa6c08SChuck Lever 	p = xdr_inline_decode(argp->xdr, setclientid->se_callback_netid_len);
1364a084daf5SJ. Bruce Fields 	if (!p)
1365a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
136692fa6c08SChuck Lever 	setclientid->se_callback_netid_val = svcxdr_savemem(argp, p,
136792fa6c08SChuck Lever 						setclientid->se_callback_netid_len);
1368a084daf5SJ. Bruce Fields 	if (!setclientid->se_callback_netid_val)
136992fa6c08SChuck Lever 		return nfserr_jukebox;
137092fa6c08SChuck Lever 
137192fa6c08SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_addr_len) < 0)
137292fa6c08SChuck Lever 		return nfserr_bad_xdr;
137392fa6c08SChuck Lever 	p = xdr_inline_decode(argp->xdr, setclientid->se_callback_addr_len);
13747b723008SChuck Lever 	if (!p)
137592fa6c08SChuck Lever 		return nfserr_bad_xdr;
137692fa6c08SChuck Lever 	setclientid->se_callback_addr_val = svcxdr_savemem(argp, p,
137792fa6c08SChuck Lever 						setclientid->se_callback_addr_len);
13781da177e4SLinus Torvalds 	if (!setclientid->se_callback_addr_val)
137992fa6c08SChuck Lever 		return nfserr_jukebox;
138092fa6c08SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_ident) < 0)
138192fa6c08SChuck Lever 		return nfserr_bad_xdr;
138292fa6c08SChuck Lever 
138392fa6c08SChuck Lever 	return nfs_ok;
13847b723008SChuck Lever }
138592fa6c08SChuck Lever 
138692fa6c08SChuck Lever static __be32
nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)138792fa6c08SChuck Lever nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp,
138892fa6c08SChuck Lever 				 union nfsd4_op_u *u)
138992fa6c08SChuck Lever {
13901da177e4SLinus Torvalds 	struct nfsd4_setclientid_confirm *scd_c = &u->setclientid_confirm;
139192fa6c08SChuck Lever 	__be32 status;
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds 	if (argp->minorversion >= 1)
1394b37ad28bSAl Viro 		return nfserr_notsupp;
1395e78e274eSKees Cook 
1396e78e274eSKees Cook 	status = nfsd4_decode_clientid4(argp, &scd_c->sc_clientid);
13971da177e4SLinus Torvalds 	if (status)
1398e78e274eSKees Cook 		return status;
1399d1ca5514SChuck Lever 	return nfsd4_decode_verifier4(argp, &scd_c->sc_confirm);
14001da177e4SLinus Torvalds }
1401e1a90ebdSAnna Schumaker 
1402e1a90ebdSAnna Schumaker /* Also used for NVERIFY */
1403e1a90ebdSAnna Schumaker static __be32
nfsd4_decode_verify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1404d1ca5514SChuck Lever nfsd4_decode_verify(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1405d1ca5514SChuck Lever {
1406d1ca5514SChuck Lever 	struct nfsd4_verify *verify = &u->verify;
1407d1ca5514SChuck Lever 	__be32 *p, status;
14081da177e4SLinus Torvalds 
14091da177e4SLinus Torvalds 	memset(verify, 0, sizeof(*verify));
14101da177e4SLinus Torvalds 
1411b37ad28bSAl Viro 	status = nfsd4_decode_bitmap4(argp, verify->ve_bmval,
1412e78e274eSKees Cook 				      ARRAY_SIZE(verify->ve_bmval));
14131da177e4SLinus Torvalds 	if (status)
1414e78e274eSKees Cook 		return status;
141567cd453eSChuck Lever 
14161da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
14173fdc5464SChuck Lever 	 * nfsd4_proc_verify */
14183fdc5464SChuck Lever 
141967cd453eSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &verify->ve_attrlen) < 0)
142067cd453eSChuck Lever 		return nfserr_bad_xdr;
142167cd453eSChuck Lever 	p = xdr_inline_decode(argp->xdr, verify->ve_attrlen);
142267cd453eSChuck Lever 	if (!p)
14231da177e4SLinus Torvalds 		return nfserr_bad_xdr;
14241da177e4SLinus Torvalds 	verify->ve_attrval = svcxdr_savemem(argp, p, verify->ve_attrlen);
1425e5f95703SJ. Bruce Fields 	if (!verify->ve_attrval)
1426e5f95703SJ. Bruce Fields 		return nfserr_jukebox;
142767cd453eSChuck Lever 
142867cd453eSChuck Lever 	return nfs_ok;
142967cd453eSChuck Lever }
143067cd453eSChuck Lever 
143167cd453eSChuck Lever static __be32
nfsd4_decode_write(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)14327b723008SChuck Lever nfsd4_decode_write(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
143367cd453eSChuck Lever {
143467cd453eSChuck Lever 	struct nfsd4_write *write = &u->write;
14351da177e4SLinus Torvalds 	__be32 status;
143667cd453eSChuck Lever 
14371da177e4SLinus Torvalds 	status = nfsd4_decode_stateid4(argp, &write->wr_stateid);
14381da177e4SLinus Torvalds 	if (status)
1439b37ad28bSAl Viro 		return status;
1440e78e274eSKees Cook 	if (xdr_stream_decode_u64(argp->xdr, &write->wr_offset) < 0)
14411da177e4SLinus Torvalds 		return nfserr_bad_xdr;
1442e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &write->wr_stable_how) < 0)
1443244e2befSChuck Lever 		return nfserr_bad_xdr;
14441da177e4SLinus Torvalds 	if (write->wr_stable_how > NFS_FILE_SYNC)
1445244e2befSChuck Lever 		return nfserr_bad_xdr;
1446e31a1b66SBenny Halevy 	if (xdr_stream_decode_u32(argp->xdr, &write->wr_buflen) < 0)
1447e31a1b66SBenny Halevy 		return nfserr_bad_xdr;
1448244e2befSChuck Lever 	if (!xdr_stream_subsegment(argp->xdr, &write->wr_payload, write->wr_buflen))
1449244e2befSChuck Lever 		return nfserr_bad_xdr;
1450244e2befSChuck Lever 
1451244e2befSChuck Lever 	write->wr_bytes_written = 0;
145254bbb7d2SKinglong Mee 	write->wr_how_written = 0;
1453244e2befSChuck Lever 	memset(&write->wr_verifier, 0, sizeof(write->wr_verifier));
1454244e2befSChuck Lever 	return nfs_ok;
1455244e2befSChuck Lever }
1456c1346a12SChuck Lever 
1457244e2befSChuck Lever static __be32
nfsd4_decode_release_lockowner(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)14581da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp,
14593fdc5464SChuck Lever 			       union nfsd4_op_u *u)
14603fdc5464SChuck Lever {
14613fdc5464SChuck Lever 	struct nfsd4_release_lockowner *rlockowner = &u->release_lockowner;
1462244e2befSChuck Lever 	__be32 status;
14631da177e4SLinus Torvalds 
14641da177e4SLinus Torvalds 	if (argp->minorversion >= 1)
1465b37ad28bSAl Viro 		return nfserr_notsupp;
1466e78e274eSKees Cook 
1467e78e274eSKees Cook 	status = nfsd4_decode_state_owner4(argp, &rlockowner->rl_clientid,
14681da177e4SLinus Torvalds 					   &rlockowner->rl_owner);
1469e78e274eSKees Cook 	if (status)
1470a4a80c15SChuck Lever 		return status;
14711da177e4SLinus Torvalds 
1472e1a90ebdSAnna Schumaker 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
1473e1a90ebdSAnna Schumaker 		return nfserr_inval;
1474e1a90ebdSAnna Schumaker 
1475a4a80c15SChuck Lever 	return nfs_ok;
1476a4a80c15SChuck Lever }
1477a4a80c15SChuck Lever 
nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1478a4a80c15SChuck Lever static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp,
14791da177e4SLinus Torvalds 					   union nfsd4_op_u *u)
148060adfc50SAndy Adamson {
148160adfc50SAndy Adamson 	struct nfsd4_backchannel_ctl *bc = &u->backchannel_ctl;
1482a4a80c15SChuck Lever 	memset(bc, 0, sizeof(*bc));
1483a4a80c15SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &bc->bc_cb_program) < 0)
14841da177e4SLinus Torvalds 		return nfserr_bad_xdr;
14851da177e4SLinus Torvalds 	return nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
1486e78e274eSKees Cook }
1487e78e274eSKees Cook 
nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)14880f81d960SChuck Lever static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
1489e78e274eSKees Cook 						union nfsd4_op_u *u)
14903fdc5464SChuck Lever {
14910f81d960SChuck Lever 	struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
14920f81d960SChuck Lever 	u32 use_conn_in_rdma_mode;
14930f81d960SChuck Lever 	__be32 status;
14940f81d960SChuck Lever 
14950f81d960SChuck Lever 	memset(bcts, 0, sizeof(*bcts));
1496e78e274eSKees Cook 	status = nfsd4_decode_sessionid4(argp, &bcts->sessionid);
1497e78e274eSKees Cook 	if (status)
1498571e0451SChuck Lever 		return status;
1499e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &bcts->dir) < 0)
1500571e0451SChuck Lever 		return nfserr_bad_xdr;
1501571e0451SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &use_conn_in_rdma_mode) < 0)
1502571e0451SChuck Lever 		return nfserr_bad_xdr;
15033fdc5464SChuck Lever 
1504571e0451SChuck Lever 	return nfs_ok;
1505571e0451SChuck Lever }
1506571e0451SChuck Lever 
1507571e0451SChuck Lever static __be32
nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1508571e0451SChuck Lever nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs *argp,
1509571e0451SChuck Lever 			       struct nfsd4_exchange_id *exid)
1510571e0451SChuck Lever {
1511571e0451SChuck Lever 	__be32 status;
1512571e0451SChuck Lever 
1513571e0451SChuck Lever 	status = nfsd4_decode_bitmap4(argp, exid->spo_must_enforce,
1514571e0451SChuck Lever 				      ARRAY_SIZE(exid->spo_must_enforce));
1515b37ad28bSAl Viro 	if (status)
15162548aa78SChuck Lever 		return nfserr_bad_xdr;
15172548aa78SChuck Lever 	status = nfsd4_decode_bitmap4(argp, exid->spo_must_allow,
15182548aa78SChuck Lever 				      ARRAY_SIZE(exid->spo_must_allow));
15192548aa78SChuck Lever 	if (status)
15202548aa78SChuck Lever 		return nfserr_bad_xdr;
15212548aa78SChuck Lever 
15222548aa78SChuck Lever 	return nfs_ok;
15232548aa78SChuck Lever }
15242548aa78SChuck Lever 
15252548aa78SChuck Lever /*
15262548aa78SChuck Lever  * This implementation currently does not support SP4_SSV.
15272548aa78SChuck Lever  * This decoder simply skips over these arguments.
15282548aa78SChuck Lever  */
15292548aa78SChuck Lever static noinline __be32
nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)15302548aa78SChuck Lever nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs *argp,
15312548aa78SChuck Lever 			  struct nfsd4_exchange_id *exid)
15322548aa78SChuck Lever {
1533547bfeb4SChuck Lever 	u32 count, window, num_gss_handles;
1534547bfeb4SChuck Lever 	__be32 status;
1535547bfeb4SChuck Lever 
1536547bfeb4SChuck Lever 	/* ssp_ops */
1537547bfeb4SChuck Lever 	status = nfsd4_decode_state_protect_ops(argp, exid);
1538547bfeb4SChuck Lever 	if (status)
1539547bfeb4SChuck Lever 		return status;
1540547bfeb4SChuck Lever 
1541547bfeb4SChuck Lever 	/* ssp_hash_algs<> */
1542547bfeb4SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1543547bfeb4SChuck Lever 		return nfserr_bad_xdr;
1544547bfeb4SChuck Lever 	while (count--) {
1545547bfeb4SChuck Lever 		status = nfsd4_decode_ignored_string(argp, 0);
1546547bfeb4SChuck Lever 		if (status)
1547547bfeb4SChuck Lever 			return status;
1548547bfeb4SChuck Lever 	}
1549547bfeb4SChuck Lever 
1550547bfeb4SChuck Lever 	/* ssp_encr_algs<> */
1551547bfeb4SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1552547bfeb4SChuck Lever 		return nfserr_bad_xdr;
1553547bfeb4SChuck Lever 	while (count--) {
1554547bfeb4SChuck Lever 		status = nfsd4_decode_ignored_string(argp, 0);
1555547bfeb4SChuck Lever 		if (status)
1556547bfeb4SChuck Lever 			return status;
1557547bfeb4SChuck Lever 	}
1558547bfeb4SChuck Lever 
1559547bfeb4SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &window) < 0)
1560547bfeb4SChuck Lever 		return nfserr_bad_xdr;
1561547bfeb4SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &num_gss_handles) < 0)
1562547bfeb4SChuck Lever 		return nfserr_bad_xdr;
1563547bfeb4SChuck Lever 
1564547bfeb4SChuck Lever 	return nfs_ok;
1565547bfeb4SChuck Lever }
1566547bfeb4SChuck Lever 
1567547bfeb4SChuck Lever static __be32
nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1568547bfeb4SChuck Lever nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs *argp,
1569547bfeb4SChuck Lever 			      struct nfsd4_exchange_id *exid)
1570547bfeb4SChuck Lever {
1571547bfeb4SChuck Lever 	__be32 status;
1572547bfeb4SChuck Lever 
1573547bfeb4SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &exid->spa_how) < 0)
1574547bfeb4SChuck Lever 		return nfserr_bad_xdr;
15752548aa78SChuck Lever 	switch (exid->spa_how) {
1576523ec6edSChuck Lever 	case SP4_NONE:
15770733d213SAndy Adamson 		break;
15782db134ebSAndy Adamson 	case SP4_MACH_CRED:
1579523ec6edSChuck Lever 		status = nfsd4_decode_state_protect_ops(argp, exid);
15800733d213SAndy Adamson 		if (status)
1581523ec6edSChuck Lever 			return status;
1582a084daf5SJ. Bruce Fields 		break;
15830733d213SAndy Adamson 	case SP4_SSV:
15840733d213SAndy Adamson 		status = nfsd4_decode_ssv_sp_parms(argp, exid);
15850733d213SAndy Adamson 		if (status)
15860733d213SAndy Adamson 			return status;
15872548aa78SChuck Lever 		break;
1588ed941643SAndrew Elble 	default:
15892548aa78SChuck Lever 		return nfserr_bad_xdr;
15900733d213SAndy Adamson 	}
15910733d213SAndy Adamson 
1592547bfeb4SChuck Lever 	return nfs_ok;
15932548aa78SChuck Lever }
15942548aa78SChuck Lever 
15950733d213SAndy Adamson static __be32
nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)15960733d213SAndy Adamson nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs *argp,
1597523ec6edSChuck Lever 			  struct nfsd4_exchange_id *exid)
15980733d213SAndy Adamson {
15990733d213SAndy Adamson 	__be32 status;
1600523ec6edSChuck Lever 	u32 count;
1601523ec6edSChuck Lever 
1602523ec6edSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1603523ec6edSChuck Lever 		return nfserr_bad_xdr;
160410ff8422SChuck Lever 	switch (count) {
160510ff8422SChuck Lever 	case 0:
160610ff8422SChuck Lever 		break;
160710ff8422SChuck Lever 	case 1:
160810ff8422SChuck Lever 		/* Note that RFC 8881 places no length limit on
160910ff8422SChuck Lever 		 * nii_domain, but this implementation permits no
161010ff8422SChuck Lever 		 * more than NFS4_OPAQUE_LIMIT bytes */
161110ff8422SChuck Lever 		status = nfsd4_decode_opaque(argp, &exid->nii_domain);
161210ff8422SChuck Lever 		if (status)
161310ff8422SChuck Lever 			return status;
161410ff8422SChuck Lever 		/* Note that RFC 8881 places no length limit on
161510ff8422SChuck Lever 		 * nii_name, but this implementation permits no
161610ff8422SChuck Lever 		 * more than NFS4_OPAQUE_LIMIT bytes */
161710ff8422SChuck Lever 		status = nfsd4_decode_opaque(argp, &exid->nii_name);
161810ff8422SChuck Lever 		if (status)
161910ff8422SChuck Lever 			return status;
162010ff8422SChuck Lever 		status = nfsd4_decode_nfstime4(argp, &exid->nii_time);
162110ff8422SChuck Lever 		if (status)
162210ff8422SChuck Lever 			return status;
162310ff8422SChuck Lever 		break;
162410ff8422SChuck Lever 	default:
162510ff8422SChuck Lever 		return nfserr_bad_xdr;
162610ff8422SChuck Lever 	}
162710ff8422SChuck Lever 
162810ff8422SChuck Lever 	return nfs_ok;
162910ff8422SChuck Lever }
163010ff8422SChuck Lever 
163110ff8422SChuck Lever static __be32
nfsd4_decode_exchange_id(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)163210ff8422SChuck Lever nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
163310ff8422SChuck Lever 			 union nfsd4_op_u *u)
163410ff8422SChuck Lever {
163510ff8422SChuck Lever 	struct nfsd4_exchange_id *exid = &u->exchange_id;
163610ff8422SChuck Lever 	__be32 status;
163710ff8422SChuck Lever 
163810ff8422SChuck Lever 	memset(exid, 0, sizeof(*exid));
163910ff8422SChuck Lever 	status = nfsd4_decode_verifier4(argp, &exid->verifier);
1640523ec6edSChuck Lever 	if (status)
1641e78e274eSKees Cook 		return status;
1642523ec6edSChuck Lever 	status = nfsd4_decode_opaque(argp, &exid->clname);
1643e78e274eSKees Cook 	if (status)
164410ff8422SChuck Lever 		return status;
1645523ec6edSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &exid->flags) < 0)
16463fdc5464SChuck Lever 		return nfserr_bad_xdr;
1647523ec6edSChuck Lever 	status = nfsd4_decode_state_protect4_a(argp, exid);
1648523ec6edSChuck Lever 	if (status)
1649523ec6edSChuck Lever 		return status;
1650523ec6edSChuck Lever 	return nfsd4_decode_nfs_impl_id4(argp, exid);
1651523ec6edSChuck Lever }
1652523ec6edSChuck Lever 
1653523ec6edSChuck Lever static __be32
nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs * argp,struct nfsd4_channel_attrs * ca)1654523ec6edSChuck Lever nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs *argp,
1655523ec6edSChuck Lever 			    struct nfsd4_channel_attrs *ca)
1656523ec6edSChuck Lever {
1657523ec6edSChuck Lever 	__be32 *p;
165810ff8422SChuck Lever 
16592db134ebSAndy Adamson 	p = xdr_inline_decode(argp->xdr, XDR_UNIT * 7);
16602db134ebSAndy Adamson 	if (!p)
16612db134ebSAndy Adamson 		return nfserr_bad_xdr;
16623a3f1fbaSChuck Lever 
16633a3f1fbaSChuck Lever 	/* headerpadsz is ignored */
16643a3f1fbaSChuck Lever 	p++;
16653a3f1fbaSChuck Lever 	ca->maxreq_sz = be32_to_cpup(p++);
16663a3f1fbaSChuck Lever 	ca->maxresp_sz = be32_to_cpup(p++);
16673a3f1fbaSChuck Lever 	ca->maxresp_cached = be32_to_cpup(p++);
16683a3f1fbaSChuck Lever 	ca->maxops = be32_to_cpup(p++);
16693a3f1fbaSChuck Lever 	ca->maxreqs = be32_to_cpup(p++);
16703a3f1fbaSChuck Lever 	ca->nr_rdma_attrs = be32_to_cpup(p);
16713a3f1fbaSChuck Lever 	switch (ca->nr_rdma_attrs) {
16723a3f1fbaSChuck Lever 	case 0:
16733a3f1fbaSChuck Lever 		break;
16743a3f1fbaSChuck Lever 	case 1:
16753a3f1fbaSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &ca->rdma_attrs) < 0)
16763a3f1fbaSChuck Lever 			return nfserr_bad_xdr;
16773a3f1fbaSChuck Lever 		break;
16783a3f1fbaSChuck Lever 	default:
16793a3f1fbaSChuck Lever 		return nfserr_bad_xdr;
16803a3f1fbaSChuck Lever 	}
16813a3f1fbaSChuck Lever 
16823a3f1fbaSChuck Lever 	return nfs_ok;
16833a3f1fbaSChuck Lever }
16843a3f1fbaSChuck Lever 
16853a3f1fbaSChuck Lever static __be32
nfsd4_decode_create_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)16863a3f1fbaSChuck Lever nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
16873a3f1fbaSChuck Lever 			    union nfsd4_op_u *u)
16883a3f1fbaSChuck Lever {
16893a3f1fbaSChuck Lever 	struct nfsd4_create_session *sess = &u->create_session;
16903a3f1fbaSChuck Lever 	__be32 status;
16913a3f1fbaSChuck Lever 
16923a3f1fbaSChuck Lever 	memset(sess, 0, sizeof(*sess));
16933a3f1fbaSChuck Lever 	status = nfsd4_decode_clientid4(argp, &sess->clientid);
16942db134ebSAndy Adamson 	if (status)
1695e78e274eSKees Cook 		return status;
16962db134ebSAndy Adamson 	if (xdr_stream_decode_u32(argp->xdr, &sess->seqid) < 0)
1697e78e274eSKees Cook 		return nfserr_bad_xdr;
169881243e3fSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sess->flags) < 0)
1699ec6b5d7bSAndy Adamson 		return nfserr_bad_xdr;
17003fdc5464SChuck Lever 	status = nfsd4_decode_channel_attrs4(argp, &sess->fore_channel);
170181243e3fSChuck Lever 	if (status)
170281243e3fSChuck Lever 		return status;
170381243e3fSChuck Lever 	status = nfsd4_decode_channel_attrs4(argp, &sess->back_channel);
170481243e3fSChuck Lever 	if (status)
170581243e3fSChuck Lever 		return status;
170681243e3fSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sess->callback_prog) < 0)
170781243e3fSChuck Lever 		return nfserr_bad_xdr;
17083a3f1fbaSChuck Lever 	return nfsd4_decode_cb_sec(argp, &sess->cb_sec);
17093a3f1fbaSChuck Lever }
17103a3f1fbaSChuck Lever 
17113a3f1fbaSChuck Lever static __be32
nfsd4_decode_destroy_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17123a3f1fbaSChuck Lever nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
17133a3f1fbaSChuck Lever 			     union nfsd4_op_u *u)
171481243e3fSChuck Lever {
171581243e3fSChuck Lever 	struct nfsd4_destroy_session *destroy_session = &u->destroy_session;
17163fdc5464SChuck Lever 	return nfsd4_decode_sessionid4(argp, &destroy_session->sessionid);
17172db134ebSAndy Adamson }
17182db134ebSAndy Adamson 
17192db134ebSAndy Adamson static __be32
nfsd4_decode_free_stateid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17202db134ebSAndy Adamson nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1721e78e274eSKees Cook 			  union nfsd4_op_u *u)
17222db134ebSAndy Adamson {
1723e78e274eSKees Cook 	struct nfsd4_free_stateid *free_stateid = &u->free_stateid;
172494e254afSChuck Lever 	return nfsd4_decode_stateid4(argp, &free_stateid->fr_stateid);
17252db134ebSAndy Adamson }
17262db134ebSAndy Adamson 
17272db134ebSAndy Adamson static __be32
nfsd4_decode_get_dir_delegation(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1728e1ca12dfSBryan Schumaker nfsd4_decode_get_dir_delegation(struct nfsd4_compoundargs *argp,
1729e78e274eSKees Cook 		union nfsd4_op_u *u)
1730e1ca12dfSBryan Schumaker {
1731e78e274eSKees Cook 	struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
1732aec387d5SChuck Lever 	__be32 status;
1733e1ca12dfSBryan Schumaker 
1734e1ca12dfSBryan Schumaker 	memset(gdd, 0, sizeof(*gdd));
173533a1e6eaSJeff Layton 
173633a1e6eaSJeff Layton 	if (xdr_stream_decode_bool(argp->xdr, &gdd->gdda_signal_deleg_avail) < 0)
173733a1e6eaSJeff Layton 		return nfserr_bad_xdr;
173833a1e6eaSJeff Layton 	status = nfsd4_decode_bitmap4(argp, gdd->gdda_notification_types,
173933a1e6eaSJeff Layton 				      ARRAY_SIZE(gdd->gdda_notification_types));
174033a1e6eaSJeff Layton 	if (status)
174133a1e6eaSJeff Layton 		return status;
174233a1e6eaSJeff Layton 	status = nfsd4_decode_nfstime4(argp, &gdd->gdda_child_attr_delay);
174333a1e6eaSJeff Layton 	if (status)
174433a1e6eaSJeff Layton 		return status;
174533a1e6eaSJeff Layton 	status = nfsd4_decode_nfstime4(argp, &gdd->gdda_dir_attr_delay);
174633a1e6eaSJeff Layton 	if (status)
174733a1e6eaSJeff Layton 		return status;
174833a1e6eaSJeff Layton 	status = nfsd4_decode_bitmap4(argp, gdd->gdda_child_attributes,
174933a1e6eaSJeff Layton 					ARRAY_SIZE(gdd->gdda_child_attributes));
175033a1e6eaSJeff Layton 	if (status)
175133a1e6eaSJeff Layton 		return status;
175233a1e6eaSJeff Layton 	return nfsd4_decode_bitmap4(argp, gdd->gdda_dir_attributes,
175333a1e6eaSJeff Layton 					ARRAY_SIZE(gdd->gdda_dir_attributes));
175433a1e6eaSJeff Layton }
175533a1e6eaSJeff Layton 
175633a1e6eaSJeff Layton #ifdef CONFIG_NFSD_PNFS
175733a1e6eaSJeff Layton static __be32
nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)175833a1e6eaSJeff Layton nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
175933a1e6eaSJeff Layton 		union nfsd4_op_u *u)
176033a1e6eaSJeff Layton {
176133a1e6eaSJeff Layton 	struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
176233a1e6eaSJeff Layton 	__be32 status;
176333a1e6eaSJeff Layton 
17649cf514ccSChristoph Hellwig 	memset(gdev, 0, sizeof(*gdev));
17659cf514ccSChristoph Hellwig 	status = nfsd4_decode_deviceid4(argp, &gdev->gd_devid);
17669cf514ccSChristoph Hellwig 	if (status)
1767e78e274eSKees Cook 		return status;
17689cf514ccSChristoph Hellwig 	if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_layout_type) < 0)
1769e78e274eSKees Cook 		return nfserr_bad_xdr;
177004495971SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_maxcount) < 0)
17719cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
17723fdc5464SChuck Lever 	if (xdr_stream_decode_uint32_array(argp->xdr,
177304495971SChuck Lever 					   &gdev->gd_notify_types, 1) < 0)
177404495971SChuck Lever 		return nfserr_bad_xdr;
177504495971SChuck Lever 
177604495971SChuck Lever 	return nfs_ok;
177704495971SChuck Lever }
177804495971SChuck Lever 
177904495971SChuck Lever static __be32
nfsd4_decode_layoutcommit(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)178004495971SChuck Lever nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp,
178104495971SChuck Lever 			  union nfsd4_op_u *u)
178204495971SChuck Lever {
178304495971SChuck Lever 	struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
178404495971SChuck Lever 	__be32 *p, status;
17859cf514ccSChristoph Hellwig 
17869cf514ccSChristoph Hellwig 	memset(lcp, 0, sizeof(*lcp));
17879cf514ccSChristoph Hellwig 	if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.offset) < 0)
17885185980dSChuck Lever 		return nfserr_bad_xdr;
1789e78e274eSKees Cook 	if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.length) < 0)
17905185980dSChuck Lever 		return nfserr_bad_xdr;
1791e78e274eSKees Cook 	if (xdr_stream_decode_bool(argp->xdr, &lcp->lc_reclaim) < 0)
17925185980dSChuck Lever 		return nfserr_bad_xdr;
17935185980dSChuck Lever 	status = nfsd4_decode_stateid4(argp, &lcp->lc_sid);
17943fdc5464SChuck Lever 	if (status)
17955185980dSChuck Lever 		return status;
17965185980dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_newoffset) < 0)
17975185980dSChuck Lever 		return nfserr_bad_xdr;
17985185980dSChuck Lever 	if (lcp->lc_newoffset) {
17995185980dSChuck Lever 		if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_last_wr) < 0)
18005185980dSChuck Lever 			return nfserr_bad_xdr;
18015185980dSChuck Lever 	} else
18025185980dSChuck Lever 		lcp->lc_last_wr = 0;
18035185980dSChuck Lever 	p = xdr_inline_decode(argp->xdr, XDR_UNIT);
18045185980dSChuck Lever 	if (!p)
18055185980dSChuck Lever 		return nfserr_bad_xdr;
18065185980dSChuck Lever 	if (xdr_item_is_present(p)) {
18075185980dSChuck Lever 		status = nfsd4_decode_nfstime4(argp, &lcp->lc_mtime);
18085185980dSChuck Lever 		if (status)
18095185980dSChuck Lever 			return status;
18105185980dSChuck Lever 	} else {
18115185980dSChuck Lever 		lcp->lc_mtime.tv_nsec = UTIME_NOW;
18125185980dSChuck Lever 	}
18135185980dSChuck Lever 	return nfsd4_decode_layoutupdate4(argp, lcp);
18145185980dSChuck Lever }
18155185980dSChuck Lever 
18165185980dSChuck Lever static __be32
nfsd4_decode_layoutget(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)18175185980dSChuck Lever nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
18185185980dSChuck Lever 		union nfsd4_op_u *u)
18195185980dSChuck Lever {
18205185980dSChuck Lever 	struct nfsd4_layoutget *lgp = &u->layoutget;
18215185980dSChuck Lever 	__be32 status;
18225185980dSChuck Lever 
18235185980dSChuck Lever 	memset(lgp, 0, sizeof(*lgp));
18245185980dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_signal) < 0)
18259cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
1826e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_layout_type) < 0)
18279cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
1828e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_seg.iomode) < 0)
1829c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
18309cf514ccSChristoph Hellwig 	if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.offset) < 0)
18313fdc5464SChuck Lever 		return nfserr_bad_xdr;
1832c8e88e3aSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.length) < 0)
1833c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1834c8e88e3aSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_minlength) < 0)
1835c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1836c8e88e3aSChuck Lever 	status = nfsd4_decode_stateid4(argp, &lgp->lg_sid);
1837c8e88e3aSChuck Lever 	if (status)
1838c8e88e3aSChuck Lever 		return status;
1839c8e88e3aSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_maxcount) < 0)
1840c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1841c8e88e3aSChuck Lever 
1842c8e88e3aSChuck Lever 	return nfs_ok;
1843c8e88e3aSChuck Lever }
1844c8e88e3aSChuck Lever 
1845db59c0efSKinglong Mee static __be32
nfsd4_decode_layoutreturn(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1846db59c0efSKinglong Mee nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp,
1847c8e88e3aSChuck Lever 		union nfsd4_op_u *u)
1848c8e88e3aSChuck Lever {
1849db59c0efSKinglong Mee 	struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
1850c8e88e3aSChuck Lever 	memset(lrp, 0, sizeof(*lrp));
18519cf514ccSChristoph Hellwig 	if (xdr_stream_decode_bool(argp->xdr, &lrp->lr_reclaim) < 0)
18529cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
18539cf514ccSChristoph Hellwig 	if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_layout_type) < 0)
18549cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
1855e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_seg.iomode) < 0)
18569cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
1857e78e274eSKees Cook 	return nfsd4_decode_layoutreturn4(argp, lrp);
18583fdc5464SChuck Lever }
1859645fcad3SChuck Lever #endif /* CONFIG_NFSD_PNFS */
1860645fcad3SChuck Lever 
nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1861645fcad3SChuck Lever static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
1862645fcad3SChuck Lever 					   union nfsd4_op_u *u)
1863645fcad3SChuck Lever {
1864645fcad3SChuck Lever 	struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name;
1865645fcad3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0)
18669cf514ccSChristoph Hellwig 		return nfserr_bad_xdr;
18679cf514ccSChristoph Hellwig 
18689cf514ccSChristoph Hellwig 	sin->sin_exp = NULL;
186953d70873SChuck Lever 	return nfs_ok;
1870e78e274eSKees Cook }
187153d70873SChuck Lever 
1872e78e274eSKees Cook static __be32
nfsd4_decode_sequence(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)187353d70873SChuck Lever nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
187453d70873SChuck Lever 		      union nfsd4_op_u *u)
18753fdc5464SChuck Lever {
18763fdc5464SChuck Lever 	struct nfsd4_sequence *seq = &u->sequence;
187753d70873SChuck Lever 	__be32 *p, status;
187853d70873SChuck Lever 
187953d70873SChuck Lever 	status = nfsd4_decode_sessionid4(argp, &seq->sessionid);
18802db134ebSAndy Adamson 	if (status)
1881cf907b11SChuck Lever 		return status;
1882e78e274eSKees Cook 	p = xdr_inline_decode(argp->xdr, XDR_UNIT * 4);
1883cf907b11SChuck Lever 	if (!p)
1884e78e274eSKees Cook 		return nfserr_bad_xdr;
1885cf907b11SChuck Lever 	seq->seqid = be32_to_cpup(p++);
1886cf907b11SChuck Lever 	seq->slotid = be32_to_cpup(p++);
1887cf907b11SChuck Lever 	seq->maxslots = be32_to_cpup(p++);
1888cf907b11SChuck Lever 	seq->cachethis = be32_to_cpup(p);
1889cf907b11SChuck Lever 
1890cf907b11SChuck Lever 	seq->status_flags = 0;
1891cf907b11SChuck Lever 	return nfs_ok;
1892cf907b11SChuck Lever }
1893cf907b11SChuck Lever 
1894cf907b11SChuck Lever static __be32
nfsd4_decode_test_stateid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1895cf907b11SChuck Lever nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp,
1896cf907b11SChuck Lever 			  union nfsd4_op_u *u)
1897cf907b11SChuck Lever {
18983fdc5464SChuck Lever 	struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
1899cf907b11SChuck Lever 	struct nfsd4_test_stateid_id *stateid;
1900cf907b11SChuck Lever 	__be32 status;
1901cf907b11SChuck Lever 	u32 i;
1902cf907b11SChuck Lever 
1903e78e274eSKees Cook 	memset(test_stateid, 0, sizeof(*test_stateid));
1904e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &test_stateid->ts_num_ids) < 0)
1905b7a0c8f6SChuck Lever 		return nfserr_bad_xdr;
1906e78e274eSKees Cook 
1907b7a0c8f6SChuck Lever 	INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
1908b7a0c8f6SChuck Lever 	for (i = 0; i < test_stateid->ts_num_ids; i++) {
1909b7a0c8f6SChuck Lever 		stateid = svcxdr_tmpalloc(argp, sizeof(*stateid));
1910b7a0c8f6SChuck Lever 		if (!stateid)
19113fdc5464SChuck Lever 			return nfserr_jukebox;
1912b7a0c8f6SChuck Lever 		INIT_LIST_HEAD(&stateid->ts_id_list);
1913b7a0c8f6SChuck Lever 		list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
1914b7a0c8f6SChuck Lever 		status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid);
1915b7a0c8f6SChuck Lever 		if (status)
1916b7a0c8f6SChuck Lever 			return status;
1917b7a0c8f6SChuck Lever 	}
1918b7a0c8f6SChuck Lever 
1919bb4d8427SChuck Lever 	return nfs_ok;
1920b7a0c8f6SChuck Lever }
1921b7a0c8f6SChuck Lever 
nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1922b7a0c8f6SChuck Lever static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp,
1923b7a0c8f6SChuck Lever 					    union nfsd4_op_u *u)
1924b7a0c8f6SChuck Lever {
1925b7a0c8f6SChuck Lever 	struct nfsd4_destroy_clientid *dc = &u->destroy_clientid;
1926b7a0c8f6SChuck Lever 	return nfsd4_decode_clientid4(argp, &dc->clientid);
1927b7a0c8f6SChuck Lever }
1928b7a0c8f6SChuck Lever 
nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1929b7a0c8f6SChuck Lever static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp,
1930c95f2ec3SChuck Lever 					    union nfsd4_op_u *u)
1931e78e274eSKees Cook {
1932c95f2ec3SChuck Lever 	struct nfsd4_reclaim_complete *rc = &u->reclaim_complete;
1933e78e274eSKees Cook 	if (xdr_stream_decode_bool(argp->xdr, &rc->rca_one_fs) < 0)
1934c95f2ec3SChuck Lever 		return nfserr_bad_xdr;
1935c95f2ec3SChuck Lever 	return nfs_ok;
1936c95f2ec3SChuck Lever }
19370d646784SChuck Lever 
1938e78e274eSKees Cook static __be32
nfsd4_decode_fallocate(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)19390d646784SChuck Lever nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
1940e78e274eSKees Cook 		       union nfsd4_op_u *u)
19410d646784SChuck Lever {
19420d646784SChuck Lever 	struct nfsd4_fallocate *fallocate = &u->allocate;
19430d646784SChuck Lever 	__be32 status;
19440d646784SChuck Lever 
19450d646784SChuck Lever 	status = nfsd4_decode_stateid4(argp, &fallocate->falloc_stateid);
1946b7a0c8f6SChuck Lever 	if (status)
194795d871f0SAnna Schumaker 		return status;
1948e78e274eSKees Cook 	if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_offset) < 0)
194995d871f0SAnna Schumaker 		return nfserr_bad_xdr;
1950e78e274eSKees Cook 	if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_length) < 0)
19516aef27aaSChuck Lever 		return nfserr_bad_xdr;
195295d871f0SAnna Schumaker 
19536aef27aaSChuck Lever 	return nfs_ok;
195495d871f0SAnna Schumaker }
195595d871f0SAnna Schumaker 
nfsd4_decode_nl4_server(struct nfsd4_compoundargs * argp,struct nl4_server * ns)19566aef27aaSChuck Lever static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
19576aef27aaSChuck Lever 				      struct nl4_server *ns)
19586aef27aaSChuck Lever {
19596aef27aaSChuck Lever 	struct nfs42_netaddr *naddr;
196095d871f0SAnna Schumaker 	__be32 *p;
19616aef27aaSChuck Lever 
196295d871f0SAnna Schumaker 	if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0)
196395d871f0SAnna Schumaker 		return nfserr_bad_xdr;
196484e1b21dSOlga Kornievskaia 
196584e1b21dSOlga Kornievskaia 	/* currently support for 1 inter-server source server */
196684e1b21dSOlga Kornievskaia 	switch (ns->nl4_type) {
196784e1b21dSOlga Kornievskaia 	case NL4_NETADDR:
1968f49e4b4dSChuck Lever 		naddr = &ns->u.nl4_addr;
196984e1b21dSOlga Kornievskaia 
1970f49e4b4dSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &naddr->netid_len) < 0)
1971f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
197284e1b21dSOlga Kornievskaia 		if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
197384e1b21dSOlga Kornievskaia 			return nfserr_bad_xdr;
197484e1b21dSOlga Kornievskaia 
197584e1b21dSOlga Kornievskaia 		p = xdr_inline_decode(argp->xdr, naddr->netid_len);
197684e1b21dSOlga Kornievskaia 		if (!p)
197784e1b21dSOlga Kornievskaia 			return nfserr_bad_xdr;
1978f49e4b4dSChuck Lever 		memcpy(naddr->netid, p, naddr->netid_len);
1979f49e4b4dSChuck Lever 
198084e1b21dSOlga Kornievskaia 		if (xdr_stream_decode_u32(argp->xdr, &naddr->addr_len) < 0)
1981f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
198284e1b21dSOlga Kornievskaia 		if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
1983f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
1984f49e4b4dSChuck Lever 
1985f49e4b4dSChuck Lever 		p = xdr_inline_decode(argp->xdr, naddr->addr_len);
1986f49e4b4dSChuck Lever 		if (!p)
198784e1b21dSOlga Kornievskaia 			return nfserr_bad_xdr;
1988f49e4b4dSChuck Lever 		memcpy(naddr->addr, p, naddr->addr_len);
1989f49e4b4dSChuck Lever 		break;
199084e1b21dSOlga Kornievskaia 	default:
1991f49e4b4dSChuck Lever 		return nfserr_bad_xdr;
199284e1b21dSOlga Kornievskaia 	}
1993f49e4b4dSChuck Lever 
1994f49e4b4dSChuck Lever 	return nfs_ok;
1995f49e4b4dSChuck Lever }
1996f49e4b4dSChuck Lever 
199784e1b21dSOlga Kornievskaia static __be32
nfsd4_decode_copy(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)199884e1b21dSOlga Kornievskaia nfsd4_decode_copy(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
1999f49e4b4dSChuck Lever {
200084e1b21dSOlga Kornievskaia 	struct nfsd4_copy *copy = &u->copy;
2001f49e4b4dSChuck Lever 	u32 consecutive, i, count, sync;
2002f49e4b4dSChuck Lever 	struct nl4_server *ns_dummy;
200384e1b21dSOlga Kornievskaia 	__be32 status;
200484e1b21dSOlga Kornievskaia 
2005ffa0160aSChristoph Hellwig 	memset(copy, 0, sizeof(*copy));
2006e78e274eSKees Cook 	status = nfsd4_decode_stateid4(argp, &copy->cp_src_stateid);
200729ae7f9dSAnna Schumaker 	if (status)
2008e78e274eSKees Cook 		return status;
20091913cdf5SChuck Lever 	status = nfsd4_decode_stateid4(argp, &copy->cp_dst_stateid);
201084e1b21dSOlga Kornievskaia 	if (status)
2011e8febea7SChuck Lever 		return status;
201229ae7f9dSAnna Schumaker 	if (xdr_stream_decode_u64(argp->xdr, &copy->cp_src_pos) < 0)
20133fdc5464SChuck Lever 		return nfserr_bad_xdr;
2014e8febea7SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &copy->cp_dst_pos) < 0)
201529ae7f9dSAnna Schumaker 		return nfserr_bad_xdr;
201629ae7f9dSAnna Schumaker 	if (xdr_stream_decode_u64(argp->xdr, &copy->cp_count) < 0)
2017e8febea7SChuck Lever 		return nfserr_bad_xdr;
201829ae7f9dSAnna Schumaker 	/* ca_consecutive: we always do consecutive copies */
201929ae7f9dSAnna Schumaker 	if (xdr_stream_decode_u32(argp->xdr, &consecutive) < 0)
2020e8febea7SChuck Lever 		return nfserr_bad_xdr;
2021e8febea7SChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &sync) < 0)
2022e8febea7SChuck Lever 		return nfserr_bad_xdr;
2023e8febea7SChuck Lever 	nfsd4_copy_set_sync(copy, sync);
2024e8febea7SChuck Lever 
2025e8febea7SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
2026e8febea7SChuck Lever 		return nfserr_bad_xdr;
2027e8febea7SChuck Lever 	copy->cp_src = svcxdr_tmpalloc(argp, sizeof(*copy->cp_src));
2028e8febea7SChuck Lever 	if (copy->cp_src == NULL)
20291913cdf5SChuck Lever 		return nfserr_jukebox;
2030e8febea7SChuck Lever 	if (count == 0) { /* intra-server copy */
20311913cdf5SChuck Lever 		__set_bit(NFSD4_COPY_F_INTRA, &copy->cp_flags);
203229ae7f9dSAnna Schumaker 		return nfs_ok;
2033e8febea7SChuck Lever 	}
2034e8febea7SChuck Lever 
203587689df6SChuck Lever 	/* decode all the supplied server addresses but use only the first */
203687689df6SChuck Lever 	status = nfsd4_decode_nl4_server(argp, copy->cp_src);
203787689df6SChuck Lever 	if (status)
203884e1b21dSOlga Kornievskaia 		return status;
20391913cdf5SChuck Lever 
2040e8febea7SChuck Lever 	ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
204184e1b21dSOlga Kornievskaia 	if (ns_dummy == NULL)
204284e1b21dSOlga Kornievskaia 		return nfserr_jukebox;
2043e8febea7SChuck Lever 	for (i = 0; i < count - 1; i++) {
204487689df6SChuck Lever 		status = nfsd4_decode_nl4_server(argp, ns_dummy);
204584e1b21dSOlga Kornievskaia 		if (status) {
204684e1b21dSOlga Kornievskaia 			kfree(ns_dummy);
204784e1b21dSOlga Kornievskaia 			return status;
204884e1b21dSOlga Kornievskaia 		}
204984e1b21dSOlga Kornievskaia 	}
2050bb4d8427SChuck Lever 	kfree(ns_dummy);
205184e1b21dSOlga Kornievskaia 
205284e1b21dSOlga Kornievskaia 	return nfs_ok;
205384e1b21dSOlga Kornievskaia }
205484e1b21dSOlga Kornievskaia 
205584e1b21dSOlga Kornievskaia static __be32
nfsd4_decode_copy_notify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)205684e1b21dSOlga Kornievskaia nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
205784e1b21dSOlga Kornievskaia 			 union nfsd4_op_u *u)
205884e1b21dSOlga Kornievskaia {
205929ae7f9dSAnna Schumaker 	struct nfsd4_copy_notify *cn = &u->copy_notify;
2060e8febea7SChuck Lever 	__be32 status;
206129ae7f9dSAnna Schumaker 
206229ae7f9dSAnna Schumaker 	memset(cn, 0, sizeof(*cn));
206329ae7f9dSAnna Schumaker 	cn->cpn_src = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_src));
206451911868SOlga Kornievskaia 	if (cn->cpn_src == NULL)
2065e78e274eSKees Cook 		return nfserr_jukebox;
206651911868SOlga Kornievskaia 	cn->cpn_dst = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_dst));
2067e78e274eSKees Cook 	if (cn->cpn_dst == NULL)
20685aff7d08SChuck Lever 		return nfserr_jukebox;
206951911868SOlga Kornievskaia 
20703fdc5464SChuck Lever 	status = nfsd4_decode_stateid4(argp, &cn->cpn_src_stateid);
207109426ef2SChuck Lever 	if (status)
207209426ef2SChuck Lever 		return status;
207309426ef2SChuck Lever 	return nfsd4_decode_nl4_server(argp, cn->cpn_dst);
207409426ef2SChuck Lever }
207509426ef2SChuck Lever 
207609426ef2SChuck Lever static __be32
nfsd4_decode_offload_status(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)207709426ef2SChuck Lever nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp,
2078f9a953fbSChuck Lever 			    union nfsd4_op_u *u)
207951911868SOlga Kornievskaia {
208051911868SOlga Kornievskaia 	struct nfsd4_offload_status *os = &u->offload_status;
208109426ef2SChuck Lever 	os->count = 0;
208251911868SOlga Kornievskaia 	os->status = 0;
208351911868SOlga Kornievskaia 	return nfsd4_decode_stateid4(argp, &os->stateid);
208451911868SOlga Kornievskaia }
2085f9a953fbSChuck Lever 
2086e78e274eSKees Cook static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2087f9a953fbSChuck Lever nfsd4_decode_seek(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
2088e78e274eSKees Cook {
20893fdc5464SChuck Lever 	struct nfsd4_seek *seek = &u->seek;
20903fdc5464SChuck Lever 	__be32 status;
20912846bb05SChuck Lever 
2092f9a953fbSChuck Lever 	status = nfsd4_decode_stateid4(argp, &seek->seek_stateid);
2093f9a953fbSChuck Lever 	if (status)
2094f9a953fbSChuck Lever 		return status;
2095e78e274eSKees Cook 	if (xdr_stream_decode_u64(argp->xdr, &seek->seek_offset) < 0)
209624bab491SAnna Schumaker 		return nfserr_bad_xdr;
2097e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &seek->seek_whence) < 0)
20989d32b412SChuck Lever 		return nfserr_bad_xdr;
209924bab491SAnna Schumaker 
21009d32b412SChuck Lever 	seek->seek_eof = 0;
210124bab491SAnna Schumaker 	seek->seek_pos = 0;
210224bab491SAnna Schumaker 	return nfs_ok;
21039d32b412SChuck Lever }
21049d32b412SChuck Lever 
21059d32b412SChuck Lever static __be32
nfsd4_decode_clone(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)21069d32b412SChuck Lever nfsd4_decode_clone(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
210724bab491SAnna Schumaker {
21083fdc5464SChuck Lever 	struct nfsd4_clone *clone = &u->clone;
21093fdc5464SChuck Lever 	__be32 status;
21109d32b412SChuck Lever 
211124bab491SAnna Schumaker 	status = nfsd4_decode_stateid4(argp, &clone->cl_src_stateid);
211224bab491SAnna Schumaker 	if (status)
21133dfd0b0eSChuck Lever 		return status;
2114e78e274eSKees Cook 	status = nfsd4_decode_stateid4(argp, &clone->cl_dst_stateid);
21153dfd0b0eSChuck Lever 	if (status)
2116e78e274eSKees Cook 		return status;
21173dfd0b0eSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &clone->cl_src_pos) < 0)
21183dfd0b0eSChuck Lever 		return nfserr_bad_xdr;
21193dfd0b0eSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &clone->cl_dst_pos) < 0)
21203dfd0b0eSChuck Lever 		return nfserr_bad_xdr;
21213dfd0b0eSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &clone->cl_count) < 0)
21223dfd0b0eSChuck Lever 		return nfserr_bad_xdr;
21233dfd0b0eSChuck Lever 
21243dfd0b0eSChuck Lever 	return nfs_ok;
21253dfd0b0eSChuck Lever }
21263dfd0b0eSChuck Lever 
21273dfd0b0eSChuck Lever /*
21283dfd0b0eSChuck Lever  * XDR data that is more than PAGE_SIZE in size is normally part of a
21293dfd0b0eSChuck Lever  * read or write. However, the size of extended attributes is limited
21303dfd0b0eSChuck Lever  * by the maximum request size, and then further limited by the underlying
21313dfd0b0eSChuck Lever  * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
21323dfd0b0eSChuck Lever  * is 64k). Since there is no kvec- or page-based interface to xattrs,
21333dfd0b0eSChuck Lever  * and we're not dealing with contiguous pages, we need to do some copying.
21343dfd0b0eSChuck Lever  */
213523e50fe3SFrank van der Linden 
213623e50fe3SFrank van der Linden /*
213723e50fe3SFrank van der Linden  * Decode data into buffer.
213823e50fe3SFrank van der Linden  */
213923e50fe3SFrank van der Linden static __be32
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs * argp,struct xdr_buf * xdr,char ** bufp,size_t buflen)214023e50fe3SFrank van der Linden nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
214123e50fe3SFrank van der Linden 		       char **bufp, size_t buflen)
214223e50fe3SFrank van der Linden {
214323e50fe3SFrank van der Linden 	struct page **pages = xdr->pages;
214423e50fe3SFrank van der Linden 	struct kvec *head = xdr->head;
2145c1346a12SChuck Lever 	char *tmp, *dp;
214623e50fe3SFrank van der Linden 	u32 len;
214723e50fe3SFrank van der Linden 
2148c1346a12SChuck Lever 	if (buflen <= head->iov_len) {
2149dbc834e5SDan Carpenter 		/*
215023e50fe3SFrank van der Linden 		 * We're in luck, the head has enough space. Just return
2151c1346a12SChuck Lever 		 * the head, no need for copying.
2152c1346a12SChuck Lever 		 */
215323e50fe3SFrank van der Linden 		*bufp = head->iov_base;
215423e50fe3SFrank van der Linden 		return 0;
215523e50fe3SFrank van der Linden 	}
215623e50fe3SFrank van der Linden 
215723e50fe3SFrank van der Linden 	tmp = svcxdr_tmpalloc(argp, buflen);
215823e50fe3SFrank van der Linden 	if (tmp == NULL)
215923e50fe3SFrank van der Linden 		return nfserr_jukebox;
216023e50fe3SFrank van der Linden 
216123e50fe3SFrank van der Linden 	dp = tmp;
216223e50fe3SFrank van der Linden 	memcpy(dp, head->iov_base, head->iov_len);
216323e50fe3SFrank van der Linden 	buflen -= head->iov_len;
216423e50fe3SFrank van der Linden 	dp += head->iov_len;
216523e50fe3SFrank van der Linden 
216623e50fe3SFrank van der Linden 	while (buflen > 0) {
216723e50fe3SFrank van der Linden 		len = min_t(u32, buflen, PAGE_SIZE);
216823e50fe3SFrank van der Linden 		memcpy(dp, page_address(*pages), len);
216923e50fe3SFrank van der Linden 
217023e50fe3SFrank van der Linden 		buflen -= len;
217123e50fe3SFrank van der Linden 		dp += len;
217223e50fe3SFrank van der Linden 		pages++;
217323e50fe3SFrank van der Linden 	}
217423e50fe3SFrank van der Linden 
217523e50fe3SFrank van der Linden 	*bufp = tmp;
217623e50fe3SFrank van der Linden 	return 0;
217723e50fe3SFrank van der Linden }
217823e50fe3SFrank van der Linden 
217923e50fe3SFrank van der Linden /*
218023e50fe3SFrank van der Linden  * Get a user extended attribute name from the XDR buffer.
218123e50fe3SFrank van der Linden  * It will not have the "user." prefix, so prepend it.
218223e50fe3SFrank van der Linden  * Lastly, check for nul characters in the name.
218323e50fe3SFrank van der Linden  */
218423e50fe3SFrank van der Linden static __be32
nfsd4_decode_xattr_name(struct nfsd4_compoundargs * argp,char ** namep)218523e50fe3SFrank van der Linden nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
218623e50fe3SFrank van der Linden {
218723e50fe3SFrank van der Linden 	char *name, *sp, *dp;
218823e50fe3SFrank van der Linden 	u32 namelen, cnt;
218923e50fe3SFrank van der Linden 	__be32 *p;
219023e50fe3SFrank van der Linden 
219123e50fe3SFrank van der Linden 	if (xdr_stream_decode_u32(argp->xdr, &namelen) < 0)
219223e50fe3SFrank van der Linden 		return nfserr_bad_xdr;
219323e50fe3SFrank van der Linden 	if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
219423e50fe3SFrank van der Linden 		return nfserr_nametoolong;
219523e50fe3SFrank van der Linden 	if (namelen == 0)
219623e50fe3SFrank van der Linden 		return nfserr_bad_xdr;
2197830c7150SChuck Lever 	p = xdr_inline_decode(argp->xdr, namelen);
219823e50fe3SFrank van der Linden 	if (!p)
2199830c7150SChuck Lever 		return nfserr_bad_xdr;
2200830c7150SChuck Lever 	name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
220123e50fe3SFrank van der Linden 	if (!name)
220223e50fe3SFrank van der Linden 		return nfserr_jukebox;
220323e50fe3SFrank van der Linden 	memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
2204830c7150SChuck Lever 
2205830c7150SChuck Lever 	/*
2206830c7150SChuck Lever 	 * Copy the extended attribute name over while checking for 0
2207830c7150SChuck Lever 	 * characters.
220823e50fe3SFrank van der Linden 	 */
220923e50fe3SFrank van der Linden 	sp = (char *)p;
221023e50fe3SFrank van der Linden 	dp = name + XATTR_USER_PREFIX_LEN;
221123e50fe3SFrank van der Linden 	cnt = namelen;
221223e50fe3SFrank van der Linden 
221323e50fe3SFrank van der Linden 	while (cnt-- > 0) {
221423e50fe3SFrank van der Linden 		if (*sp == '\0')
221523e50fe3SFrank van der Linden 			return nfserr_bad_xdr;
221623e50fe3SFrank van der Linden 		*dp++ = *sp++;
221723e50fe3SFrank van der Linden 	}
221823e50fe3SFrank van der Linden 	*dp = '\0';
221923e50fe3SFrank van der Linden 
222023e50fe3SFrank van der Linden 	*namep = name;
222123e50fe3SFrank van der Linden 
222223e50fe3SFrank van der Linden 	return nfs_ok;
2223830c7150SChuck Lever }
222423e50fe3SFrank van der Linden 
222523e50fe3SFrank van der Linden /*
222623e50fe3SFrank van der Linden  * A GETXATTR op request comes without a length specifier. We just set the
222723e50fe3SFrank van der Linden  * maximum length for the reply based on XATTR_SIZE_MAX and the maximum
222823e50fe3SFrank van der Linden  * channel reply size. nfsd_getxattr will probe the length of the xattr,
222923e50fe3SFrank van der Linden  * check it against getxa_len, and allocate + return the value.
2230830c7150SChuck Lever  */
223123e50fe3SFrank van der Linden static __be32
nfsd4_decode_getxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)223223e50fe3SFrank van der Linden nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
223323e50fe3SFrank van der Linden 		      union nfsd4_op_u *u)
223423e50fe3SFrank van der Linden {
223523e50fe3SFrank van der Linden 	struct nfsd4_getxattr *getxattr = &u->getxattr;
223623e50fe3SFrank van der Linden 	__be32 status;
223723e50fe3SFrank van der Linden 	u32 maxcount;
223823e50fe3SFrank van der Linden 
223923e50fe3SFrank van der Linden 	memset(getxattr, 0, sizeof(*getxattr));
224023e50fe3SFrank van der Linden 	status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
2241e78e274eSKees Cook 	if (status)
224223e50fe3SFrank van der Linden 		return status;
2243e78e274eSKees Cook 
224423e50fe3SFrank van der Linden 	maxcount = svc_max_payload(argp->rqstp);
224523e50fe3SFrank van der Linden 	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
224623e50fe3SFrank van der Linden 
22473fdc5464SChuck Lever 	getxattr->getxa_len = maxcount;
224823e50fe3SFrank van der Linden 	return nfs_ok;
224923e50fe3SFrank van der Linden }
225023e50fe3SFrank van der Linden 
225123e50fe3SFrank van der Linden static __be32
nfsd4_decode_setxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)225223e50fe3SFrank van der Linden nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
225323e50fe3SFrank van der Linden 		      union nfsd4_op_u *u)
225423e50fe3SFrank van der Linden {
225523e50fe3SFrank van der Linden 	struct nfsd4_setxattr *setxattr = &u->setxattr;
22563fdc5464SChuck Lever 	u32 flags, maxcount, size;
225723e50fe3SFrank van der Linden 	__be32 status;
225823e50fe3SFrank van der Linden 
225923e50fe3SFrank van der Linden 	memset(setxattr, 0, sizeof(*setxattr));
226023e50fe3SFrank van der Linden 
2261e78e274eSKees Cook 	if (xdr_stream_decode_u32(argp->xdr, &flags) < 0)
226223e50fe3SFrank van der Linden 		return nfserr_bad_xdr;
2263e78e274eSKees Cook 
226423e50fe3SFrank van der Linden 	if (flags > SETXATTR4_REPLACE)
2265403366a7SChuck Lever 		return nfserr_inval;
226623e50fe3SFrank van der Linden 	setxattr->setxa_flags = flags;
22673fdc5464SChuck Lever 
22683fdc5464SChuck Lever 	status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name);
2269403366a7SChuck Lever 	if (status)
2270403366a7SChuck Lever 		return status;
227123e50fe3SFrank van der Linden 
227223e50fe3SFrank van der Linden 	maxcount = svc_max_payload(argp->rqstp);
227323e50fe3SFrank van der Linden 	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
227423e50fe3SFrank van der Linden 
227523e50fe3SFrank van der Linden 	if (xdr_stream_decode_u32(argp->xdr, &size) < 0)
227623e50fe3SFrank van der Linden 		return nfserr_bad_xdr;
227723e50fe3SFrank van der Linden 	if (size > maxcount)
227823e50fe3SFrank van der Linden 		return nfserr_xattr2big;
227923e50fe3SFrank van der Linden 
228023e50fe3SFrank van der Linden 	setxattr->setxa_len = size;
228123e50fe3SFrank van der Linden 	if (size > 0) {
228223e50fe3SFrank van der Linden 		struct xdr_buf payload;
2283403366a7SChuck Lever 
2284403366a7SChuck Lever 		if (!xdr_stream_subsegment(argp->xdr, &payload, size))
228523e50fe3SFrank van der Linden 			return nfserr_bad_xdr;
228623e50fe3SFrank van der Linden 		status = nfsd4_vbuf_from_vector(argp, &payload,
228723e50fe3SFrank van der Linden 						&setxattr->setxa_buf, size);
228823e50fe3SFrank van der Linden 	}
228923e50fe3SFrank van der Linden 
2290c1346a12SChuck Lever 	return nfs_ok;
229123e50fe3SFrank van der Linden }
2292c1346a12SChuck Lever 
2293403366a7SChuck Lever static __be32
nfsd4_decode_listxattrs(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2294c1346a12SChuck Lever nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
229523e50fe3SFrank van der Linden 			union nfsd4_op_u *u)
229623e50fe3SFrank van der Linden {
229723e50fe3SFrank van der Linden 	struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
2298403366a7SChuck Lever 	u32 maxcount;
229923e50fe3SFrank van der Linden 
230023e50fe3SFrank van der Linden 	memset(listxattrs, 0, sizeof(*listxattrs));
230123e50fe3SFrank van der Linden 
230223e50fe3SFrank van der Linden 	if (xdr_stream_decode_u64(argp->xdr, &listxattrs->lsxa_cookie) < 0)
2303e78e274eSKees Cook 		return nfserr_bad_xdr;
230423e50fe3SFrank van der Linden 
2305e78e274eSKees Cook 	/*
230623e50fe3SFrank van der Linden 	 * If the cookie  is too large to have even one user.x attribute
230723e50fe3SFrank van der Linden 	 * plus trailing '\0' left in a maximum size buffer, it's invalid.
23083fdc5464SChuck Lever 	 */
23093fdc5464SChuck Lever 	if (listxattrs->lsxa_cookie >=
23102212036cSChuck Lever 	    (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
23112212036cSChuck Lever 		return nfserr_badcookie;
231223e50fe3SFrank van der Linden 
231323e50fe3SFrank van der Linden 	if (xdr_stream_decode_u32(argp->xdr, &maxcount) < 0)
231423e50fe3SFrank van der Linden 		return nfserr_bad_xdr;
231523e50fe3SFrank van der Linden 	if (maxcount < 8)
231623e50fe3SFrank van der Linden 		/* Always need at least 2 words (length and one character) */
231723e50fe3SFrank van der Linden 		return nfserr_inval;
231823e50fe3SFrank van der Linden 
231923e50fe3SFrank van der Linden 	maxcount = min(maxcount, svc_max_payload(argp->rqstp));
232023e50fe3SFrank van der Linden 	listxattrs->lsxa_maxcount = maxcount;
23212212036cSChuck Lever 
23222212036cSChuck Lever 	return nfs_ok;
232323e50fe3SFrank van der Linden }
232423e50fe3SFrank van der Linden 
232523e50fe3SFrank van der Linden static __be32
nfsd4_decode_removexattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)232623e50fe3SFrank van der Linden nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
232723e50fe3SFrank van der Linden 			 union nfsd4_op_u *u)
232823e50fe3SFrank van der Linden {
232923e50fe3SFrank van der Linden 	struct nfsd4_removexattr *removexattr = &u->removexattr;
23302212036cSChuck Lever 	memset(removexattr, 0, sizeof(*removexattr));
233123e50fe3SFrank van der Linden 	return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
233223e50fe3SFrank van der Linden }
233323e50fe3SFrank van der Linden 
233423e50fe3SFrank van der Linden static __be32
nfsd4_decode_noop(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)2335e78e274eSKees Cook nfsd4_decode_noop(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
233623e50fe3SFrank van der Linden {
2337e78e274eSKees Cook 	return nfs_ok;
23383fdc5464SChuck Lever }
233923e50fe3SFrank van der Linden 
234023e50fe3SFrank van der Linden static __be32
nfsd4_decode_notsupp(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)234123e50fe3SFrank van der Linden nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
234224bab491SAnna Schumaker {
2343e78e274eSKees Cook 	return nfserr_notsupp;
2344347e0ad9SBenny Halevy }
2345347e0ad9SBenny Halevy 
2346347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u);
2347347e0ad9SBenny Halevy 
23483c375c6fSBenny Halevy static const nfsd4_dec nfsd4_dec_ops[] = {
2349e78e274eSKees Cook 	[OP_ACCESS]		= nfsd4_decode_access,
23503c375c6fSBenny Halevy 	[OP_CLOSE]		= nfsd4_decode_close,
23511e685ec2SBenny Halevy 	[OP_COMMIT]		= nfsd4_decode_commit,
23523c375c6fSBenny Halevy 	[OP_CREATE]		= nfsd4_decode_create,
23533c375c6fSBenny Halevy 	[OP_DELEGPURGE]		= nfsd4_decode_notsupp,
2354e78e274eSKees Cook 	[OP_DELEGRETURN]	= nfsd4_decode_delegreturn,
2355347e0ad9SBenny Halevy 	[OP_GETATTR]		= nfsd4_decode_getattr,
2356c1df609dSChuck Lever 	[OP_GETFH]		= nfsd4_decode_noop,
2357e78e274eSKees Cook 	[OP_LINK]		= nfsd4_decode_link,
2358e78e274eSKees Cook 	[OP_LOCK]		= nfsd4_decode_lock,
2359e78e274eSKees Cook 	[OP_LOCKT]		= nfsd4_decode_lockt,
2360e78e274eSKees Cook 	[OP_LOCKU]		= nfsd4_decode_locku,
2361e78e274eSKees Cook 	[OP_LOOKUP]		= nfsd4_decode_lookup,
2362e78e274eSKees Cook 	[OP_LOOKUPP]		= nfsd4_decode_noop,
2363e78e274eSKees Cook 	[OP_NVERIFY]		= nfsd4_decode_verify,
2364e78e274eSKees Cook 	[OP_OPEN]		= nfsd4_decode_open,
2365e78e274eSKees Cook 	[OP_OPENATTR]		= nfsd4_decode_notsupp,
2366e78e274eSKees Cook 	[OP_OPEN_CONFIRM]	= nfsd4_decode_open_confirm,
2367e78e274eSKees Cook 	[OP_OPEN_DOWNGRADE]	= nfsd4_decode_open_downgrade,
2368e78e274eSKees Cook 	[OP_PUTFH]		= nfsd4_decode_putfh,
2369e78e274eSKees Cook 	[OP_PUTPUBFH]		= nfsd4_decode_noop,
2370e78e274eSKees Cook 	[OP_PUTROOTFH]		= nfsd4_decode_noop,
2371e78e274eSKees Cook 	[OP_READ]		= nfsd4_decode_read,
2372e78e274eSKees Cook 	[OP_READDIR]		= nfsd4_decode_readdir,
2373e78e274eSKees Cook 	[OP_READLINK]		= nfsd4_decode_noop,
2374e78e274eSKees Cook 	[OP_REMOVE]		= nfsd4_decode_remove,
2375e78e274eSKees Cook 	[OP_RENAME]		= nfsd4_decode_rename,
2376e78e274eSKees Cook 	[OP_RENEW]		= nfsd4_decode_renew,
2377e78e274eSKees Cook 	[OP_RESTOREFH]		= nfsd4_decode_noop,
2378e78e274eSKees Cook 	[OP_SAVEFH]		= nfsd4_decode_noop,
2379e78e274eSKees Cook 	[OP_SECINFO]		= nfsd4_decode_secinfo,
2380e78e274eSKees Cook 	[OP_SETATTR]		= nfsd4_decode_setattr,
2381e78e274eSKees Cook 	[OP_SETCLIENTID]	= nfsd4_decode_setclientid,
2382e78e274eSKees Cook 	[OP_SETCLIENTID_CONFIRM] = nfsd4_decode_setclientid_confirm,
2383e78e274eSKees Cook 	[OP_VERIFY]		= nfsd4_decode_verify,
2384e78e274eSKees Cook 	[OP_WRITE]		= nfsd4_decode_write,
2385e78e274eSKees Cook 	[OP_RELEASE_LOCKOWNER]	= nfsd4_decode_release_lockowner,
2386e78e274eSKees Cook 
2387e78e274eSKees Cook 	/* new operations for NFSv4.1 */
2388e78e274eSKees Cook 	[OP_BACKCHANNEL_CTL]	= nfsd4_decode_backchannel_ctl,
2389e78e274eSKees Cook 	[OP_BIND_CONN_TO_SESSION] = nfsd4_decode_bind_conn_to_session,
2390e78e274eSKees Cook 	[OP_EXCHANGE_ID]	= nfsd4_decode_exchange_id,
2391e78e274eSKees Cook 	[OP_CREATE_SESSION]	= nfsd4_decode_create_session,
2392e78e274eSKees Cook 	[OP_DESTROY_SESSION]	= nfsd4_decode_destroy_session,
2393e78e274eSKees Cook 	[OP_FREE_STATEID]	= nfsd4_decode_free_stateid,
23942db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= nfsd4_decode_get_dir_delegation,
23952db134ebSAndy Adamson #ifdef CONFIG_NFSD_PNFS
2396e78e274eSKees Cook 	[OP_GETDEVICEINFO]	= nfsd4_decode_getdeviceinfo,
2397e78e274eSKees Cook 	[OP_GETDEVICELIST]	= nfsd4_decode_notsupp,
2398e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_decode_layoutcommit,
2399e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_decode_layoutget,
2400e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_decode_layoutreturn,
2401e78e274eSKees Cook #else
240233a1e6eaSJeff Layton 	[OP_GETDEVICEINFO]	= nfsd4_decode_notsupp,
24039cf514ccSChristoph Hellwig 	[OP_GETDEVICELIST]	= nfsd4_decode_notsupp,
2404e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_decode_notsupp,
2405e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_decode_notsupp,
2406e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_decode_notsupp,
2407e78e274eSKees Cook #endif
2408e78e274eSKees Cook 	[OP_SECINFO_NO_NAME]	= nfsd4_decode_secinfo_no_name,
24099cf514ccSChristoph Hellwig 	[OP_SEQUENCE]		= nfsd4_decode_sequence,
2410e78e274eSKees Cook 	[OP_SET_SSV]		= nfsd4_decode_notsupp,
2411e78e274eSKees Cook 	[OP_TEST_STATEID]	= nfsd4_decode_test_stateid,
2412e78e274eSKees Cook 	[OP_WANT_DELEGATION]	= nfsd4_decode_notsupp,
2413e78e274eSKees Cook 	[OP_DESTROY_CLIENTID]	= nfsd4_decode_destroy_clientid,
2414e78e274eSKees Cook 	[OP_RECLAIM_COMPLETE]	= nfsd4_decode_reclaim_complete,
24159cf514ccSChristoph Hellwig 
2416e78e274eSKees Cook 	/* new operations for NFSv4.2 */
2417e78e274eSKees Cook 	[OP_ALLOCATE]		= nfsd4_decode_fallocate,
2418e78e274eSKees Cook 	[OP_COPY]		= nfsd4_decode_copy,
2419e78e274eSKees Cook 	[OP_COPY_NOTIFY]	= nfsd4_decode_copy_notify,
2420e78e274eSKees Cook 	[OP_DEALLOCATE]		= nfsd4_decode_fallocate,
2421e78e274eSKees Cook 	[OP_IO_ADVISE]		= nfsd4_decode_notsupp,
2422e78e274eSKees Cook 	[OP_LAYOUTERROR]	= nfsd4_decode_notsupp,
242387a15a80SAnna Schumaker 	[OP_LAYOUTSTATS]	= nfsd4_decode_notsupp,
242487a15a80SAnna Schumaker 	[OP_OFFLOAD_CANCEL]	= nfsd4_decode_offload_status,
2425e78e274eSKees Cook 	[OP_OFFLOAD_STATUS]	= nfsd4_decode_offload_status,
2426e78e274eSKees Cook 	[OP_READ_PLUS]		= nfsd4_decode_read,
2427e78e274eSKees Cook 	[OP_SEEK]		= nfsd4_decode_seek,
2428e78e274eSKees Cook 	[OP_WRITE_SAME]		= nfsd4_decode_notsupp,
2429e78e274eSKees Cook 	[OP_CLONE]		= nfsd4_decode_clone,
2430e78e274eSKees Cook 	/* RFC 8276 extended atributes operations */
2431e78e274eSKees Cook 	[OP_GETXATTR]		= nfsd4_decode_getxattr,
2432e78e274eSKees Cook 	[OP_SETXATTR]		= nfsd4_decode_setxattr,
2433e78e274eSKees Cook 	[OP_LISTXATTRS]		= nfsd4_decode_listxattrs,
2434e78e274eSKees Cook 	[OP_REMOVEXATTR]	= nfsd4_decode_removexattr,
2435e78e274eSKees Cook };
2436e78e274eSKees Cook 
2437e78e274eSKees Cook static inline bool
nfsd4_opnum_in_range(struct nfsd4_compoundargs * argp,struct nfsd4_op * op)243823e50fe3SFrank van der Linden nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
2439e78e274eSKees Cook {
2440e78e274eSKees Cook 	if (op->opnum < FIRST_NFS4_OP)
2441e78e274eSKees Cook 		return false;
2442e78e274eSKees Cook 	else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
24432db134ebSAndy Adamson 		return false;
24442db134ebSAndy Adamson 	else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
2445e1a90ebdSAnna Schumaker 		return false;
2446e1a90ebdSAnna Schumaker 	else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
2447e1a90ebdSAnna Schumaker 		return false;
24488217d146SAnna Schumaker 	return true;
2449e1a90ebdSAnna Schumaker }
24508217d146SAnna Schumaker 
2451e1a90ebdSAnna Schumaker static bool
nfsd4_decode_compound(struct nfsd4_compoundargs * argp)24528217d146SAnna Schumaker nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
24538217d146SAnna Schumaker {
24548217d146SAnna Schumaker 	struct nfsd4_op *op;
2455e1a90ebdSAnna Schumaker 	bool cachethis = false;
2456e1a90ebdSAnna Schumaker 	int auth_slack= argp->rqstp->rq_auth_slack;
2457e1a90ebdSAnna Schumaker 	int max_reply = auth_slack + 8; /* opcnt, status */
2458f2feb96bSBenny Halevy 	int readcount = 0;
2459c44b31c2SChuck Lever 	int readbytes = 0;
24601da177e4SLinus Torvalds 	__be32 *p;
24611da177e4SLinus Torvalds 	int i;
24621da177e4SLinus Torvalds 
24631091006cSJ. Bruce Fields 	if (xdr_stream_decode_u32(argp->xdr, &argp->taglen) < 0)
2464a5cddc88SJ. Bruce Fields 		return false;
2465a5cddc88SJ. Bruce Fields 	max_reply += XDR_UNIT;
2466b0e35fdaSJ. Bruce Fields 	argp->tag = NULL;
2467b0e35fdaSJ. Bruce Fields 	if (unlikely(argp->taglen)) {
2468d9b74bdaSChuck Lever 		if (argp->taglen > NFSD4_MAX_TAGLEN)
24691da177e4SLinus Torvalds 			return false;
24701da177e4SLinus Torvalds 		p = xdr_inline_decode(argp->xdr, argp->taglen);
2471d9b74bdaSChuck Lever 		if (!p)
2472c44b31c2SChuck Lever 			return false;
2473d9b74bdaSChuck Lever 		argp->tag = svcxdr_savemem(argp, p, argp->taglen);
2474d9b74bdaSChuck Lever 		if (!argp->tag)
2475d9b74bdaSChuck Lever 			return false;
24761da177e4SLinus Torvalds 		max_reply += xdr_align_size(argp->taglen);
2477c44b31c2SChuck Lever 	}
2478d9b74bdaSChuck Lever 
2479d9b74bdaSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &argp->minorversion) < 0)
2480c44b31c2SChuck Lever 		return false;
24817b723008SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &argp->client_opcnt) < 0)
2482d9b74bdaSChuck Lever 		return false;
2483c44b31c2SChuck Lever 	argp->opcnt = min_t(u32, argp->client_opcnt,
2484d9b74bdaSChuck Lever 			    NFSD_MAX_OPS_PER_COMPOUND);
2485d9b74bdaSChuck Lever 
2486d9b74bdaSChuck Lever 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
2487d9b74bdaSChuck Lever 		argp->ops = vcalloc(argp->opcnt, sizeof(*argp->ops));
2488c44b31c2SChuck Lever 		if (!argp->ops) {
24897518a3dcSChuck Lever 			argp->ops = argp->iops;
2490c44b31c2SChuck Lever 			return false;
24917518a3dcSChuck Lever 		}
24927518a3dcSChuck Lever 	}
24931da177e4SLinus Torvalds 
2494e8c96f8cSTobias Klauser 	if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
249580e591ceSChuck Lever 		argp->opcnt = 0;
24961da177e4SLinus Torvalds 
24971da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
2498c44b31c2SChuck Lever 		op = &argp->ops[i];
24991da177e4SLinus Torvalds 		op->replay = NULL;
25001da177e4SLinus Torvalds 		op->opdesc = NULL;
25011da177e4SLinus Torvalds 
2502e1a90ebdSAnna Schumaker 		if (xdr_stream_decode_u32(argp->xdr, &op->opnum) < 0)
250330cff1ffSBenny Halevy 			return false;
250430cff1ffSBenny Halevy 		if (nfsd4_opnum_in_range(argp, op)) {
25051da177e4SLinus Torvalds 			op->opdesc = OPDESC(op);
25061da177e4SLinus Torvalds 			op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
25071da177e4SLinus Torvalds 			if (op->status != nfs_ok)
2508804d8e0aSChuck Lever 				trace_nfsd_compound_decode_err(argp->rqstp,
25091da177e4SLinus Torvalds 							       argp->opcnt, i,
25103a237b4aSChuck Lever 							       op->opnum,
2511c44b31c2SChuck Lever 							       op->status);
251208281341SChuck Lever 		} else {
2513804d8e0aSChuck Lever 			op->opnum = OP_ILLEGAL;
2514e1a90ebdSAnna Schumaker 			op->status = nfserr_op_illegal;
251508281341SChuck Lever 		}
251608281341SChuck Lever 
251708281341SChuck Lever 		/*
251808281341SChuck Lever 		 * We'll try to cache the result in the DRC if any one
251908281341SChuck Lever 		 * op in the compound wants to be cached:
252008281341SChuck Lever 		 */
25211da177e4SLinus Torvalds 		cachethis |= nfsd4_cache_this_op(op);
25221da177e4SLinus Torvalds 
25231da177e4SLinus Torvalds 		if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
2524804d8e0aSChuck Lever 			readcount++;
25251091006cSJ. Bruce Fields 			readbytes += nfsd4_max_reply(argp->rqstp, op);
25261091006cSJ. Bruce Fields 		} else
25271091006cSJ. Bruce Fields 			max_reply += nfsd4_max_reply(argp->rqstp, op);
25281091006cSJ. Bruce Fields 		/*
25291091006cSJ. Bruce Fields 		 * OP_LOCK and OP_LOCKT may return a conflicting lock.
25306ff40decSJ. Bruce Fields 		 * (Special case because it will just skip encoding this
2531528b8493SAnna Schumaker 		 * if it runs out of xdr buffer space, and it is the only
2532b0e35fdaSJ. Bruce Fields 		 * operation that behaves this way.)
2533b0e35fdaSJ. Bruce Fields 		 */
2534b0e35fdaSJ. Bruce Fields 		if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT)
25354f0cefbfSJ. Bruce Fields 			max_reply += NFS4_OPAQUE_LIMIT;
2536f7b43d0cSJ. Bruce Fields 
25377323f0d2SKinglong Mee 		if (op->status) {
25387323f0d2SKinglong Mee 			argp->opcnt = i+1;
25397323f0d2SKinglong Mee 			break;
25407323f0d2SKinglong Mee 		}
2541f7b43d0cSJ. Bruce Fields 	}
25427323f0d2SKinglong Mee 	/* Sessions make the DRC unnecessary: */
2543f7b43d0cSJ. Bruce Fields 	if (argp->minorversion)
2544e372ba60SJ. Bruce Fields 		cachethis = false;
2545e372ba60SJ. Bruce Fields 	svc_reserve(argp->rqstp, max_reply + readbytes);
2546e372ba60SJ. Bruce Fields 	argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
2547e372ba60SJ. Bruce Fields 
2548e372ba60SJ. Bruce Fields 	argp->splice_ok = nfsd_read_splice_ok(argp->rqstp);
25491da177e4SLinus Torvalds 	if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
25501091006cSJ. Bruce Fields 		argp->splice_ok = false;
25511091006cSJ. Bruce Fields 
25521091006cSJ. Bruce Fields 	return true;
2553b0e35fdaSJ. Bruce Fields }
25541091006cSJ. Bruce Fields 
nfsd4_encode_nfs_fh4(struct xdr_stream * xdr,struct knfsd_fh * fh_handle)25551da177e4SLinus Torvalds static __be32 nfsd4_encode_nfs_fh4(struct xdr_stream *xdr,
2556a2c91753SChuck Lever 				   struct knfsd_fh *fh_handle)
2557a5cddc88SJ. Bruce Fields {
2558a2c91753SChuck Lever 	return nfsd4_encode_opaque(xdr, fh_handle->fh_raw, fh_handle->fh_size);
2559b0e35fdaSJ. Bruce Fields }
2560c44b31c2SChuck Lever 
25611da177e4SLinus Torvalds /* This is a frequently-encoded type; open-coded for speed */
nfsd4_encode_nfstime4(struct xdr_stream * xdr,const struct timespec64 * tv)25621da177e4SLinus Torvalds static __be32 nfsd4_encode_nfstime4(struct xdr_stream *xdr,
25633283bf64SChuck Lever 				    const struct timespec64 *tv)
25643283bf64SChuck Lever {
25653283bf64SChuck Lever 	__be32 *p;
25663283bf64SChuck Lever 
25673283bf64SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 3);
25683283bf64SChuck Lever 	if (!p)
2569eed4d1adSChuck Lever 		return nfserr_resource;
257026217679SChuck Lever 	p = xdr_encode_hyper(p, tv->tv_sec);
2571eed4d1adSChuck Lever 	*p = cpu_to_be32(tv->tv_nsec);
257226217679SChuck Lever 	return nfs_ok;
257326217679SChuck Lever }
257426217679SChuck Lever 
nfsd4_encode_specdata4(struct xdr_stream * xdr,unsigned int major,unsigned int minor)257526217679SChuck Lever static __be32 nfsd4_encode_specdata4(struct xdr_stream *xdr,
257626217679SChuck Lever 				     unsigned int major, unsigned int minor)
257726217679SChuck Lever {
2578eed4d1adSChuck Lever 	__be32 status;
257926217679SChuck Lever 
258026217679SChuck Lever 	status = nfsd4_encode_uint32_t(xdr, major);
258126217679SChuck Lever 	if (status != nfs_ok)
258226217679SChuck Lever 		return status;
2583a460cda2SChuck Lever 	return nfsd4_encode_uint32_t(xdr, minor);
2584a460cda2SChuck Lever }
2585a460cda2SChuck Lever 
2586a460cda2SChuck Lever static __be32
nfsd4_encode_change_info4(struct xdr_stream * xdr,const struct nfsd4_change_info * c)2587a460cda2SChuck Lever nfsd4_encode_change_info4(struct xdr_stream *xdr, const struct nfsd4_change_info *c)
2588a460cda2SChuck Lever {
2589a460cda2SChuck Lever 	__be32 status;
2590a460cda2SChuck Lever 
2591a460cda2SChuck Lever 	status = nfsd4_encode_bool(xdr, c->atomic);
2592a460cda2SChuck Lever 	if (status != nfs_ok)
2593a460cda2SChuck Lever 		return status;
259466a21db7SChuck Lever 	status = nfsd4_encode_changeid4(xdr, c->before_change);
2595263453d9SChuck Lever 	if (status != nfs_ok)
2596c654b8a9SJ. Bruce Fields 		return status;
2597263453d9SChuck Lever 	return nfsd4_encode_changeid4(xdr, c->after_change);
2598263453d9SChuck Lever }
2599263453d9SChuck Lever 
nfsd4_encode_netaddr4(struct xdr_stream * xdr,const struct nfs42_netaddr * addr)2600263453d9SChuck Lever static __be32 nfsd4_encode_netaddr4(struct xdr_stream *xdr,
2601263453d9SChuck Lever 				    const struct nfs42_netaddr *addr)
2602263453d9SChuck Lever {
2603263453d9SChuck Lever 	__be32 status;
2604263453d9SChuck Lever 
2605263453d9SChuck Lever 	/* na_r_netid */
2606c654b8a9SJ. Bruce Fields 	status = nfsd4_encode_opaque(xdr, addr->netid, addr->netid_len);
26071da177e4SLinus Torvalds 	if (status != nfs_ok)
260821d316a7SChuck Lever 		return status;
260921d316a7SChuck Lever 	/* na_r_addr */
261021d316a7SChuck Lever 	return nfsd4_encode_opaque(xdr, addr->addr, addr->addr_len);
261121d316a7SChuck Lever }
261221d316a7SChuck Lever 
261321d316a7SChuck Lever /* Encode as an array of strings the string given with components
261421d316a7SChuck Lever  * separated @sep, escaped with esc_enter and esc_exit.
261521d316a7SChuck Lever  */
nfsd4_encode_components_esc(struct xdr_stream * xdr,char sep,char * components,char esc_enter,char esc_exit)261621d316a7SChuck Lever static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
261721d316a7SChuck Lever 					  char *components, char esc_enter,
261821d316a7SChuck Lever 					  char esc_exit)
261921d316a7SChuck Lever {
262021d316a7SChuck Lever 	__be32 *p;
262181c3f413SJ.Bruce Fields 	__be32 pathlen;
2622e7a0444aSWeston Andros Adamson 	int pathlen_offset;
262381c3f413SJ.Bruce Fields 	int strlen, count=0;
2624ddd1ea56SJ. Bruce Fields 	char *str, *end, *next;
2625ddd1ea56SJ. Bruce Fields 
2626ddd1ea56SJ. Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
262781c3f413SJ.Bruce Fields 
2628ddd1ea56SJ. Bruce Fields 	pathlen_offset = xdr->buf->len;
2629082d4bd7SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2630082d4bd7SJ. Bruce Fields 	if (!p)
263181c3f413SJ.Bruce Fields 		return nfserr_resource;
2632e7a0444aSWeston Andros Adamson 	p++; /* We will fill this in with @count later */
263381c3f413SJ.Bruce Fields 
263481c3f413SJ.Bruce Fields 	end = str = components;
2635082d4bd7SJ. Bruce Fields 	while (*end) {
2636082d4bd7SJ. Bruce Fields 		bool found_esc = false;
2637ddd1ea56SJ. Bruce Fields 
2638ddd1ea56SJ. Bruce Fields 		/* try to parse as esc_start, ..., esc_end, sep */
263981c3f413SJ.Bruce Fields 		if (*str == esc_enter) {
2640082d4bd7SJ. Bruce Fields 			for (; *end && (*end != esc_exit); end++)
2641082d4bd7SJ. Bruce Fields 				/* find esc_exit or end of string */;
264281c3f413SJ.Bruce Fields 			next = end + 1;
264381c3f413SJ.Bruce Fields 			if (*end && (!*next || *next == sep)) {
2644e7a0444aSWeston Andros Adamson 				str++;
2645e7a0444aSWeston Andros Adamson 				found_esc = true;
2646e7a0444aSWeston Andros Adamson 			}
2647e7a0444aSWeston Andros Adamson 		}
2648e7a0444aSWeston Andros Adamson 
2649e7a0444aSWeston Andros Adamson 		if (!found_esc)
2650e7a0444aSWeston Andros Adamson 			for (; *end && (*end != sep); end++)
2651e7a0444aSWeston Andros Adamson 				/* find sep or end of string */;
2652e7a0444aSWeston Andros Adamson 
2653e7a0444aSWeston Andros Adamson 		strlen = end - str;
2654e7a0444aSWeston Andros Adamson 		if (strlen) {
2655e7a0444aSWeston Andros Adamson 			p = xdr_reserve_space(xdr, strlen + 4);
2656e7a0444aSWeston Andros Adamson 			if (!p)
2657e7a0444aSWeston Andros Adamson 				return nfserr_resource;
265881c3f413SJ.Bruce Fields 			p = xdr_encode_opaque(p, str, strlen);
2659e7a0444aSWeston Andros Adamson 			count++;
2660e7a0444aSWeston Andros Adamson 		}
266181c3f413SJ.Bruce Fields 		else
266281c3f413SJ.Bruce Fields 			end++;
2663ddd1ea56SJ. Bruce Fields 		if (found_esc)
2664ddd1ea56SJ. Bruce Fields 			end = next;
266581c3f413SJ.Bruce Fields 
26660c0c267bSJ. Bruce Fields 		str = end;
266781c3f413SJ.Bruce Fields 	}
266881c3f413SJ.Bruce Fields 	pathlen = htonl(count);
266981c3f413SJ.Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
267081c3f413SJ.Bruce Fields 	return 0;
26715a64e569SBenjamin Coddington }
26725a64e569SBenjamin Coddington 
26735a64e569SBenjamin Coddington /* Encode as an array of strings the string given with components
267481c3f413SJ.Bruce Fields  * separated @sep.
267581c3f413SJ.Bruce Fields  */
nfsd4_encode_components(struct xdr_stream * xdr,char sep,char * components)2676bf7491f1SBenjamin Coddington static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
2677082d4bd7SJ. Bruce Fields 				      char *components)
267881c3f413SJ.Bruce Fields {
267981c3f413SJ.Bruce Fields 	return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
268081c3f413SJ.Bruce Fields }
2681e7a0444aSWeston Andros Adamson 
nfsd4_encode_fs_location4(struct xdr_stream * xdr,struct nfsd4_fs_location * location)2682e7a0444aSWeston Andros Adamson static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
2683e7a0444aSWeston Andros Adamson 					struct nfsd4_fs_location *location)
2684ddd1ea56SJ. Bruce Fields {
2685ddd1ea56SJ. Bruce Fields 	__be32 status;
2686e7a0444aSWeston Andros Adamson 
2687ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
2688e7a0444aSWeston Andros Adamson 						'[', ']');
2689e7a0444aSWeston Andros Adamson 	if (status)
2690ddd1ea56SJ. Bruce Fields 		return status;
2691ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components(xdr, '/', location->path);
269281c3f413SJ.Bruce Fields 	if (status)
2693b37ad28bSAl Viro 		return status;
269481c3f413SJ.Bruce Fields 	return nfs_ok;
2695ddd1ea56SJ. Bruce Fields }
2696e7a0444aSWeston Andros Adamson 
nfsd4_encode_pathname4(struct xdr_stream * xdr,const struct path * root,const struct path * path)269781c3f413SJ.Bruce Fields static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
269881c3f413SJ.Bruce Fields 				     const struct path *root,
2699ddd1ea56SJ. Bruce Fields 				     const struct path *path)
270081c3f413SJ.Bruce Fields {
270181c3f413SJ.Bruce Fields 	struct path cur = *path;
2702a1469a37SChuck Lever 	__be32 *p;
270381c3f413SJ.Bruce Fields 	struct dentry **components = NULL;
270481c3f413SJ.Bruce Fields 	unsigned int ncomponents = 0;
2705a1469a37SChuck Lever 	__be32 err = nfserr_jukebox;
2706ddd1ea56SJ. Bruce Fields 
2707ddd1ea56SJ. Bruce Fields 	dprintk("nfsd4_encode_components(");
270881c3f413SJ.Bruce Fields 
2709301f0268SAl Viro 	path_get(&cur);
2710ddd1ea56SJ. Bruce Fields 	/* First walk the path up to the nfsd root, and store the
2711ed748aacSTrond Myklebust 	 * dentries/path components in an array.
2712ed748aacSTrond Myklebust 	 */
2713ed748aacSTrond Myklebust 	for (;;) {
271481c3f413SJ.Bruce Fields 		if (path_equal(&cur, root))
2715ed748aacSTrond Myklebust 			break;
271681c3f413SJ.Bruce Fields 		if (cur.dentry == cur.mnt->mnt_root) {
2717ed748aacSTrond Myklebust 			if (follow_up(&cur))
2718ed748aacSTrond Myklebust 				continue;
2719ed748aacSTrond Myklebust 			goto out_free;
2720ed748aacSTrond Myklebust 		}
2721ed748aacSTrond Myklebust 		if ((ncomponents & 15) == 0) {
2722b77a4b2eSKinglong Mee 			struct dentry **new;
2723ed748aacSTrond Myklebust 			new = krealloc(components,
2724ed748aacSTrond Myklebust 					sizeof(*new) * (ncomponents + 16),
2725ed748aacSTrond Myklebust 					GFP_KERNEL);
2726ed748aacSTrond Myklebust 			if (!new)
2727ed748aacSTrond Myklebust 				goto out_free;
272881c3f413SJ.Bruce Fields 			components = new;
2729ed748aacSTrond Myklebust 		}
2730ed748aacSTrond Myklebust 		components[ncomponents++] = cur.dentry;
2731ed748aacSTrond Myklebust 		cur.dentry = dget_parent(cur.dentry);
2732ed748aacSTrond Myklebust 	}
2733ed748aacSTrond Myklebust 	err = nfserr_resource;
2734ed748aacSTrond Myklebust 	p = xdr_reserve_space(xdr, 4);
2735ed748aacSTrond Myklebust 	if (!p)
2736ed748aacSTrond Myklebust 		goto out_free;
2737ed748aacSTrond Myklebust 	*p++ = cpu_to_be32(ncomponents);
2738ed748aacSTrond Myklebust 
2739ed748aacSTrond Myklebust 	while (ncomponents) {
2740ed748aacSTrond Myklebust 		struct dentry *dentry = components[ncomponents - 1];
2741ddd1ea56SJ. Bruce Fields 		unsigned int len;
2742ddd1ea56SJ. Bruce Fields 
2743ddd1ea56SJ. Bruce Fields 		spin_lock(&dentry->d_lock);
2744ed748aacSTrond Myklebust 		len = dentry->d_name.len;
2745c373b0a4SJ. Bruce Fields 		p = xdr_reserve_space(xdr, len + 4);
2746ed748aacSTrond Myklebust 		if (!p) {
2747ed748aacSTrond Myklebust 			spin_unlock(&dentry->d_lock);
2748ed748aacSTrond Myklebust 			goto out_free;
2749301f0268SAl Viro 		}
2750ed748aacSTrond Myklebust 		p = xdr_encode_opaque(p, dentry->d_name.name, len);
2751301f0268SAl Viro 		dprintk("/%pd", dentry);
2752301f0268SAl Viro 		spin_unlock(&dentry->d_lock);
2753ddd1ea56SJ. Bruce Fields 		dput(dentry);
2754ddd1ea56SJ. Bruce Fields 		ncomponents--;
2755301f0268SAl Viro 	}
2756ed748aacSTrond Myklebust 
2757301f0268SAl Viro 	err = 0;
27580c0c267bSJ. Bruce Fields out_free:
2759a455589fSAl Viro 	dprintk(")\n");
2760301f0268SAl Viro 	while (ncomponents)
2761ed748aacSTrond Myklebust 		dput(components[--ncomponents]);
2762ed748aacSTrond Myklebust 	kfree(components);
2763ed748aacSTrond Myklebust 	path_put(&cur);
2764ed748aacSTrond Myklebust 	return err;
2765ed748aacSTrond Myklebust }
2766ed748aacSTrond Myklebust 
nfsd4_encode_fs_locations4(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct svc_export * exp)2767ed748aacSTrond Myklebust static __be32 nfsd4_encode_fs_locations4(struct xdr_stream *xdr,
2768ed748aacSTrond Myklebust 					 struct svc_rqst *rqstp,
2769ed748aacSTrond Myklebust 					 struct svc_export *exp)
2770ed748aacSTrond Myklebust {
2771ed748aacSTrond Myklebust 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
2772ed748aacSTrond Myklebust 	struct svc_export *exp_ps;
2773ed748aacSTrond Myklebust 	unsigned int i;
2774ed748aacSTrond Myklebust 	__be32 status;
2775a1469a37SChuck Lever 
2776a1469a37SChuck Lever 	/* fs_root */
2777a1469a37SChuck Lever 	exp_ps = rqst_find_fsidzero_export(rqstp);
2778ed748aacSTrond Myklebust 	if (IS_ERR(exp_ps))
2779a1469a37SChuck Lever 		return nfserrno(PTR_ERR(exp_ps));
2780ed748aacSTrond Myklebust 	status = nfsd4_encode_pathname4(xdr, &exp_ps->ex_path, &exp->ex_path);
2781a1469a37SChuck Lever 	exp_put(exp_ps);
2782a1469a37SChuck Lever 	if (status != nfs_ok)
2783ed748aacSTrond Myklebust 		return status;
2784a1469a37SChuck Lever 
2785ed748aacSTrond Myklebust 	/* locations<> */
2786ed748aacSTrond Myklebust 	if (xdr_stream_encode_u32(xdr, fslocs->locations_count) != XDR_UNIT)
2787ed748aacSTrond Myklebust 		return nfserr_resource;
2788a1469a37SChuck Lever 	for (i = 0; i < fslocs->locations_count; i++) {
2789ed748aacSTrond Myklebust 		status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
2790a1469a37SChuck Lever 		if (status != nfs_ok)
279181c3f413SJ.Bruce Fields 			return status;
2792a1469a37SChuck Lever 	}
2793a1469a37SChuck Lever 
2794a1469a37SChuck Lever 	return nfs_ok;
279581c3f413SJ.Bruce Fields }
279681c3f413SJ.Bruce Fields 
nfsd4_encode_nfsace4(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct nfs4_ace * ace)2797ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_nfsace4(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2798a1469a37SChuck Lever 				   struct nfs4_ace *ace)
279981c3f413SJ.Bruce Fields {
280081c3f413SJ.Bruce Fields 	__be32 status;
2801a1469a37SChuck Lever 
2802a1469a37SChuck Lever 	/* type */
280381c3f413SJ.Bruce Fields 	status = nfsd4_encode_acetype4(xdr, ace->type);
28041da177e4SLinus Torvalds 	if (status != nfs_ok)
28050207ee08SChuck Lever 		return nfserr_resource;
2806ddd1ea56SJ. Bruce Fields 	/* flag */
28071da177e4SLinus Torvalds 	status = nfsd4_encode_aceflag4(xdr, ace->flag);
28080207ee08SChuck Lever 	if (status != nfs_ok)
28090207ee08SChuck Lever 		return nfserr_resource;
28100207ee08SChuck Lever 	/* access mask */
28110207ee08SChuck Lever 	status = nfsd4_encode_acemask4(xdr, ace->access_mask & NFS4_ACE_MASK_ALL);
28120207ee08SChuck Lever 	if (status != nfs_ok)
28130207ee08SChuck Lever 		return nfserr_resource;
28140207ee08SChuck Lever 	/* who */
28150207ee08SChuck Lever 	if (ace->whotype != NFS4_ACL_WHO_NAMED)
28160207ee08SChuck Lever 		return nfs4_acl_write_who(xdr, ace->whotype);
28170207ee08SChuck Lever 	if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
28180207ee08SChuck Lever 		return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
28190207ee08SChuck Lever 	return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
28200207ee08SChuck Lever }
28210207ee08SChuck Lever 
28220207ee08SChuck Lever #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
28233554116dSJ. Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
2824ddd1ea56SJ. Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
28250207ee08SChuck Lever #define WORD2_ABSENT_FS_ATTRS 0
2826ddd1ea56SJ. Bruce Fields 
2827ddd1ea56SJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
28281da177e4SLinus Torvalds static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)28291da177e4SLinus Torvalds nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
283042ca0993SJ.Bruce Fields 			    void *context, int len)
283142ca0993SJ.Bruce Fields {
283242ca0993SJ.Bruce Fields 	__be32 *p;
2833c2227a39SKinglong Mee 
283442ca0993SJ.Bruce Fields 	p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
283518032ca0SDavid Quigley 	if (!p)
283618032ca0SDavid Quigley 		return nfserr_resource;
2837ddd1ea56SJ. Bruce Fields 
2838ddd1ea56SJ. Bruce Fields 	/*
283918032ca0SDavid Quigley 	 * For now we use a 0 here to indicate the null translation; in
2840ddd1ea56SJ. Bruce Fields 	 * the future we may place a call to translation code here.
284118032ca0SDavid Quigley 	 */
2842ddd1ea56SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* lfs */
2843ddd1ea56SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* pi */
284418032ca0SDavid Quigley 	p = xdr_encode_opaque(p, context, len);
284518032ca0SDavid Quigley 	return 0;
284618032ca0SDavid Quigley }
284718032ca0SDavid Quigley #else
284818032ca0SDavid Quigley static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)284918032ca0SDavid Quigley nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2850c373b0a4SJ. Bruce Fields 			    void *context, int len)
2851c373b0a4SJ. Bruce Fields { return 0; }
285218032ca0SDavid Quigley #endif
285318032ca0SDavid Quigley 
fattr_handle_absent_fs(u32 * bmval0,u32 * bmval1,u32 * bmval2,u32 * rdattr_err)285418032ca0SDavid Quigley static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
285518032ca0SDavid Quigley {
285618032ca0SDavid Quigley 	/* As per referral draft:  */
2857ddd1ea56SJ. Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
2858ddd1ea56SJ. Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
285918032ca0SDavid Quigley 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
286018032ca0SDavid Quigley 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
286118032ca0SDavid Quigley 			*rdattr_err = NFSERR_MOVED;
2862c2227a39SKinglong Mee 		else
286342ca0993SJ.Bruce Fields 			return nfserr_moved;
286442ca0993SJ.Bruce Fields 	}
286542ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
286642ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
286742ca0993SJ.Bruce Fields 	*bmval2 &= WORD2_ABSENT_FS_ATTRS;
286842ca0993SJ.Bruce Fields 	return 0;
286942ca0993SJ.Bruce Fields }
287042ca0993SJ.Bruce Fields 
287142ca0993SJ.Bruce Fields 
nfsd4_get_mounted_on_ino(struct svc_export * exp,u64 * pino)287242ca0993SJ.Bruce Fields static int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino)
287342ca0993SJ.Bruce Fields {
287442ca0993SJ.Bruce Fields 	struct path path = exp->ex_path;
2875c2227a39SKinglong Mee 	struct kstat stat;
287642ca0993SJ.Bruce Fields 	int err;
287742ca0993SJ.Bruce Fields 
28781da177e4SLinus Torvalds 	path_get(&path);
2879ae7095a7SJ. Bruce Fields 	while (follow_up(&path)) {
28806106d911SJeff Layton 		if (path.dentry != path.mnt->mnt_root)
2881ae7095a7SJ. Bruce Fields 			break;
2882ae7095a7SJ. Bruce Fields 	}
28836106d911SJeff Layton 	err = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT);
2884ae7095a7SJ. Bruce Fields 	path_put(&path);
2885ae7095a7SJ. Bruce Fields 	if (!err)
2886ae7095a7SJ. Bruce Fields 		*pino = stat.ino;
2887ae7095a7SJ. Bruce Fields 	return err;
2888ae7095a7SJ. Bruce Fields }
2889ae7095a7SJ. Bruce Fields 
2890ae7095a7SJ. Bruce Fields static __be32
nfsd4_encode_bitmap4(struct xdr_stream * xdr,u32 bmval0,u32 bmval1,u32 bmval2)28916106d911SJeff Layton nfsd4_encode_bitmap4(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2)
2892ae7095a7SJ. Bruce Fields {
28936106d911SJeff Layton 	__be32 *p;
28946106d911SJeff Layton 
2895ae7095a7SJ. Bruce Fields 	if (bmval2) {
2896ae7095a7SJ. Bruce Fields 		p = xdr_reserve_space(xdr, XDR_UNIT * 4);
2897ae7095a7SJ. Bruce Fields 		if (!p)
289875976de6SKinglong Mee 			goto out_resource;
2899e64301f5SChuck Lever 		*p++ = cpu_to_be32(3);
290075976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
290175976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval1);
290275976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval2);
290375976de6SKinglong Mee 	} else if (bmval1) {
2904e64301f5SChuck Lever 		p = xdr_reserve_space(xdr, XDR_UNIT * 3);
290575976de6SKinglong Mee 		if (!p)
290675976de6SKinglong Mee 			goto out_resource;
290775976de6SKinglong Mee 		*p++ = cpu_to_be32(2);
290875976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
290975976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval1);
291075976de6SKinglong Mee 	} else {
291175976de6SKinglong Mee 		p = xdr_reserve_space(xdr, XDR_UNIT * 2);
2912e64301f5SChuck Lever 		if (!p)
291375976de6SKinglong Mee 			goto out_resource;
291475976de6SKinglong Mee 		*p++ = cpu_to_be32(1);
291575976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
291675976de6SKinglong Mee 	}
291775976de6SKinglong Mee 
291875976de6SKinglong Mee 	return nfs_ok;
2919e64301f5SChuck Lever out_resource:
292075976de6SKinglong Mee 	return nfserr_resource;
292175976de6SKinglong Mee }
292275976de6SKinglong Mee 
292375976de6SKinglong Mee struct nfsd4_fattr_args {
292475976de6SKinglong Mee 	struct svc_rqst		*rqstp;
292575976de6SKinglong Mee 	struct svc_fh		*fhp;
2926e64301f5SChuck Lever 	struct svc_export	*exp;
292775976de6SKinglong Mee 	struct dentry		*dentry;
292875976de6SKinglong Mee 	struct kstat		stat;
292975976de6SKinglong Mee 	struct kstatfs		statfs;
293075976de6SKinglong Mee 	struct nfs4_acl		*acl;
293183ab8678SChuck Lever 	u64			size;
2932c9090e27SChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
293383ab8678SChuck Lever 	void			*context;
293436ed7e64SChuck Lever 	int			contextlen;
2935c9090e27SChuck Lever #endif
293683ab8678SChuck Lever 	u32			rdattr_err;
293783ab8678SChuck Lever 	bool			contextsupport;
293883ab8678SChuck Lever 	bool			ignore_crossmnt;
293983ab8678SChuck Lever };
2940f59388a5SChuck Lever 
2941f59388a5SChuck Lever typedef __be32(*nfsd4_enc_attr)(struct xdr_stream *xdr,
2942f59388a5SChuck Lever 				const struct nfsd4_fattr_args *args);
2943f59388a5SChuck Lever 
nfsd4_encode_fattr4__noop(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)294483ab8678SChuck Lever static __be32 nfsd4_encode_fattr4__noop(struct xdr_stream *xdr,
294583ab8678SChuck Lever 					const struct nfsd4_fattr_args *args)
29461b9097e3SChuck Lever {
294783ab8678SChuck Lever 	return nfs_ok;
294883ab8678SChuck Lever }
2949fce7913bSChuck Lever 
nfsd4_encode_fattr4__true(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2950fce7913bSChuck Lever static __be32 nfsd4_encode_fattr4__true(struct xdr_stream *xdr,
2951fce7913bSChuck Lever 					const struct nfsd4_fattr_args *args)
2952fce7913bSChuck Lever {
2953fce7913bSChuck Lever 	return nfsd4_encode_bool(xdr, true);
2954fce7913bSChuck Lever }
2955fce7913bSChuck Lever 
nfsd4_encode_fattr4__false(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2956fce7913bSChuck Lever static __be32 nfsd4_encode_fattr4__false(struct xdr_stream *xdr,
2957fce7913bSChuck Lever 					 const struct nfsd4_fattr_args *args)
2958c88cb472SChuck Lever {
2959c88cb472SChuck Lever 	return nfsd4_encode_bool(xdr, false);
2960c88cb472SChuck Lever }
2961c88cb472SChuck Lever 
nfsd4_encode_fattr4_supported_attrs(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2962c88cb472SChuck Lever static __be32 nfsd4_encode_fattr4_supported_attrs(struct xdr_stream *xdr,
2963c88cb472SChuck Lever 						  const struct nfsd4_fattr_args *args)
29648c442288SChuck Lever {
29658c442288SChuck Lever 	struct nfsd4_compoundres *resp = args->rqstp->rq_resp;
29668c442288SChuck Lever 	u32 minorversion = resp->cstate.minorversion;
29678c442288SChuck Lever 	u32 supp[3];
29688c442288SChuck Lever 
29698c442288SChuck Lever 	memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
2970c9090e27SChuck Lever 	if (!IS_POSIXACL(d_inode(args->dentry)))
2971c9090e27SChuck Lever 		supp[0] &= ~FATTR4_WORD0_ACL;
2972c9090e27SChuck Lever 	if (!args->contextsupport)
2973c9090e27SChuck Lever 		supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
2974c9090e27SChuck Lever 
2975c9090e27SChuck Lever 	return nfsd4_encode_bitmap4(xdr, supp[0], supp[1], supp[2]);
2976c9090e27SChuck Lever }
2977c9090e27SChuck Lever 
nfsd4_encode_fattr4_type(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)2978c9090e27SChuck Lever static __be32 nfsd4_encode_fattr4_type(struct xdr_stream *xdr,
2979c9090e27SChuck Lever 				       const struct nfsd4_fattr_args *args)
2980c9090e27SChuck Lever {
2981c9090e27SChuck Lever 	__be32 *p;
2982c9090e27SChuck Lever 
2983c9090e27SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT);
2984c9090e27SChuck Lever 	if (!p)
2985c9090e27SChuck Lever 		return nfserr_resource;
2986b06cf375SChuck Lever 
2987b06cf375SChuck Lever 	switch (args->stat.mode & S_IFMT) {
2988b06cf375SChuck Lever 	case S_IFIFO:
2989b06cf375SChuck Lever 		*p = cpu_to_be32(NF4FIFO);
2990b06cf375SChuck Lever 		break;
2991b06cf375SChuck Lever 	case S_IFCHR:
2992b06cf375SChuck Lever 		*p = cpu_to_be32(NF4CHR);
2993b06cf375SChuck Lever 		break;
2994b06cf375SChuck Lever 	case S_IFDIR:
2995b06cf375SChuck Lever 		*p = cpu_to_be32(NF4DIR);
2996b06cf375SChuck Lever 		break;
2997b06cf375SChuck Lever 	case S_IFBLK:
2998b06cf375SChuck Lever 		*p = cpu_to_be32(NF4BLK);
2999b06cf375SChuck Lever 		break;
3000b06cf375SChuck Lever 	case S_IFLNK:
3001b06cf375SChuck Lever 		*p = cpu_to_be32(NF4LNK);
3002b06cf375SChuck Lever 		break;
3003b06cf375SChuck Lever 	case S_IFREG:
3004b06cf375SChuck Lever 		*p = cpu_to_be32(NF4REG);
3005b06cf375SChuck Lever 		break;
3006b06cf375SChuck Lever 	case S_IFSOCK:
3007b06cf375SChuck Lever 		*p = cpu_to_be32(NF4SOCK);
3008b06cf375SChuck Lever 		break;
3009b06cf375SChuck Lever 	default:
3010b06cf375SChuck Lever 		return nfserr_serverfault;
3011b06cf375SChuck Lever 	}
3012b06cf375SChuck Lever 
3013b06cf375SChuck Lever 	return nfs_ok;
3014b06cf375SChuck Lever }
3015b06cf375SChuck Lever 
nfsd4_encode_fattr4_fh_expire_type(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3016b06cf375SChuck Lever static __be32 nfsd4_encode_fattr4_fh_expire_type(struct xdr_stream *xdr,
3017b06cf375SChuck Lever 						 const struct nfsd4_fattr_args *args)
3018b06cf375SChuck Lever {
3019b06cf375SChuck Lever 	u32 mask;
3020b06cf375SChuck Lever 
3021b06cf375SChuck Lever 	mask = NFS4_FH_PERSISTENT;
3022b06cf375SChuck Lever 	if (!(args->exp->ex_flags & NFSEXP_NOSUBTREECHECK))
3023b06cf375SChuck Lever 		mask |= NFS4_FH_VOL_RENAME;
302436ed7e64SChuck Lever 	return nfsd4_encode_uint32_t(xdr, mask);
302536ed7e64SChuck Lever }
302636ed7e64SChuck Lever 
nfsd4_encode_fattr4_change(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)302736ed7e64SChuck Lever static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
302836ed7e64SChuck Lever 					 const struct nfsd4_fattr_args *args)
302936ed7e64SChuck Lever {
303036ed7e64SChuck Lever 	const struct svc_export *exp = args->exp;
303136ed7e64SChuck Lever 	u64 c;
303236ed7e64SChuck Lever 
303336ed7e64SChuck Lever 	if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
303436ed7e64SChuck Lever 		u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
3035263453d9SChuck Lever 
3036263453d9SChuck Lever 		if (xdr_stream_encode_u32(xdr, flush_time) != XDR_UNIT)
3037263453d9SChuck Lever 			return nfserr_resource;
3038263453d9SChuck Lever 		if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
3039263453d9SChuck Lever 			return nfserr_resource;
3040263453d9SChuck Lever 		return nfs_ok;
3041263453d9SChuck Lever 	}
3042263453d9SChuck Lever 
3043263453d9SChuck Lever 	c = nfsd4_change_attribute(&args->stat, d_inode(args->dentry));
3044263453d9SChuck Lever 	return nfsd4_encode_changeid4(xdr, c);
3045263453d9SChuck Lever }
3046263453d9SChuck Lever 
nfsd4_encode_fattr4_size(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3047263453d9SChuck Lever static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
3048263453d9SChuck Lever 				       const struct nfsd4_fattr_args *args)
3049263453d9SChuck Lever {
3050263453d9SChuck Lever 	return nfsd4_encode_uint64_t(xdr, args->size);
3051263453d9SChuck Lever }
3052263453d9SChuck Lever 
nfsd4_encode_fattr4_fsid(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3053263453d9SChuck Lever static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr,
3054263453d9SChuck Lever 				       const struct nfsd4_fattr_args *args)
3055d0b28aadSChuck Lever {
3056d0b28aadSChuck Lever 	__be32 *p;
3057d0b28aadSChuck Lever 
3058d0b28aadSChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 2 + XDR_UNIT * 2);
3059d0b28aadSChuck Lever 	if (!p)
3060d0b28aadSChuck Lever 		return nfserr_resource;
3061b6b62595SChuck Lever 
3062b6b62595SChuck Lever 	if (unlikely(args->exp->ex_fslocs.migrated)) {
3063b6b62595SChuck Lever 		p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
3064b6b62595SChuck Lever 		xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
3065b6b62595SChuck Lever 		return nfs_ok;
3066b6b62595SChuck Lever 	}
3067b6b62595SChuck Lever 	switch (fsid_source(args->fhp)) {
3068b6b62595SChuck Lever 	case FSIDSOURCE_FSID:
3069b6b62595SChuck Lever 		p = xdr_encode_hyper(p, (u64)args->exp->ex_fsid);
3070b6b62595SChuck Lever 		xdr_encode_hyper(p, (u64)0);
3071b6b62595SChuck Lever 		break;
3072b6b62595SChuck Lever 	case FSIDSOURCE_DEV:
3073b6b62595SChuck Lever 		*p++ = xdr_zero;
3074b6b62595SChuck Lever 		*p++ = cpu_to_be32(MAJOR(args->stat.dev));
3075b6b62595SChuck Lever 		*p++ = xdr_zero;
3076b6b62595SChuck Lever 		*p   = cpu_to_be32(MINOR(args->stat.dev));
3077b6b62595SChuck Lever 		break;
3078b6b62595SChuck Lever 	case FSIDSOURCE_UUID:
3079b6b62595SChuck Lever 		xdr_encode_opaque_fixed(p, args->exp->ex_uuid, EX_UUID_LEN);
3080b6b62595SChuck Lever 		break;
3081b6b62595SChuck Lever 	}
3082b6b62595SChuck Lever 
3083b6b62595SChuck Lever 	return nfs_ok;
3084b6b62595SChuck Lever }
3085b6b62595SChuck Lever 
nfsd4_encode_fattr4_lease_time(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3086b6b62595SChuck Lever static __be32 nfsd4_encode_fattr4_lease_time(struct xdr_stream *xdr,
3087b6b62595SChuck Lever 					     const struct nfsd4_fattr_args *args)
3088b6b62595SChuck Lever {
3089b6b62595SChuck Lever 	struct nfsd_net *nn = net_generic(SVC_NET(args->rqstp), nfsd_net_id);
3090b6b62595SChuck Lever 
3091b6b62595SChuck Lever 	return nfsd4_encode_nfs_lease4(xdr, nn->nfsd4_lease);
3092b6b62595SChuck Lever }
3093b6b62595SChuck Lever 
nfsd4_encode_fattr4_rdattr_error(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)30941252b283SChuck Lever static __be32 nfsd4_encode_fattr4_rdattr_error(struct xdr_stream *xdr,
30951252b283SChuck Lever 					       const struct nfsd4_fattr_args *args)
30961252b283SChuck Lever {
30971252b283SChuck Lever 	return nfsd4_encode_uint32_t(xdr, args->rdattr_err);
30981252b283SChuck Lever }
30991252b283SChuck Lever 
nfsd4_encode_fattr4_aclsupport(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31001252b283SChuck Lever static __be32 nfsd4_encode_fattr4_aclsupport(struct xdr_stream *xdr,
31011252b283SChuck Lever 					     const struct nfsd4_fattr_args *args)
3102782448e1SChuck Lever {
3103782448e1SChuck Lever 	u32 mask;
3104782448e1SChuck Lever 
3105782448e1SChuck Lever 	mask = 0;
3106782448e1SChuck Lever 	if (IS_POSIXACL(d_inode(args->dentry)))
3107782448e1SChuck Lever 		mask = ACL4_SUPPORT_ALLOW_ACL | ACL4_SUPPORT_DENY_ACL;
31086515b7d7SChuck Lever 	return nfsd4_encode_uint32_t(xdr, mask);
31096515b7d7SChuck Lever }
31106515b7d7SChuck Lever 
nfsd4_encode_fattr4_acl(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31116515b7d7SChuck Lever static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr,
31126515b7d7SChuck Lever 				      const struct nfsd4_fattr_args *args)
31136515b7d7SChuck Lever {
31146515b7d7SChuck Lever 	struct nfs4_acl *acl = args->acl;
31156515b7d7SChuck Lever 	struct nfs4_ace *ace;
31166515b7d7SChuck Lever 	__be32 status;
31176515b7d7SChuck Lever 
31186515b7d7SChuck Lever 	/* nfsace4<> */
311907455dc4SChuck Lever 	if (!acl) {
312007455dc4SChuck Lever 		if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
312107455dc4SChuck Lever 			return nfserr_resource;
312207455dc4SChuck Lever 	} else {
312307455dc4SChuck Lever 		if (xdr_stream_encode_u32(xdr, acl->naces) != XDR_UNIT)
312407455dc4SChuck Lever 			return nfserr_resource;
312507455dc4SChuck Lever 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
312607455dc4SChuck Lever 			status = nfsd4_encode_nfsace4(xdr, args->rqstp, ace);
312707455dc4SChuck Lever 			if (status != nfs_ok)
312807455dc4SChuck Lever 				return status;
312907455dc4SChuck Lever 		}
313007455dc4SChuck Lever 	}
313107455dc4SChuck Lever 	return nfs_ok;
313207455dc4SChuck Lever }
313307455dc4SChuck Lever 
nfsd4_encode_fattr4_filehandle(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)313407455dc4SChuck Lever static __be32 nfsd4_encode_fattr4_filehandle(struct xdr_stream *xdr,
313507455dc4SChuck Lever 					     const struct nfsd4_fattr_args *args)
313607455dc4SChuck Lever {
313707455dc4SChuck Lever 	return nfsd4_encode_nfs_fh4(xdr, &args->fhp->fh_handle);
313807455dc4SChuck Lever }
313907455dc4SChuck Lever 
nfsd4_encode_fattr4_fileid(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)314007455dc4SChuck Lever static __be32 nfsd4_encode_fattr4_fileid(struct xdr_stream *xdr,
314107455dc4SChuck Lever 					 const struct nfsd4_fattr_args *args)
31423283bf64SChuck Lever {
31433283bf64SChuck Lever 	return nfsd4_encode_uint64_t(xdr, args->stat.ino);
31443283bf64SChuck Lever }
31453283bf64SChuck Lever 
nfsd4_encode_fattr4_files_avail(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31463283bf64SChuck Lever static __be32 nfsd4_encode_fattr4_files_avail(struct xdr_stream *xdr,
31473283bf64SChuck Lever 					      const struct nfsd4_fattr_args *args)
3148eb7ece81SChuck Lever {
3149eb7ece81SChuck Lever 	return nfsd4_encode_uint64_t(xdr, args->statfs.f_ffree);
3150eb7ece81SChuck Lever }
3151eb7ece81SChuck Lever 
nfsd4_encode_fattr4_files_free(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3152eb7ece81SChuck Lever static __be32 nfsd4_encode_fattr4_files_free(struct xdr_stream *xdr,
3153eb7ece81SChuck Lever 					     const struct nfsd4_fattr_args *args)
3154b0c3a5f8SChuck Lever {
3155b0c3a5f8SChuck Lever 	return nfsd4_encode_uint64_t(xdr, args->statfs.f_ffree);
3156b0c3a5f8SChuck Lever }
3157b0c3a5f8SChuck Lever 
nfsd4_encode_fattr4_files_total(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3158b0c3a5f8SChuck Lever static __be32 nfsd4_encode_fattr4_files_total(struct xdr_stream *xdr,
3159b0c3a5f8SChuck Lever 					      const struct nfsd4_fattr_args *args)
316074361e2bSChuck Lever {
316174361e2bSChuck Lever 	return nfsd4_encode_uint64_t(xdr, args->statfs.f_files);
316274361e2bSChuck Lever }
316374361e2bSChuck Lever 
nfsd4_encode_fattr4_fs_locations(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)316474361e2bSChuck Lever static __be32 nfsd4_encode_fattr4_fs_locations(struct xdr_stream *xdr,
316574361e2bSChuck Lever 					       const struct nfsd4_fattr_args *args)
3166b56b7526SChuck Lever {
3167b56b7526SChuck Lever 	return nfsd4_encode_fs_locations4(xdr, args->rqstp, args->exp);
3168b56b7526SChuck Lever }
3169b56b7526SChuck Lever 
nfsd4_encode_fattr4_maxfilesize(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3170b56b7526SChuck Lever static __be32 nfsd4_encode_fattr4_maxfilesize(struct xdr_stream *xdr,
3171b56b7526SChuck Lever 					      const struct nfsd4_fattr_args *args)
3172a1469a37SChuck Lever {
3173a1469a37SChuck Lever 	struct super_block *sb = args->exp->ex_path.mnt->mnt_sb;
3174a1469a37SChuck Lever 
3175a1469a37SChuck Lever 	return nfsd4_encode_uint64_t(xdr, sb->s_maxbytes);
3176a1469a37SChuck Lever }
3177a1469a37SChuck Lever 
nfsd4_encode_fattr4_maxlink(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31787c605dccSChuck Lever static __be32 nfsd4_encode_fattr4_maxlink(struct xdr_stream *xdr,
31797c605dccSChuck Lever 					  const struct nfsd4_fattr_args *args)
31807c605dccSChuck Lever {
31817c605dccSChuck Lever 	return nfsd4_encode_uint32_t(xdr, 255);
31827c605dccSChuck Lever }
31837c605dccSChuck Lever 
nfsd4_encode_fattr4_maxname(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31847c605dccSChuck Lever static __be32 nfsd4_encode_fattr4_maxname(struct xdr_stream *xdr,
31857c605dccSChuck Lever 					  const struct nfsd4_fattr_args *args)
3186b066aa5cSChuck Lever {
3187b066aa5cSChuck Lever 	return nfsd4_encode_uint32_t(xdr, args->statfs.f_namelen);
3188b066aa5cSChuck Lever }
3189b066aa5cSChuck Lever 
nfsd4_encode_fattr4_maxread(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3190b066aa5cSChuck Lever static __be32 nfsd4_encode_fattr4_maxread(struct xdr_stream *xdr,
3191b066aa5cSChuck Lever 					  const struct nfsd4_fattr_args *args)
31929c1adaccSChuck Lever {
31939c1adaccSChuck Lever 	return nfsd4_encode_uint64_t(xdr, svc_max_payload(args->rqstp));
31949c1adaccSChuck Lever }
31959c1adaccSChuck Lever 
nfsd4_encode_fattr4_maxwrite(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)31969c1adaccSChuck Lever static __be32 nfsd4_encode_fattr4_maxwrite(struct xdr_stream *xdr,
31979c1adaccSChuck Lever 					   const struct nfsd4_fattr_args *args)
3198c17195c3SChuck Lever {
3199c17195c3SChuck Lever 	return nfsd4_encode_uint64_t(xdr, svc_max_payload(args->rqstp));
3200c17195c3SChuck Lever }
3201c17195c3SChuck Lever 
nfsd4_encode_fattr4_mode(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3202c17195c3SChuck Lever static __be32 nfsd4_encode_fattr4_mode(struct xdr_stream *xdr,
3203c17195c3SChuck Lever 				       const struct nfsd4_fattr_args *args)
3204951378dcSChuck Lever {
3205951378dcSChuck Lever 	return nfsd4_encode_mode4(xdr, args->stat.mode & S_IALLUGO);
3206951378dcSChuck Lever }
3207951378dcSChuck Lever 
nfsd4_encode_fattr4_numlinks(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3208951378dcSChuck Lever static __be32 nfsd4_encode_fattr4_numlinks(struct xdr_stream *xdr,
3209951378dcSChuck Lever 					   const struct nfsd4_fattr_args *args)
3210f4cf5042SChuck Lever {
3211f4cf5042SChuck Lever 	return nfsd4_encode_uint32_t(xdr, args->stat.nlink);
3212f4cf5042SChuck Lever }
3213f4cf5042SChuck Lever 
nfsd4_encode_fattr4_owner(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3214f4cf5042SChuck Lever static __be32 nfsd4_encode_fattr4_owner(struct xdr_stream *xdr,
3215f4cf5042SChuck Lever 					const struct nfsd4_fattr_args *args)
32169f329feaSChuck Lever {
32179f329feaSChuck Lever 	return nfsd4_encode_user(xdr, args->rqstp, args->stat.uid);
32189f329feaSChuck Lever }
32199f329feaSChuck Lever 
nfsd4_encode_fattr4_owner_group(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)32209f329feaSChuck Lever static __be32 nfsd4_encode_fattr4_owner_group(struct xdr_stream *xdr,
32219f329feaSChuck Lever 					      const struct nfsd4_fattr_args *args)
3222fa51a520SChuck Lever {
3223fa51a520SChuck Lever 	return nfsd4_encode_group(xdr, args->rqstp, args->stat.gid);
3224fa51a520SChuck Lever }
3225fa51a520SChuck Lever 
nfsd4_encode_fattr4_rawdev(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3226fa51a520SChuck Lever static __be32 nfsd4_encode_fattr4_rawdev(struct xdr_stream *xdr,
3227fa51a520SChuck Lever 					 const struct nfsd4_fattr_args *args)
322862f31e56SChuck Lever {
322962f31e56SChuck Lever 	return nfsd4_encode_specdata4(xdr, MAJOR(args->stat.rdev),
323062f31e56SChuck Lever 				      MINOR(args->stat.rdev));
323162f31e56SChuck Lever }
323262f31e56SChuck Lever 
nfsd4_encode_fattr4_space_avail(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)323362f31e56SChuck Lever static __be32 nfsd4_encode_fattr4_space_avail(struct xdr_stream *xdr,
3234a460cda2SChuck Lever 					      const struct nfsd4_fattr_args *args)
3235a460cda2SChuck Lever {
3236a460cda2SChuck Lever 	u64 avail = (u64)args->statfs.f_bavail * (u64)args->statfs.f_bsize;
3237a460cda2SChuck Lever 
3238a460cda2SChuck Lever 	return nfsd4_encode_uint64_t(xdr, avail);
3239a460cda2SChuck Lever }
3240a460cda2SChuck Lever 
nfsd4_encode_fattr4_space_free(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)324183afa091SChuck Lever static __be32 nfsd4_encode_fattr4_space_free(struct xdr_stream *xdr,
324283afa091SChuck Lever 					     const struct nfsd4_fattr_args *args)
324383afa091SChuck Lever {
324483afa091SChuck Lever 	u64 free = (u64)args->statfs.f_bfree * (u64)args->statfs.f_bsize;
324583afa091SChuck Lever 
324683afa091SChuck Lever 	return nfsd4_encode_uint64_t(xdr, free);
324783afa091SChuck Lever }
324883afa091SChuck Lever 
nfsd4_encode_fattr4_space_total(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)324974ebc697SChuck Lever static __be32 nfsd4_encode_fattr4_space_total(struct xdr_stream *xdr,
325074ebc697SChuck Lever 					      const struct nfsd4_fattr_args *args)
325174ebc697SChuck Lever {
325274ebc697SChuck Lever 	u64 total = (u64)args->statfs.f_blocks * (u64)args->statfs.f_bsize;
325374ebc697SChuck Lever 
325474ebc697SChuck Lever 	return nfsd4_encode_uint64_t(xdr, total);
325574ebc697SChuck Lever }
325674ebc697SChuck Lever 
nfsd4_encode_fattr4_space_used(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3257d0cde979SChuck Lever static __be32 nfsd4_encode_fattr4_space_used(struct xdr_stream *xdr,
3258d0cde979SChuck Lever 					     const struct nfsd4_fattr_args *args)
3259d0cde979SChuck Lever {
3260d0cde979SChuck Lever 	return nfsd4_encode_uint64_t(xdr, (u64)args->stat.blocks << 9);
3261d0cde979SChuck Lever }
3262d0cde979SChuck Lever 
nfsd4_encode_fattr4_time_access(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3263d0cde979SChuck Lever static __be32 nfsd4_encode_fattr4_time_access(struct xdr_stream *xdr,
3264d0cde979SChuck Lever 					      const struct nfsd4_fattr_args *args)
32656d37ac3aSChuck Lever {
32666d37ac3aSChuck Lever 	return nfsd4_encode_nfstime4(xdr, &args->stat.atime);
32676d37ac3aSChuck Lever }
32686d37ac3aSChuck Lever 
nfsd4_encode_fattr4_time_create(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)32696d37ac3aSChuck Lever static __be32 nfsd4_encode_fattr4_time_create(struct xdr_stream *xdr,
32706d37ac3aSChuck Lever 					      const struct nfsd4_fattr_args *args)
3271eed4d1adSChuck Lever {
3272eed4d1adSChuck Lever 	return nfsd4_encode_nfstime4(xdr, &args->stat.btime);
3273eed4d1adSChuck Lever }
3274eed4d1adSChuck Lever 
3275eed4d1adSChuck Lever /*
3276eed4d1adSChuck Lever  * ctime (in NFSv4, time_metadata) is not writeable, and the client
32772e38722dSChuck Lever  * doesn't really care what resolution could theoretically be stored by
32782e38722dSChuck Lever  * the filesystem.
32792e38722dSChuck Lever  *
32802e38722dSChuck Lever  * The client cares how close together changes can be while still
32812e38722dSChuck Lever  * guaranteeing ctime changes.  For most filesystems (which have
32822e38722dSChuck Lever  * timestamps with nanosecond fields) that is limited by the resolution
32831da177e4SLinus Torvalds  * of the time returned from current_time() (which I'm assuming to be
3284993474e8SChuck Lever  * 1/HZ).
3285993474e8SChuck Lever  */
nfsd4_encode_fattr4_time_delta(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3286993474e8SChuck Lever static __be32 nfsd4_encode_fattr4_time_delta(struct xdr_stream *xdr,
3287993474e8SChuck Lever 					     const struct nfsd4_fattr_args *args)
3288993474e8SChuck Lever {
3289993474e8SChuck Lever 	const struct inode *inode = d_inode(args->dentry);
3290993474e8SChuck Lever 	u32 ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
3291993474e8SChuck Lever 	struct timespec64 ts = ns_to_timespec64(ns);
3292993474e8SChuck Lever 
3293993474e8SChuck Lever 	return nfsd4_encode_nfstime4(xdr, &ts);
3294993474e8SChuck Lever }
3295993474e8SChuck Lever 
nfsd4_encode_fattr4_time_metadata(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3296993474e8SChuck Lever static __be32 nfsd4_encode_fattr4_time_metadata(struct xdr_stream *xdr,
3297993474e8SChuck Lever 						const struct nfsd4_fattr_args *args)
3298993474e8SChuck Lever {
3299993474e8SChuck Lever 	return nfsd4_encode_nfstime4(xdr, &args->stat.ctime);
3300993474e8SChuck Lever }
3301993474e8SChuck Lever 
nfsd4_encode_fattr4_time_modify(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3302993474e8SChuck Lever static __be32 nfsd4_encode_fattr4_time_modify(struct xdr_stream *xdr,
3303993474e8SChuck Lever 					      const struct nfsd4_fattr_args *args)
3304673720bcSChuck Lever {
3305673720bcSChuck Lever 	return nfsd4_encode_nfstime4(xdr, &args->stat.mtime);
3306673720bcSChuck Lever }
3307673720bcSChuck Lever 
nfsd4_encode_fattr4_mounted_on_fileid(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3308673720bcSChuck Lever static __be32 nfsd4_encode_fattr4_mounted_on_fileid(struct xdr_stream *xdr,
3309673720bcSChuck Lever 						    const struct nfsd4_fattr_args *args)
3310d1828611SChuck Lever {
3311d1828611SChuck Lever 	u64 ino;
3312d1828611SChuck Lever 	int err;
3313d1828611SChuck Lever 
3314d1828611SChuck Lever 	if (!args->ignore_crossmnt &&
3315d1828611SChuck Lever 	    args->dentry == args->exp->ex_path.mnt->mnt_root) {
33161b9097e3SChuck Lever 		err = nfsd4_get_mounted_on_ino(args->exp, &ino);
33171b9097e3SChuck Lever 		if (err)
33181b9097e3SChuck Lever 			return nfserrno(err);
33191b9097e3SChuck Lever 	} else
33201b9097e3SChuck Lever 		ino = args->stat.ino;
33211b9097e3SChuck Lever 
33221b9097e3SChuck Lever 	return nfsd4_encode_uint64_t(xdr, ino);
33231b9097e3SChuck Lever }
33241b9097e3SChuck Lever 
33251b9097e3SChuck Lever #ifdef CONFIG_NFSD_PNFS
33261b9097e3SChuck Lever 
nfsd4_encode_fattr4_fs_layout_types(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)33271b9097e3SChuck Lever static __be32 nfsd4_encode_fattr4_fs_layout_types(struct xdr_stream *xdr,
33281b9097e3SChuck Lever 						  const struct nfsd4_fattr_args *args)
33291b9097e3SChuck Lever {
33301b9097e3SChuck Lever 	unsigned long mask = args->exp->ex_layout_types;
33311b9097e3SChuck Lever 	int i;
33321b9097e3SChuck Lever 
3333e7a5b1b2SChuck Lever 	/* Hamming weight of @mask is the number of layout types to return */
3334e7a5b1b2SChuck Lever 	if (xdr_stream_encode_u32(xdr, hweight_long(mask)) != XDR_UNIT)
3335e7a5b1b2SChuck Lever 		return nfserr_resource;
3336e7a5b1b2SChuck Lever 	for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
3337e7a5b1b2SChuck Lever 		if (mask & BIT(i)) {
3338e7a5b1b2SChuck Lever 			/* layouttype4 */
3339e7a5b1b2SChuck Lever 			if (xdr_stream_encode_u32(xdr, i) != XDR_UNIT)
3340e7a5b1b2SChuck Lever 				return nfserr_resource;
3341e7a5b1b2SChuck Lever 		}
3342e7a5b1b2SChuck Lever 	return nfs_ok;
3343e7a5b1b2SChuck Lever }
3344e7a5b1b2SChuck Lever 
nfsd4_encode_fattr4_layout_types(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3345e7a5b1b2SChuck Lever static __be32 nfsd4_encode_fattr4_layout_types(struct xdr_stream *xdr,
3346e7a5b1b2SChuck Lever 					       const struct nfsd4_fattr_args *args)
3347e7a5b1b2SChuck Lever {
3348e7a5b1b2SChuck Lever 	unsigned long mask = args->exp->ex_layout_types;
3349e7a5b1b2SChuck Lever 	int i;
3350e7a5b1b2SChuck Lever 
3351e7a5b1b2SChuck Lever 	/* Hamming weight of @mask is the number of layout types to return */
3352e7a5b1b2SChuck Lever 	if (xdr_stream_encode_u32(xdr, hweight_long(mask)) != XDR_UNIT)
33534c15878eSChuck Lever 		return nfserr_resource;
33544c15878eSChuck Lever 	for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
33554c15878eSChuck Lever 		if (mask & BIT(i)) {
33564c15878eSChuck Lever 			/* layouttype4 */
33574c15878eSChuck Lever 			if (xdr_stream_encode_u32(xdr, i) != XDR_UNIT)
33584c15878eSChuck Lever 				return nfserr_resource;
33594c15878eSChuck Lever 		}
33604c15878eSChuck Lever 	return nfs_ok;
33614c15878eSChuck Lever }
33624c15878eSChuck Lever 
nfsd4_encode_fattr4_layout_blksize(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)33634c15878eSChuck Lever static __be32 nfsd4_encode_fattr4_layout_blksize(struct xdr_stream *xdr,
33644c15878eSChuck Lever 						 const struct nfsd4_fattr_args *args)
33654c15878eSChuck Lever {
33664c15878eSChuck Lever 	return nfsd4_encode_uint32_t(xdr, args->stat.blksize);
33674c15878eSChuck Lever }
33684c15878eSChuck Lever 
33694c15878eSChuck Lever #endif
33704c15878eSChuck Lever 
nfsd4_encode_fattr4_suppattr_exclcreat(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)33714c584731SChuck Lever static __be32 nfsd4_encode_fattr4_suppattr_exclcreat(struct xdr_stream *xdr,
33724c584731SChuck Lever 						     const struct nfsd4_fattr_args *args)
33734c584731SChuck Lever {
33744c584731SChuck Lever 	struct nfsd4_compoundres *resp = args->rqstp->rq_resp;
33754c584731SChuck Lever 	u32 supp[3];
33764c584731SChuck Lever 
3377e7a5b1b2SChuck Lever 	memcpy(supp, nfsd_suppattrs[resp->cstate.minorversion], sizeof(supp));
3378e7a5b1b2SChuck Lever 	supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0;
3379345c3877SChuck Lever 	supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1;
3380345c3877SChuck Lever 	supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2;
3381345c3877SChuck Lever 
3382345c3877SChuck Lever 	return nfsd4_encode_bitmap4(xdr, supp[0], supp[1], supp[2]);
3383345c3877SChuck Lever }
3384345c3877SChuck Lever 
3385345c3877SChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
nfsd4_encode_fattr4_sec_label(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3386345c3877SChuck Lever static __be32 nfsd4_encode_fattr4_sec_label(struct xdr_stream *xdr,
3387345c3877SChuck Lever 					    const struct nfsd4_fattr_args *args)
3388345c3877SChuck Lever {
3389345c3877SChuck Lever 	return nfsd4_encode_security_label(xdr, args->rqstp,
3390345c3877SChuck Lever 					   args->context, args->contextlen);
3391345c3877SChuck Lever }
3392345c3877SChuck Lever #endif
3393f59388a5SChuck Lever 
nfsd4_encode_fattr4_xattr_support(struct xdr_stream * xdr,const struct nfsd4_fattr_args * args)3394f59388a5SChuck Lever static __be32 nfsd4_encode_fattr4_xattr_support(struct xdr_stream *xdr,
3395f59388a5SChuck Lever 						const struct nfsd4_fattr_args *args)
3396f59388a5SChuck Lever {
3397f59388a5SChuck Lever 	int err = xattr_supports_user_prefix(d_inode(args->dentry));
3398f59388a5SChuck Lever 
3399f59388a5SChuck Lever 	return nfsd4_encode_bool(xdr, err == 0);
3400f59388a5SChuck Lever }
3401f59388a5SChuck Lever 
3402b3dbf4e4SChuck Lever static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
3403b3dbf4e4SChuck Lever 	[FATTR4_SUPPORTED_ATTRS]	= nfsd4_encode_fattr4_supported_attrs,
3404b3dbf4e4SChuck Lever 	[FATTR4_TYPE]			= nfsd4_encode_fattr4_type,
3405b3dbf4e4SChuck Lever 	[FATTR4_FH_EXPIRE_TYPE]		= nfsd4_encode_fattr4_fh_expire_type,
3406b3dbf4e4SChuck Lever 	[FATTR4_CHANGE]			= nfsd4_encode_fattr4_change,
3407b3dbf4e4SChuck Lever 	[FATTR4_SIZE]			= nfsd4_encode_fattr4_size,
3408b3dbf4e4SChuck Lever 	[FATTR4_LINK_SUPPORT]		= nfsd4_encode_fattr4__true,
3409b3dbf4e4SChuck Lever 	[FATTR4_SYMLINK_SUPPORT]	= nfsd4_encode_fattr4__true,
3410fce7913bSChuck Lever 	[FATTR4_NAMED_ATTR]		= nfsd4_encode_fattr4__false,
3411fce7913bSChuck Lever 	[FATTR4_FSID]			= nfsd4_encode_fattr4_fsid,
3412fce7913bSChuck Lever 	[FATTR4_UNIQUE_HANDLES]		= nfsd4_encode_fattr4__true,
3413fce7913bSChuck Lever 	[FATTR4_LEASE_TIME]		= nfsd4_encode_fattr4_lease_time,
3414fce7913bSChuck Lever 	[FATTR4_RDATTR_ERROR]		= nfsd4_encode_fattr4_rdattr_error,
3415fce7913bSChuck Lever 	[FATTR4_ACL]			= nfsd4_encode_fattr4_acl,
3416fce7913bSChuck Lever 	[FATTR4_ACLSUPPORT]		= nfsd4_encode_fattr4_aclsupport,
3417fce7913bSChuck Lever 	[FATTR4_ARCHIVE]		= nfsd4_encode_fattr4__noop,
3418fce7913bSChuck Lever 	[FATTR4_CANSETTIME]		= nfsd4_encode_fattr4__true,
3419fce7913bSChuck Lever 	[FATTR4_CASE_INSENSITIVE]	= nfsd4_encode_fattr4__false,
3420fce7913bSChuck Lever 	[FATTR4_CASE_PRESERVING]	= nfsd4_encode_fattr4__true,
3421fce7913bSChuck Lever 	[FATTR4_CHOWN_RESTRICTED]	= nfsd4_encode_fattr4__true,
3422fce7913bSChuck Lever 	[FATTR4_FILEHANDLE]		= nfsd4_encode_fattr4_filehandle,
3423fce7913bSChuck Lever 	[FATTR4_FILEID]			= nfsd4_encode_fattr4_fileid,
3424fce7913bSChuck Lever 	[FATTR4_FILES_AVAIL]		= nfsd4_encode_fattr4_files_avail,
3425fce7913bSChuck Lever 	[FATTR4_FILES_FREE]		= nfsd4_encode_fattr4_files_free,
3426fce7913bSChuck Lever 	[FATTR4_FILES_TOTAL]		= nfsd4_encode_fattr4_files_total,
3427fce7913bSChuck Lever 	[FATTR4_FS_LOCATIONS]		= nfsd4_encode_fattr4_fs_locations,
3428fce7913bSChuck Lever 	[FATTR4_HIDDEN]			= nfsd4_encode_fattr4__noop,
3429fce7913bSChuck Lever 	[FATTR4_HOMOGENEOUS]		= nfsd4_encode_fattr4__true,
3430fce7913bSChuck Lever 	[FATTR4_MAXFILESIZE]		= nfsd4_encode_fattr4_maxfilesize,
3431fce7913bSChuck Lever 	[FATTR4_MAXLINK]		= nfsd4_encode_fattr4_maxlink,
3432fce7913bSChuck Lever 	[FATTR4_MAXNAME]		= nfsd4_encode_fattr4_maxname,
3433fce7913bSChuck Lever 	[FATTR4_MAXREAD]		= nfsd4_encode_fattr4_maxread,
3434fce7913bSChuck Lever 	[FATTR4_MAXWRITE]		= nfsd4_encode_fattr4_maxwrite,
3435fce7913bSChuck Lever 	[FATTR4_MIMETYPE]		= nfsd4_encode_fattr4__noop,
3436fce7913bSChuck Lever 	[FATTR4_MODE]			= nfsd4_encode_fattr4_mode,
3437fce7913bSChuck Lever 	[FATTR4_NO_TRUNC]		= nfsd4_encode_fattr4__true,
3438fce7913bSChuck Lever 	[FATTR4_NUMLINKS]		= nfsd4_encode_fattr4_numlinks,
3439fce7913bSChuck Lever 	[FATTR4_OWNER]			= nfsd4_encode_fattr4_owner,
3440fce7913bSChuck Lever 	[FATTR4_OWNER_GROUP]		= nfsd4_encode_fattr4_owner_group,
3441fce7913bSChuck Lever 	[FATTR4_QUOTA_AVAIL_HARD]	= nfsd4_encode_fattr4__noop,
3442fce7913bSChuck Lever 	[FATTR4_QUOTA_AVAIL_SOFT]	= nfsd4_encode_fattr4__noop,
3443fce7913bSChuck Lever 	[FATTR4_QUOTA_USED]		= nfsd4_encode_fattr4__noop,
3444fce7913bSChuck Lever 	[FATTR4_RAWDEV]			= nfsd4_encode_fattr4_rawdev,
3445fce7913bSChuck Lever 	[FATTR4_SPACE_AVAIL]		= nfsd4_encode_fattr4_space_avail,
3446fce7913bSChuck Lever 	[FATTR4_SPACE_FREE]		= nfsd4_encode_fattr4_space_free,
3447fce7913bSChuck Lever 	[FATTR4_SPACE_TOTAL]		= nfsd4_encode_fattr4_space_total,
3448fce7913bSChuck Lever 	[FATTR4_SPACE_USED]		= nfsd4_encode_fattr4_space_used,
3449fce7913bSChuck Lever 	[FATTR4_SYSTEM]			= nfsd4_encode_fattr4__noop,
3450fce7913bSChuck Lever 	[FATTR4_TIME_ACCESS]		= nfsd4_encode_fattr4_time_access,
3451fce7913bSChuck Lever 	[FATTR4_TIME_ACCESS_SET]	= nfsd4_encode_fattr4__noop,
3452fce7913bSChuck Lever 	[FATTR4_TIME_BACKUP]		= nfsd4_encode_fattr4__noop,
3453fce7913bSChuck Lever 	[FATTR4_TIME_CREATE]		= nfsd4_encode_fattr4_time_create,
3454fce7913bSChuck Lever 	[FATTR4_TIME_DELTA]		= nfsd4_encode_fattr4_time_delta,
3455fce7913bSChuck Lever 	[FATTR4_TIME_METADATA]		= nfsd4_encode_fattr4_time_metadata,
3456fce7913bSChuck Lever 	[FATTR4_TIME_MODIFY]		= nfsd4_encode_fattr4_time_modify,
3457fce7913bSChuck Lever 	[FATTR4_TIME_MODIFY_SET]	= nfsd4_encode_fattr4__noop,
3458fce7913bSChuck Lever 	[FATTR4_MOUNTED_ON_FILEID]	= nfsd4_encode_fattr4_mounted_on_fileid,
3459fce7913bSChuck Lever 	[FATTR4_DIR_NOTIF_DELAY]	= nfsd4_encode_fattr4__noop,
3460fce7913bSChuck Lever 	[FATTR4_DIRENT_NOTIF_DELAY]	= nfsd4_encode_fattr4__noop,
3461fce7913bSChuck Lever 	[FATTR4_DACL]			= nfsd4_encode_fattr4__noop,
3462fce7913bSChuck Lever 	[FATTR4_SACL]			= nfsd4_encode_fattr4__noop,
3463fce7913bSChuck Lever 	[FATTR4_CHANGE_POLICY]		= nfsd4_encode_fattr4__noop,
3464fce7913bSChuck Lever 	[FATTR4_FS_STATUS]		= nfsd4_encode_fattr4__noop,
3465fce7913bSChuck Lever 
3466fce7913bSChuck Lever #ifdef CONFIG_NFSD_PNFS
3467fce7913bSChuck Lever 	[FATTR4_FS_LAYOUT_TYPES]	= nfsd4_encode_fattr4_fs_layout_types,
3468fce7913bSChuck Lever 	[FATTR4_LAYOUT_HINT]		= nfsd4_encode_fattr4__noop,
3469fce7913bSChuck Lever 	[FATTR4_LAYOUT_TYPES]		= nfsd4_encode_fattr4_layout_types,
3470fce7913bSChuck Lever 	[FATTR4_LAYOUT_BLKSIZE]		= nfsd4_encode_fattr4_layout_blksize,
3471fce7913bSChuck Lever 	[FATTR4_LAYOUT_ALIGNMENT]	= nfsd4_encode_fattr4__noop,
3472fce7913bSChuck Lever #else
3473fce7913bSChuck Lever 	[FATTR4_FS_LAYOUT_TYPES]	= nfsd4_encode_fattr4__noop,
3474fce7913bSChuck Lever 	[FATTR4_LAYOUT_HINT]		= nfsd4_encode_fattr4__noop,
3475fce7913bSChuck Lever 	[FATTR4_LAYOUT_TYPES]		= nfsd4_encode_fattr4__noop,
3476fce7913bSChuck Lever 	[FATTR4_LAYOUT_BLKSIZE]		= nfsd4_encode_fattr4__noop,
3477fce7913bSChuck Lever 	[FATTR4_LAYOUT_ALIGNMENT]	= nfsd4_encode_fattr4__noop,
3478fce7913bSChuck Lever #endif
3479fce7913bSChuck Lever 
3480fce7913bSChuck Lever 	[FATTR4_FS_LOCATIONS_INFO]	= nfsd4_encode_fattr4__noop,
3481fce7913bSChuck Lever 	[FATTR4_MDSTHRESHOLD]		= nfsd4_encode_fattr4__noop,
3482fce7913bSChuck Lever 	[FATTR4_RETENTION_GET]		= nfsd4_encode_fattr4__noop,
3483fce7913bSChuck Lever 	[FATTR4_RETENTION_SET]		= nfsd4_encode_fattr4__noop,
3484fce7913bSChuck Lever 	[FATTR4_RETENTEVT_GET]		= nfsd4_encode_fattr4__noop,
3485fce7913bSChuck Lever 	[FATTR4_RETENTEVT_SET]		= nfsd4_encode_fattr4__noop,
3486fce7913bSChuck Lever 	[FATTR4_RETENTION_HOLD]		= nfsd4_encode_fattr4__noop,
3487fce7913bSChuck Lever 	[FATTR4_MODE_SET_MASKED]	= nfsd4_encode_fattr4__noop,
3488fce7913bSChuck Lever 	[FATTR4_SUPPATTR_EXCLCREAT]	= nfsd4_encode_fattr4_suppattr_exclcreat,
3489fce7913bSChuck Lever 	[FATTR4_FS_CHARSET_CAP]		= nfsd4_encode_fattr4__noop,
3490fce7913bSChuck Lever 	[FATTR4_CLONE_BLKSIZE]		= nfsd4_encode_fattr4__noop,
3491fce7913bSChuck Lever 	[FATTR4_SPACE_FREED]		= nfsd4_encode_fattr4__noop,
3492fce7913bSChuck Lever 	[FATTR4_CHANGE_ATTR_TYPE]	= nfsd4_encode_fattr4__noop,
3493fce7913bSChuck Lever 
3494fce7913bSChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3495fce7913bSChuck Lever 	[FATTR4_SEC_LABEL]		= nfsd4_encode_fattr4_sec_label,
3496fce7913bSChuck Lever #else
3497fce7913bSChuck Lever 	[FATTR4_SEC_LABEL]		= nfsd4_encode_fattr4__noop,
3498fce7913bSChuck Lever #endif
3499fce7913bSChuck Lever 
3500fce7913bSChuck Lever 	[FATTR4_MODE_UMASK]		= nfsd4_encode_fattr4__noop,
3501fce7913bSChuck Lever 	[FATTR4_XATTR_SUPPORT]		= nfsd4_encode_fattr4_xattr_support,
3502fce7913bSChuck Lever };
3503fce7913bSChuck Lever 
3504fce7913bSChuck Lever /*
3505fce7913bSChuck Lever  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
3506fce7913bSChuck Lever  * ourselves.
3507fce7913bSChuck Lever  */
3508fce7913bSChuck Lever static __be32
nfsd4_encode_fattr4(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct svc_fh * fhp,struct svc_export * exp,struct dentry * dentry,const u32 * bmval,int ignore_crossmnt)3509fce7913bSChuck Lever nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
3510fce7913bSChuck Lever 		    struct svc_fh *fhp, struct svc_export *exp,
3511fce7913bSChuck Lever 		    struct dentry *dentry, const u32 *bmval,
3512993474e8SChuck Lever 		    int ignore_crossmnt)
35131da177e4SLinus Torvalds {
35141da177e4SLinus Torvalds 	DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
35151da177e4SLinus Torvalds 	struct nfsd4_fattr_args args;
3516da2ebce6SJeff Layton 	struct svc_fh *tempfh = NULL;
3517ae1131d4SChuck Lever 	int starting_len = xdr->buf->len;
3518ae1131d4SChuck Lever 	__be32 *attrlen_p, status;
3519fce7913bSChuck Lever 	int attrlen_offset;
3520ae1131d4SChuck Lever 	u32 attrmask[3];
35211da177e4SLinus Torvalds 	int err;
3522f488138bSVasily Gorbik 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
352383ab8678SChuck Lever 	u32 minorversion = resp->cstate.minorversion;
3524d50e6136SJ. Bruce Fields 	struct path path = {
35251fcea5b2SJ. Bruce Fields 		.mnt	= exp->ex_path.mnt,
3526b3dbf4e4SChuck Lever 		.dentry	= dentry,
3527082d4bd7SJ. Bruce Fields 	};
3528f488138bSVasily Gorbik 	unsigned long bit;
3529b8dd7b9aSAl Viro 	bool file_modified = false;
35307e705706SAndy Adamson 	u64 size = 0;
35317e705706SAndy Adamson 
3532ebabe9a9SChristoph Hellwig 	WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
3533ebabe9a9SChristoph Hellwig 	WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval));
3534ebabe9a9SChristoph Hellwig 
3535ebabe9a9SChristoph Hellwig 	args.rqstp = rqstp;
3536fce7913bSChuck Lever 	args.exp = exp;
3537c5967721SDai Ngo 	args.dentry = dentry;
3538c5967721SDai Ngo 	args.ignore_crossmnt = (ignore_crossmnt != 0);
35391da177e4SLinus Torvalds 	args.acl = NULL;
3540fce7913bSChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3541fce7913bSChuck Lever 	args.context = NULL;
35421da177e4SLinus Torvalds #endif
3543c9090e27SChuck Lever 
354436ed7e64SChuck Lever 	/*
3545c9090e27SChuck Lever 	 * Make a local copy of the attribute bitmap that can be modified.
35461b9097e3SChuck Lever 	 */
354718180a45SChuck Lever 	attrmask[0] = bmval[0];
3548f58bab6fSJeff Layton 	attrmask[1] = bmval[1];
3549f58bab6fSJeff Layton 	attrmask[2] = bmval[2];
3550f58bab6fSJeff Layton 
3551c9090e27SChuck Lever 	args.rdattr_err = 0;
3552fce7913bSChuck Lever 	if (exp->ex_fslocs.migrated) {
3553fce7913bSChuck Lever 		status = fattr_handle_absent_fs(&attrmask[0], &attrmask[1],
3554fce7913bSChuck Lever 						&attrmask[2], &args.rdattr_err);
3555f488138bSVasily Gorbik 		if (status)
3556f488138bSVasily Gorbik 			goto out;
3557f488138bSVasily Gorbik 	}
3558fce7913bSChuck Lever 	args.size = 0;
355983ab8678SChuck Lever 	if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
356042ca0993SJ.Bruce Fields 		status = nfsd4_deleg_getattr_conflict(rqstp, dentry,
3561f488138bSVasily Gorbik 					&file_modified, &size);
3562f488138bSVasily Gorbik 		if (status)
356342ca0993SJ.Bruce Fields 			goto out;
356442ca0993SJ.Bruce Fields 	}
356542ca0993SJ.Bruce Fields 
356683ab8678SChuck Lever 	err = vfs_getattr(&path, &args.stat,
3567f488138bSVasily Gorbik 			  STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
3568*7e8ae848SJeff Layton 			  AT_STATX_SYNC_AS_STAT);
3569c5967721SDai Ngo 	if (err)
3570fd19ca36SDai Ngo 		goto out_nfserr;
3571fd19ca36SDai Ngo 	if (file_modified)
3572fd19ca36SDai Ngo 		args.size = size;
357342ca0993SJ.Bruce Fields 	else
357483ab8678SChuck Lever 		args.size = args.stat.size;
3575638e3e7dSJeff Layton 
3576638e3e7dSJeff Layton 	if (!(args.stat.result_mask & STATX_BTIME))
3577b8dd7b9aSAl Viro 		/* underlying FS does not offer btime so we can't share it */
35781da177e4SLinus Torvalds 		attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
3579c5967721SDai Ngo 	if ((attrmask[0] & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
3580c5967721SDai Ngo 			FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
3581c5967721SDai Ngo 	    (attrmask[1] & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
3582862bee84SChuck Lever 		       FATTR4_WORD1_SPACE_TOTAL))) {
358383ab8678SChuck Lever 		err = vfs_statfs(&path, &args.statfs);
358483ab8678SChuck Lever 		if (err)
3585e377a3e6SOndrej Valousek 			goto out_nfserr;
3586f488138bSVasily Gorbik 	}
3587f488138bSVasily Gorbik 	if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) &&
358812337901SChristoph Hellwig 	    !fhp) {
3589f488138bSVasily Gorbik 		tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
35901da177e4SLinus Torvalds 		status = nfserr_jukebox;
359183ab8678SChuck Lever 		if (!tempfh)
3592b8dd7b9aSAl Viro 			goto out;
35931da177e4SLinus Torvalds 		fh_init(tempfh, NFS4_FHSIZE);
35941da177e4SLinus Torvalds 		status = fh_compose(tempfh, exp, dentry, NULL);
3595f488138bSVasily Gorbik 		if (status)
3596fce7913bSChuck Lever 			goto out;
3597d50e6136SJ. Bruce Fields 		args.fhp = tempfh;
3598d50e6136SJ. Bruce Fields 	} else
3599d50e6136SJ. Bruce Fields 		args.fhp = fhp;
3600d50e6136SJ. Bruce Fields 
3601d50e6136SJ. Bruce Fields 	if (attrmask[0] & FATTR4_WORD0_ACL) {
3602d50e6136SJ. Bruce Fields 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
36031da177e4SLinus Torvalds 		if (err == -EOPNOTSUPP)
36041da177e4SLinus Torvalds 			attrmask[0] &= ~FATTR4_WORD0_ACL;
360583ab8678SChuck Lever 		else if (err == -EINVAL) {
360683ab8678SChuck Lever 			status = nfserr_attrnotsupp;
360783ab8678SChuck Lever 			goto out;
360883ab8678SChuck Lever 		} else if (err != 0)
3609f488138bSVasily Gorbik 			goto out_nfserr;
361083ab8678SChuck Lever 	}
3611b8dd7b9aSAl Viro 
3612f488138bSVasily Gorbik 	args.contextsupport = false;
3613b8dd7b9aSAl Viro 
36141da177e4SLinus Torvalds #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
36151da177e4SLinus Torvalds 	if ((attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) ||
3616b8dd7b9aSAl Viro 	     attrmask[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
36171da177e4SLinus Torvalds 		if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
36181da177e4SLinus Torvalds 			err = security_inode_getsecctx(d_inode(dentry),
36192b44f1baSBenny Halevy 						&args.context, &args.contextlen);
362083ab8678SChuck Lever 		else
362183ab8678SChuck Lever 			err = -EOPNOTSUPP;
362218032ca0SDavid Quigley 		args.contextsupport = (err == 0);
3623f488138bSVasily Gorbik 		if (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) {
3624f488138bSVasily Gorbik 			if (err == -EOPNOTSUPP)
362532ddd944SJ. Bruce Fields 				attrmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
36262b0143b5SDavid Howells 			else if (err)
3627f59388a5SChuck Lever 				goto out_nfserr;
362832ddd944SJ. Bruce Fields 		}
362932ddd944SJ. Bruce Fields 	}
363083ab8678SChuck Lever #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
3631f488138bSVasily Gorbik 
363218032ca0SDavid Quigley 	/* attrmask */
3633f488138bSVasily Gorbik 	status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
363418032ca0SDavid Quigley 				      attrmask[2]);
363518032ca0SDavid Quigley 	if (status)
363618032ca0SDavid Quigley 		goto out;
363718032ca0SDavid Quigley 
363818032ca0SDavid Quigley 	/* attr_vals */
363918032ca0SDavid Quigley 	attrlen_offset = xdr->buf->len;
3640ae1131d4SChuck Lever 	attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
3641f488138bSVasily Gorbik 	if (!attrlen_p)
3642f488138bSVasily Gorbik 		goto out_resource;
364375976de6SKinglong Mee 	bitmap_from_arr32(attr_bitmap, attrmask,
364475976de6SKinglong Mee 			  ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
3645082d4bd7SJ. Bruce Fields 	for_each_set_bit(bit, attr_bitmap,
3646ae1131d4SChuck Lever 			 ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
3647082d4bd7SJ. Bruce Fields 		status = nfsd4_enc_fattr4_encode_ops[bit](xdr, &args);
3648ab04de60SChuck Lever 		if (status != nfs_ok)
3649ab04de60SChuck Lever 			goto out;
3650ddd1ea56SJ. Bruce Fields 	}
3651f488138bSVasily Gorbik 	*attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
3652f488138bSVasily Gorbik 	status = nfs_ok;
3653f488138bSVasily Gorbik 
3654fce7913bSChuck Lever out:
3655fce7913bSChuck Lever #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3656c9090e27SChuck Lever 	if (args.context)
3657c9090e27SChuck Lever 		security_release_secctx(args.context, args.contextlen);
36581da177e4SLinus Torvalds #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
3659ab04de60SChuck Lever 	kfree(args.acl);
36601da177e4SLinus Torvalds 	if (tempfh) {
36611da177e4SLinus Torvalds 		fh_put(tempfh);
36621da177e4SLinus Torvalds 		kfree(tempfh);
3663ba4e55bbSJ. Bruce Fields 	}
3664f59388a5SChuck Lever 	if (status)
3665f59388a5SChuck Lever 		xdr_truncate_encode(xdr, starting_len);
3666ba4e55bbSJ. Bruce Fields 	return status;
366783ab8678SChuck Lever out_nfserr:
366818df11d0SYan, Zheng 	status = nfserrno(err);
3669d50e6136SJ. Bruce Fields 	goto out;
367018df11d0SYan, Zheng out_resource:
367118df11d0SYan, Zheng 	status = nfserr_resource;
36721fcea5b2SJ. Bruce Fields 	goto out;
36731fcea5b2SJ. Bruce Fields }
36741da177e4SLinus Torvalds 
svcxdr_init_encode_from_buffer(struct xdr_stream * xdr,struct xdr_buf * buf,__be32 * p,int bytes)36751da177e4SLinus Torvalds static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
3676b8dd7b9aSAl Viro 				struct xdr_buf *buf, __be32 *p, int bytes)
36771da177e4SLinus Torvalds {
36781da177e4SLinus Torvalds 	xdr->scratch.iov_len = 0;
36791da177e4SLinus Torvalds 	memset(buf, 0, sizeof(struct xdr_buf));
36801da177e4SLinus Torvalds 	buf->head[0].iov_base = p;
36811da177e4SLinus Torvalds 	buf->head[0].iov_len = 0;
36821da177e4SLinus Torvalds 	buf->len = 0;
36832825a7f9SJ. Bruce Fields 	xdr->buf = buf;
36842825a7f9SJ. Bruce Fields 	xdr->iov = buf->head;
36852825a7f9SJ. Bruce Fields 	xdr->p = p;
36862825a7f9SJ. Bruce Fields 	xdr->end = (void *)p + bytes;
36872825a7f9SJ. Bruce Fields 	buf->buflen = bytes;
36882825a7f9SJ. Bruce Fields }
36892825a7f9SJ. Bruce Fields 
nfsd4_encode_fattr_to_buf(__be32 ** p,int words,struct svc_fh * fhp,struct svc_export * exp,struct dentry * dentry,u32 * bmval,struct svc_rqst * rqstp,int ignore_crossmnt)36902825a7f9SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
36912825a7f9SJ. Bruce Fields 			struct svc_fh *fhp, struct svc_export *exp,
36922825a7f9SJ. Bruce Fields 			struct dentry *dentry, u32 *bmval,
36932825a7f9SJ. Bruce Fields 			struct svc_rqst *rqstp, int ignore_crossmnt)
36942825a7f9SJ. Bruce Fields {
36952825a7f9SJ. Bruce Fields 	struct xdr_buf dummy;
36962825a7f9SJ. Bruce Fields 	struct xdr_stream xdr;
36972825a7f9SJ. Bruce Fields 	__be32 ret;
3698d5184658SJ. Bruce Fields 
3699d5184658SJ. Bruce Fields 	svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
3700d5184658SJ. Bruce Fields 	ret = nfsd4_encode_fattr4(rqstp, &xdr, fhp, exp, dentry, bmval,
3701d5184658SJ. Bruce Fields 				  ignore_crossmnt);
3702d5184658SJ. Bruce Fields 	*p = xdr.p;
37032825a7f9SJ. Bruce Fields 	return ret;
3704d5184658SJ. Bruce Fields }
3705d5184658SJ. Bruce Fields 
3706d5184658SJ. Bruce Fields /*
37072825a7f9SJ. Bruce Fields  * The buffer space for this field was reserved during a previous
3708ae1131d4SChuck Lever  * call to nfsd4_encode_entry4().
3709d5184658SJ. Bruce Fields  */
nfsd4_encode_entry4_nfs_cookie4(const struct nfsd4_readdir * readdir,u64 offset)3710d5184658SJ. Bruce Fields static void nfsd4_encode_entry4_nfs_cookie4(const struct nfsd4_readdir *readdir,
3711d5184658SJ. Bruce Fields 					    u64 offset)
3712d5184658SJ. Bruce Fields {
3713d5184658SJ. Bruce Fields 	__be64 cookie = cpu_to_be64(offset);
37143fc5048cSChuck Lever 	struct xdr_stream *xdr = readdir->xdr;
37153fc5048cSChuck Lever 
37163fc5048cSChuck Lever 	if (!readdir->cookie_offset)
37173fc5048cSChuck Lever 		return;
37183fc5048cSChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset, &cookie,
37193fc5048cSChuck Lever 			       sizeof(cookie));
37203fc5048cSChuck Lever }
37213fc5048cSChuck Lever 
attributes_need_mount(u32 * bmval)37223fc5048cSChuck Lever static inline int attributes_need_mount(u32 *bmval)
37233fc5048cSChuck Lever {
37243fc5048cSChuck Lever 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
37253fc5048cSChuck Lever 		return 1;
37263fc5048cSChuck Lever 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
37273fc5048cSChuck Lever 		return 1;
37283fc5048cSChuck Lever 	return 0;
37293fc5048cSChuck Lever }
3730c0ce6ec8SJ. Bruce Fields 
3731c0ce6ec8SJ. Bruce Fields static __be32
nfsd4_encode_entry4_fattr(struct nfsd4_readdir * cd,const char * name,int namlen)3732c0ce6ec8SJ. Bruce Fields nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
3733c0ce6ec8SJ. Bruce Fields 			  int namlen)
3734c0ce6ec8SJ. Bruce Fields {
3735c0ce6ec8SJ. Bruce Fields 	struct svc_export *exp = cd->rd_fhp->fh_export;
3736c0ce6ec8SJ. Bruce Fields 	struct dentry *dentry;
3737c0ce6ec8SJ. Bruce Fields 	__be32 nfserr;
3738c0ce6ec8SJ. Bruce Fields 	int ignore_crossmnt = 0;
3739b37ad28bSAl Viro 
3740a0f3c835SChuck Lever 	dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
3741a0f3c835SChuck Lever 	if (IS_ERR(dentry))
37421da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
37431da177e4SLinus Torvalds 
37441da177e4SLinus Torvalds 	exp_get(exp);
3745b37ad28bSAl Viro 	/*
3746406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
37471da177e4SLinus Torvalds 	 * attributes that are only properties of the underlying filesystem
37486c2d4798SAl Viro 	 * as opposed to the cross-mounted file system. In such a case,
37491da177e4SLinus Torvalds 	 * we will not follow the cross mount and will fill the attribtutes
37501da177e4SLinus Torvalds 	 * directly from the mountpoint dentry.
37511da177e4SLinus Torvalds 	 */
37521da177e4SLinus Torvalds 	if (nfsd_mountpoint(dentry, exp)) {
3753406a7ea9SFrank Filz 		int err;
3754406a7ea9SFrank Filz 
3755406a7ea9SFrank Filz 		if (!(exp->ex_flags & NFSEXP_V4ROOT)
3756406a7ea9SFrank Filz 				&& !attributes_need_mount(cd->rd_bmval)) {
3757406a7ea9SFrank Filz 			ignore_crossmnt = 1;
3758406a7ea9SFrank Filz 			goto out_encode;
3759406a7ea9SFrank Filz 		}
37603227fa41SJ. Bruce Fields 		/*
3761021d3a72SJ.Bruce Fields 		 * Why the heck aren't we just using nfsd_lookup??
3762021d3a72SJ.Bruce Fields 		 * Different "."/".." handling?  Something else?
37633227fa41SJ. Bruce Fields 		 * At least, add a comment here to explain....
37643227fa41SJ. Bruce Fields 		 */
37653227fa41SJ. Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
37663227fa41SJ. Bruce Fields 		if (err) {
37673227fa41SJ. Bruce Fields 			nfserr = nfserrno(err);
3768dcb488a3SAndy Adamson 			goto out_put;
3769dcb488a3SAndy Adamson 		}
3770dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
3771dcb488a3SAndy Adamson 		if (nfserr)
3772dcb488a3SAndy Adamson 			goto out_put;
3773021d3a72SJ.Bruce Fields 
3774021d3a72SJ.Bruce Fields 	}
3775021d3a72SJ.Bruce Fields out_encode:
37761da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr4(cd->rd_rqstp, cd->xdr, NULL, exp, dentry,
37771da177e4SLinus Torvalds 				     cd->rd_bmval, ignore_crossmnt);
3778dcb488a3SAndy Adamson out_put:
3779dcb488a3SAndy Adamson 	dput(dentry);
3780dcb488a3SAndy Adamson 	exp_put(exp);
37811da177e4SLinus Torvalds 	return nfserr;
37821da177e4SLinus Torvalds }
37833227fa41SJ. Bruce Fields 
3784a0f3c835SChuck Lever static __be32
nfsd4_encode_entry4_rdattr_error(struct xdr_stream * xdr,__be32 nfserr)3785ae1131d4SChuck Lever nfsd4_encode_entry4_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
37861da177e4SLinus Torvalds {
37871da177e4SLinus Torvalds 	__be32 status;
37881da177e4SLinus Torvalds 
37891da177e4SLinus Torvalds 	/* attrmask */
37901da177e4SLinus Torvalds 	status = nfsd4_encode_bitmap4(xdr, FATTR4_WORD0_RDATTR_ERROR, 0, 0);
37911da177e4SLinus Torvalds 	if (status != nfs_ok)
3792a0d042f8SChuck Lever 		return status;
3793a0d042f8SChuck Lever 	/* attr_vals */
37941da177e4SLinus Torvalds 	if (xdr_stream_encode_u32(xdr, XDR_UNIT) != XDR_UNIT)
3795a0d042f8SChuck Lever 		return nfserr_resource;
3796561f0ed4SJ. Bruce Fields 	/* rdattr_error */
3797a0d042f8SChuck Lever 	if (xdr_stream_encode_be32(xdr, nfserr) != XDR_UNIT)
3798a0d042f8SChuck Lever 		return nfserr_resource;
3799a0d042f8SChuck Lever 	return nfs_ok;
3800a0d042f8SChuck Lever }
3801a0d042f8SChuck Lever 
3802a0d042f8SChuck Lever static int
nfsd4_encode_entry4(void * ccdv,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)3803a0d042f8SChuck Lever nfsd4_encode_entry4(void *ccdv, const char *name, int namlen,
3804a0d042f8SChuck Lever 		    loff_t offset, u64 ino, unsigned int d_type)
3805a0d042f8SChuck Lever {
3806a0d042f8SChuck Lever 	struct readdir_cd *ccd = ccdv;
3807a0d042f8SChuck Lever 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
38081da177e4SLinus Torvalds 	struct xdr_stream *xdr = cd->xdr;
38091da177e4SLinus Torvalds 	int start_offset = xdr->buf->len;
38101da177e4SLinus Torvalds 	int cookie_offset;
3811a0f3c835SChuck Lever 	u32 name_and_cookie;
3812a0ad13efSNeilBrown 	int entry_bytes;
38131da177e4SLinus Torvalds 	__be32 nfserr = nfserr_toosmall;
3814a0ad13efSNeilBrown 
38151da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
3816561f0ed4SJ. Bruce Fields 	if (name && isdotent(name, namlen)) {
3817561f0ed4SJ. Bruce Fields 		cd->common.err = nfs_ok;
3818561f0ed4SJ. Bruce Fields 		return 0;
3819aee37764SJ. Bruce Fields 	}
3820561f0ed4SJ. Bruce Fields 
3821b37ad28bSAl Viro 	/* Encode the previous entry's cookie value */
38221da177e4SLinus Torvalds 	nfsd4_encode_entry4_nfs_cookie4(cd, offset);
38231da177e4SLinus Torvalds 
38241da177e4SLinus Torvalds 	if (xdr_stream_encode_item_present(xdr) != XDR_UNIT)
38251da177e4SLinus Torvalds 		goto fail;
38261da177e4SLinus Torvalds 
38271da177e4SLinus Torvalds 	/* Reserve send buffer space for this entry's cookie value. */
38281da177e4SLinus Torvalds 	cookie_offset = xdr->buf->len;
38293fc5048cSChuck Lever 	if (nfsd4_encode_nfs_cookie4(xdr, OFFSET_MAX) != nfs_ok)
38303fc5048cSChuck Lever 		goto fail;
38311da177e4SLinus Torvalds 	if (nfsd4_encode_component4(xdr, name, namlen) != nfs_ok)
3832a1aee9aaSChuck Lever 		goto fail;
38331da177e4SLinus Torvalds 	nfserr = nfsd4_encode_entry4_fattr(cd, name, namlen);
38341da177e4SLinus Torvalds 	switch (nfserr) {
3835a1aee9aaSChuck Lever 	case nfs_ok:
3836a1aee9aaSChuck Lever 		break;
3837a1aee9aaSChuck Lever 	case nfserr_resource:
3838a1aee9aaSChuck Lever 		nfserr = nfserr_toosmall;
3839a1aee9aaSChuck Lever 		goto fail;
3840a1aee9aaSChuck Lever 	case nfserr_noent:
3841a0f3c835SChuck Lever 		xdr_truncate_encode(xdr, start_offset);
38421da177e4SLinus Torvalds 		goto skip_entry;
38431da177e4SLinus Torvalds 	case nfserr_jukebox:
38441da177e4SLinus Torvalds 		/*
38451da177e4SLinus Torvalds 		 * The pseudoroot should only display dentries that lead to
38461da177e4SLinus Torvalds 		 * exports. If we get EJUKEBOX here, then we can't tell whether
38471da177e4SLinus Torvalds 		 * this entry should be included. Just fail the whole READDIR
3848b2c0cea6SJ. Bruce Fields 		 * with NFS4ERR_DELAY in that case, and hope that the situation
3849f41c5ad2SKinglong Mee 		 * will resolve itself by the client's next attempt.
3850b2c0cea6SJ. Bruce Fields 		 */
3851cad85337SJeff Layton 		if (cd->rd_fhp->fh_export->ex_flags & NFSEXP_V4ROOT)
3852cad85337SJeff Layton 			goto fail;
3853cad85337SJeff Layton 		fallthrough;
3854cad85337SJeff Layton 	default:
3855cad85337SJeff Layton 		/*
3856cad85337SJeff Layton 		 * If the client requested the RDATTR_ERROR attribute,
3857cad85337SJeff Layton 		 * we stuff the error code into this attribute
3858cad85337SJeff Layton 		 * and continue.  If this attribute was not requested,
3859cad85337SJeff Layton 		 * then in accordance with the spec, we fail the
3860cad85337SJeff Layton 		 * entire READDIR operation(!)
3861cad85337SJeff Layton 		 */
38621da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
38631da177e4SLinus Torvalds 			goto fail;
38641da177e4SLinus Torvalds 		if (nfsd4_encode_entry4_rdattr_error(xdr, nfserr)) {
38651da177e4SLinus Torvalds 			nfserr = nfserr_toosmall;
38661da177e4SLinus Torvalds 			goto fail;
38671da177e4SLinus Torvalds 		}
38681da177e4SLinus Torvalds 	}
38691da177e4SLinus Torvalds 	nfserr = nfserr_toosmall;
38701da177e4SLinus Torvalds 	entry_bytes = xdr->buf->len - start_offset;
38711da177e4SLinus Torvalds 	if (entry_bytes > cd->rd_maxcount)
3872a0d042f8SChuck Lever 		goto fail;
387334081efcSFred Isaman 	cd->rd_maxcount -= entry_bytes;
38741da177e4SLinus Torvalds 	/*
38751da177e4SLinus Torvalds 	 * RFC 3530 14.2.24 describes rd_dircount as only a "hint", and
387634081efcSFred Isaman 	 * notes that it could be zero. If it is zero, then the server
3877561f0ed4SJ. Bruce Fields 	 * should enforce only the rd_maxcount value.
3878561f0ed4SJ. Bruce Fields 	 */
3879561f0ed4SJ. Bruce Fields 	if (cd->rd_dircount) {
3880561f0ed4SJ. Bruce Fields 		name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8;
3881561f0ed4SJ. Bruce Fields 		if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
3882aee37764SJ. Bruce Fields 			goto fail;
3883f2e717d6STrond Myklebust 		cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
3884f2e717d6STrond Myklebust 		if (!cd->rd_dircount)
3885f2e717d6STrond Myklebust 			cd->rd_maxcount = 0;
3886aee37764SJ. Bruce Fields 	}
3887f2e717d6STrond Myklebust 
38880ec016e3SJ. Bruce Fields 	cd->cookie_offset = cookie_offset;
3889aee37764SJ. Bruce Fields skip_entry:
3890aee37764SJ. Bruce Fields 	cd->common.err = nfs_ok;
3891aee37764SJ. Bruce Fields 	return 0;
3892f2e717d6STrond Myklebust fail:
3893f2e717d6STrond Myklebust 	xdr_truncate_encode(xdr, start_offset);
3894f2e717d6STrond Myklebust 	cd->common.err = nfserr;
38950ec016e3SJ. Bruce Fields 	return -EINVAL;
3896561f0ed4SJ. Bruce Fields }
3897b2c0cea6SJ. Bruce Fields 
38981da177e4SLinus Torvalds static __be32
nfsd4_encode_verifier4(struct xdr_stream * xdr,const nfs4_verifier * verf)38991da177e4SLinus Torvalds nfsd4_encode_verifier4(struct xdr_stream *xdr, const nfs4_verifier *verf)
39001da177e4SLinus Torvalds {
3901561f0ed4SJ. Bruce Fields 	__be32 *p;
39021da177e4SLinus Torvalds 
39031da177e4SLinus Torvalds 	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
39041da177e4SLinus Torvalds 	if (!p)
39051da177e4SLinus Torvalds 		return nfserr_resource;
3906d0a381ddSJ. Bruce Fields 	memcpy(p, verf->data, sizeof(verf->data));
3907adaa7a50SChuck Lever 	return nfs_ok;
3908adaa7a50SChuck Lever }
3909adaa7a50SChuck Lever 
3910adaa7a50SChuck Lever static __be32
nfsd4_encode_clientid4(struct xdr_stream * xdr,const clientid_t * clientid)3911adaa7a50SChuck Lever nfsd4_encode_clientid4(struct xdr_stream *xdr, const clientid_t *clientid)
3912adaa7a50SChuck Lever {
3913adaa7a50SChuck Lever 	__be32 *p;
3914adaa7a50SChuck Lever 
3915adaa7a50SChuck Lever 	p = xdr_reserve_space(xdr, sizeof(__be64));
3916adaa7a50SChuck Lever 	if (!p)
3917adaa7a50SChuck Lever 		return nfserr_resource;
3918adaa7a50SChuck Lever 	memcpy(p, clientid, sizeof(*clientid));
3919adaa7a50SChuck Lever 	return nfs_ok;
3920adaa7a50SChuck Lever }
3921adaa7a50SChuck Lever 
3922adaa7a50SChuck Lever /* This is a frequently-encoded item; open-coded for speed */
3923adaa7a50SChuck Lever static __be32
nfsd4_encode_stateid4(struct xdr_stream * xdr,const stateid_t * sid)3924adaa7a50SChuck Lever nfsd4_encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid)
3925adaa7a50SChuck Lever {
3926adaa7a50SChuck Lever 	__be32 *p;
3927adaa7a50SChuck Lever 
3928adaa7a50SChuck Lever 	p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE);
3929adaa7a50SChuck Lever 	if (!p)
393040bb2baaSChuck Lever 		return nfserr_resource;
3931adaa7a50SChuck Lever 	*p++ = cpu_to_be32(sid->si_generation);
393240bb2baaSChuck Lever 	memcpy(p, &sid->si_opaque, sizeof(sid->si_opaque));
3933e2f282b9SBenny Halevy 	return nfs_ok;
3934bc749ca4SJ. Bruce Fields }
3935e2f282b9SBenny Halevy 
393640bb2baaSChuck Lever static __be32
nfsd4_encode_sessionid4(struct xdr_stream * xdr,const struct nfs4_sessionid * sessionid)3937d0a381ddSJ. Bruce Fields nfsd4_encode_sessionid4(struct xdr_stream *xdr,
3938d0a381ddSJ. Bruce Fields 			const struct nfs4_sessionid *sessionid)
3939c373b0a4SJ. Bruce Fields {
394040bb2baaSChuck Lever 	return nfsd4_encode_opaque_fixed(xdr, sessionid->data,
394140bb2baaSChuck Lever 					 NFS4_MAX_SESSIONID_LEN);
3942e2f282b9SBenny Halevy }
3943e2f282b9SBenny Halevy 
3944695e12f8SBenny Halevy static __be32
nfsd4_encode_access(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)394565baa609SChuck Lever nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr,
394665baa609SChuck Lever 		    union nfsd4_op_u *u)
394765baa609SChuck Lever {
394865baa609SChuck Lever 	struct nfsd4_access *access = &u->access;
394965baa609SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
395065baa609SChuck Lever 	__be32 status;
395165baa609SChuck Lever 
395265baa609SChuck Lever 	/* supported */
3953e78e274eSKees Cook 	status = nfsd4_encode_uint32_t(xdr, access->ac_supported);
3954e78e274eSKees Cook 	if (status != nfs_ok)
39551da177e4SLinus Torvalds 		return status;
3956e78e274eSKees Cook 	/* access */
3957bddfdbcdSChuck Lever 	return nfsd4_encode_uint32_t(xdr, access->ac_resp_access);
3958d38e570fSChuck Lever }
39591da177e4SLinus Torvalds 
nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3960d38e570fSChuck Lever static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr,
3961d38e570fSChuck Lever 						union nfsd4_op_u *u)
3962d38e570fSChuck Lever {
3963d38e570fSChuck Lever 	struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
3964d38e570fSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3965d38e570fSChuck Lever 
39661da177e4SLinus Torvalds 	/* bctsr_sessid */
39671da177e4SLinus Torvalds 	nfserr = nfsd4_encode_sessionid4(xdr, &bcts->sessionid);
3968e78e274eSKees Cook 	if (nfserr != nfs_ok)
3969e78e274eSKees Cook 		return nfserr;
39701d1bc8f2SJ. Bruce Fields 	/* bctsr_dir */
3971e78e274eSKees Cook 	if (xdr_stream_encode_u32(xdr, bcts->dir) != XDR_UNIT)
3972bddfdbcdSChuck Lever 		return nfserr_resource;
39731d1bc8f2SJ. Bruce Fields 	/* bctsr_use_conn_in_rdma_mode */
397465baa609SChuck Lever 	return nfsd4_encode_bool(xdr, false);
397565baa609SChuck Lever }
397665baa609SChuck Lever 
397765baa609SChuck Lever static __be32
nfsd4_encode_close(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)397865baa609SChuck Lever nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr,
397965baa609SChuck Lever 		   union nfsd4_op_u *u)
3980d0a381ddSJ. Bruce Fields {
398165baa609SChuck Lever 	struct nfsd4_close *close = &u->close;
398265baa609SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
39831d1bc8f2SJ. Bruce Fields 
39841d1bc8f2SJ. Bruce Fields 	/* open_stateid */
3985695e12f8SBenny Halevy 	return nfsd4_encode_stateid4(xdr, &close->cl_stateid);
3986e78e274eSKees Cook }
3987e78e274eSKees Cook 
39881da177e4SLinus Torvalds 
3989e78e274eSKees Cook static __be32
nfsd4_encode_commit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3990bddfdbcdSChuck Lever nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr,
3991d0a381ddSJ. Bruce Fields 		    union nfsd4_op_u *u)
399240bb2baaSChuck Lever {
399340bb2baaSChuck Lever 	struct nfsd4_commit *commit = &u->commit;
39941da177e4SLinus Torvalds 
39951da177e4SLinus Torvalds 	return nfsd4_encode_verifier4(resp->xdr, &commit->co_verf);
39961da177e4SLinus Torvalds }
3997695e12f8SBenny Halevy 
3998e78e274eSKees Cook static __be32
nfsd4_encode_create(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3999e78e274eSKees Cook nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr,
40001da177e4SLinus Torvalds 		    union nfsd4_op_u *u)
4001e78e274eSKees Cook {
40021da177e4SLinus Torvalds 	struct nfsd4_create *create = &u->create;
4003adaa7a50SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
40041da177e4SLinus Torvalds 
40051da177e4SLinus Torvalds 	/* cinfo */
4006695e12f8SBenny Halevy 	nfserr = nfsd4_encode_change_info4(xdr, &create->cr_cinfo);
4007e78e274eSKees Cook 	if (nfserr)
4008e78e274eSKees Cook 		return nfserr;
40091da177e4SLinus Torvalds 	/* attrset */
4010e78e274eSKees Cook 	return nfsd4_encode_bitmap4(xdr, create->cr_bmval[0],
4011bddfdbcdSChuck Lever 				    create->cr_bmval[1], create->cr_bmval[2]);
40121da177e4SLinus Torvalds }
4013e64301f5SChuck Lever 
401466a21db7SChuck Lever static __be32
nfsd4_encode_getattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)401566a21db7SChuck Lever nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr,
401666a21db7SChuck Lever 		     union nfsd4_op_u *u)
4017e64301f5SChuck Lever {
4018e64301f5SChuck Lever 	struct nfsd4_getattr *getattr = &u->getattr;
401975976de6SKinglong Mee 	struct svc_fh *fhp = getattr->ga_fhp;
40201da177e4SLinus Torvalds 	struct xdr_stream *xdr = resp->xdr;
40211da177e4SLinus Torvalds 
4022b37ad28bSAl Viro 	/* obj_attributes */
4023e78e274eSKees Cook 	return nfsd4_encode_fattr4(resp->rqstp, xdr, fhp, fhp->fh_export,
4024e78e274eSKees Cook 				   fhp->fh_dentry, getattr->ga_bmval, 0);
40251da177e4SLinus Torvalds }
4026e78e274eSKees Cook 
40271da177e4SLinus Torvalds static __be32
nfsd4_encode_getfh(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4028bddfdbcdSChuck Lever nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr,
40291da177e4SLinus Torvalds 		   union nfsd4_op_u *u)
4030ae1131d4SChuck Lever {
4031ae1131d4SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4032ae1131d4SChuck Lever 	struct svc_fh *fhp = u->getfh;
40331da177e4SLinus Torvalds 
40341da177e4SLinus Torvalds 	/* object */
4035695e12f8SBenny Halevy 	return nfsd4_encode_nfs_fh4(xdr, &fhp->fh_handle);
4036e78e274eSKees Cook }
4037e78e274eSKees Cook 
40381da177e4SLinus Torvalds static __be32
nfsd4_encode_lock_owner4(struct xdr_stream * xdr,const clientid_t * clientid,const struct xdr_netobj * owner)4039bddfdbcdSChuck Lever nfsd4_encode_lock_owner4(struct xdr_stream *xdr, const clientid_t *clientid,
40403283bf64SChuck Lever 			 const struct xdr_netobj *owner)
40411da177e4SLinus Torvalds {
40423283bf64SChuck Lever 	__be32 status;
40433283bf64SChuck Lever 
40441da177e4SLinus Torvalds 	/* clientid */
40451da177e4SLinus Torvalds 	status = nfsd4_encode_clientid4(xdr, clientid);
4046c4a29c52SChuck Lever 	if (status != nfs_ok)
4047c4a29c52SChuck Lever 		return status;
4048c4a29c52SChuck Lever 	/* owner */
4049c4a29c52SChuck Lever 	return nfsd4_encode_opaque(xdr, owner->data, owner->len);
4050c4a29c52SChuck Lever }
4051c4a29c52SChuck Lever 
4052c4a29c52SChuck Lever static __be32
nfsd4_encode_lock4denied(struct xdr_stream * xdr,const struct nfsd4_lock_denied * ld)4053c4a29c52SChuck Lever nfsd4_encode_lock4denied(struct xdr_stream *xdr,
4054c4a29c52SChuck Lever 			 const struct nfsd4_lock_denied *ld)
4055c4a29c52SChuck Lever {
4056c4a29c52SChuck Lever 	__be32 status;
4057c4a29c52SChuck Lever 
4058c4a29c52SChuck Lever 	/* offset */
4059c4a29c52SChuck Lever 	status = nfsd4_encode_offset4(xdr, ld->ld_start);
4060d0a381ddSJ. Bruce Fields 	if (status != nfs_ok)
4061c5641782SChuck Lever 		return status;
4062c5641782SChuck Lever 	/* length */
40631da177e4SLinus Torvalds 	status = nfsd4_encode_length4(xdr, ld->ld_length);
4064c5641782SChuck Lever 	if (status != nfs_ok)
40651da177e4SLinus Torvalds 		return status;
4066c5641782SChuck Lever 	/* locktype */
4067c5641782SChuck Lever 	if (xdr_stream_encode_u32(xdr, ld->ld_type) != XDR_UNIT)
4068c4a29c52SChuck Lever 		return nfserr_resource;
4069c4a29c52SChuck Lever 	/* owner */
4070c5641782SChuck Lever 	return nfsd4_encode_lock_owner4(xdr, &ld->ld_clientid,
4071c5641782SChuck Lever 					&ld->ld_owner);
4072c5641782SChuck Lever }
4073c5641782SChuck Lever 
4074c5641782SChuck Lever static __be32
nfsd4_encode_lock(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4075c5641782SChuck Lever nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr,
4076c5641782SChuck Lever 		  union nfsd4_op_u *u)
4077c5641782SChuck Lever {
4078c5641782SChuck Lever 	struct nfsd4_lock *lock = &u->lock;
4079c5641782SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
40801da177e4SLinus Torvalds 	__be32 status;
40811da177e4SLinus Torvalds 
4082695e12f8SBenny Halevy 	switch (nfserr) {
4083e78e274eSKees Cook 	case nfs_ok:
4084e78e274eSKees Cook 		/* resok4 */
40851da177e4SLinus Torvalds 		status = nfsd4_encode_stateid4(xdr, &lock->lk_resp_stateid);
4086e78e274eSKees Cook 		break;
4087bddfdbcdSChuck Lever 	case nfserr_denied:
4088c5641782SChuck Lever 		/* denied */
4089d0a381ddSJ. Bruce Fields 		status = nfsd4_encode_lock4denied(xdr, &lock->lk_denied);
4090c5641782SChuck Lever 		break;
4091c5641782SChuck Lever 	default:
4092c5641782SChuck Lever 		return nfserr;
4093c5641782SChuck Lever 	}
4094c5641782SChuck Lever 	return status != nfs_ok ? status : nfserr;
4095c5641782SChuck Lever }
4096c5641782SChuck Lever 
4097c5641782SChuck Lever static __be32
nfsd4_encode_lockt(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4098c5641782SChuck Lever nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr,
4099c5641782SChuck Lever 		   union nfsd4_op_u *u)
4100695e12f8SBenny Halevy {
41011da177e4SLinus Torvalds 	struct nfsd4_lockt *lockt = &u->lockt;
4102c5641782SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4103c5641782SChuck Lever 	__be32 status;
41041da177e4SLinus Torvalds 
4105695e12f8SBenny Halevy 	if (nfserr == nfserr_denied) {
4106e78e274eSKees Cook 		/* denied */
4107e78e274eSKees Cook 		status = nfsd4_encode_lock4denied(xdr, &lockt->lt_denied);
41081da177e4SLinus Torvalds 		if (status != nfs_ok)
4109e78e274eSKees Cook 			return status;
4110bddfdbcdSChuck Lever 	}
4111c5641782SChuck Lever 	return nfserr;
4112d0a381ddSJ. Bruce Fields }
4113c5641782SChuck Lever 
4114c5641782SChuck Lever static __be32
nfsd4_encode_locku(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4115c5641782SChuck Lever nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr,
4116c5641782SChuck Lever 		   union nfsd4_op_u *u)
4117c5641782SChuck Lever {
4118c5641782SChuck Lever 	struct nfsd4_locku *locku = &u->locku;
4119695e12f8SBenny Halevy 	struct xdr_stream *xdr = resp->xdr;
41201da177e4SLinus Torvalds 
41211da177e4SLinus Torvalds 	/* lock_stateid */
4122695e12f8SBenny Halevy 	return nfsd4_encode_stateid4(xdr, &locku->lu_stateid);
4123e78e274eSKees Cook }
4124e78e274eSKees Cook 
41251da177e4SLinus Torvalds 
4126e78e274eSKees Cook static __be32
nfsd4_encode_link(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4127bddfdbcdSChuck Lever nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr,
4128d0a381ddSJ. Bruce Fields 		  union nfsd4_op_u *u)
412940bb2baaSChuck Lever {
413040bb2baaSChuck Lever 	struct nfsd4_link *link = &u->link;
41311da177e4SLinus Torvalds 	struct xdr_stream *xdr = resp->xdr;
41321da177e4SLinus Torvalds 
41331da177e4SLinus Torvalds 	return nfsd4_encode_change_info4(xdr, &link->li_cinfo);
4134695e12f8SBenny Halevy }
4135e78e274eSKees Cook 
4136e78e274eSKees Cook /*
41371da177e4SLinus Torvalds  * This implementation does not yet support returning an ACE in an
4138e78e274eSKees Cook  * OPEN that offers a delegation.
4139bddfdbcdSChuck Lever  */
41401da177e4SLinus Torvalds static __be32
nfsd4_encode_open_nfsace4(struct xdr_stream * xdr)414166a21db7SChuck Lever nfsd4_encode_open_nfsace4(struct xdr_stream *xdr)
41421da177e4SLinus Torvalds {
41431da177e4SLinus Torvalds 	__be32 status;
4144e4ad7ce7SChuck Lever 
4145e4ad7ce7SChuck Lever 	/* type */
4146e4ad7ce7SChuck Lever 	status = nfsd4_encode_acetype4(xdr, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
4147e4ad7ce7SChuck Lever 	if (status != nfs_ok)
4148e4ad7ce7SChuck Lever 		return nfserr_resource;
4149e4ad7ce7SChuck Lever 	/* flag */
4150e4ad7ce7SChuck Lever 	status = nfsd4_encode_aceflag4(xdr, 0);
4151e4ad7ce7SChuck Lever 	if (status != nfs_ok)
4152e4ad7ce7SChuck Lever 		return nfserr_resource;
4153e4ad7ce7SChuck Lever 	/* access mask */
4154e4ad7ce7SChuck Lever 	status = nfsd4_encode_acemask4(xdr, 0);
4155e4ad7ce7SChuck Lever 	if (status != nfs_ok)
4156e4ad7ce7SChuck Lever 		return nfserr_resource;
4157e4ad7ce7SChuck Lever 	/* who - empty for now */
4158e4ad7ce7SChuck Lever 	if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
4159e4ad7ce7SChuck Lever 		return nfserr_resource;
4160e4ad7ce7SChuck Lever 	return nfs_ok;
4161e4ad7ce7SChuck Lever }
4162e4ad7ce7SChuck Lever 
4163e4ad7ce7SChuck Lever static __be32
nfsd4_encode_open_read_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)4164e4ad7ce7SChuck Lever nfsd4_encode_open_read_delegation4(struct xdr_stream *xdr, struct nfsd4_open *open)
4165e4ad7ce7SChuck Lever {
4166e4ad7ce7SChuck Lever 	__be32 status;
4167e4ad7ce7SChuck Lever 
4168e4ad7ce7SChuck Lever 	/* stateid */
4169e4ad7ce7SChuck Lever 	status = nfsd4_encode_stateid4(xdr, &open->op_delegate_stateid);
4170e4ad7ce7SChuck Lever 	if (status != nfs_ok)
4171e4ad7ce7SChuck Lever 		return status;
4172e4ad7ce7SChuck Lever 	/* recall */
4173e4ad7ce7SChuck Lever 	status = nfsd4_encode_bool(xdr, open->op_recall);
4174e4ad7ce7SChuck Lever 	if (status != nfs_ok)
4175e4ad7ce7SChuck Lever 		return status;
4176e4ad7ce7SChuck Lever 	/* permissions */
4177e4ad7ce7SChuck Lever 	return nfsd4_encode_open_nfsace4(xdr);
4178e4ad7ce7SChuck Lever }
4179e4ad7ce7SChuck Lever 
4180e4ad7ce7SChuck Lever static __be32
nfsd4_encode_nfs_space_limit4(struct xdr_stream * xdr,u64 filesize)4181e4ad7ce7SChuck Lever nfsd4_encode_nfs_space_limit4(struct xdr_stream *xdr, u64 filesize)
4182e4ad7ce7SChuck Lever {
4183e4ad7ce7SChuck Lever 	/* limitby */
4184e4ad7ce7SChuck Lever 	if (xdr_stream_encode_u32(xdr, NFS4_LIMIT_SIZE) != XDR_UNIT)
4185e4ad7ce7SChuck Lever 		return nfserr_resource;
4186e4ad7ce7SChuck Lever 	/* filesize */
41871da177e4SLinus Torvalds 	return nfsd4_encode_uint64_t(xdr, filesize);
4188695e12f8SBenny Halevy }
418932efa674SChuck Lever 
419032efa674SChuck Lever static __be32
nfsd4_encode_open_write_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)419132efa674SChuck Lever nfsd4_encode_open_write_delegation4(struct xdr_stream *xdr,
419232efa674SChuck Lever 				    struct nfsd4_open *open)
419332efa674SChuck Lever {
419432efa674SChuck Lever 	__be32 status;
419532efa674SChuck Lever 
419632efa674SChuck Lever 	/* stateid */
419732efa674SChuck Lever 	status = nfsd4_encode_stateid4(xdr, &open->op_delegate_stateid);
419832efa674SChuck Lever 	if (status != nfs_ok)
419932efa674SChuck Lever 		return status;
420032efa674SChuck Lever 	/* recall */
420132efa674SChuck Lever 	status = nfsd4_encode_bool(xdr, open->op_recall);
420232efa674SChuck Lever 	if (status != nfs_ok)
420332efa674SChuck Lever 		return status;
420432efa674SChuck Lever 	/* space_limit */
420532efa674SChuck Lever 	status = nfsd4_encode_nfs_space_limit4(xdr, 0);
420632efa674SChuck Lever 	if (status != nfs_ok)
420732efa674SChuck Lever 		return status;
420832efa674SChuck Lever 	return nfsd4_encode_open_nfsace4(xdr);
420932efa674SChuck Lever }
421032efa674SChuck Lever 
421132efa674SChuck Lever static __be32
nfsd4_encode_open_none_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)421232efa674SChuck Lever nfsd4_encode_open_none_delegation4(struct xdr_stream *xdr,
421332efa674SChuck Lever 				   struct nfsd4_open *open)
421432efa674SChuck Lever {
421532efa674SChuck Lever 	__be32 status = nfs_ok;
421632efa674SChuck Lever 
421732efa674SChuck Lever 	/* ond_why */
421832efa674SChuck Lever 	if (xdr_stream_encode_u32(xdr, open->op_why_no_deleg) != XDR_UNIT)
421932efa674SChuck Lever 		return nfserr_resource;
42206dd43c6dSChuck Lever 	switch (open->op_why_no_deleg) {
42216dd43c6dSChuck Lever 	case WND4_CONTENTION:
42226dd43c6dSChuck Lever 		/* ond_server_will_push_deleg */
42236dd43c6dSChuck Lever 		status = nfsd4_encode_bool(xdr, false);
42246dd43c6dSChuck Lever 		break;
42256dd43c6dSChuck Lever 	case WND4_RESOURCE:
42266dd43c6dSChuck Lever 		/* ond_server_will_signal_avail */
42276dd43c6dSChuck Lever 		status = nfsd4_encode_bool(xdr, false);
42286dd43c6dSChuck Lever 	}
42296dd43c6dSChuck Lever 	return status;
42306dd43c6dSChuck Lever }
42316dd43c6dSChuck Lever 
42326dd43c6dSChuck Lever static __be32
nfsd4_encode_open_delegation4(struct xdr_stream * xdr,struct nfsd4_open * open)42336dd43c6dSChuck Lever nfsd4_encode_open_delegation4(struct xdr_stream *xdr, struct nfsd4_open *open)
42346dd43c6dSChuck Lever {
42356dd43c6dSChuck Lever 	__be32 status;
42366dd43c6dSChuck Lever 
42376dd43c6dSChuck Lever 	/* delegation_type */
42386dd43c6dSChuck Lever 	if (xdr_stream_encode_u32(xdr, open->op_delegate_type) != XDR_UNIT)
42396dd43c6dSChuck Lever 		return nfserr_resource;
42406dd43c6dSChuck Lever 	switch (open->op_delegate_type) {
4241802e1913SChuck Lever 	case NFS4_OPEN_DELEGATE_NONE:
4242802e1913SChuck Lever 		status = nfs_ok;
4243802e1913SChuck Lever 		break;
4244802e1913SChuck Lever 	case NFS4_OPEN_DELEGATE_READ:
4245802e1913SChuck Lever 		/* read */
4246802e1913SChuck Lever 		status = nfsd4_encode_open_read_delegation4(xdr, open);
4247802e1913SChuck Lever 		break;
4248802e1913SChuck Lever 	case NFS4_OPEN_DELEGATE_WRITE:
4249802e1913SChuck Lever 		/* write */
4250802e1913SChuck Lever 		status = nfsd4_encode_open_write_delegation4(xdr, open);
4251802e1913SChuck Lever 		break;
4252802e1913SChuck Lever 	case NFS4_OPEN_DELEGATE_NONE_EXT:
4253802e1913SChuck Lever 		/* od_whynone */
4254802e1913SChuck Lever 		status = nfsd4_encode_open_none_delegation4(xdr, open);
4255802e1913SChuck Lever 		break;
4256802e1913SChuck Lever 	default:
4257802e1913SChuck Lever 		status = nfserr_serverfault;
4258802e1913SChuck Lever 	}
4259802e1913SChuck Lever 
4260802e1913SChuck Lever 	return status;
4261802e1913SChuck Lever }
4262802e1913SChuck Lever 
4263802e1913SChuck Lever static __be32
nfsd4_encode_open(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4264802e1913SChuck Lever nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr,
4265802e1913SChuck Lever 		  union nfsd4_op_u *u)
4266802e1913SChuck Lever {
4267802e1913SChuck Lever 	struct nfsd4_open *open = &u->open;
4268802e1913SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4269802e1913SChuck Lever 
4270802e1913SChuck Lever 	/* stateid */
4271802e1913SChuck Lever 	nfserr = nfsd4_encode_stateid4(xdr, &open->op_stateid);
4272e78e274eSKees Cook 	if (nfserr != nfs_ok)
4273e78e274eSKees Cook 		return nfserr;
42741da177e4SLinus Torvalds 	/* cinfo */
4275e78e274eSKees Cook 	nfserr = nfsd4_encode_change_info4(xdr, &open->op_cinfo);
4276bddfdbcdSChuck Lever 	if (nfserr != nfs_ok)
42771da177e4SLinus Torvalds 		return nfserr;
4278841735b3SChuck Lever 	/* rflags */
427940bb2baaSChuck Lever 	nfserr = nfsd4_encode_uint32_t(xdr, open->op_rflags);
4280841735b3SChuck Lever 	if (nfserr != nfs_ok)
4281bac966d6SJ. Bruce Fields 		return nfserr;
4282841735b3SChuck Lever 	/* attrset */
428366a21db7SChuck Lever 	nfserr = nfsd4_encode_bitmap4(xdr, open->op_bmval[0],
4284841735b3SChuck Lever 				      open->op_bmval[1], open->op_bmval[2]);
428566a21db7SChuck Lever 	if (nfserr != nfs_ok)
4286841735b3SChuck Lever 		return nfserr;
4287841735b3SChuck Lever 	/* delegation */
4288841735b3SChuck Lever 	return nfsd4_encode_open_delegation4(xdr, open);
4289841735b3SChuck Lever }
4290841735b3SChuck Lever 
4291e64301f5SChuck Lever static __be32
nfsd4_encode_open_confirm(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4292e64301f5SChuck Lever nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr,
4293841735b3SChuck Lever 			  union nfsd4_op_u *u)
4294bac966d6SJ. Bruce Fields {
4295802e1913SChuck Lever 	struct nfsd4_open_confirm *oc = &u->open_confirm;
4296802e1913SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
42971da177e4SLinus Torvalds 
42981da177e4SLinus Torvalds 	/* open_stateid */
4299695e12f8SBenny Halevy 	return nfsd4_encode_stateid4(xdr, &oc->oc_resp_stateid);
4300e78e274eSKees Cook }
4301e78e274eSKees Cook 
43021da177e4SLinus Torvalds static __be32
nfsd4_encode_open_downgrade(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4303e78e274eSKees Cook nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr,
4304bddfdbcdSChuck Lever 			    union nfsd4_op_u *u)
4305d0a381ddSJ. Bruce Fields {
430640bb2baaSChuck Lever 	struct nfsd4_open_downgrade *od = &u->open_downgrade;
430740bb2baaSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
43081da177e4SLinus Torvalds 
43091da177e4SLinus Torvalds 	/* open_stateid */
4310695e12f8SBenny Halevy 	return nfsd4_encode_stateid4(xdr, &od->od_stateid);
4311e78e274eSKees Cook }
4312e78e274eSKees Cook 
43131da177e4SLinus Torvalds /*
4314e78e274eSKees Cook  * The operation of this function assumes that this is the only
4315bddfdbcdSChuck Lever  * READ operation in the COMPOUND. If there are multiple READs,
4316d0a381ddSJ. Bruce Fields  * we use nfsd4_encode_readv().
431740bb2baaSChuck Lever  */
nfsd4_encode_splice_read(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)431840bb2baaSChuck Lever static __be32 nfsd4_encode_splice_read(
43191da177e4SLinus Torvalds 				struct nfsd4_compoundres *resp,
43201da177e4SLinus Torvalds 				struct nfsd4_read *read,
4321ba21e20bSChuck Lever 				struct file *file, unsigned long maxcount)
4322ba21e20bSChuck Lever {
4323ba21e20bSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4324ba21e20bSChuck Lever 	struct xdr_buf *buf = xdr->buf;
4325ba21e20bSChuck Lever 	int status, space_left;
4326dc97618dSJ. Bruce Fields 	__be32 nfserr;
4327dc97618dSJ. Bruce Fields 
4328dc97618dSJ. Bruce Fields 	/*
4329dc97618dSJ. Bruce Fields 	 * Make sure there is room at the end of buf->head for
43301da177e4SLinus Torvalds 	 * svcxdr_encode_opaque_pages() to create a tail buffer
4331bddfdbcdSChuck Lever 	 * to XDR-pad the payload.
433234a78b48SJ. Bruce Fields 	 */
433376e5492bSChuck Lever 	if (xdr->iov != xdr->buf->head || xdr->end - xdr->p < 1)
4334dc97618dSJ. Bruce Fields 		return nfserr_resource;
4335dc97618dSJ. Bruce Fields 
4336ba21e20bSChuck Lever 	nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
4337ba21e20bSChuck Lever 				  file, read->rd_offset, &maxcount,
4338ba21e20bSChuck Lever 				  &read->rd_eof);
4339ba21e20bSChuck Lever 	read->rd_length = maxcount;
4340ba21e20bSChuck Lever 	if (nfserr)
4341ba21e20bSChuck Lever 		goto out_err;
4342dc97618dSJ. Bruce Fields 	svcxdr_encode_opaque_pages(read->rd_rqstp, xdr, buf->pages,
4343dc97618dSJ. Bruce Fields 				   buf->page_base, maxcount);
434487c5942eSChuck Lever 	status = svc_encode_result_payload(read->rd_rqstp,
434524c7fb85SChuck Lever 					   buf->head[0].iov_len, maxcount);
434624c7fb85SChuck Lever 	if (status) {
434787c5942eSChuck Lever 		nfserr = nfserrno(status);
434876e5492bSChuck Lever 		goto out_err;
434976e5492bSChuck Lever 	}
4350ba21e20bSChuck Lever 
4351ba21e20bSChuck Lever 	/*
435276e5492bSChuck Lever 	 * Prepare to encode subsequent operations.
435376e5492bSChuck Lever 	 *
435476e5492bSChuck Lever 	 * xdr_truncate_encode() is not safe to use after a successful
435576e5492bSChuck Lever 	 * splice read has been done, so the following stream
435676e5492bSChuck Lever 	 * manipulations are open-coded.
4357dc97618dSJ. Bruce Fields 	 */
4358dc97618dSJ. Bruce Fields 	space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
4359ba21e20bSChuck Lever 				buf->buflen - buf->len);
4360ba21e20bSChuck Lever 	buf->buflen = buf->len + space_left;
4361ba21e20bSChuck Lever 	xdr->end = (__be32 *)((void *)xdr->end + space_left);
4362ba21e20bSChuck Lever 
4363ba21e20bSChuck Lever 	return nfs_ok;
4364ba21e20bSChuck Lever 
4365ba21e20bSChuck Lever out_err:
4366dc97618dSJ. Bruce Fields 	/*
436734a78b48SJ. Bruce Fields 	 * nfsd_splice_actor may have already messed with the
436834a78b48SJ. Bruce Fields 	 * page length; reset it so as not to confuse
4369dc97618dSJ. Bruce Fields 	 * xdr_truncate_encode in our caller.
4370dc97618dSJ. Bruce Fields 	 */
4371ba21e20bSChuck Lever 	buf->page_len = 0;
437276e5492bSChuck Lever 	return nfserr;
437376e5492bSChuck Lever }
437476e5492bSChuck Lever 
nfsd4_encode_readv(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)437576e5492bSChuck Lever static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
437676e5492bSChuck Lever 				 struct nfsd4_read *read,
437776e5492bSChuck Lever 				 struct file *file, unsigned long maxcount)
437876e5492bSChuck Lever {
437976e5492bSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
438076e5492bSChuck Lever 	unsigned int base = xdr->buf->page_len & ~PAGE_MASK;
4381dc97618dSJ. Bruce Fields 	unsigned int starting_len = xdr->buf->len;
4382dc97618dSJ. Bruce Fields 	__be32 zero = xdr_zero;
4383dc97618dSJ. Bruce Fields 	__be32 nfserr;
4384dc97618dSJ. Bruce Fields 
4385dc97618dSJ. Bruce Fields 	if (xdr_reserve_space_vec(xdr, maxcount) < 0)
4386dc97618dSJ. Bruce Fields 		return nfserr_resource;
4387bddfdbcdSChuck Lever 
43880d32a6bbSChuck Lever 	nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file,
4389071ae99fSChuck Lever 				read->rd_offset, &maxcount, base,
43905e64d85cSChuck Lever 				&read->rd_eof);
4391dc97618dSJ. Bruce Fields 	read->rd_length = maxcount;
43921da177e4SLinus Torvalds 	if (nfserr)
4393703d7521SChuck Lever 		return nfserr;
4394403217f3SAnna Schumaker 	if (svc_encode_result_payload(resp->rqstp, starting_len, maxcount))
43951da177e4SLinus Torvalds 		return nfserr_io;
4396703d7521SChuck Lever 	xdr_truncate_encode(xdr, starting_len + xdr_align_size(maxcount));
43970d32a6bbSChuck Lever 
439824c7fb85SChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, starting_len + maxcount, &zero,
439987c5942eSChuck Lever 			       xdr_pad_size(maxcount));
4400dc97618dSJ. Bruce Fields 	return nfs_ok;
44011da177e4SLinus Torvalds }
4402071ae99fSChuck Lever 
440341205539SChuck Lever static __be32
nfsd4_encode_read(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4404071ae99fSChuck Lever nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
4405dc97618dSJ. Bruce Fields 		  union nfsd4_op_u *u)
44065e64d85cSChuck Lever {
44075e64d85cSChuck Lever 	struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
44085e64d85cSChuck Lever 	struct nfsd4_read *read = &u->read;
4409dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = resp->xdr;
4410dc97618dSJ. Bruce Fields 	int starting_len = xdr->buf->len;
4411dc97618dSJ. Bruce Fields 	bool splice_ok = argp->splice_ok;
4412dc97618dSJ. Bruce Fields 	unsigned long maxcount;
4413e78e274eSKees Cook 	struct file *file;
4414dc97618dSJ. Bruce Fields 	__be32 *p;
4415a2c91753SChuck Lever 
4416e78e274eSKees Cook 	if (nfserr)
4417bddfdbcdSChuck Lever 		return nfserr;
4418dc97618dSJ. Bruce Fields 	file = read->rd_nf->nf_file;
4419a2c91753SChuck Lever 
4420a2c91753SChuck Lever 	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
4421a2c91753SChuck Lever 	if (!p) {
4422dc97618dSJ. Bruce Fields 		WARN_ON_ONCE(splice_ok);
4423dc97618dSJ. Bruce Fields 		return nfserr_resource;
44245c4583b2SJeff Layton 	}
44255c4583b2SJeff Layton 	if (resp->xdr->buf->page_len && splice_ok) {
44265c4583b2SJeff Layton 		WARN_ON_ONCE(1);
44275c4583b2SJeff Layton 		return nfserr_serverfault;
4428dc97618dSJ. Bruce Fields 	}
4429dc97618dSJ. Bruce Fields 	xdr_commit_encode(xdr);
4430c738b218SChuck Lever 
4431bac966d6SJ. Bruce Fields 	maxcount = min_t(unsigned long, read->rd_length,
4432dc97618dSJ. Bruce Fields 			 (xdr->buf->buflen - xdr->buf->len));
4433c738b218SChuck Lever 
4434b0420980SJ. Bruce Fields 	if (file->f_op->splice_read && splice_ok)
443506981d56SAnna Schumaker 		nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
4436dc97618dSJ. Bruce Fields 	else
4437dc97618dSJ. Bruce Fields 		nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
4438dc97618dSJ. Bruce Fields 	if (nfserr) {
44390cb4d23aSChuck Lever 		xdr_truncate_encode(xdr, starting_len);
444068e8bb03SChristoph Hellwig 		return nfserr;
4441dc97618dSJ. Bruce Fields 	}
4442c738b218SChuck Lever 
444396bcad50SChristoph Hellwig 	p = xdr_encode_bool(p, read->rd_eof);
4444dc97618dSJ. Bruce Fields 	*p = cpu_to_be32(read->rd_length);
444596bcad50SChristoph Hellwig 	return nfs_ok;
444628d5bc46SChuck Lever }
4447dc97618dSJ. Bruce Fields 
444896bcad50SChristoph Hellwig static __be32
nfsd4_encode_readlink(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)44491da177e4SLinus Torvalds nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
44501da177e4SLinus Torvalds 		      union nfsd4_op_u *u)
445128d5bc46SChuck Lever {
445228d5bc46SChuck Lever 	struct nfsd4_readlink *readlink = &u->readlink;
445328d5bc46SChuck Lever 	__be32 *p, *maxcount_p, zero = xdr_zero;
445428d5bc46SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
445528d5bc46SChuck Lever 	int length_offset = xdr->buf->len;
4456b37ad28bSAl Viro 	int maxcount, status;
4457e78e274eSKees Cook 
4458e78e274eSKees Cook 	maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
44591da177e4SLinus Torvalds 	if (!maxcount_p)
4460e78e274eSKees Cook 		return nfserr_resource;
446199b002a1SChuck Lever 	maxcount = PAGE_SIZE;
4462bddfdbcdSChuck Lever 
44631fcea5b2SJ. Bruce Fields 	p = xdr_reserve_space(xdr, maxcount);
446499b002a1SChuck Lever 	if (!p)
44651da177e4SLinus Torvalds 		return nfserr_resource;
446699b002a1SChuck Lever 	/*
446799b002a1SChuck Lever 	 * XXX: By default, vfs_readlink() will truncate symlinks if they
44682825a7f9SJ. Bruce Fields 	 * would overflow the buffer.  Is this kosher in NFSv4?  If not, one
44691da177e4SLinus Torvalds 	 * easy fix is: if vfs_readlink() precisely fills the buffer, assume
4470d0a381ddSJ. Bruce Fields 	 * that truncation occurred, and return NFS4ERR_RESOURCE.
4471476a7b1fSJ. Bruce Fields 	 */
4472476a7b1fSJ. Bruce Fields 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
44734e21ac4bSJ. Bruce Fields 						(char *)p, &maxcount);
44741da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
4475fd4a0edfSMiklos Szeredi 		nfserr = nfserr_inval;
4476fd4a0edfSMiklos Szeredi 	if (nfserr)
4477fd4a0edfSMiklos Szeredi 		goto out_err;
4478fd4a0edfSMiklos Szeredi 	status = svc_encode_result_payload(readlink->rl_rqstp, length_offset,
44791da177e4SLinus Torvalds 					   maxcount);
4480476a7b1fSJ. Bruce Fields 	if (status) {
4481476a7b1fSJ. Bruce Fields 		nfserr = nfserrno(status);
44821da177e4SLinus Torvalds 		goto out_err;
4483d3f627c8SJ. Bruce Fields 	}
448476e5492bSChuck Lever 	*maxcount_p = cpu_to_be32(maxcount);
448576e5492bSChuck Lever 	xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
448676e5492bSChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
448776e5492bSChuck Lever 			       xdr_pad_size(maxcount));
448876e5492bSChuck Lever 	return nfs_ok;
448976e5492bSChuck Lever 
449076e5492bSChuck Lever out_err:
4491d3f627c8SJ. Bruce Fields 	xdr_truncate_encode(xdr, length_offset);
449299b002a1SChuck Lever 	return nfserr;
449399b002a1SChuck Lever }
449499b002a1SChuck Lever 
nfsd4_encode_dirlist4(struct xdr_stream * xdr,struct nfsd4_readdir * readdir,u32 max_payload)449599b002a1SChuck Lever static __be32 nfsd4_encode_dirlist4(struct xdr_stream *xdr,
449699b002a1SChuck Lever 				    struct nfsd4_readdir *readdir,
449776e5492bSChuck Lever 				    u32 max_payload)
449876e5492bSChuck Lever {
449976e5492bSChuck Lever 	int bytes_left, maxcount, starting_len = xdr->buf->len;
450076e5492bSChuck Lever 	loff_t offset;
45011da177e4SLinus Torvalds 	__be32 status;
45021da177e4SLinus Torvalds 
450325c307acSChuck Lever 	/*
450425c307acSChuck Lever 	 * Number of bytes left for directory entries allowing for the
450525c307acSChuck Lever 	 * final 8 bytes of the readdir and a following failed op.
450625c307acSChuck Lever 	 */
450725c307acSChuck Lever 	bytes_left = xdr->buf->buflen - xdr->buf->len -
450825c307acSChuck Lever 		COMPOUND_ERR_SLACK_SPACE - XDR_UNIT * 2;
450925c307acSChuck Lever 	if (bytes_left < 0)
451025c307acSChuck Lever 		return nfserr_resource;
451125c307acSChuck Lever 	maxcount = min_t(u32, readdir->rd_maxcount, max_payload);
451225c307acSChuck Lever 
451325c307acSChuck Lever 	/*
451425c307acSChuck Lever 	 * The RFC defines rd_maxcount as the size of the
451525c307acSChuck Lever 	 * READDIR4resok structure, which includes the verifier
451625c307acSChuck Lever 	 * and the 8 bytes encoded at the end of this function.
451725c307acSChuck Lever 	 */
451825c307acSChuck Lever 	if (maxcount < XDR_UNIT * 4)
451925c307acSChuck Lever 		return nfserr_toosmall;
452025c307acSChuck Lever 	maxcount = min_t(int, maxcount - XDR_UNIT * 4, bytes_left);
452125c307acSChuck Lever 
452225c307acSChuck Lever 	/* RFC 3530 14.2.24 allows us to ignore dircount when it's 0 */
452325c307acSChuck Lever 	if (!readdir->rd_dircount)
452425c307acSChuck Lever 		readdir->rd_dircount = max_payload;
452525c307acSChuck Lever 
452625c307acSChuck Lever 	/* *entries */
452725c307acSChuck Lever 	readdir->xdr = xdr;
452825c307acSChuck Lever 	readdir->rd_maxcount = maxcount;
452925c307acSChuck Lever 	readdir->common.err = 0;
453025c307acSChuck Lever 	readdir->cookie_offset = 0;
453125c307acSChuck Lever 	offset = readdir->rd_cookie;
453225c307acSChuck Lever 	status = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, &offset,
453325c307acSChuck Lever 			      &readdir->common, nfsd4_encode_entry4);
453425c307acSChuck Lever 	if (status)
453525c307acSChuck Lever 		return status;
453625c307acSChuck Lever 	if (readdir->common.err == nfserr_toosmall &&
453725c307acSChuck Lever 	    xdr->buf->len == starting_len) {
453825c307acSChuck Lever 		/* No entries were encoded. Which limit did we hit? */
453925c307acSChuck Lever 		if (maxcount - XDR_UNIT * 4 < bytes_left)
454025c307acSChuck Lever 			/* It was the fault of rd_maxcount */
454125c307acSChuck Lever 			return nfserr_toosmall;
454225c307acSChuck Lever 		/* We ran out of buffer space */
454325c307acSChuck Lever 		return nfserr_resource;
454425c307acSChuck Lever 	}
454525c307acSChuck Lever 	/* Encode the final entry's cookie value */
454625c307acSChuck Lever 	nfsd4_encode_entry4_nfs_cookie4(readdir, offset);
454725c307acSChuck Lever 	/* No entries follow */
454825c307acSChuck Lever 	if (xdr_stream_encode_item_absent(xdr) != XDR_UNIT)
454925c307acSChuck Lever 		return nfserr_resource;
455025c307acSChuck Lever 
455125c307acSChuck Lever 	/* eof */
455225c307acSChuck Lever 	return nfsd4_encode_bool(xdr, readdir->common.err == nfserr_eof);
455325c307acSChuck Lever }
455425c307acSChuck Lever 
455525c307acSChuck Lever static __be32
nfsd4_encode_readdir(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)455625c307acSChuck Lever nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr,
455725c307acSChuck Lever 		     union nfsd4_op_u *u)
455825c307acSChuck Lever {
455925c307acSChuck Lever 	struct nfsd4_readdir *readdir = &u->readdir;
456025c307acSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
456125c307acSChuck Lever 	int starting_len = xdr->buf->len;
456225c307acSChuck Lever 
4563b37ad28bSAl Viro 	/* cookieverf */
4564e78e274eSKees Cook 	nfserr = nfsd4_encode_verifier4(xdr, &readdir->rd_verf);
4565e78e274eSKees Cook 	if (nfserr != nfs_ok)
45661da177e4SLinus Torvalds 		return nfserr;
4567e78e274eSKees Cook 
4568bddfdbcdSChuck Lever 	/* reply */
45691fcea5b2SJ. Bruce Fields 	nfserr = nfsd4_encode_dirlist4(xdr, readdir, svc_max_payload(resp->rqstp));
45701da177e4SLinus Torvalds 	if (nfserr != nfs_ok)
457125c307acSChuck Lever 		xdr_truncate_encode(xdr, starting_len);
4572adaa7a50SChuck Lever 	return nfserr;
4573adaa7a50SChuck Lever }
4574adaa7a50SChuck Lever 
45751da177e4SLinus Torvalds static __be32
nfsd4_encode_remove(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)457625c307acSChuck Lever nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr,
457725c307acSChuck Lever 		    union nfsd4_op_u *u)
457825c307acSChuck Lever {
45791fcea5b2SJ. Bruce Fields 	struct nfsd4_remove *remove = &u->remove;
45801da177e4SLinus Torvalds 	struct xdr_stream *xdr = resp->xdr;
45811da177e4SLinus Torvalds 
45821da177e4SLinus Torvalds 	return nfsd4_encode_change_info4(xdr, &remove->rm_cinfo);
4583695e12f8SBenny Halevy }
4584e78e274eSKees Cook 
4585e78e274eSKees Cook static __be32
nfsd4_encode_rename(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)45861da177e4SLinus Torvalds nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr,
4587e78e274eSKees Cook 		    union nfsd4_op_u *u)
4588bddfdbcdSChuck Lever {
45891da177e4SLinus Torvalds 	struct nfsd4_rename *rename = &u->rename;
459066a21db7SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
45911da177e4SLinus Torvalds 
45921da177e4SLinus Torvalds 	nfserr = nfsd4_encode_change_info4(xdr, &rename->rn_sinfo);
4593695e12f8SBenny Halevy 	if (nfserr)
4594e78e274eSKees Cook 		return nfserr;
4595e78e274eSKees Cook 	return nfsd4_encode_change_info4(xdr, &rename->rn_tinfo);
45961da177e4SLinus Torvalds }
4597e78e274eSKees Cook 
4598bddfdbcdSChuck Lever static __be32
nfsd4_encode_rpcsec_gss_info(struct xdr_stream * xdr,struct rpcsec_gss_info * info)45991da177e4SLinus Torvalds nfsd4_encode_rpcsec_gss_info(struct xdr_stream *xdr,
460066a21db7SChuck Lever 			     struct rpcsec_gss_info *info)
460166a21db7SChuck Lever {
460266a21db7SChuck Lever 	__be32 status;
460366a21db7SChuck Lever 
46041da177e4SLinus Torvalds 	/* oid */
46051da177e4SLinus Torvalds 	if (xdr_stream_encode_opaque(xdr, info->oid.data, info->oid.len) < 0)
4606695e12f8SBenny Halevy 		return nfserr_resource;
460791c7a905SChuck Lever 	/* qop */
460891c7a905SChuck Lever 	status = nfsd4_encode_qop4(xdr, info->qop);
460991c7a905SChuck Lever 	if (status != nfs_ok)
461091c7a905SChuck Lever 		return status;
461191c7a905SChuck Lever 	/* service */
461291c7a905SChuck Lever 	if (xdr_stream_encode_u32(xdr, info->service) != XDR_UNIT)
461391c7a905SChuck Lever 		return nfserr_resource;
461491c7a905SChuck Lever 
461591c7a905SChuck Lever 	return nfs_ok;
461691c7a905SChuck Lever }
461791c7a905SChuck Lever 
461891c7a905SChuck Lever static __be32
nfsd4_do_encode_secinfo(struct xdr_stream * xdr,struct svc_export * exp)461991c7a905SChuck Lever nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
462091c7a905SChuck Lever {
462191c7a905SChuck Lever 	u32 i, nflavs, supported;
462291c7a905SChuck Lever 	struct exp_flavor_info *flavs;
462391c7a905SChuck Lever 	struct exp_flavor_info def_flavs[2];
462491c7a905SChuck Lever 	static bool report = true;
462591c7a905SChuck Lever 	__be32 *flavorsp;
462691c7a905SChuck Lever 	__be32 status;
4627bac966d6SJ. Bruce Fields 
4628dcb488a3SAndy Adamson 	if (exp->ex_nflavors) {
4629676e4ebdSChuck Lever 		flavs = exp->ex_flavors;
46304796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
46314796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
4632676e4ebdSChuck Lever 		flavs = def_flavs;
463391c7a905SChuck Lever 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
463491c7a905SChuck Lever 			nflavs = 2;
4635dcb488a3SAndy Adamson 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
46364796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
46374796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
46384796f457SJ. Bruce Fields 			nflavs = 1;
46394796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
46404796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
46414796f457SJ. Bruce Fields 		} else {
46424796f457SJ. Bruce Fields 			nflavs = 1;
46434796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
46444796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
46454796f457SJ. Bruce Fields 		}
46464796f457SJ. Bruce Fields 	}
46474796f457SJ. Bruce Fields 
46484796f457SJ. Bruce Fields 	supported = 0;
46494796f457SJ. Bruce Fields 	flavorsp = xdr_reserve_space(xdr, XDR_UNIT);
46504796f457SJ. Bruce Fields 	if (!flavorsp)
46514796f457SJ. Bruce Fields 		return nfserr_resource;
46524796f457SJ. Bruce Fields 
46534796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
46544796f457SJ. Bruce Fields 		rpc_authflavor_t pf = flavs[i].pseudoflavor;
46554796f457SJ. Bruce Fields 		struct rpcsec_gss_info info;
4656676e4ebdSChuck Lever 
465791c7a905SChuck Lever 		if (rpcauth_get_gssinfo(pf, &info) == 0) {
465891c7a905SChuck Lever 			supported++;
4659bac966d6SJ. Bruce Fields 
4660676e4ebdSChuck Lever 			/* flavor */
46614796f457SJ. Bruce Fields 			status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
4662676e4ebdSChuck Lever 			if (status != nfs_ok)
4663a77c806fSChuck Lever 				return status;
4664dcb488a3SAndy Adamson 			/* flavor_info */
4665676e4ebdSChuck Lever 			status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
4666676e4ebdSChuck Lever 			if (status != nfs_ok)
466791c7a905SChuck Lever 				return status;
466891c7a905SChuck Lever 		} else if (pf < RPC_AUTH_MAXFLAVOR) {
466991c7a905SChuck Lever 			supported++;
467091c7a905SChuck Lever 
467191c7a905SChuck Lever 			/* flavor */
467291c7a905SChuck Lever 			status = nfsd4_encode_uint32_t(xdr, pf);
467391c7a905SChuck Lever 			if (status != nfs_ok)
467491c7a905SChuck Lever 				return status;
467591c7a905SChuck Lever 		} else {
4676676e4ebdSChuck Lever 			if (report)
4677676e4ebdSChuck Lever 				pr_warn("NFS: SECINFO: security flavor %u "
467891c7a905SChuck Lever 					"is not supported\n", pf);
467991c7a905SChuck Lever 		}
468091c7a905SChuck Lever 	}
468191c7a905SChuck Lever 
468291c7a905SChuck Lever 	if (nflavs != supported)
4683676e4ebdSChuck Lever 		report = false;
4684676e4ebdSChuck Lever 	*flavorsp = cpu_to_be32(supported);
4685676e4ebdSChuck Lever 	return 0;
4686676e4ebdSChuck Lever }
4687dcb488a3SAndy Adamson 
4688dcb488a3SAndy Adamson static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4689a77c806fSChuck Lever nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
4690676e4ebdSChuck Lever 		     union nfsd4_op_u *u)
4691676e4ebdSChuck Lever {
469291c7a905SChuck Lever 	struct nfsd4_secinfo *secinfo = &u->secinfo;
4693bac966d6SJ. Bruce Fields 	struct xdr_stream *xdr = resp->xdr;
4694dcb488a3SAndy Adamson 
4695dcb488a3SAndy Adamson 	return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
469622b6dee8SMi Jinlong }
469722b6dee8SMi Jinlong 
4698e78e274eSKees Cook static __be32
nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)469922b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
4700e78e274eSKees Cook 		     union nfsd4_op_u *u)
4701bddfdbcdSChuck Lever {
4702d0a381ddSJ. Bruce Fields 	struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
4703bac966d6SJ. Bruce Fields 	struct xdr_stream *xdr = resp->xdr;
470422b6dee8SMi Jinlong 
470522b6dee8SMi Jinlong 	return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
470622b6dee8SMi Jinlong }
470722b6dee8SMi Jinlong 
4708e78e274eSKees Cook static __be32
nfsd4_encode_setattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)470922b6dee8SMi Jinlong nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr,
4710e78e274eSKees Cook 		     union nfsd4_op_u *u)
4711bddfdbcdSChuck Lever {
4712d0a381ddSJ. Bruce Fields 	struct nfsd4_setattr *setattr = &u->setattr;
4713bac966d6SJ. Bruce Fields 	__be32 status;
471422b6dee8SMi Jinlong 
471522b6dee8SMi Jinlong 	switch (nfserr) {
4716695e12f8SBenny Halevy 	case nfs_ok:
4717e78e274eSKees Cook 		/* attrsset */
4718e78e274eSKees Cook 		status = nfsd4_encode_bitmap4(resp->xdr, setattr->sa_bmval[0],
47191da177e4SLinus Torvalds 					      setattr->sa_bmval[1],
4720e78e274eSKees Cook 					      setattr->sa_bmval[2]);
4721c3dcb45bSChuck Lever 		break;
47221da177e4SLinus Torvalds 	default:
4723c3dcb45bSChuck Lever 		/* attrsset */
4724c3dcb45bSChuck Lever 		status = nfsd4_encode_bitmap4(resp->xdr, 0, 0, 0);
4725c3dcb45bSChuck Lever 	}
4726c3dcb45bSChuck Lever 	return status != nfs_ok ? status : nfserr;
4727c3dcb45bSChuck Lever }
4728c3dcb45bSChuck Lever 
4729c3dcb45bSChuck Lever static __be32
nfsd4_encode_setclientid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4730c3dcb45bSChuck Lever nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr,
4731c3dcb45bSChuck Lever 			 union nfsd4_op_u *u)
4732c3dcb45bSChuck Lever {
47331da177e4SLinus Torvalds 	struct nfsd4_setclientid *scd = &u->setclientid;
4734c3dcb45bSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
47351da177e4SLinus Torvalds 
47361da177e4SLinus Torvalds 	if (!nfserr) {
4737695e12f8SBenny Halevy 		nfserr = nfsd4_encode_clientid4(xdr, &scd->se_clientid);
4738e78e274eSKees Cook 		if (nfserr != nfs_ok)
4739e78e274eSKees Cook 			goto out;
47401da177e4SLinus Torvalds 		nfserr = nfsd4_encode_verifier4(xdr, &scd->se_confirm);
4741e78e274eSKees Cook 	} else if (nfserr == nfserr_clid_inuse) {
4742bddfdbcdSChuck Lever 		/* empty network id */
47431da177e4SLinus Torvalds 		if (xdr_stream_encode_u32(xdr, 0) < 0) {
47441da177e4SLinus Torvalds 			nfserr = nfserr_resource;
4745adaa7a50SChuck Lever 			goto out;
4746adaa7a50SChuck Lever 		}
4747adaa7a50SChuck Lever 		/* empty universal address */
4748adaa7a50SChuck Lever 		if (xdr_stream_encode_u32(xdr, 0) < 0) {
4749adaa7a50SChuck Lever 			nfserr = nfserr_resource;
4750adaa7a50SChuck Lever 			goto out;
4751adaa7a50SChuck Lever 		}
4752adaa7a50SChuck Lever 	}
4753adaa7a50SChuck Lever out:
47541da177e4SLinus Torvalds 	return nfserr;
4755adaa7a50SChuck Lever }
4756adaa7a50SChuck Lever 
4757adaa7a50SChuck Lever static __be32
nfsd4_encode_write(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4758adaa7a50SChuck Lever nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr,
47591da177e4SLinus Torvalds 		   union nfsd4_op_u *u)
4760adaa7a50SChuck Lever {
4761adaa7a50SChuck Lever 	struct nfsd4_write *write = &u->write;
4762695e12f8SBenny Halevy 	struct xdr_stream *xdr = resp->xdr;
47631da177e4SLinus Torvalds 
47641da177e4SLinus Torvalds 	/* count */
4765695e12f8SBenny Halevy 	nfserr = nfsd4_encode_count4(xdr, write->wr_bytes_written);
4766e78e274eSKees Cook 	if (nfserr)
4767e78e274eSKees Cook 		return nfserr;
47681da177e4SLinus Torvalds 	/* committed */
4769e78e274eSKees Cook 	if (xdr_stream_encode_u32(xdr, write->wr_how_written) != XDR_UNIT)
477076bebcc7SChuck Lever 		return nfserr_resource;
47711da177e4SLinus Torvalds 	/* writeverf */
477276bebcc7SChuck Lever 	return nfsd4_encode_verifier4(xdr, &write->wr_verifier);
477376bebcc7SChuck Lever }
477476bebcc7SChuck Lever 
477576bebcc7SChuck Lever static __be32
nfsd4_encode_state_protect_ops4(struct xdr_stream * xdr,struct nfsd4_exchange_id * exid)477676bebcc7SChuck Lever nfsd4_encode_state_protect_ops4(struct xdr_stream *xdr,
477776bebcc7SChuck Lever 				struct nfsd4_exchange_id *exid)
4778d0a381ddSJ. Bruce Fields {
477976bebcc7SChuck Lever 	__be32 status;
478076bebcc7SChuck Lever 
47811da177e4SLinus Torvalds 	/* spo_must_enforce */
47821da177e4SLinus Torvalds 	status = nfsd4_encode_bitmap4(xdr, exid->spo_must_enforce[0],
4783695e12f8SBenny Halevy 				      exid->spo_must_enforce[1],
4784abef972cSChuck Lever 				      exid->spo_must_enforce[2]);
4785abef972cSChuck Lever 	if (status != nfs_ok)
47862db134ebSAndy Adamson 		return status;
4787abef972cSChuck Lever 	/* spo_must_allow */
47880733d213SAndy Adamson 	return nfsd4_encode_bitmap4(xdr, exid->spo_must_allow[0],
4789abef972cSChuck Lever 				    exid->spo_must_allow[1],
4790abef972cSChuck Lever 				    exid->spo_must_allow[2]);
4791abef972cSChuck Lever }
4792abef972cSChuck Lever 
4793abef972cSChuck Lever static __be32
nfsd4_encode_state_protect4_r(struct xdr_stream * xdr,struct nfsd4_exchange_id * exid)4794abef972cSChuck Lever nfsd4_encode_state_protect4_r(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid)
4795abef972cSChuck Lever {
4796abef972cSChuck Lever 	__be32 status;
4797abef972cSChuck Lever 
4798abef972cSChuck Lever 	if (xdr_stream_encode_u32(xdr, exid->spa_how) != XDR_UNIT)
4799abef972cSChuck Lever 		return nfserr_resource;
48000733d213SAndy Adamson 	switch (exid->spa_how) {
4801abef972cSChuck Lever 	case SP4_NONE:
4802abef972cSChuck Lever 		status = nfs_ok;
4803abef972cSChuck Lever 		break;
4804abef972cSChuck Lever 	case SP4_MACH_CRED:
48050733d213SAndy Adamson 		/* spr_mach_ops */
4806abef972cSChuck Lever 		status = nfsd4_encode_state_protect_ops4(xdr, exid);
4807adaa7a50SChuck Lever 		break;
480857266a6eSJ. Bruce Fields 	default:
480957266a6eSJ. Bruce Fields 		status = nfserr_serverfault;
4810abef972cSChuck Lever 	}
481157266a6eSJ. Bruce Fields 	return status;
481257266a6eSJ. Bruce Fields }
4813abef972cSChuck Lever 
4814abef972cSChuck Lever static __be32
nfsd4_encode_server_owner4(struct xdr_stream * xdr,struct svc_rqst * rqstp)481557266a6eSJ. Bruce Fields nfsd4_encode_server_owner4(struct xdr_stream *xdr, struct svc_rqst *rqstp)
481657266a6eSJ. Bruce Fields {
4817abef972cSChuck Lever 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
4818abef972cSChuck Lever 	__be32 status;
4819abef972cSChuck Lever 
482057266a6eSJ. Bruce Fields 	/* so_minor_id */
48210733d213SAndy Adamson 	status = nfsd4_encode_uint64_t(xdr, 0);
4822abef972cSChuck Lever 	if (status != nfs_ok)
4823abef972cSChuck Lever 		return status;
4824abef972cSChuck Lever 	/* so_major_id */
4825abef972cSChuck Lever 	return nfsd4_encode_opaque(xdr, nn->nfsd_name, strlen(nn->nfsd_name));
4826abef972cSChuck Lever }
4827abef972cSChuck Lever 
4828abef972cSChuck Lever static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4829abef972cSChuck Lever nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
4830abef972cSChuck Lever 			 union nfsd4_op_u *u)
4831abef972cSChuck Lever {
4832abef972cSChuck Lever 	struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id);
4833abef972cSChuck Lever 	struct nfsd4_exchange_id *exid = &u->exchange_id;
4834abef972cSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4835abef972cSChuck Lever 
4836abef972cSChuck Lever 	/* eir_clientid */
4837abef972cSChuck Lever 	nfserr = nfsd4_encode_clientid4(xdr, &exid->clientid);
4838abef972cSChuck Lever 	if (nfserr != nfs_ok)
4839abef972cSChuck Lever 		return nfserr;
4840abef972cSChuck Lever 	/* eir_sequenceid */
4841abef972cSChuck Lever 	nfserr = nfsd4_encode_sequenceid4(xdr, exid->seqid);
4842abef972cSChuck Lever 	if (nfserr != nfs_ok)
4843abef972cSChuck Lever 		return nfserr;
4844abef972cSChuck Lever 	/* eir_flags */
4845abef972cSChuck Lever 	nfserr = nfsd4_encode_uint32_t(xdr, exid->flags);
4846abef972cSChuck Lever 	if (nfserr != nfs_ok)
4847abef972cSChuck Lever 		return nfserr;
4848abef972cSChuck Lever 	/* eir_state_protect */
4849abef972cSChuck Lever 	nfserr = nfsd4_encode_state_protect4_r(xdr, exid);
4850abef972cSChuck Lever 	if (nfserr != nfs_ok)
4851abef972cSChuck Lever 		return nfserr;
4852abef972cSChuck Lever 	/* eir_server_owner */
4853abef972cSChuck Lever 	nfserr = nfsd4_encode_server_owner4(xdr, resp->rqstp);
4854abef972cSChuck Lever 	if (nfserr != nfs_ok)
4855abef972cSChuck Lever 		return nfserr;
4856abef972cSChuck Lever 	/* eir_server_scope */
4857abef972cSChuck Lever 	nfserr = nfsd4_encode_opaque(xdr, nn->nfsd_name,
4858abef972cSChuck Lever 				     strlen(nn->nfsd_name));
4859abef972cSChuck Lever 	if (nfserr != nfs_ok)
4860abef972cSChuck Lever 		return nfserr;
4861abef972cSChuck Lever 	/* eir_server_impl_id<1> */
4862abef972cSChuck Lever 	if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
4863abef972cSChuck Lever 		return nfserr_resource;
4864abef972cSChuck Lever 
4865abef972cSChuck Lever 	return nfs_ok;
4866abef972cSChuck Lever }
4867abef972cSChuck Lever 
4868abef972cSChuck Lever static __be32
nfsd4_encode_channel_attrs4(struct xdr_stream * xdr,const struct nfsd4_channel_attrs * attrs)4869abef972cSChuck Lever nfsd4_encode_channel_attrs4(struct xdr_stream *xdr,
4870abef972cSChuck Lever 			    const struct nfsd4_channel_attrs *attrs)
4871d0a381ddSJ. Bruce Fields {
4872a8bb84bcSKinglong Mee 	__be32 status;
4873abef972cSChuck Lever 
48742db134ebSAndy Adamson 	/* ca_headerpadsize */
48752db134ebSAndy Adamson 	status = nfsd4_encode_count4(xdr, 0);
48762db134ebSAndy Adamson 	if (status != nfs_ok)
4877150990f4SChuck Lever 		return status;
4878150990f4SChuck Lever 	/* ca_maxrequestsize */
4879150990f4SChuck Lever 	status = nfsd4_encode_count4(xdr, attrs->maxreq_sz);
4880150990f4SChuck Lever 	if (status != nfs_ok)
4881150990f4SChuck Lever 		return status;
4882150990f4SChuck Lever 	/* ca_maxresponsesize */
4883150990f4SChuck Lever 	status = nfsd4_encode_count4(xdr, attrs->maxresp_sz);
4884150990f4SChuck Lever 	if (status != nfs_ok)
4885150990f4SChuck Lever 		return status;
4886150990f4SChuck Lever 	/* ca_maxresponsesize_cached */
4887150990f4SChuck Lever 	status = nfsd4_encode_count4(xdr, attrs->maxresp_cached);
4888150990f4SChuck Lever 	if (status != nfs_ok)
4889150990f4SChuck Lever 		return status;
4890150990f4SChuck Lever 	/* ca_maxoperations */
4891150990f4SChuck Lever 	status = nfsd4_encode_count4(xdr, attrs->maxops);
4892150990f4SChuck Lever 	if (status != nfs_ok)
4893150990f4SChuck Lever 		return status;
4894150990f4SChuck Lever 	/* ca_maxrequests */
4895150990f4SChuck Lever 	status = nfsd4_encode_count4(xdr, attrs->maxreqs);
4896150990f4SChuck Lever 	if (status != nfs_ok)
4897150990f4SChuck Lever 		return status;
4898150990f4SChuck Lever 	/* ca_rdma_ird<1> */
4899150990f4SChuck Lever 	if (xdr_stream_encode_u32(xdr, attrs->nr_rdma_attrs) != XDR_UNIT)
4900150990f4SChuck Lever 		return nfserr_resource;
4901150990f4SChuck Lever 	if (attrs->nr_rdma_attrs)
4902150990f4SChuck Lever 		return nfsd4_encode_uint32_t(xdr, attrs->rdma_attrs);
4903150990f4SChuck Lever 	return nfs_ok;
4904150990f4SChuck Lever }
4905150990f4SChuck Lever 
4906150990f4SChuck Lever static __be32
nfsd4_encode_create_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4907150990f4SChuck Lever nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
4908150990f4SChuck Lever 			    union nfsd4_op_u *u)
4909150990f4SChuck Lever {
4910150990f4SChuck Lever 	struct nfsd4_create_session *sess = &u->create_session;
4911150990f4SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4912150990f4SChuck Lever 
4913150990f4SChuck Lever 	/* csr_sessionid */
4914150990f4SChuck Lever 	nfserr = nfsd4_encode_sessionid4(xdr, &sess->sessionid);
491557b7b43bSJ. Bruce Fields 	if (nfserr != nfs_ok)
4916e78e274eSKees Cook 		return nfserr;
49172db134ebSAndy Adamson 	/* csr_sequence */
4918e78e274eSKees Cook 	nfserr = nfsd4_encode_sequenceid4(xdr, sess->seqid);
4919bddfdbcdSChuck Lever 	if (nfserr != nfs_ok)
4920ec6b5d7bSAndy Adamson 		return nfserr;
4921b0c1b1baSChuck Lever 	/* csr_flags */
4922b0c1b1baSChuck Lever 	nfserr = nfsd4_encode_uint32_t(xdr, sess->flags);
4923b0c1b1baSChuck Lever 	if (nfserr != nfs_ok)
4924b0c1b1baSChuck Lever 		return nfserr;
4925b0c1b1baSChuck Lever 	/* csr_fore_chan_attrs */
4926b0c1b1baSChuck Lever 	nfserr = nfsd4_encode_channel_attrs4(xdr, &sess->fore_channel);
4927b0c1b1baSChuck Lever 	if (nfserr != nfs_ok)
4928b0c1b1baSChuck Lever 		return nfserr;
4929b0c1b1baSChuck Lever 	/* csr_back_chan_attrs */
4930b0c1b1baSChuck Lever 	return nfsd4_encode_channel_attrs4(xdr, &sess->back_channel);
4931b0c1b1baSChuck Lever }
4932b0c1b1baSChuck Lever 
4933150990f4SChuck Lever static __be32
nfsd4_encode_sequence(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4934150990f4SChuck Lever nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
4935150990f4SChuck Lever 		      union nfsd4_op_u *u)
4936150990f4SChuck Lever {
4937150990f4SChuck Lever 	struct nfsd4_sequence *seq = &u->sequence;
4938150990f4SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
49392db134ebSAndy Adamson 
49402db134ebSAndy Adamson 	/* sr_sessionid */
49412db134ebSAndy Adamson 	nfserr = nfsd4_encode_sessionid4(xdr, &seq->sessionid);
494257b7b43bSJ. Bruce Fields 	if (nfserr != nfs_ok)
4943e78e274eSKees Cook 		return nfserr;
49442db134ebSAndy Adamson 	/* sr_sequenceid */
4945e78e274eSKees Cook 	nfserr = nfsd4_encode_sequenceid4(xdr, seq->seqid);
4946bddfdbcdSChuck Lever 	if (nfserr != nfs_ok)
4947b85d4c01SBenny Halevy 		return nfserr;
49486621b88bSChuck Lever 	/* sr_slotid */
49496621b88bSChuck Lever 	nfserr = nfsd4_encode_slotid4(xdr, seq->slotid);
49506621b88bSChuck Lever 	if (nfserr != nfs_ok)
49516621b88bSChuck Lever 		return nfserr;
49526621b88bSChuck Lever 	/* Note slotid's are numbered from zero: */
49536621b88bSChuck Lever 	/* sr_highest_slotid */
49546621b88bSChuck Lever 	nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1);
49556621b88bSChuck Lever 	if (nfserr != nfs_ok)
49566621b88bSChuck Lever 		return nfserr;
49576621b88bSChuck Lever 	/* sr_target_highest_slotid */
49586621b88bSChuck Lever 	nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1);
49596621b88bSChuck Lever 	if (nfserr != nfs_ok)
4960b7d7ca35SJ. Bruce Fields 		return nfserr;
49616621b88bSChuck Lever 	/* sr_status_flags */
49626621b88bSChuck Lever 	nfserr = nfsd4_encode_uint32_t(xdr, seq->status_flags);
49636621b88bSChuck Lever 	if (nfserr != nfs_ok)
49646621b88bSChuck Lever 		return nfserr;
49656621b88bSChuck Lever 
49666621b88bSChuck Lever 	resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
49676621b88bSChuck Lever 	return nfs_ok;
49686621b88bSChuck Lever }
49696621b88bSChuck Lever 
49706621b88bSChuck Lever static __be32
nfsd4_encode_test_stateid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)49716621b88bSChuck Lever nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
49726621b88bSChuck Lever 			  union nfsd4_op_u *u)
4973b85d4c01SBenny Halevy {
4974f5236013SJ. Bruce Fields 	struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
49756621b88bSChuck Lever 	struct nfsd4_test_stateid_id *stateid, *next;
49762db134ebSAndy Adamson 	struct xdr_stream *xdr = resp->xdr;
49772db134ebSAndy Adamson 
49782355c596SJ. Bruce Fields 	/* tsr_status_codes<> */
497957b7b43bSJ. Bruce Fields 	if (xdr_stream_encode_u32(xdr, test_stateid->ts_num_ids) != XDR_UNIT)
4980e78e274eSKees Cook 		return nfserr_resource;
498117456804SBryan Schumaker 	list_for_each_entry_safe(stateid, next,
4982e78e274eSKees Cook 				 &test_stateid->ts_stateid_list, ts_id_list) {
498303cfb420SBryan Schumaker 		if (xdr_stream_encode_be32(xdr, stateid->ts_id_status) != XDR_UNIT)
498408b4436aSChuck Lever 			return nfserr_resource;
498517456804SBryan Schumaker 	}
498608b4436aSChuck Lever 	return nfs_ok;
498708b4436aSChuck Lever }
4988d0a381ddSJ. Bruce Fields 
498908b4436aSChuck Lever static __be32
nfsd4_encode_get_dir_delegation(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)499008b4436aSChuck Lever nfsd4_encode_get_dir_delegation(struct nfsd4_compoundres *resp, __be32 nfserr,
499108b4436aSChuck Lever 				union nfsd4_op_u *u)
499208b4436aSChuck Lever {
499317456804SBryan Schumaker 	struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
499408b4436aSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
499517456804SBryan Schumaker 	__be32 status = nfserr_resource;
499617456804SBryan Schumaker 
499733a1e6eaSJeff Layton 	switch(gdd->gddrnf_status) {
499833a1e6eaSJeff Layton 	case GDD4_OK:
499933a1e6eaSJeff Layton 		if (xdr_stream_encode_u32(xdr, GDD4_OK) != XDR_UNIT)
500033a1e6eaSJeff Layton 			break;
500133a1e6eaSJeff Layton 		status = nfsd4_encode_verifier4(xdr, &gdd->gddr_cookieverf);
500233a1e6eaSJeff Layton 		if (status)
500333a1e6eaSJeff Layton 			break;
500433a1e6eaSJeff Layton 		status = nfsd4_encode_stateid4(xdr, &gdd->gddr_stateid);
500533a1e6eaSJeff Layton 		if (status)
500633a1e6eaSJeff Layton 			break;
500733a1e6eaSJeff Layton 		status = nfsd4_encode_bitmap4(xdr, gdd->gddr_notification[0], 0, 0);
500833a1e6eaSJeff Layton 		if (status)
500933a1e6eaSJeff Layton 			break;
501033a1e6eaSJeff Layton 		status = nfsd4_encode_bitmap4(xdr, gdd->gddr_child_attributes[0],
501133a1e6eaSJeff Layton 						   gdd->gddr_child_attributes[1],
501233a1e6eaSJeff Layton 						   gdd->gddr_child_attributes[2]);
501333a1e6eaSJeff Layton 		if (status)
501433a1e6eaSJeff Layton 			break;
501533a1e6eaSJeff Layton 		status = nfsd4_encode_bitmap4(xdr, gdd->gddr_dir_attributes[0],
501633a1e6eaSJeff Layton 						   gdd->gddr_dir_attributes[1],
501733a1e6eaSJeff Layton 						   gdd->gddr_dir_attributes[2]);
501833a1e6eaSJeff Layton 		break;
501933a1e6eaSJeff Layton 	default:
502033a1e6eaSJeff Layton 		pr_warn("nfsd: bad gddrnf_status (%u)\n", gdd->gddrnf_status);
502133a1e6eaSJeff Layton 		gdd->gddrnf_will_signal_deleg_avail = 0;
502233a1e6eaSJeff Layton 		fallthrough;
502333a1e6eaSJeff Layton 	case GDD4_UNAVAIL:
502433a1e6eaSJeff Layton 		if (xdr_stream_encode_u32(xdr, GDD4_UNAVAIL) != XDR_UNIT)
502533a1e6eaSJeff Layton 			break;
502633a1e6eaSJeff Layton 		status = nfsd4_encode_bool(xdr, gdd->gddrnf_will_signal_deleg_avail);
502733a1e6eaSJeff Layton 		break;
502833a1e6eaSJeff Layton 	}
502933a1e6eaSJeff Layton 	return status;
503033a1e6eaSJeff Layton }
503133a1e6eaSJeff Layton 
503233a1e6eaSJeff Layton #ifdef CONFIG_NFSD_PNFS
503333a1e6eaSJeff Layton static __be32
nfsd4_encode_device_addr4(struct xdr_stream * xdr,const struct nfsd4_getdeviceinfo * gdev)503433a1e6eaSJeff Layton nfsd4_encode_device_addr4(struct xdr_stream *xdr,
503533a1e6eaSJeff Layton 			  const struct nfsd4_getdeviceinfo *gdev)
503633a1e6eaSJeff Layton {
503733a1e6eaSJeff Layton 	u32 needed_len, starting_len = xdr->buf->len;
503833a1e6eaSJeff Layton 	const struct nfsd4_layout_ops *ops;
503933a1e6eaSJeff Layton 	__be32 status;
50409cf514ccSChristoph Hellwig 
50419cf514ccSChristoph Hellwig 	/* da_layout_type */
50424bbe42e8SChuck Lever 	if (xdr_stream_encode_u32(xdr, gdev->gd_layout_type) != XDR_UNIT)
50434bbe42e8SChuck Lever 		return nfserr_resource;
50444bbe42e8SChuck Lever 	/* da_addr_body */
50454bbe42e8SChuck Lever 	ops = nfsd4_layout_ops[gdev->gd_layout_type];
50464bbe42e8SChuck Lever 	status = ops->encode_getdeviceinfo(xdr, gdev);
50474bbe42e8SChuck Lever 	if (status != nfs_ok) {
50484bbe42e8SChuck Lever 		/*
50494bbe42e8SChuck Lever 		 * Don't burden the layout drivers with enforcing
50504bbe42e8SChuck Lever 		 * gd_maxcount. Just tell the client to come back
50514bbe42e8SChuck Lever 		 * with a bigger buffer if it's not enough.
50524bbe42e8SChuck Lever 		 */
50534bbe42e8SChuck Lever 		if (xdr->buf->len + XDR_UNIT > gdev->gd_maxcount)
50544bbe42e8SChuck Lever 			goto toosmall;
50554bbe42e8SChuck Lever 		return status;
50564bbe42e8SChuck Lever 	}
50574bbe42e8SChuck Lever 
50584bbe42e8SChuck Lever 	return nfs_ok;
50594bbe42e8SChuck Lever 
50604bbe42e8SChuck Lever toosmall:
50614bbe42e8SChuck Lever 	needed_len = xdr->buf->len + XDR_UNIT;	/* notifications */
50624bbe42e8SChuck Lever 	xdr_truncate_encode(xdr, starting_len);
50634bbe42e8SChuck Lever 
50644bbe42e8SChuck Lever 	status = nfsd4_encode_count4(xdr, needed_len);
50654bbe42e8SChuck Lever 	if (status != nfs_ok)
50664bbe42e8SChuck Lever 		return status;
50674bbe42e8SChuck Lever 	return nfserr_toosmall;
50684bbe42e8SChuck Lever }
50694bbe42e8SChuck Lever 
50704bbe42e8SChuck Lever static __be32
nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)50714bbe42e8SChuck Lever nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
50724bbe42e8SChuck Lever 		union nfsd4_op_u *u)
50734bbe42e8SChuck Lever {
50744bbe42e8SChuck Lever 	struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
50754bbe42e8SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
50764bbe42e8SChuck Lever 
50774bbe42e8SChuck Lever 	/* gdir_device_addr */
50784bbe42e8SChuck Lever 	nfserr = nfsd4_encode_device_addr4(xdr, gdev);
50799cf514ccSChristoph Hellwig 	if (nfserr)
5080e78e274eSKees Cook 		return nfserr;
50819cf514ccSChristoph Hellwig 	/* gdir_notification */
5082e78e274eSKees Cook 	return nfsd4_encode_bitmap4(xdr, gdev->gd_notify_types, 0, 0);
5083bddfdbcdSChuck Lever }
50849cf514ccSChristoph Hellwig 
50854bbe42e8SChuck Lever static __be32
nfsd4_encode_layout4(struct xdr_stream * xdr,const struct nfsd4_layoutget * lgp)50864bbe42e8SChuck Lever nfsd4_encode_layout4(struct xdr_stream *xdr, const struct nfsd4_layoutget *lgp)
50874bbe42e8SChuck Lever {
5088bac966d6SJ. Bruce Fields 	const struct nfsd4_layout_ops *ops = nfsd4_layout_ops[lgp->lg_layout_type];
50894bbe42e8SChuck Lever 	__be32 status;
50904bbe42e8SChuck Lever 
50919cf514ccSChristoph Hellwig 	/* lo_offset */
50929cf514ccSChristoph Hellwig 	status = nfsd4_encode_offset4(xdr, lgp->lg_seg.offset);
50939cf514ccSChristoph Hellwig 	if (status != nfs_ok)
509469f5f019SChuck Lever 		return status;
509569f5f019SChuck Lever 	/* lo_length */
509669f5f019SChuck Lever 	status = nfsd4_encode_length4(xdr, lgp->lg_seg.length);
509769f5f019SChuck Lever 	if (status != nfs_ok)
509869f5f019SChuck Lever 		return status;
509969f5f019SChuck Lever 	/* lo_iomode */
510069f5f019SChuck Lever 	if (xdr_stream_encode_u32(xdr, lgp->lg_seg.iomode) != XDR_UNIT)
510169f5f019SChuck Lever 		return nfserr_resource;
510269f5f019SChuck Lever 	/* lo_content */
510369f5f019SChuck Lever 	if (xdr_stream_encode_u32(xdr, lgp->lg_layout_type) != XDR_UNIT)
510469f5f019SChuck Lever 		return nfserr_resource;
510569f5f019SChuck Lever 	return ops->encode_layoutget(xdr, lgp);
510669f5f019SChuck Lever }
510769f5f019SChuck Lever 
510869f5f019SChuck Lever static __be32
nfsd4_encode_layoutget(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)510969f5f019SChuck Lever nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
511069f5f019SChuck Lever 		union nfsd4_op_u *u)
511169f5f019SChuck Lever {
511269f5f019SChuck Lever 	struct nfsd4_layoutget *lgp = &u->layoutget;
511369f5f019SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
511469f5f019SChuck Lever 
511569f5f019SChuck Lever 	/* logr_return_on_close */
511669f5f019SChuck Lever 	nfserr = nfsd4_encode_bool(xdr, true);
51179cf514ccSChristoph Hellwig 	if (nfserr != nfs_ok)
5118e78e274eSKees Cook 		return nfserr;
51199cf514ccSChristoph Hellwig 	/* logr_stateid */
5120e78e274eSKees Cook 	nfserr = nfsd4_encode_stateid4(xdr, &lgp->lg_sid);
5121bddfdbcdSChuck Lever 	if (nfserr != nfs_ok)
51229cf514ccSChristoph Hellwig 		return nfserr;
512369f5f019SChuck Lever 	/* logr_layout<> */
512469f5f019SChuck Lever 	if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
512569f5f019SChuck Lever 		return nfserr_resource;
512669f5f019SChuck Lever 	return nfsd4_encode_layout4(xdr, lgp);
512769f5f019SChuck Lever }
512869f5f019SChuck Lever 
512969f5f019SChuck Lever static __be32
nfsd4_encode_layoutcommit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)513069f5f019SChuck Lever nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr,
513169f5f019SChuck Lever 			  union nfsd4_op_u *u)
513269f5f019SChuck Lever {
5133bac966d6SJ. Bruce Fields 	struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
513469f5f019SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
51359cf514ccSChristoph Hellwig 
51369cf514ccSChristoph Hellwig 	/* ns_sizechanged */
51379cf514ccSChristoph Hellwig 	nfserr = nfsd4_encode_bool(xdr, lcp->lc_size_chg);
51389cf514ccSChristoph Hellwig 	if (nfserr != nfs_ok)
5139e78e274eSKees Cook 		return nfserr;
51409cf514ccSChristoph Hellwig 	if (lcp->lc_size_chg)
5141e78e274eSKees Cook 		/* ns_size */
5142bddfdbcdSChuck Lever 		return nfsd4_encode_length4(xdr, lcp->lc_newsize);
51439cf514ccSChristoph Hellwig 	return nfs_ok;
5144cc313f80SChuck Lever }
5145cc313f80SChuck Lever 
5146cc313f80SChuck Lever static __be32
nfsd4_encode_layoutreturn(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5147cc313f80SChuck Lever nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
5148cc313f80SChuck Lever 		union nfsd4_op_u *u)
5149cc313f80SChuck Lever {
5150cc313f80SChuck Lever 	struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
5151cc313f80SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
51529cf514ccSChristoph Hellwig 
51539cf514ccSChristoph Hellwig 	/* lrs_present */
51549cf514ccSChristoph Hellwig 	nfserr = nfsd4_encode_bool(xdr, lrp->lrs_present);
51559cf514ccSChristoph Hellwig 	if (nfserr != nfs_ok)
5156e78e274eSKees Cook 		return nfserr;
51579cf514ccSChristoph Hellwig 	if (lrp->lrs_present)
5158e78e274eSKees Cook 		/* lrs_stateid */
5159bddfdbcdSChuck Lever 		return nfsd4_encode_stateid4(xdr, &lrp->lr_sid);
51609cf514ccSChristoph Hellwig 	return nfs_ok;
516185dbc978SChuck Lever }
516285dbc978SChuck Lever #endif /* CONFIG_NFSD_PNFS */
516385dbc978SChuck Lever 
516485dbc978SChuck Lever static __be32
nfsd4_encode_write_response4(struct xdr_stream * xdr,const struct nfsd4_copy * copy)51659cf514ccSChristoph Hellwig nfsd4_encode_write_response4(struct xdr_stream *xdr,
516685dbc978SChuck Lever 			     const struct nfsd4_copy *copy)
516740bb2baaSChuck Lever {
516885dbc978SChuck Lever 	const struct nfsd42_write_res *write = &copy->cp_res;
51699cf514ccSChristoph Hellwig 	u32 count = nfsd4_copy_is_sync(copy) ? 0 : 1;
51709cf514ccSChristoph Hellwig 	__be32 status;
51719cf514ccSChristoph Hellwig 
51722db134ebSAndy Adamson 	/* wr_callback_id<1> */
517302e0297fSChuck Lever 	if (xdr_stream_encode_u32(xdr, count) != XDR_UNIT)
517402e0297fSChuck Lever 		return nfserr_resource;
517529ae7f9dSAnna Schumaker 	if (count) {
517602e0297fSChuck Lever 		status = nfsd4_encode_stateid4(xdr, &write->cb_stateid);
517702e0297fSChuck Lever 		if (status != nfs_ok)
517802e0297fSChuck Lever 			return status;
517929ae7f9dSAnna Schumaker 	}
518002e0297fSChuck Lever 
518102e0297fSChuck Lever 	/* wr_count */
518202e0297fSChuck Lever 	status = nfsd4_encode_length4(xdr, write->wr_bytes_written);
518302e0297fSChuck Lever 	if (status != nfs_ok)
518402e0297fSChuck Lever 		return status;
518502e0297fSChuck Lever 	/* wr_committed */
518602e0297fSChuck Lever 	if (xdr_stream_encode_u32(xdr, write->wr_stable_how) != XDR_UNIT)
5187e0639dc5SOlga Kornievskaia 		return nfserr_resource;
5188e0639dc5SOlga Kornievskaia 	/* wr_writeverf */
518902e0297fSChuck Lever 	return nfsd4_encode_verifier4(xdr, &write->wr_verifier);
519002e0297fSChuck Lever }
519102e0297fSChuck Lever 
nfsd4_encode_copy_requirements4(struct xdr_stream * xdr,const struct nfsd4_copy * copy)519202e0297fSChuck Lever static __be32 nfsd4_encode_copy_requirements4(struct xdr_stream *xdr,
519302e0297fSChuck Lever 					      const struct nfsd4_copy *copy)
519402e0297fSChuck Lever {
519502e0297fSChuck Lever 	__be32 status;
519602e0297fSChuck Lever 
519702e0297fSChuck Lever 	/* cr_consecutive */
519802e0297fSChuck Lever 	status = nfsd4_encode_bool(xdr, true);
519902e0297fSChuck Lever 	if (status != nfs_ok)
520002e0297fSChuck Lever 		return status;
520102e0297fSChuck Lever 	/* cr_synchronous */
520202e0297fSChuck Lever 	return nfsd4_encode_bool(xdr, nfsd4_copy_is_sync(copy));
520302e0297fSChuck Lever }
520402e0297fSChuck Lever 
520502e0297fSChuck Lever static __be32
nfsd4_encode_copy(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)520602e0297fSChuck Lever nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
520702e0297fSChuck Lever 		  union nfsd4_op_u *u)
520802e0297fSChuck Lever {
520902e0297fSChuck Lever 	struct nfsd4_copy *copy = &u->copy;
521002e0297fSChuck Lever 
521102e0297fSChuck Lever 	nfserr = nfsd4_encode_write_response4(resp->xdr, copy);
521202e0297fSChuck Lever 	if (nfserr != nfs_ok)
521302e0297fSChuck Lever 		return nfserr;
521402e0297fSChuck Lever 	return nfsd4_encode_copy_requirements4(resp->xdr, copy);
521502e0297fSChuck Lever }
521602e0297fSChuck Lever 
521702e0297fSChuck Lever static __be32
nfsd4_encode_netloc4(struct xdr_stream * xdr,const struct nl4_server * ns)521802e0297fSChuck Lever nfsd4_encode_netloc4(struct xdr_stream *xdr, const struct nl4_server *ns)
521902e0297fSChuck Lever {
522002e0297fSChuck Lever 	__be32 status;
522102e0297fSChuck Lever 
522202e0297fSChuck Lever 	if (xdr_stream_encode_u32(xdr, ns->nl4_type) != XDR_UNIT)
522329ae7f9dSAnna Schumaker 		return nfserr_resource;
522429ae7f9dSAnna Schumaker 	switch (ns->nl4_type) {
522529ae7f9dSAnna Schumaker 	case NL4_NETADDR:
522621d316a7SChuck Lever 		/* nl_addr */
522751911868SOlga Kornievskaia 		status = nfsd4_encode_netaddr4(xdr, &ns->u.nl4_addr);
522821d316a7SChuck Lever 		break;
522951911868SOlga Kornievskaia 	default:
523021d316a7SChuck Lever 		status = nfserr_serverfault;
523121d316a7SChuck Lever 	}
523251911868SOlga Kornievskaia 	return status;
523351911868SOlga Kornievskaia }
523421d316a7SChuck Lever 
523521d316a7SChuck Lever static __be32
nfsd4_encode_copy_notify(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)523651911868SOlga Kornievskaia nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
523751911868SOlga Kornievskaia 			 union nfsd4_op_u *u)
523821d316a7SChuck Lever {
523921d316a7SChuck Lever 	struct nfsd4_copy_notify *cn = &u->copy_notify;
524021d316a7SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
524151911868SOlga Kornievskaia 
524251911868SOlga Kornievskaia 	/* cnr_lease_time */
524321d316a7SChuck Lever 	nfserr = nfsd4_encode_nfstime4(xdr, &cn->cpn_lease_time);
524421d316a7SChuck Lever 	if (nfserr)
524521d316a7SChuck Lever 		return nfserr;
524621d316a7SChuck Lever 	/* cnr_stateid */
524721d316a7SChuck Lever 	nfserr = nfsd4_encode_stateid4(xdr, &cn->cpn_cnr_stateid);
524821d316a7SChuck Lever 	if (nfserr)
524921d316a7SChuck Lever 		return nfserr;
525021d316a7SChuck Lever 	/* cnr_source_server<> */
525121d316a7SChuck Lever 	if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
525221d316a7SChuck Lever 		return nfserr_resource;
525321d316a7SChuck Lever 	return nfsd4_encode_netloc4(xdr, cn->cpn_src);
525421d316a7SChuck Lever }
525521d316a7SChuck Lever 
525621d316a7SChuck Lever static __be32
nfsd4_encode_offload_status(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)525721d316a7SChuck Lever nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
525821d316a7SChuck Lever 			    union nfsd4_op_u *u)
525921d316a7SChuck Lever {
526021d316a7SChuck Lever 	struct nfsd4_offload_status *os = &u->offload_status;
526121d316a7SChuck Lever 	struct xdr_stream *xdr = resp->xdr;
526251911868SOlga Kornievskaia 
526351911868SOlga Kornievskaia 	/* osr_count */
526451911868SOlga Kornievskaia 	nfserr = nfsd4_encode_length4(xdr, os->count);
52656308bc98SOlga Kornievskaia 	if (nfserr != nfs_ok)
5266e78e274eSKees Cook 		return nfserr;
52676308bc98SOlga Kornievskaia 	/* osr_complete<1> */
5268e78e274eSKees Cook 	if (os->completed) {
5269bddfdbcdSChuck Lever 		if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
52706308bc98SOlga Kornievskaia 			return nfserr_resource;
5271b609ad60SChuck Lever 		if (xdr_stream_encode_be32(xdr, os->status) != XDR_UNIT)
5272b609ad60SChuck Lever 			return nfserr_resource;
5273b609ad60SChuck Lever 	} else if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
5274528b8493SAnna Schumaker 		return nfserr_resource;
5275b609ad60SChuck Lever 	return nfs_ok;
5276cc63c216SChuck Lever }
5277cc63c216SChuck Lever 
5278cc63c216SChuck Lever static __be32
nfsd4_encode_read_plus_data(struct nfsd4_compoundres * resp,struct nfsd4_read * read)5279cc63c216SChuck Lever nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
5280cc63c216SChuck Lever 			    struct nfsd4_read *read)
5281cc63c216SChuck Lever {
5282b609ad60SChuck Lever 	struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
5283b609ad60SChuck Lever 	struct file *file = read->rd_nf->nf_file;
5284528b8493SAnna Schumaker 	struct xdr_stream *xdr = resp->xdr;
5285528b8493SAnna Schumaker 	bool splice_ok = argp->splice_ok;
5286528b8493SAnna Schumaker 	unsigned long maxcount;
5287528b8493SAnna Schumaker 	__be32 nfserr, *p;
5288eeadcb75SAnna Schumaker 
5289528b8493SAnna Schumaker 	/* Content type, offset, byte count */
5290a2c91753SChuck Lever 	p = xdr_reserve_space(xdr, 4 + 8 + 4);
5291528b8493SAnna Schumaker 	if (!p)
5292eeadcb75SAnna Schumaker 		return nfserr_io;
5293a2c91753SChuck Lever 	if (resp->xdr->buf->page_len && splice_ok) {
5294eeadcb75SAnna Schumaker 		WARN_ON_ONCE(splice_ok);
5295eeadcb75SAnna Schumaker 		return nfserr_serverfault;
5296528b8493SAnna Schumaker 	}
5297528b8493SAnna Schumaker 
5298528b8493SAnna Schumaker 	maxcount = min_t(unsigned long, read->rd_length,
5299528b8493SAnna Schumaker 			 (xdr->buf->buflen - xdr->buf->len));
5300eeadcb75SAnna Schumaker 
5301eeadcb75SAnna Schumaker 	if (file->f_op->splice_read && splice_ok)
5302eeadcb75SAnna Schumaker 		nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
5303eeadcb75SAnna Schumaker 	else
5304528b8493SAnna Schumaker 		nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
5305528b8493SAnna Schumaker 	if (nfserr)
5306eeadcb75SAnna Schumaker 		return nfserr;
5307eeadcb75SAnna Schumaker 
53082db27992SAnna Schumaker 	*p++ = cpu_to_be32(NFS4_CONTENT_DATA);
5309eeadcb75SAnna Schumaker 	p = xdr_encode_hyper(p, read->rd_offset);
5310eeadcb75SAnna Schumaker 	*p = cpu_to_be32(read->rd_length);
5311eeadcb75SAnna Schumaker 
5312eeadcb75SAnna Schumaker 	return nfs_ok;
5313eeadcb75SAnna Schumaker }
5314eeadcb75SAnna Schumaker 
5315278765eaSAnna Schumaker static __be32
nfsd4_encode_read_plus(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5316eeadcb75SAnna Schumaker nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
53172db27992SAnna Schumaker 		       union nfsd4_op_u *u)
5318eeadcb75SAnna Schumaker {
53192db27992SAnna Schumaker 	struct nfsd4_read *read = &u->read;
53202db27992SAnna Schumaker 	struct file *file = read->rd_nf->nf_file;
53212db27992SAnna Schumaker 	struct xdr_stream *xdr = resp->xdr;
53222db27992SAnna Schumaker 	int starting_len = xdr->buf->len;
53232db27992SAnna Schumaker 	u32 segments = 0;
5324528b8493SAnna Schumaker 	__be32 *p;
5325e78e274eSKees Cook 
5326528b8493SAnna Schumaker 	if (nfserr)
5327e78e274eSKees Cook 		return nfserr;
5328eeadcb75SAnna Schumaker 
5329bddfdbcdSChuck Lever 	/* eof flag, segment count */
5330528b8493SAnna Schumaker 	p = xdr_reserve_space(xdr, 4 + 4);
5331eeadcb75SAnna Schumaker 	if (!p)
5332eeadcb75SAnna Schumaker 		return nfserr_io;
5333528b8493SAnna Schumaker 	xdr_commit_encode(xdr);
5334528b8493SAnna Schumaker 
5335528b8493SAnna Schumaker 	read->rd_eof = read->rd_offset >= i_size_read(file_inode(file));
5336528b8493SAnna Schumaker 	if (read->rd_eof)
5337528b8493SAnna Schumaker 		goto out;
5338528b8493SAnna Schumaker 
5339528b8493SAnna Schumaker 	nfserr = nfsd4_encode_read_plus_data(resp, read);
5340eeadcb75SAnna Schumaker 	if (nfserr) {
5341528b8493SAnna Schumaker 		xdr_truncate_encode(xdr, starting_len);
5342528b8493SAnna Schumaker 		return nfserr;
5343eeadcb75SAnna Schumaker 	}
5344eeadcb75SAnna Schumaker 
53452db27992SAnna Schumaker 	segments++;
53462db27992SAnna Schumaker 
5347eeadcb75SAnna Schumaker out:
5348eeadcb75SAnna Schumaker 	p = xdr_encode_bool(p, read->rd_eof);
5349eeadcb75SAnna Schumaker 	*p = cpu_to_be32(segments);
5350eeadcb75SAnna Schumaker 	return nfserr;
5351528b8493SAnna Schumaker }
5352528b8493SAnna Schumaker 
5353eeadcb75SAnna Schumaker static __be32
nfsd4_encode_seek(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5354eeadcb75SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
53552db27992SAnna Schumaker 		  union nfsd4_op_u *u)
5356eeadcb75SAnna Schumaker {
5357eeadcb75SAnna Schumaker 	struct nfsd4_seek *seek = &u->seek;
53586308bc98SOlga Kornievskaia 	struct xdr_stream *xdr = resp->xdr;
53596308bc98SOlga Kornievskaia 
53606308bc98SOlga Kornievskaia 	/* sr_eof */
53616308bc98SOlga Kornievskaia 	nfserr = nfsd4_encode_bool(xdr, seek->seek_eof);
536224bab491SAnna Schumaker 	if (nfserr != nfs_ok)
5363e78e274eSKees Cook 		return nfserr;
536424bab491SAnna Schumaker 	/* sr_offset */
5365e78e274eSKees Cook 	return nfsd4_encode_offset4(xdr, seek->seek_pos);
53661f121e2dSChuck Lever }
536724bab491SAnna Schumaker 
53681f121e2dSChuck Lever static __be32
nfsd4_encode_noop(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * p)53691f121e2dSChuck Lever nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr,
53701f121e2dSChuck Lever 		  union nfsd4_op_u *p)
53711f121e2dSChuck Lever {
53721f121e2dSChuck Lever 	return nfserr;
53731f121e2dSChuck Lever }
537424bab491SAnna Schumaker 
537524bab491SAnna Schumaker /*
537624bab491SAnna Schumaker  * Encode kmalloc-ed buffer in to XDR stream.
5377e78e274eSKees Cook  */
5378e78e274eSKees Cook static __be32
nfsd4_vbuf_to_stream(struct xdr_stream * xdr,char * buf,u32 buflen)5379695e12f8SBenny Halevy nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
5380695e12f8SBenny Halevy {
5381695e12f8SBenny Halevy 	u32 cplen;
5382695e12f8SBenny Halevy 	__be32 *p;
538323e50fe3SFrank van der Linden 
538423e50fe3SFrank van der Linden 	cplen = min_t(unsigned long, buflen,
538523e50fe3SFrank van der Linden 		      ((void *)xdr->end - (void *)xdr->p));
5386b9a49237SChuck Lever 	p = xdr_reserve_space(xdr, cplen);
538723e50fe3SFrank van der Linden 	if (!p)
538823e50fe3SFrank van der Linden 		return nfserr_resource;
538923e50fe3SFrank van der Linden 
539023e50fe3SFrank van der Linden 	memcpy(p, buf, cplen);
539123e50fe3SFrank van der Linden 	buf += cplen;
539223e50fe3SFrank van der Linden 	buflen -= cplen;
539323e50fe3SFrank van der Linden 
539423e50fe3SFrank van der Linden 	while (buflen) {
539523e50fe3SFrank van der Linden 		cplen = min_t(u32, buflen, PAGE_SIZE);
539623e50fe3SFrank van der Linden 		p = xdr_reserve_space(xdr, cplen);
539723e50fe3SFrank van der Linden 		if (!p)
539823e50fe3SFrank van der Linden 			return nfserr_resource;
539923e50fe3SFrank van der Linden 
540023e50fe3SFrank van der Linden 		memcpy(p, buf, cplen);
540123e50fe3SFrank van der Linden 
540223e50fe3SFrank van der Linden 		if (cplen < PAGE_SIZE) {
540323e50fe3SFrank van der Linden 			/*
540423e50fe3SFrank van der Linden 			 * We're done, with a length that wasn't page
540523e50fe3SFrank van der Linden 			 * aligned, so possibly not word aligned. Pad
540623e50fe3SFrank van der Linden 			 * any trailing bytes with 0.
540723e50fe3SFrank van der Linden 			 */
540823e50fe3SFrank van der Linden 			xdr_encode_opaque_fixed(p, NULL, cplen);
540923e50fe3SFrank van der Linden 			break;
541023e50fe3SFrank van der Linden 		}
541123e50fe3SFrank van der Linden 
541223e50fe3SFrank van der Linden 		buflen -= PAGE_SIZE;
541323e50fe3SFrank van der Linden 		buf += PAGE_SIZE;
541423e50fe3SFrank van der Linden 	}
541523e50fe3SFrank van der Linden 
541623e50fe3SFrank van der Linden 	return 0;
541723e50fe3SFrank van der Linden }
541823e50fe3SFrank van der Linden 
541923e50fe3SFrank van der Linden static __be32
nfsd4_encode_getxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)542023e50fe3SFrank van der Linden nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
542123e50fe3SFrank van der Linden 		      union nfsd4_op_u *u)
542223e50fe3SFrank van der Linden {
542323e50fe3SFrank van der Linden 	struct nfsd4_getxattr *getxattr = &u->getxattr;
542423e50fe3SFrank van der Linden 	struct xdr_stream *xdr = resp->xdr;
542523e50fe3SFrank van der Linden 	__be32 *p, err;
542623e50fe3SFrank van der Linden 
542723e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 4);
542823e50fe3SFrank van der Linden 	if (!p)
5429e78e274eSKees Cook 		return nfserr_resource;
543023e50fe3SFrank van der Linden 
5431e78e274eSKees Cook 	*p = cpu_to_be32(getxattr->getxa_len);
5432bddfdbcdSChuck Lever 
543323e50fe3SFrank van der Linden 	if (getxattr->getxa_len == 0)
543423e50fe3SFrank van der Linden 		return 0;
543523e50fe3SFrank van der Linden 
543623e50fe3SFrank van der Linden 	err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
543723e50fe3SFrank van der Linden 				    getxattr->getxa_len);
543823e50fe3SFrank van der Linden 
543923e50fe3SFrank van der Linden 	kvfree(getxattr->getxa_buf);
544023e50fe3SFrank van der Linden 
544123e50fe3SFrank van der Linden 	return err;
544223e50fe3SFrank van der Linden }
544323e50fe3SFrank van der Linden 
544423e50fe3SFrank van der Linden static __be32
nfsd4_encode_setxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)544523e50fe3SFrank van der Linden nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
544623e50fe3SFrank van der Linden 		      union nfsd4_op_u *u)
544723e50fe3SFrank van der Linden {
544823e50fe3SFrank van der Linden 	struct nfsd4_setxattr *setxattr = &u->setxattr;
544923e50fe3SFrank van der Linden 	struct xdr_stream *xdr = resp->xdr;
545023e50fe3SFrank van der Linden 
545123e50fe3SFrank van der Linden 	return nfsd4_encode_change_info4(xdr, &setxattr->setxa_cinfo);
545223e50fe3SFrank van der Linden }
545323e50fe3SFrank van der Linden 
5454e78e274eSKees Cook /*
545523e50fe3SFrank van der Linden  * See if there are cookie values that can be rejected outright.
5456e78e274eSKees Cook  */
5457bddfdbcdSChuck Lever static __be32
nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs * listxattrs,u32 * offsetp)545823e50fe3SFrank van der Linden nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
545966a21db7SChuck Lever 				u32 *offsetp)
546023e50fe3SFrank van der Linden {
546123e50fe3SFrank van der Linden 	u64 cookie = listxattrs->lsxa_cookie;
546223e50fe3SFrank van der Linden 
546323e50fe3SFrank van der Linden 	/*
546423e50fe3SFrank van der Linden 	 * If the cookie is larger than the maximum number we can fit
546523e50fe3SFrank van der Linden 	 * in the buffer we just got back from vfs_listxattr, it's invalid.
546623e50fe3SFrank van der Linden 	 */
546723e50fe3SFrank van der Linden 	if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
546823e50fe3SFrank van der Linden 		return nfserr_badcookie;
546923e50fe3SFrank van der Linden 
547023e50fe3SFrank van der Linden 	*offsetp = (u32)cookie;
547123e50fe3SFrank van der Linden 	return 0;
547223e50fe3SFrank van der Linden }
547352a357dbSJorge Mora 
547423e50fe3SFrank van der Linden static __be32
nfsd4_encode_listxattrs(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)547523e50fe3SFrank van der Linden nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
547623e50fe3SFrank van der Linden 			union nfsd4_op_u *u)
547723e50fe3SFrank van der Linden {
547823e50fe3SFrank van der Linden 	struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
547923e50fe3SFrank van der Linden 	struct xdr_stream *xdr = resp->xdr;
548023e50fe3SFrank van der Linden 	u32 cookie_offset, count_offset, eof;
548123e50fe3SFrank van der Linden 	u32 left, xdrleft, slen, count;
548223e50fe3SFrank van der Linden 	u32 xdrlen, offset;
548323e50fe3SFrank van der Linden 	u64 cookie;
5484e78e274eSKees Cook 	char *sp;
548523e50fe3SFrank van der Linden 	__be32 status, tmp;
5486e78e274eSKees Cook 	__be64 wire_cookie;
5487bddfdbcdSChuck Lever 	__be32 *p;
548823e50fe3SFrank van der Linden 	u32 nuser;
548923e50fe3SFrank van der Linden 
549023e50fe3SFrank van der Linden 	eof = 1;
549123e50fe3SFrank van der Linden 
549223e50fe3SFrank van der Linden 	status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
5493b9a49237SChuck Lever 	if (status)
549461ab5e07SJorge Mora 		goto out;
549523e50fe3SFrank van der Linden 
549623e50fe3SFrank van der Linden 	/*
549723e50fe3SFrank van der Linden 	 * Reserve space for the cookie and the name array count. Record
549823e50fe3SFrank van der Linden 	 * the offsets to save them later.
549923e50fe3SFrank van der Linden 	 */
550023e50fe3SFrank van der Linden 	cookie_offset = xdr->buf->len;
550123e50fe3SFrank van der Linden 	count_offset = cookie_offset + 8;
550223e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, XDR_UNIT * 3);
550323e50fe3SFrank van der Linden 	if (!p) {
550423e50fe3SFrank van der Linden 		status = nfserr_resource;
550523e50fe3SFrank van der Linden 		goto out;
550623e50fe3SFrank van der Linden 	}
550723e50fe3SFrank van der Linden 
550823e50fe3SFrank van der Linden 	count = 0;
550923e50fe3SFrank van der Linden 	left = listxattrs->lsxa_len;
551031e4bb8fSJorge Mora 	sp = listxattrs->lsxa_buf;
551123e50fe3SFrank van der Linden 	nuser = 0;
551223e50fe3SFrank van der Linden 
551323e50fe3SFrank van der Linden 	/* Bytes left is maxcount - 8 (cookie) - 4 (array count) */
551423e50fe3SFrank van der Linden 	xdrleft = listxattrs->lsxa_maxcount - XDR_UNIT * 3;
551523e50fe3SFrank van der Linden 
551623e50fe3SFrank van der Linden 	while (left > 0 && xdrleft > 0) {
551723e50fe3SFrank van der Linden 		slen = strlen(sp);
551823e50fe3SFrank van der Linden 
551923e50fe3SFrank van der Linden 		/*
552023e50fe3SFrank van der Linden 		 * Check if this is a "user." attribute, skip it if not.
552131e4bb8fSJorge Mora 		 */
552231e4bb8fSJorge Mora 		if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
552323e50fe3SFrank van der Linden 			goto contloop;
552423e50fe3SFrank van der Linden 
552523e50fe3SFrank van der Linden 		slen -= XATTR_USER_PREFIX_LEN;
552623e50fe3SFrank van der Linden 		xdrlen = 4 + ((slen + 3) & ~3);
552723e50fe3SFrank van der Linden 		/* Check if both entry and eof can fit in the XDR buffer */
55284cce11faSAlex Dewar 		if (xdrlen + XDR_UNIT > xdrleft) {
552923e50fe3SFrank van der Linden 			if (count == 0) {
553023e50fe3SFrank van der Linden 				/*
553123e50fe3SFrank van der Linden 				 * Can't even fit the first attribute name.
553223e50fe3SFrank van der Linden 				 */
553323e50fe3SFrank van der Linden 				status = nfserr_toosmall;
553423e50fe3SFrank van der Linden 				goto out;
55352f73f37dSJorge Mora 			}
55362f73f37dSJorge Mora 			eof = 0;
553723e50fe3SFrank van der Linden 			goto wreof;
553823e50fe3SFrank van der Linden 		}
553923e50fe3SFrank van der Linden 
554023e50fe3SFrank van der Linden 		left -= XATTR_USER_PREFIX_LEN;
554123e50fe3SFrank van der Linden 		sp += XATTR_USER_PREFIX_LEN;
554223e50fe3SFrank van der Linden 		if (nuser++ < offset)
554323e50fe3SFrank van der Linden 			goto contloop;
554423e50fe3SFrank van der Linden 
554523e50fe3SFrank van der Linden 
554623e50fe3SFrank van der Linden 		p = xdr_reserve_space(xdr, xdrlen);
554723e50fe3SFrank van der Linden 		if (!p) {
554823e50fe3SFrank van der Linden 			status = nfserr_resource;
554923e50fe3SFrank van der Linden 			goto out;
555023e50fe3SFrank van der Linden 		}
555123e50fe3SFrank van der Linden 
555223e50fe3SFrank van der Linden 		xdr_encode_opaque(p, sp, slen);
555323e50fe3SFrank van der Linden 
555423e50fe3SFrank van der Linden 		xdrleft -= xdrlen;
555523e50fe3SFrank van der Linden 		count++;
555623e50fe3SFrank van der Linden contloop:
555723e50fe3SFrank van der Linden 		sp += slen + 1;
555823e50fe3SFrank van der Linden 		left -= slen + 1;
555923e50fe3SFrank van der Linden 	}
5560e2a1840eSAlex Dewar 
556123e50fe3SFrank van der Linden 	/*
556223e50fe3SFrank van der Linden 	 * If there were user attributes to copy, but we didn't copy
556323e50fe3SFrank van der Linden 	 * any, the offset was too large (e.g. the cookie was invalid).
556423e50fe3SFrank van der Linden 	 */
556523e50fe3SFrank van der Linden 	if (nuser > 0 && count == 0) {
556623e50fe3SFrank van der Linden 		status = nfserr_badcookie;
556723e50fe3SFrank van der Linden 		goto out;
556823e50fe3SFrank van der Linden 	}
556923e50fe3SFrank van der Linden 
557023e50fe3SFrank van der Linden wreof:
557123e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 4);
557223e50fe3SFrank van der Linden 	if (!p) {
557323e50fe3SFrank van der Linden 		status = nfserr_resource;
557423e50fe3SFrank van der Linden 		goto out;
557523e50fe3SFrank van der Linden 	}
557623e50fe3SFrank van der Linden 	*p = cpu_to_be32(eof);
557723e50fe3SFrank van der Linden 
557823e50fe3SFrank van der Linden 	cookie = offset + count;
557923e50fe3SFrank van der Linden 
558023e50fe3SFrank van der Linden 	wire_cookie = cpu_to_be64(cookie);
558123e50fe3SFrank van der Linden 	write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &wire_cookie, 8);
558223e50fe3SFrank van der Linden 	tmp = cpu_to_be32(count);
558323e50fe3SFrank van der Linden 	write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4);
558423e50fe3SFrank van der Linden out:
558523e50fe3SFrank van der Linden 	if (listxattrs->lsxa_len)
558623e50fe3SFrank van der Linden 		kvfree(listxattrs->lsxa_buf);
558723e50fe3SFrank van der Linden 	return status;
558861ab5e07SJorge Mora }
558961ab5e07SJorge Mora 
5590b9a49237SChuck Lever static __be32
nfsd4_encode_removexattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)5591b9a49237SChuck Lever nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
559223e50fe3SFrank van der Linden 			 union nfsd4_op_u *u)
559323e50fe3SFrank van der Linden {
559423e50fe3SFrank van der Linden 	struct nfsd4_removexattr *removexattr = &u->removexattr;
559523e50fe3SFrank van der Linden 	struct xdr_stream *xdr = resp->xdr;
559623e50fe3SFrank van der Linden 
559723e50fe3SFrank van der Linden 	return nfsd4_encode_change_info4(xdr, &removexattr->rmxa_cinfo);
559823e50fe3SFrank van der Linden }
559923e50fe3SFrank van der Linden 
5600e78e274eSKees Cook typedef __be32(*nfsd4_enc)(struct nfsd4_compoundres *, __be32, union nfsd4_op_u *u);
560123e50fe3SFrank van der Linden 
5602e78e274eSKees Cook /*
5603bddfdbcdSChuck Lever  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
560423e50fe3SFrank van der Linden  * since we don't need to filter out obsolete ops as this is
560566a21db7SChuck Lever  * done in the decoding phase.
560623e50fe3SFrank van der Linden  */
560723e50fe3SFrank van der Linden static const nfsd4_enc nfsd4_enc_ops[] = {
5608e78e274eSKees Cook 	[OP_ACCESS]		= nfsd4_encode_access,
5609695e12f8SBenny Halevy 	[OP_CLOSE]		= nfsd4_encode_close,
56102db134ebSAndy Adamson 	[OP_COMMIT]		= nfsd4_encode_commit,
56112db134ebSAndy Adamson 	[OP_CREATE]		= nfsd4_encode_create,
56122db134ebSAndy Adamson 	[OP_DELEGPURGE]		= nfsd4_encode_noop,
56132db134ebSAndy Adamson 	[OP_DELEGRETURN]	= nfsd4_encode_noop,
56142db134ebSAndy Adamson 	[OP_GETATTR]		= nfsd4_encode_getattr,
5615c1df609dSChuck Lever 	[OP_GETFH]		= nfsd4_encode_getfh,
5616e78e274eSKees Cook 	[OP_LINK]		= nfsd4_encode_link,
5617e78e274eSKees Cook 	[OP_LOCK]		= nfsd4_encode_lock,
5618e78e274eSKees Cook 	[OP_LOCKT]		= nfsd4_encode_lockt,
5619e78e274eSKees Cook 	[OP_LOCKU]		= nfsd4_encode_locku,
5620e78e274eSKees Cook 	[OP_LOOKUP]		= nfsd4_encode_noop,
5621e78e274eSKees Cook 	[OP_LOOKUPP]		= nfsd4_encode_noop,
5622e78e274eSKees Cook 	[OP_NVERIFY]		= nfsd4_encode_noop,
5623e78e274eSKees Cook 	[OP_OPEN]		= nfsd4_encode_open,
5624e78e274eSKees Cook 	[OP_OPENATTR]		= nfsd4_encode_noop,
5625e78e274eSKees Cook 	[OP_OPEN_CONFIRM]	= nfsd4_encode_open_confirm,
5626e78e274eSKees Cook 	[OP_OPEN_DOWNGRADE]	= nfsd4_encode_open_downgrade,
5627e78e274eSKees Cook 	[OP_PUTFH]		= nfsd4_encode_noop,
5628e78e274eSKees Cook 	[OP_PUTPUBFH]		= nfsd4_encode_noop,
5629e78e274eSKees Cook 	[OP_PUTROOTFH]		= nfsd4_encode_noop,
5630e78e274eSKees Cook 	[OP_READ]		= nfsd4_encode_read,
5631e78e274eSKees Cook 	[OP_READDIR]		= nfsd4_encode_readdir,
5632e78e274eSKees Cook 	[OP_READLINK]		= nfsd4_encode_readlink,
5633e78e274eSKees Cook 	[OP_REMOVE]		= nfsd4_encode_remove,
5634e78e274eSKees Cook 	[OP_RENAME]		= nfsd4_encode_rename,
5635e78e274eSKees Cook 	[OP_RENEW]		= nfsd4_encode_noop,
5636e78e274eSKees Cook 	[OP_RESTOREFH]		= nfsd4_encode_noop,
5637e78e274eSKees Cook 	[OP_SAVEFH]		= nfsd4_encode_noop,
5638e78e274eSKees Cook 	[OP_SECINFO]		= nfsd4_encode_secinfo,
5639e78e274eSKees Cook 	[OP_SETATTR]		= nfsd4_encode_setattr,
5640e78e274eSKees Cook 	[OP_SETCLIENTID]	= nfsd4_encode_setclientid,
5641e78e274eSKees Cook 	[OP_SETCLIENTID_CONFIRM] = nfsd4_encode_noop,
5642e78e274eSKees Cook 	[OP_VERIFY]		= nfsd4_encode_noop,
5643e78e274eSKees Cook 	[OP_WRITE]		= nfsd4_encode_write,
5644e78e274eSKees Cook 	[OP_RELEASE_LOCKOWNER]	= nfsd4_encode_noop,
5645e78e274eSKees Cook 
5646e78e274eSKees Cook 	/* NFSv4.1 operations */
5647e78e274eSKees Cook 	[OP_BACKCHANNEL_CTL]	= nfsd4_encode_noop,
5648e78e274eSKees Cook 	[OP_BIND_CONN_TO_SESSION] = nfsd4_encode_bind_conn_to_session,
5649e78e274eSKees Cook 	[OP_EXCHANGE_ID]	= nfsd4_encode_exchange_id,
5650e78e274eSKees Cook 	[OP_CREATE_SESSION]	= nfsd4_encode_create_session,
5651e78e274eSKees Cook 	[OP_DESTROY_SESSION]	= nfsd4_encode_noop,
5652e78e274eSKees Cook 	[OP_FREE_STATEID]	= nfsd4_encode_noop,
56532db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= nfsd4_encode_get_dir_delegation,
56542db134ebSAndy Adamson #ifdef CONFIG_NFSD_PNFS
5655e78e274eSKees Cook 	[OP_GETDEVICEINFO]	= nfsd4_encode_getdeviceinfo,
5656e78e274eSKees Cook 	[OP_GETDEVICELIST]	= nfsd4_encode_noop,
5657e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_encode_layoutcommit,
5658e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_encode_layoutget,
5659e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_encode_layoutreturn,
5660e78e274eSKees Cook #else
566133a1e6eaSJeff Layton 	[OP_GETDEVICEINFO]	= nfsd4_encode_noop,
56629cf514ccSChristoph Hellwig 	[OP_GETDEVICELIST]	= nfsd4_encode_noop,
5663e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_encode_noop,
5664e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_encode_noop,
5665e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_encode_noop,
5666e78e274eSKees Cook #endif
5667e78e274eSKees Cook 	[OP_SECINFO_NO_NAME]	= nfsd4_encode_secinfo_no_name,
56689cf514ccSChristoph Hellwig 	[OP_SEQUENCE]		= nfsd4_encode_sequence,
5669e78e274eSKees Cook 	[OP_SET_SSV]		= nfsd4_encode_noop,
5670e78e274eSKees Cook 	[OP_TEST_STATEID]	= nfsd4_encode_test_stateid,
5671e78e274eSKees Cook 	[OP_WANT_DELEGATION]	= nfsd4_encode_noop,
5672e78e274eSKees Cook 	[OP_DESTROY_CLIENTID]	= nfsd4_encode_noop,
5673e78e274eSKees Cook 	[OP_RECLAIM_COMPLETE]	= nfsd4_encode_noop,
56749cf514ccSChristoph Hellwig 
5675e78e274eSKees Cook 	/* NFSv4.2 operations */
5676e78e274eSKees Cook 	[OP_ALLOCATE]		= nfsd4_encode_noop,
5677e78e274eSKees Cook 	[OP_COPY]		= nfsd4_encode_copy,
5678e78e274eSKees Cook 	[OP_COPY_NOTIFY]	= nfsd4_encode_copy_notify,
5679e78e274eSKees Cook 	[OP_DEALLOCATE]		= nfsd4_encode_noop,
5680e78e274eSKees Cook 	[OP_IO_ADVISE]		= nfsd4_encode_noop,
5681e78e274eSKees Cook 	[OP_LAYOUTERROR]	= nfsd4_encode_noop,
568287a15a80SAnna Schumaker 	[OP_LAYOUTSTATS]	= nfsd4_encode_noop,
568387a15a80SAnna Schumaker 	[OP_OFFLOAD_CANCEL]	= nfsd4_encode_noop,
5684e78e274eSKees Cook 	[OP_OFFLOAD_STATUS]	= nfsd4_encode_offload_status,
5685e78e274eSKees Cook 	[OP_READ_PLUS]		= nfsd4_encode_read_plus,
5686e78e274eSKees Cook 	[OP_SEEK]		= nfsd4_encode_seek,
5687e78e274eSKees Cook 	[OP_WRITE_SAME]		= nfsd4_encode_noop,
5688e78e274eSKees Cook 	[OP_CLONE]		= nfsd4_encode_noop,
5689e78e274eSKees Cook 
5690e78e274eSKees Cook 	/* RFC 8276 extended atributes operations */
5691e78e274eSKees Cook 	[OP_GETXATTR]		= nfsd4_encode_getxattr,
5692e78e274eSKees Cook 	[OP_SETXATTR]		= nfsd4_encode_setxattr,
5693e78e274eSKees Cook 	[OP_LISTXATTRS]		= nfsd4_encode_listxattrs,
5694e78e274eSKees Cook 	[OP_REMOVEXATTR]	= nfsd4_encode_removexattr,
5695e78e274eSKees Cook };
5696e78e274eSKees Cook 
569723e50fe3SFrank van der Linden /*
569823e50fe3SFrank van der Linden  * Calculate whether we still have space to encode repsize bytes.
5699e78e274eSKees Cook  * There are two considerations:
5700e78e274eSKees Cook  *     - For NFS versions >=4.1, the size of the reply must stay within
5701e78e274eSKees Cook  *       session limits
5702e78e274eSKees Cook  *     - For all NFS versions, we must stay within limited preallocated
5703695e12f8SBenny Halevy  *       buffer space.
5704695e12f8SBenny Halevy  *
5705496c262cSAndy Adamson  * This is called before the operation is processed, so can only provide
5706a8095f7eSJ. Bruce Fields  * an upper estimate.  For some nonidempotent operations (such as
5707a8095f7eSJ. Bruce Fields  * getattr), it's not necessarily a problem if that estimate is wrong,
5708a8095f7eSJ. Bruce Fields  * as we can fail it after processing without significant side effects.
5709a8095f7eSJ. Bruce Fields  */
nfsd4_check_resp_size(struct nfsd4_compoundres * resp,u32 respsize)5710a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
5711a8095f7eSJ. Bruce Fields {
5712496c262cSAndy Adamson 	struct xdr_buf *buf = &resp->rqstp->rq_res;
5713a8095f7eSJ. Bruce Fields 	struct nfsd4_slot *slot = resp->cstate.slot;
5714a8095f7eSJ. Bruce Fields 
5715a8095f7eSJ. Bruce Fields 	if (buf->len + respsize <= buf->buflen)
5716a8095f7eSJ. Bruce Fields 		return nfs_ok;
5717496c262cSAndy Adamson 	if (!nfsd4_has_session(&resp->cstate))
5718a8095f7eSJ. Bruce Fields 		return nfserr_resource;
5719496c262cSAndy Adamson 	if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
572067492c99SJ. Bruce Fields 		WARN_ON_ONCE(1);
5721a8095f7eSJ. Bruce Fields 		return nfserr_rep_too_big_to_cache;
5722496c262cSAndy Adamson 	}
572347ee5298SJ. Bruce Fields 	return nfserr_rep_too_big;
572447ee5298SJ. Bruce Fields }
572547ee5298SJ. Bruce Fields 
nfsd4_map_status(__be32 status,u32 minor)572647ee5298SJ. Bruce Fields static __be32 nfsd4_map_status(__be32 status, u32 minor)
572747ee5298SJ. Bruce Fields {
572847ee5298SJ. Bruce Fields 	switch (status) {
5729496c262cSAndy Adamson 	case nfs_ok:
5730ea8d7720SJ. Bruce Fields 		break;
573147ee5298SJ. Bruce Fields 	case nfserr_wrong_type:
5732496c262cSAndy Adamson 		/* RFC 8881 - 15.1.2.9 */
5733496c262cSAndy Adamson 		if (minor == 0)
57341da177e4SLinus Torvalds 			status = nfserr_inval;
57351da177e4SLinus Torvalds 		break;
57361da177e4SLinus Torvalds 	case nfserr_symlink_not_dir:
5737bddfdbcdSChuck Lever 		status = nfserr_symlink;
57389411b1d4SJ. Bruce Fields 		break;
57395f4ab945SJ. Bruce Fields 	}
574034b1744cSJ. Bruce Fields 	return status;
5741082d4bd7SJ. Bruce Fields }
574207d1f802SJ. Bruce Fields 
5743bc749ca4SJ. Bruce Fields void
nfsd4_encode_operation(struct nfsd4_compoundres * resp,struct nfsd4_op * op)57441da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
5745d0a381ddSJ. Bruce Fields {
574615a8b55dSJeff Layton 	struct xdr_stream *xdr = resp->xdr;
574715a8b55dSJeff Layton 	struct nfs4_stateowner *so = resp->cstate.replay_owner;
5748c373b0a4SJ. Bruce Fields 	struct svc_rqst *rqstp = resp->rqstp;
5749082d4bd7SJ. Bruce Fields 	const struct nfsd4_operation *opdesc = op->opdesc;
57501da177e4SLinus Torvalds 	int post_err_offset;
5751695e12f8SBenny Halevy 	nfsd4_enc encoder;
5752695e12f8SBenny Halevy 	__be32 *p;
5753b7571e4cSJ. Bruce Fields 
5754b7571e4cSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
5755b7571e4cSJ. Bruce Fields 	if (!p)
57563a237b4aSChuck Lever 		goto release;
5757695e12f8SBenny Halevy 	*p++ = cpu_to_be32(op->opnum);
575807d1f802SJ. Bruce Fields 	post_err_offset = xdr->buf->len;
575907d1f802SJ. Bruce Fields 
576008281341SChuck Lever 	if (op->opnum == OP_ILLEGAL)
576108281341SChuck Lever 		goto status;
57622825a7f9SJ. Bruce Fields 	if (op->status && opdesc &&
57632825a7f9SJ. Bruce Fields 			!(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE))
5764067e1aceSJ. Bruce Fields 		goto status;
57655f4ab945SJ. Bruce Fields 	BUG_ON(op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
57665f4ab945SJ. Bruce Fields 	       !nfsd4_enc_ops[op->opnum]);
57675f4ab945SJ. Bruce Fields 	encoder = nfsd4_enc_ops[op->opnum];
57685f4ab945SJ. Bruce Fields 	op->status = encoder(resp, op->status, &op->u);
57695f4ab945SJ. Bruce Fields 	if (op->status)
57705f4ab945SJ. Bruce Fields 		trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status);
5771c8f13d97SJ. Bruce Fields 	xdr_commit_encode(xdr);
5772c8f13d97SJ. Bruce Fields 
5773c8f13d97SJ. Bruce Fields 	/* nfsd4_check_resp_size guarantees enough room for error status */
5774c8f13d97SJ. Bruce Fields 	if (!op->status) {
5775c8f13d97SJ. Bruce Fields 		int space_needed = 0;
5776c8f13d97SJ. Bruce Fields 		if (!nfsd4_last_compound_op(rqstp))
5777c8f13d97SJ. Bruce Fields 			space_needed = COMPOUND_ERR_SLACK_SPACE;
5778c8f13d97SJ. Bruce Fields 		op->status = nfsd4_check_resp_size(resp, space_needed);
577907d1f802SJ. Bruce Fields 	}
578007d1f802SJ. Bruce Fields 	if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
578107d1f802SJ. Bruce Fields 		struct nfsd4_slot *slot = resp->cstate.slot;
578207d1f802SJ. Bruce Fields 
578307d1f802SJ. Bruce Fields 		if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
578407d1f802SJ. Bruce Fields 			op->status = nfserr_rep_too_big_to_cache;
578507d1f802SJ. Bruce Fields 		else
578607d1f802SJ. Bruce Fields 			op->status = nfserr_rep_too_big;
578707d1f802SJ. Bruce Fields 	}
578807d1f802SJ. Bruce Fields 	if (op->status == nfserr_resource ||
578907d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big ||
5790082d4bd7SJ. Bruce Fields 	    op->status == nfserr_rep_too_big_to_cache) {
579107d1f802SJ. Bruce Fields 		/*
57929411b1d4SJ. Bruce Fields 		 * The operation may have already been encoded or
5793082d4bd7SJ. Bruce Fields 		 * partially encoded.  No op returns anything additional
5794082d4bd7SJ. Bruce Fields 		 * in the case of one of these three errors, so we can
57959411b1d4SJ. Bruce Fields 		 * just truncate back to after the status.  But it's a
5796082d4bd7SJ. Bruce Fields 		 * bug if we had to do this on a non-idempotent op:
5797082d4bd7SJ. Bruce Fields 		 */
5798082d4bd7SJ. Bruce Fields 		warn_on_nonidempotent_op(op);
57999411b1d4SJ. Bruce Fields 		xdr_truncate_encode(xdr, post_err_offset);
5800695e12f8SBenny Halevy 	}
5801095a764bSChuck Lever 	if (so) {
580215a8b55dSJeff Layton 		int len = xdr->buf->len - post_err_offset;
580315a8b55dSJeff Layton 
580415a8b55dSJeff Layton 		so->so_replay.rp_status = op->status;
5805ed4a567aSChuck Lever 		so->so_replay.rp_buflen = len;
5806ed4a567aSChuck Lever 		read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
5807ed4a567aSChuck Lever 						so->so_replay.rp_buf, len);
5808ed4a567aSChuck Lever 	}
5809ed4a567aSChuck Lever status:
5810ed4a567aSChuck Lever 	op->status = nfsd4_map_status(op->status,
58111da177e4SLinus Torvalds 				      resp->cstate.minorversion);
58121da177e4SLinus Torvalds 	*p = op->status;
58139b350d3eSChuck Lever release:
58149b350d3eSChuck Lever 	if (opdesc && opdesc->op_release)
58159b350d3eSChuck Lever 		opdesc->op_release(&op->u);
58169b350d3eSChuck Lever 
58171da177e4SLinus Torvalds 	/*
58189b350d3eSChuck Lever 	 * Account for pages consumed while encoding this operation.
58191da177e4SLinus Torvalds 	 * The xdr_stream primitives don't manage rq_next_page.
58209b350d3eSChuck Lever 	 */
58211da177e4SLinus Torvalds 	rqstp->rq_next_page = xdr->page_ptr + 1;
58221da177e4SLinus Torvalds }
58231da177e4SLinus Torvalds 
58249b350d3eSChuck Lever /**
58251da177e4SLinus Torvalds  * nfsd4_encode_replay - encode a result stored in the stateowner reply cache
58269b350d3eSChuck Lever  * @xdr: send buffer's XDR stream
58279b350d3eSChuck Lever  * @op: operation being replayed
58289b350d3eSChuck Lever  *
58299b350d3eSChuck Lever  * @op->replay->rp_buf contains the previously-sent already-encoded result.
58309b350d3eSChuck Lever  */
nfsd4_encode_replay(struct xdr_stream * xdr,struct nfsd4_op * op)58311da177e4SLinus Torvalds void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
58321da177e4SLinus Torvalds {
58338537488bSChristoph Hellwig 	struct nfs4_replay *rp = op->replay;
58341da177e4SLinus Torvalds 
58353e98abffSJ. Bruce Fields 	trace_nfsd_stateowner_replay(op->opnum, rp);
58363e98abffSJ. Bruce Fields 
58371da177e4SLinus Torvalds 	if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT)
583880e591ceSChuck Lever 		return;
58391da177e4SLinus Torvalds 	if (xdr_stream_encode_be32(xdr, rp->rp_status) != XDR_UNIT)
58401da177e4SLinus Torvalds 		return;
58411da177e4SLinus Torvalds 	xdr_stream_encode_opaque_fixed(xdr, rp->rp_buf, rp->rp_buflen);
5842d5e23383SJ. Bruce Fields }
58431da177e4SLinus Torvalds 
nfsd4_release_compoundargs(struct svc_rqst * rqstp)58441da177e4SLinus Torvalds void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
58451da177e4SLinus Torvalds {
58461da177e4SLinus Torvalds 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
58471da177e4SLinus Torvalds 
5848c44b31c2SChuck Lever 	if (args->ops != args->iops) {
584916c66364SChuck Lever 		vfree(args->ops);
58501da177e4SLinus Torvalds 		args->ops = args->iops;
5851026fec7eSChristoph Hellwig 	}
5852026fec7eSChristoph Hellwig 	while (args->to_free) {
5853c1346a12SChuck Lever 		struct svcxdr_tmpbuf *tb = args->to_free;
58541da177e4SLinus Torvalds 		args->to_free = tb->next;
5855c1346a12SChuck Lever 		kfree(tb);
585616c66364SChuck Lever 	}
58571da177e4SLinus Torvalds }
58581da177e4SLinus Torvalds 
58591da177e4SLinus Torvalds bool
nfs4svc_decode_compoundargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)5860d9b74bdaSChuck Lever nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
58611da177e4SLinus Torvalds {
58621da177e4SLinus Torvalds 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
5863130e2054SChuck Lever 
5864fda49441SChuck Lever 	/* svcxdr_tmp_alloc */
58651da177e4SLinus Torvalds 	args->to_free = NULL;
586663f8de37SChristoph Hellwig 
5867fda49441SChuck Lever 	args->xdr = xdr;
58686ac90391SJ. Bruce Fields 	args->ops = args->iops;
58693b0ebb25SChuck Lever 	args->rqstp = rqstp;
58703b0ebb25SChuck Lever 
58713b0ebb25SChuck Lever 	return nfsd4_decode_compound(args);
58723b0ebb25SChuck Lever }
58733b0ebb25SChuck Lever 
5874cc028a10SChuck Lever bool
nfs4svc_encode_compoundres(struct svc_rqst * rqstp,struct xdr_stream * xdr)58753b0ebb25SChuck Lever nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
58761da177e4SLinus Torvalds {
58771da177e4SLinus Torvalds 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
58781da177e4SLinus Torvalds 	__be32 *p;
58791da177e4SLinus Torvalds 
58801da177e4SLinus Torvalds 	/*
5881b607664eSTrond Myklebust 	 * Send buffer space for the following items is reserved
5882130e2054SChuck Lever 	 * at the top of nfsd4_proc_compound().
58831da177e4SLinus Torvalds 	 */
5884 	p = resp->statusp;
5885 
5886 	*p++ = resp->cstate.status;
5887 	*p++ = htonl(resp->taglen);
5888 	memcpy(p, resp->tag, resp->taglen);
5889 	p += XDR_QUADLEN(resp->taglen);
5890 	*p++ = htonl(resp->opcnt);
5891 
5892 	nfsd4_sequence_done(resp);
5893 	return true;
5894 }
5895