xref: /openbmc/linux/fs/nfsd/nfs4xdr.c (revision fac59652993f075d57860769c99045b3ca18780d)
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,u32 len)121d5e23383SJ. Bruce Fields svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)
1221da177e4SLinus Torvalds {
123d5e23383SJ. Bruce Fields 	struct svcxdr_tmpbuf *tb;
1241da177e4SLinus Torvalds 
125d5e23383SJ. Bruce Fields 	tb = kmalloc(sizeof(*tb) + 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,u32 len)14129c353b3SJ. Bruce Fields svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
14229c353b3SJ. Bruce Fields {
143d5e23383SJ. Bruce Fields 	char *p = svcxdr_tmpalloc(argp, 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,u32 len)1537b723008SChuck Lever svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, u32 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)
12501da177e4SLinus Torvalds {
1251e78e274eSKees Cook 	struct nfsd4_read *read = &u->read;
12523909c3bcSChuck Lever 	__be32 status;
12531da177e4SLinus Torvalds 
12543fdc5464SChuck Lever 	memset(read, 0, sizeof(*read));
12553909c3bcSChuck Lever 	status = nfsd4_decode_stateid4(argp, &read->rd_stateid);
1256e31a1b66SBenny Halevy 	if (status)
1257e31a1b66SBenny Halevy 		return status;
12583909c3bcSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &read->rd_offset) < 0)
12593909c3bcSChuck Lever 		return nfserr_bad_xdr;
12603909c3bcSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &read->rd_length) < 0)
12613909c3bcSChuck Lever 		return nfserr_bad_xdr;
12621da177e4SLinus Torvalds 
12633909c3bcSChuck Lever 	return nfs_ok;
12641da177e4SLinus Torvalds }
12651da177e4SLinus Torvalds 
1266b37ad28bSAl Viro static __be32
nfsd4_decode_readdir(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1267e78e274eSKees Cook nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12681da177e4SLinus Torvalds {
1269e78e274eSKees Cook 	struct nfsd4_readdir *readdir = &u->readdir;
12700dfaf2a3SChuck Lever 	__be32 status;
12711da177e4SLinus Torvalds 
12723fdc5464SChuck Lever 	memset(readdir, 0, sizeof(*readdir));
12730dfaf2a3SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &readdir->rd_cookie) < 0)
12740dfaf2a3SChuck Lever 		return nfserr_bad_xdr;
12750dfaf2a3SChuck Lever 	status = nfsd4_decode_verifier4(argp, &readdir->rd_verf);
12760dfaf2a3SChuck Lever 	if (status)
12770dfaf2a3SChuck Lever 		return status;
12780dfaf2a3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &readdir->rd_dircount) < 0)
12790dfaf2a3SChuck Lever 		return nfserr_bad_xdr;
12800dfaf2a3SChuck 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;
12851da177e4SLinus Torvalds 
12860dfaf2a3SChuck Lever 	return nfs_ok;
12871da177e4SLinus Torvalds }
12881da177e4SLinus Torvalds 
1289b37ad28bSAl Viro static __be32
nfsd4_decode_remove(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1290e78e274eSKees Cook nfsd4_decode_remove(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
12911da177e4SLinus Torvalds {
1292e78e274eSKees Cook 	struct nfsd4_remove *remove = &u->remove;
12933fdc5464SChuck Lever 	memset(&remove->rm_cinfo, 0, sizeof(remove->rm_cinfo));
1294b7f5fbf2SChuck 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;
1301ba881a0aSChuck Lever 	__be32 status;
13021da177e4SLinus Torvalds 
13033fdc5464SChuck Lever 	memset(rename, 0, sizeof(*rename));
1304ba881a0aSChuck Lever 	status = nfsd4_decode_component4(argp, &rename->rn_sname, &rename->rn_snamelen);
1305ba881a0aSChuck Lever 	if (status)
13061da177e4SLinus Torvalds 		return status;
1307ba881a0aSChuck Lever 	return nfsd4_decode_component4(argp, &rename->rn_tname, &rename->rn_tnamelen);
13081da177e4SLinus Torvalds }
13091da177e4SLinus Torvalds 
1310b37ad28bSAl Viro static __be32
nfsd4_decode_renew(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1311e78e274eSKees Cook nfsd4_decode_renew(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
13121da177e4SLinus Torvalds {
1313e78e274eSKees Cook 	clientid_t *clientid = &u->renew;
1314d12f9045SChuck Lever 	return nfsd4_decode_clientid4(argp, clientid);
13151da177e4SLinus Torvalds }
13161da177e4SLinus Torvalds 
1317b37ad28bSAl Viro static __be32
nfsd4_decode_secinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1318dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1319e78e274eSKees Cook 		     union nfsd4_op_u *u)
1320dcb488a3SAndy Adamson {
1321e78e274eSKees Cook 	struct nfsd4_secinfo *secinfo = &u->secinfo;
13223fdc5464SChuck Lever 	secinfo->si_exp = NULL;
1323d0abdae5SChuck Lever 	return nfsd4_decode_component4(argp, &secinfo->si_name, &secinfo->si_namelen);
1324dcb488a3SAndy Adamson }
1325dcb488a3SAndy Adamson 
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)
13281da177e4SLinus Torvalds {
1329e78e274eSKees Cook 	struct nfsd4_setattr *setattr = &u->setattr;
1330e31a1b66SBenny Halevy 	__be32 status;
13311da177e4SLinus Torvalds 
13323fdc5464SChuck Lever 	memset(setattr, 0, sizeof(*setattr));
133344592fe9SChuck Lever 	status = nfsd4_decode_stateid4(argp, &setattr->sa_stateid);
1334e31a1b66SBenny Halevy 	if (status)
1335e31a1b66SBenny Halevy 		return status;
1336d1c263a0SChuck Lever 	return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
1337d1c263a0SChuck Lever 				   ARRAY_SIZE(setattr->sa_bmval),
1338d1c263a0SChuck Lever 				   &setattr->sa_iattr, &setattr->sa_acl,
1339d1c263a0SChuck Lever 				   &setattr->sa_label, NULL);
13401da177e4SLinus Torvalds }
13411da177e4SLinus Torvalds 
1342b37ad28bSAl Viro static __be32
nfsd4_decode_setclientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1343e78e274eSKees Cook nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
13441da177e4SLinus Torvalds {
1345e78e274eSKees Cook 	struct nfsd4_setclientid *setclientid = &u->setclientid;
134692fa6c08SChuck Lever 	__be32 *p, status;
13471da177e4SLinus Torvalds 
13483fdc5464SChuck Lever 	memset(setclientid, 0, sizeof(*setclientid));
13493fdc5464SChuck Lever 
1350e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1351e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1352e1a90ebdSAnna Schumaker 
135392fa6c08SChuck Lever 	status = nfsd4_decode_verifier4(argp, &setclientid->se_verf);
135492fa6c08SChuck Lever 	if (status)
135592fa6c08SChuck Lever 		return status;
1356a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &setclientid->se_name);
1357a084daf5SJ. Bruce Fields 	if (status)
135892fa6c08SChuck Lever 		return status;
135992fa6c08SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_prog) < 0)
1360a084daf5SJ. Bruce Fields 		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);
136492fa6c08SChuck Lever 	if (!p)
136592fa6c08SChuck Lever 		return nfserr_bad_xdr;
13667b723008SChuck Lever 	setclientid->se_callback_netid_val = svcxdr_savemem(argp, p,
136792fa6c08SChuck Lever 						setclientid->se_callback_netid_len);
136892fa6c08SChuck Lever 	if (!setclientid->se_callback_netid_val)
136992fa6c08SChuck Lever 		return nfserr_jukebox;
13701da177e4SLinus Torvalds 
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);
137492fa6c08SChuck Lever 	if (!p)
137592fa6c08SChuck Lever 		return nfserr_bad_xdr;
13767b723008SChuck Lever 	setclientid->se_callback_addr_val = svcxdr_savemem(argp, p,
137792fa6c08SChuck Lever 						setclientid->se_callback_addr_len);
137892fa6c08SChuck Lever 	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;
13821da177e4SLinus Torvalds 
138392fa6c08SChuck Lever 	return nfs_ok;
13841da177e4SLinus Torvalds }
13851da177e4SLinus Torvalds 
1386b37ad28bSAl Viro static __be32
nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1387e78e274eSKees Cook nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp,
1388e78e274eSKees Cook 				 union nfsd4_op_u *u)
13891da177e4SLinus Torvalds {
1390e78e274eSKees Cook 	struct nfsd4_setclientid_confirm *scd_c = &u->setclientid_confirm;
1391d1ca5514SChuck Lever 	__be32 status;
13921da177e4SLinus Torvalds 
1393e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1394e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1395e1a90ebdSAnna Schumaker 
1396d1ca5514SChuck Lever 	status = nfsd4_decode_clientid4(argp, &scd_c->sc_clientid);
1397d1ca5514SChuck Lever 	if (status)
1398d1ca5514SChuck Lever 		return status;
1399d1ca5514SChuck Lever 	return nfsd4_decode_verifier4(argp, &scd_c->sc_confirm);
14001da177e4SLinus Torvalds }
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds /* Also used for NVERIFY */
1403b37ad28bSAl Viro static __be32
nfsd4_decode_verify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1404e78e274eSKees Cook nfsd4_decode_verify(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
14051da177e4SLinus Torvalds {
1406e78e274eSKees Cook 	struct nfsd4_verify *verify = &u->verify;
140767cd453eSChuck Lever 	__be32 *p, status;
14081da177e4SLinus Torvalds 
14093fdc5464SChuck Lever 	memset(verify, 0, sizeof(*verify));
14103fdc5464SChuck Lever 
141167cd453eSChuck Lever 	status = nfsd4_decode_bitmap4(argp, verify->ve_bmval,
141267cd453eSChuck Lever 				      ARRAY_SIZE(verify->ve_bmval));
141367cd453eSChuck Lever 	if (status)
141467cd453eSChuck Lever 		return status;
14151da177e4SLinus Torvalds 
14161da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
1417e5f95703SJ. Bruce Fields 	 * nfsd4_proc_verify */
1418e5f95703SJ. Bruce Fields 
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)
142367cd453eSChuck Lever 		return nfserr_bad_xdr;
14247b723008SChuck Lever 	verify->ve_attrval = svcxdr_savemem(argp, p, verify->ve_attrlen);
142567cd453eSChuck Lever 	if (!verify->ve_attrval)
142667cd453eSChuck Lever 		return nfserr_jukebox;
14271da177e4SLinus Torvalds 
142867cd453eSChuck Lever 	return nfs_ok;
14291da177e4SLinus Torvalds }
14301da177e4SLinus Torvalds 
1431b37ad28bSAl Viro static __be32
nfsd4_decode_write(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1432e78e274eSKees Cook nfsd4_decode_write(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
14331da177e4SLinus Torvalds {
1434e78e274eSKees Cook 	struct nfsd4_write *write = &u->write;
1435244e2befSChuck Lever 	__be32 status;
14361da177e4SLinus Torvalds 
1437244e2befSChuck Lever 	status = nfsd4_decode_stateid4(argp, &write->wr_stateid);
1438e31a1b66SBenny Halevy 	if (status)
1439e31a1b66SBenny Halevy 		return status;
1440244e2befSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &write->wr_offset) < 0)
1441244e2befSChuck Lever 		return nfserr_bad_xdr;
1442244e2befSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &write->wr_stable_how) < 0)
1443244e2befSChuck Lever 		return nfserr_bad_xdr;
144454bbb7d2SKinglong Mee 	if (write->wr_stable_how > NFS_FILE_SYNC)
1445244e2befSChuck Lever 		return nfserr_bad_xdr;
1446244e2befSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &write->wr_buflen) < 0)
1447244e2befSChuck Lever 		return nfserr_bad_xdr;
1448c1346a12SChuck Lever 	if (!xdr_stream_subsegment(argp->xdr, &write->wr_payload, write->wr_buflen))
1449244e2befSChuck Lever 		return nfserr_bad_xdr;
14501da177e4SLinus Torvalds 
14513fdc5464SChuck Lever 	write->wr_bytes_written = 0;
14523fdc5464SChuck Lever 	write->wr_how_written = 0;
14533fdc5464SChuck Lever 	memset(&write->wr_verifier, 0, sizeof(write->wr_verifier));
1454244e2befSChuck Lever 	return nfs_ok;
14551da177e4SLinus Torvalds }
14561da177e4SLinus Torvalds 
1457b37ad28bSAl Viro static __be32
nfsd4_decode_release_lockowner(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1458e78e274eSKees Cook nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp,
1459e78e274eSKees Cook 			       union nfsd4_op_u *u)
14601da177e4SLinus Torvalds {
1461e78e274eSKees Cook 	struct nfsd4_release_lockowner *rlockowner = &u->release_lockowner;
1462a4a80c15SChuck Lever 	__be32 status;
14631da177e4SLinus Torvalds 
1464e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1465e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1466e1a90ebdSAnna Schumaker 
1467a4a80c15SChuck Lever 	status = nfsd4_decode_state_owner4(argp, &rlockowner->rl_clientid,
1468a4a80c15SChuck Lever 					   &rlockowner->rl_owner);
1469a4a80c15SChuck Lever 	if (status)
1470a4a80c15SChuck Lever 		return status;
14711da177e4SLinus Torvalds 
147260adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
147360adfc50SAndy Adamson 		return nfserr_inval;
1474a4a80c15SChuck Lever 
1475a4a80c15SChuck Lever 	return nfs_ok;
14761da177e4SLinus Torvalds }
14771da177e4SLinus Torvalds 
nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1478e78e274eSKees Cook static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp,
1479e78e274eSKees Cook 					   union nfsd4_op_u *u)
14800f81d960SChuck Lever {
1481e78e274eSKees Cook 	struct nfsd4_backchannel_ctl *bc = &u->backchannel_ctl;
14823fdc5464SChuck Lever 	memset(bc, 0, sizeof(*bc));
14830f81d960SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &bc->bc_cb_program) < 0)
14840f81d960SChuck Lever 		return nfserr_bad_xdr;
14850f81d960SChuck Lever 	return nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
14860f81d960SChuck Lever }
14870f81d960SChuck Lever 
nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1488e78e274eSKees Cook static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
1489e78e274eSKees Cook 						union nfsd4_op_u *u)
1490571e0451SChuck Lever {
1491e78e274eSKees Cook 	struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
1492571e0451SChuck Lever 	u32 use_conn_in_rdma_mode;
1493571e0451SChuck Lever 	__be32 status;
1494571e0451SChuck Lever 
14953fdc5464SChuck Lever 	memset(bcts, 0, sizeof(*bcts));
1496571e0451SChuck Lever 	status = nfsd4_decode_sessionid4(argp, &bcts->sessionid);
1497571e0451SChuck Lever 	if (status)
1498571e0451SChuck Lever 		return status;
1499571e0451SChuck Lever 	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;
1503571e0451SChuck Lever 
1504571e0451SChuck Lever 	return nfs_ok;
1505571e0451SChuck Lever }
1506571e0451SChuck Lever 
1507b37ad28bSAl Viro static __be32
nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)15082548aa78SChuck Lever nfsd4_decode_state_protect_ops(struct nfsd4_compoundargs *argp,
15092548aa78SChuck Lever 			       struct nfsd4_exchange_id *exid)
15102548aa78SChuck Lever {
15112548aa78SChuck Lever 	__be32 status;
15122548aa78SChuck Lever 
15132548aa78SChuck Lever 	status = nfsd4_decode_bitmap4(argp, exid->spo_must_enforce,
15142548aa78SChuck Lever 				      ARRAY_SIZE(exid->spo_must_enforce));
15152548aa78SChuck Lever 	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 
1525547bfeb4SChuck Lever /*
1526547bfeb4SChuck Lever  * This implementation currently does not support SP4_SSV.
1527547bfeb4SChuck Lever  * This decoder simply skips over these arguments.
1528547bfeb4SChuck Lever  */
1529547bfeb4SChuck Lever static noinline __be32
nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1530547bfeb4SChuck Lever nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs *argp,
1531547bfeb4SChuck Lever 			  struct nfsd4_exchange_id *exid)
1532547bfeb4SChuck 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 
15672548aa78SChuck Lever static __be32
nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)1568523ec6edSChuck Lever nfsd4_decode_state_protect4_a(struct nfsd4_compoundargs *argp,
15690733d213SAndy Adamson 			      struct nfsd4_exchange_id *exid)
15702db134ebSAndy Adamson {
1571523ec6edSChuck Lever 	__be32 status;
15720733d213SAndy Adamson 
1573523ec6edSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &exid->spa_how) < 0)
1574a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
15750733d213SAndy Adamson 	switch (exid->spa_how) {
15760733d213SAndy Adamson 	case SP4_NONE:
15770733d213SAndy Adamson 		break;
15780733d213SAndy Adamson 	case SP4_MACH_CRED:
15792548aa78SChuck Lever 		status = nfsd4_decode_state_protect_ops(argp, exid);
1580ed941643SAndrew Elble 		if (status)
15812548aa78SChuck Lever 			return status;
15820733d213SAndy Adamson 		break;
15830733d213SAndy Adamson 	case SP4_SSV:
1584547bfeb4SChuck Lever 		status = nfsd4_decode_ssv_sp_parms(argp, exid);
15852548aa78SChuck Lever 		if (status)
15862548aa78SChuck Lever 			return status;
15870733d213SAndy Adamson 		break;
15880733d213SAndy Adamson 	default:
1589523ec6edSChuck Lever 		return nfserr_bad_xdr;
15900733d213SAndy Adamson 	}
15910733d213SAndy Adamson 
1592523ec6edSChuck Lever 	return nfs_ok;
1593523ec6edSChuck Lever }
1594523ec6edSChuck Lever 
1595523ec6edSChuck Lever static __be32
nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs * argp,struct nfsd4_exchange_id * exid)159610ff8422SChuck Lever nfsd4_decode_nfs_impl_id4(struct nfsd4_compoundargs *argp,
159710ff8422SChuck Lever 			  struct nfsd4_exchange_id *exid)
159810ff8422SChuck Lever {
159910ff8422SChuck Lever 	__be32 status;
160010ff8422SChuck Lever 	u32 count;
160110ff8422SChuck Lever 
160210ff8422SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
160310ff8422SChuck 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)1632523ec6edSChuck Lever nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
1633e78e274eSKees Cook 			 union nfsd4_op_u *u)
1634523ec6edSChuck Lever {
1635e78e274eSKees Cook 	struct nfsd4_exchange_id *exid = &u->exchange_id;
163610ff8422SChuck Lever 	__be32 status;
1637523ec6edSChuck Lever 
16383fdc5464SChuck Lever 	memset(exid, 0, sizeof(*exid));
1639523ec6edSChuck Lever 	status = nfsd4_decode_verifier4(argp, &exid->verifier);
1640523ec6edSChuck Lever 	if (status)
1641523ec6edSChuck Lever 		return status;
1642523ec6edSChuck Lever 	status = nfsd4_decode_opaque(argp, &exid->clname);
1643523ec6edSChuck Lever 	if (status)
1644523ec6edSChuck Lever 		return status;
1645523ec6edSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &exid->flags) < 0)
1646523ec6edSChuck Lever 		return nfserr_bad_xdr;
1647523ec6edSChuck Lever 	status = nfsd4_decode_state_protect4_a(argp, exid);
1648523ec6edSChuck Lever 	if (status)
1649523ec6edSChuck Lever 		return status;
165010ff8422SChuck Lever 	return nfsd4_decode_nfs_impl_id4(argp, exid);
16512db134ebSAndy Adamson }
16522db134ebSAndy Adamson 
16532db134ebSAndy Adamson static __be32
nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs * argp,struct nfsd4_channel_attrs * ca)16543a3f1fbaSChuck Lever nfsd4_decode_channel_attrs4(struct nfsd4_compoundargs *argp,
16553a3f1fbaSChuck Lever 			    struct nfsd4_channel_attrs *ca)
16563a3f1fbaSChuck Lever {
16573a3f1fbaSChuck Lever 	__be32 *p;
16583a3f1fbaSChuck Lever 
16593a3f1fbaSChuck Lever 	p = xdr_inline_decode(argp->xdr, XDR_UNIT * 7);
16603a3f1fbaSChuck Lever 	if (!p)
16613a3f1fbaSChuck Lever 		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)16862db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
1687e78e274eSKees Cook 			    union nfsd4_op_u *u)
16882db134ebSAndy Adamson {
1689e78e274eSKees Cook 	struct nfsd4_create_session *sess = &u->create_session;
169081243e3fSChuck Lever 	__be32 status;
1691ec6b5d7bSAndy Adamson 
16923fdc5464SChuck Lever 	memset(sess, 0, sizeof(*sess));
169381243e3fSChuck Lever 	status = nfsd4_decode_clientid4(argp, &sess->clientid);
169481243e3fSChuck Lever 	if (status)
169581243e3fSChuck Lever 		return status;
169681243e3fSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sess->seqid) < 0)
169781243e3fSChuck Lever 		return nfserr_bad_xdr;
169881243e3fSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sess->flags) < 0)
169981243e3fSChuck Lever 		return nfserr_bad_xdr;
17003a3f1fbaSChuck Lever 	status = nfsd4_decode_channel_attrs4(argp, &sess->fore_channel);
17013a3f1fbaSChuck Lever 	if (status)
17023a3f1fbaSChuck Lever 		return status;
17033a3f1fbaSChuck Lever 	status = nfsd4_decode_channel_attrs4(argp, &sess->back_channel);
17043a3f1fbaSChuck Lever 	if (status)
17053a3f1fbaSChuck Lever 		return status;
170681243e3fSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sess->callback_prog) < 0)
170781243e3fSChuck Lever 		return nfserr_bad_xdr;
17083fdc5464SChuck Lever 	return nfsd4_decode_cb_sec(argp, &sess->cb_sec);
17092db134ebSAndy Adamson }
17102db134ebSAndy Adamson 
17112db134ebSAndy Adamson static __be32
nfsd4_decode_destroy_session(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17122db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
1713e78e274eSKees Cook 			     union nfsd4_op_u *u)
17142db134ebSAndy Adamson {
1715e78e274eSKees Cook 	struct nfsd4_destroy_session *destroy_session = &u->destroy_session;
171694e254afSChuck 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)1720e1ca12dfSBryan Schumaker nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1721e78e274eSKees Cook 			  union nfsd4_op_u *u)
1722e1ca12dfSBryan Schumaker {
1723e78e274eSKees Cook 	struct nfsd4_free_stateid *free_stateid = &u->free_stateid;
1724aec387d5SChuck Lever 	return nfsd4_decode_stateid4(argp, &free_stateid->fr_stateid);
1725e1ca12dfSBryan Schumaker }
1726e1ca12dfSBryan Schumaker 
17279cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
17289cf514ccSChristoph Hellwig static __be32
nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17299cf514ccSChristoph Hellwig nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
1730e78e274eSKees Cook 		union nfsd4_op_u *u)
17319cf514ccSChristoph Hellwig {
1732e78e274eSKees Cook 	struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
173304495971SChuck Lever 	__be32 status;
17349cf514ccSChristoph Hellwig 
17353fdc5464SChuck Lever 	memset(gdev, 0, sizeof(*gdev));
173604495971SChuck Lever 	status = nfsd4_decode_deviceid4(argp, &gdev->gd_devid);
173704495971SChuck Lever 	if (status)
173804495971SChuck Lever 		return status;
173904495971SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_layout_type) < 0)
174004495971SChuck Lever 		return nfserr_bad_xdr;
174104495971SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &gdev->gd_maxcount) < 0)
174204495971SChuck Lever 		return nfserr_bad_xdr;
174304495971SChuck Lever 	if (xdr_stream_decode_uint32_array(argp->xdr,
174404495971SChuck Lever 					   &gdev->gd_notify_types, 1) < 0)
174504495971SChuck Lever 		return nfserr_bad_xdr;
174604495971SChuck Lever 
174704495971SChuck Lever 	return nfs_ok;
17489cf514ccSChristoph Hellwig }
17499cf514ccSChristoph Hellwig 
17509cf514ccSChristoph Hellwig static __be32
nfsd4_decode_layoutcommit(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17515185980dSChuck Lever nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp,
1752e78e274eSKees Cook 			  union nfsd4_op_u *u)
17535185980dSChuck Lever {
1754e78e274eSKees Cook 	struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
17555185980dSChuck Lever 	__be32 *p, status;
17565185980dSChuck Lever 
17573fdc5464SChuck Lever 	memset(lcp, 0, sizeof(*lcp));
17585185980dSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.offset) < 0)
17595185980dSChuck Lever 		return nfserr_bad_xdr;
17605185980dSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_seg.length) < 0)
17615185980dSChuck Lever 		return nfserr_bad_xdr;
17625185980dSChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &lcp->lc_reclaim) < 0)
17635185980dSChuck Lever 		return nfserr_bad_xdr;
17645185980dSChuck Lever 	status = nfsd4_decode_stateid4(argp, &lcp->lc_sid);
17655185980dSChuck Lever 	if (status)
17665185980dSChuck Lever 		return status;
17675185980dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lcp->lc_newoffset) < 0)
17685185980dSChuck Lever 		return nfserr_bad_xdr;
17695185980dSChuck Lever 	if (lcp->lc_newoffset) {
17705185980dSChuck Lever 		if (xdr_stream_decode_u64(argp->xdr, &lcp->lc_last_wr) < 0)
17715185980dSChuck Lever 			return nfserr_bad_xdr;
17725185980dSChuck Lever 	} else
17735185980dSChuck Lever 		lcp->lc_last_wr = 0;
17745185980dSChuck Lever 	p = xdr_inline_decode(argp->xdr, XDR_UNIT);
17755185980dSChuck Lever 	if (!p)
17765185980dSChuck Lever 		return nfserr_bad_xdr;
17775185980dSChuck Lever 	if (xdr_item_is_present(p)) {
17785185980dSChuck Lever 		status = nfsd4_decode_nfstime4(argp, &lcp->lc_mtime);
17795185980dSChuck Lever 		if (status)
17805185980dSChuck Lever 			return status;
17815185980dSChuck Lever 	} else {
17825185980dSChuck Lever 		lcp->lc_mtime.tv_nsec = UTIME_NOW;
17835185980dSChuck Lever 	}
17845185980dSChuck Lever 	return nfsd4_decode_layoutupdate4(argp, lcp);
17855185980dSChuck Lever }
17865185980dSChuck Lever 
17875185980dSChuck Lever static __be32
nfsd4_decode_layoutget(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)17889cf514ccSChristoph Hellwig nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
1789e78e274eSKees Cook 		union nfsd4_op_u *u)
17909cf514ccSChristoph Hellwig {
1791e78e274eSKees Cook 	struct nfsd4_layoutget *lgp = &u->layoutget;
1792c8e88e3aSChuck Lever 	__be32 status;
17939cf514ccSChristoph Hellwig 
17943fdc5464SChuck Lever 	memset(lgp, 0, sizeof(*lgp));
1795c8e88e3aSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_signal) < 0)
1796c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1797c8e88e3aSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_layout_type) < 0)
1798c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1799c8e88e3aSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_seg.iomode) < 0)
1800c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1801c8e88e3aSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.offset) < 0)
1802c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1803c8e88e3aSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_seg.length) < 0)
1804c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1805c8e88e3aSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &lgp->lg_minlength) < 0)
1806c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1807c8e88e3aSChuck Lever 	status = nfsd4_decode_stateid4(argp, &lgp->lg_sid);
1808db59c0efSKinglong Mee 	if (status)
1809db59c0efSKinglong Mee 		return status;
1810c8e88e3aSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lgp->lg_maxcount) < 0)
1811c8e88e3aSChuck Lever 		return nfserr_bad_xdr;
1812db59c0efSKinglong Mee 
1813c8e88e3aSChuck Lever 	return nfs_ok;
18149cf514ccSChristoph Hellwig }
18159cf514ccSChristoph Hellwig 
18169cf514ccSChristoph Hellwig static __be32
nfsd4_decode_layoutreturn(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)18179cf514ccSChristoph Hellwig nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp,
1818e78e274eSKees Cook 		union nfsd4_op_u *u)
18199cf514ccSChristoph Hellwig {
1820e78e274eSKees Cook 	struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
18213fdc5464SChuck Lever 	memset(lrp, 0, sizeof(*lrp));
1822645fcad3SChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &lrp->lr_reclaim) < 0)
1823645fcad3SChuck Lever 		return nfserr_bad_xdr;
1824645fcad3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_layout_type) < 0)
1825645fcad3SChuck Lever 		return nfserr_bad_xdr;
1826645fcad3SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &lrp->lr_seg.iomode) < 0)
1827645fcad3SChuck Lever 		return nfserr_bad_xdr;
1828645fcad3SChuck Lever 	return nfsd4_decode_layoutreturn4(argp, lrp);
18299cf514ccSChristoph Hellwig }
18309cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
18319cf514ccSChristoph Hellwig 
nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)183253d70873SChuck Lever static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
1833e78e274eSKees Cook 					   union nfsd4_op_u *u)
183453d70873SChuck Lever {
1835e78e274eSKees Cook 	struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name;
183653d70873SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0)
183753d70873SChuck Lever 		return nfserr_bad_xdr;
18383fdc5464SChuck Lever 
18393fdc5464SChuck Lever 	sin->sin_exp = NULL;
184053d70873SChuck Lever 	return nfs_ok;
184153d70873SChuck Lever }
184253d70873SChuck Lever 
18432db134ebSAndy Adamson static __be32
nfsd4_decode_sequence(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1844cf907b11SChuck Lever nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
1845e78e274eSKees Cook 		      union nfsd4_op_u *u)
1846cf907b11SChuck Lever {
1847e78e274eSKees Cook 	struct nfsd4_sequence *seq = &u->sequence;
1848cf907b11SChuck Lever 	__be32 *p, status;
1849cf907b11SChuck Lever 
1850cf907b11SChuck Lever 	status = nfsd4_decode_sessionid4(argp, &seq->sessionid);
1851cf907b11SChuck Lever 	if (status)
1852cf907b11SChuck Lever 		return status;
1853cf907b11SChuck Lever 	p = xdr_inline_decode(argp->xdr, XDR_UNIT * 4);
1854cf907b11SChuck Lever 	if (!p)
1855cf907b11SChuck Lever 		return nfserr_bad_xdr;
1856cf907b11SChuck Lever 	seq->seqid = be32_to_cpup(p++);
1857cf907b11SChuck Lever 	seq->slotid = be32_to_cpup(p++);
1858cf907b11SChuck Lever 	seq->maxslots = be32_to_cpup(p++);
1859cf907b11SChuck Lever 	seq->cachethis = be32_to_cpup(p);
1860cf907b11SChuck Lever 
18613fdc5464SChuck Lever 	seq->status_flags = 0;
1862cf907b11SChuck Lever 	return nfs_ok;
1863cf907b11SChuck Lever }
1864cf907b11SChuck Lever 
1865cf907b11SChuck Lever static __be32
nfsd4_decode_test_stateid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1866e78e274eSKees Cook nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp,
1867e78e274eSKees Cook 			  union nfsd4_op_u *u)
1868b7a0c8f6SChuck Lever {
1869e78e274eSKees Cook 	struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
1870b7a0c8f6SChuck Lever 	struct nfsd4_test_stateid_id *stateid;
1871b7a0c8f6SChuck Lever 	__be32 status;
1872b7a0c8f6SChuck Lever 	u32 i;
1873b7a0c8f6SChuck Lever 
18743fdc5464SChuck Lever 	memset(test_stateid, 0, sizeof(*test_stateid));
1875b7a0c8f6SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &test_stateid->ts_num_ids) < 0)
1876b7a0c8f6SChuck Lever 		return nfserr_bad_xdr;
1877b7a0c8f6SChuck Lever 
1878b7a0c8f6SChuck Lever 	INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
1879b7a0c8f6SChuck Lever 	for (i = 0; i < test_stateid->ts_num_ids; i++) {
1880b7a0c8f6SChuck Lever 		stateid = svcxdr_tmpalloc(argp, sizeof(*stateid));
1881b7a0c8f6SChuck Lever 		if (!stateid)
1882bb4d8427SChuck Lever 			return nfserr_jukebox;
1883b7a0c8f6SChuck Lever 		INIT_LIST_HEAD(&stateid->ts_id_list);
1884b7a0c8f6SChuck Lever 		list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
1885b7a0c8f6SChuck Lever 		status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid);
1886b7a0c8f6SChuck Lever 		if (status)
1887b7a0c8f6SChuck Lever 			return status;
1888b7a0c8f6SChuck Lever 	}
1889b7a0c8f6SChuck Lever 
1890b7a0c8f6SChuck Lever 	return nfs_ok;
1891b7a0c8f6SChuck Lever }
1892b7a0c8f6SChuck Lever 
nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1893c95f2ec3SChuck Lever static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp,
1894e78e274eSKees Cook 					    union nfsd4_op_u *u)
1895c95f2ec3SChuck Lever {
1896e78e274eSKees Cook 	struct nfsd4_destroy_clientid *dc = &u->destroy_clientid;
1897c95f2ec3SChuck Lever 	return nfsd4_decode_clientid4(argp, &dc->clientid);
1898c95f2ec3SChuck Lever }
1899c95f2ec3SChuck Lever 
nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)19000d646784SChuck Lever static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp,
1901e78e274eSKees Cook 					    union nfsd4_op_u *u)
19020d646784SChuck Lever {
1903e78e274eSKees Cook 	struct nfsd4_reclaim_complete *rc = &u->reclaim_complete;
19040d646784SChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &rc->rca_one_fs) < 0)
19050d646784SChuck Lever 		return nfserr_bad_xdr;
19060d646784SChuck Lever 	return nfs_ok;
19070d646784SChuck Lever }
19080d646784SChuck Lever 
1909b7a0c8f6SChuck Lever static __be32
nfsd4_decode_fallocate(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)191095d871f0SAnna Schumaker nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
1911e78e274eSKees Cook 		       union nfsd4_op_u *u)
191295d871f0SAnna Schumaker {
1913e78e274eSKees Cook 	struct nfsd4_fallocate *fallocate = &u->allocate;
19146aef27aaSChuck Lever 	__be32 status;
191595d871f0SAnna Schumaker 
19166aef27aaSChuck Lever 	status = nfsd4_decode_stateid4(argp, &fallocate->falloc_stateid);
191795d871f0SAnna Schumaker 	if (status)
191895d871f0SAnna Schumaker 		return status;
19196aef27aaSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_offset) < 0)
19206aef27aaSChuck Lever 		return nfserr_bad_xdr;
19216aef27aaSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &fallocate->falloc_length) < 0)
19226aef27aaSChuck Lever 		return nfserr_bad_xdr;
192395d871f0SAnna Schumaker 
19246aef27aaSChuck Lever 	return nfs_ok;
192595d871f0SAnna Schumaker }
192695d871f0SAnna Schumaker 
nfsd4_decode_nl4_server(struct nfsd4_compoundargs * argp,struct nl4_server * ns)192784e1b21dSOlga Kornievskaia static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
192884e1b21dSOlga Kornievskaia 				      struct nl4_server *ns)
192984e1b21dSOlga Kornievskaia {
193084e1b21dSOlga Kornievskaia 	struct nfs42_netaddr *naddr;
1931f49e4b4dSChuck Lever 	__be32 *p;
193284e1b21dSOlga Kornievskaia 
1933f49e4b4dSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0)
1934f49e4b4dSChuck Lever 		return nfserr_bad_xdr;
193584e1b21dSOlga Kornievskaia 
193684e1b21dSOlga Kornievskaia 	/* currently support for 1 inter-server source server */
193784e1b21dSOlga Kornievskaia 	switch (ns->nl4_type) {
193884e1b21dSOlga Kornievskaia 	case NL4_NETADDR:
193984e1b21dSOlga Kornievskaia 		naddr = &ns->u.nl4_addr;
194084e1b21dSOlga Kornievskaia 
1941f49e4b4dSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &naddr->netid_len) < 0)
1942f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
194384e1b21dSOlga Kornievskaia 		if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
1944f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
194584e1b21dSOlga Kornievskaia 
1946f49e4b4dSChuck Lever 		p = xdr_inline_decode(argp->xdr, naddr->netid_len);
1947f49e4b4dSChuck Lever 		if (!p)
1948f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
1949f49e4b4dSChuck Lever 		memcpy(naddr->netid, p, naddr->netid_len);
195084e1b21dSOlga Kornievskaia 
1951f49e4b4dSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &naddr->addr_len) < 0)
1952f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
195384e1b21dSOlga Kornievskaia 		if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
1954f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
195584e1b21dSOlga Kornievskaia 
1956f49e4b4dSChuck Lever 		p = xdr_inline_decode(argp->xdr, naddr->addr_len);
1957f49e4b4dSChuck Lever 		if (!p)
1958f49e4b4dSChuck Lever 			return nfserr_bad_xdr;
1959f49e4b4dSChuck Lever 		memcpy(naddr->addr, p, naddr->addr_len);
196084e1b21dSOlga Kornievskaia 		break;
196184e1b21dSOlga Kornievskaia 	default:
1962f49e4b4dSChuck Lever 		return nfserr_bad_xdr;
196384e1b21dSOlga Kornievskaia 	}
1964f49e4b4dSChuck Lever 
1965f49e4b4dSChuck Lever 	return nfs_ok;
196684e1b21dSOlga Kornievskaia }
196784e1b21dSOlga Kornievskaia 
1968ffa0160aSChristoph Hellwig static __be32
nfsd4_decode_copy(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)1969e78e274eSKees Cook nfsd4_decode_copy(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
197029ae7f9dSAnna Schumaker {
1971e78e274eSKees Cook 	struct nfsd4_copy *copy = &u->copy;
19721913cdf5SChuck Lever 	u32 consecutive, i, count, sync;
197384e1b21dSOlga Kornievskaia 	struct nl4_server *ns_dummy;
1974e8febea7SChuck Lever 	__be32 status;
197529ae7f9dSAnna Schumaker 
19763fdc5464SChuck Lever 	memset(copy, 0, sizeof(*copy));
1977e8febea7SChuck Lever 	status = nfsd4_decode_stateid4(argp, &copy->cp_src_stateid);
197829ae7f9dSAnna Schumaker 	if (status)
197929ae7f9dSAnna Schumaker 		return status;
1980e8febea7SChuck Lever 	status = nfsd4_decode_stateid4(argp, &copy->cp_dst_stateid);
198129ae7f9dSAnna Schumaker 	if (status)
198229ae7f9dSAnna Schumaker 		return status;
1983e8febea7SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &copy->cp_src_pos) < 0)
1984e8febea7SChuck Lever 		return nfserr_bad_xdr;
1985e8febea7SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &copy->cp_dst_pos) < 0)
1986e8febea7SChuck Lever 		return nfserr_bad_xdr;
1987e8febea7SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &copy->cp_count) < 0)
1988e8febea7SChuck Lever 		return nfserr_bad_xdr;
1989e8febea7SChuck Lever 	/* ca_consecutive: we always do consecutive copies */
1990e8febea7SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &consecutive) < 0)
1991e8febea7SChuck Lever 		return nfserr_bad_xdr;
19921913cdf5SChuck Lever 	if (xdr_stream_decode_bool(argp->xdr, &sync) < 0)
1993e8febea7SChuck Lever 		return nfserr_bad_xdr;
19941913cdf5SChuck Lever 	nfsd4_copy_set_sync(copy, sync);
199529ae7f9dSAnna Schumaker 
1996e8febea7SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
1997e8febea7SChuck Lever 		return nfserr_bad_xdr;
199887689df6SChuck Lever 	copy->cp_src = svcxdr_tmpalloc(argp, sizeof(*copy->cp_src));
199987689df6SChuck Lever 	if (copy->cp_src == NULL)
200087689df6SChuck Lever 		return nfserr_jukebox;
200184e1b21dSOlga Kornievskaia 	if (count == 0) { /* intra-server copy */
20021913cdf5SChuck Lever 		__set_bit(NFSD4_COPY_F_INTRA, &copy->cp_flags);
2003e8febea7SChuck Lever 		return nfs_ok;
200484e1b21dSOlga Kornievskaia 	}
200584e1b21dSOlga Kornievskaia 
2006e8febea7SChuck Lever 	/* decode all the supplied server addresses but use only the first */
200787689df6SChuck Lever 	status = nfsd4_decode_nl4_server(argp, copy->cp_src);
200884e1b21dSOlga Kornievskaia 	if (status)
200984e1b21dSOlga Kornievskaia 		return status;
201084e1b21dSOlga Kornievskaia 
201184e1b21dSOlga Kornievskaia 	ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
201284e1b21dSOlga Kornievskaia 	if (ns_dummy == NULL)
2013bb4d8427SChuck Lever 		return nfserr_jukebox;
201484e1b21dSOlga Kornievskaia 	for (i = 0; i < count - 1; i++) {
201584e1b21dSOlga Kornievskaia 		status = nfsd4_decode_nl4_server(argp, ns_dummy);
201684e1b21dSOlga Kornievskaia 		if (status) {
201784e1b21dSOlga Kornievskaia 			kfree(ns_dummy);
201884e1b21dSOlga Kornievskaia 			return status;
201984e1b21dSOlga Kornievskaia 		}
202084e1b21dSOlga Kornievskaia 	}
202184e1b21dSOlga Kornievskaia 	kfree(ns_dummy);
202229ae7f9dSAnna Schumaker 
2023e8febea7SChuck Lever 	return nfs_ok;
202429ae7f9dSAnna Schumaker }
202529ae7f9dSAnna Schumaker 
202629ae7f9dSAnna Schumaker static __be32
nfsd4_decode_copy_notify(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)202751911868SOlga Kornievskaia nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
2028e78e274eSKees Cook 			 union nfsd4_op_u *u)
202951911868SOlga Kornievskaia {
2030e78e274eSKees Cook 	struct nfsd4_copy_notify *cn = &u->copy_notify;
20315aff7d08SChuck Lever 	__be32 status;
203251911868SOlga Kornievskaia 
20333fdc5464SChuck Lever 	memset(cn, 0, sizeof(*cn));
203409426ef2SChuck Lever 	cn->cpn_src = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_src));
203509426ef2SChuck Lever 	if (cn->cpn_src == NULL)
203609426ef2SChuck Lever 		return nfserr_jukebox;
203709426ef2SChuck Lever 	cn->cpn_dst = svcxdr_tmpalloc(argp, sizeof(*cn->cpn_dst));
203809426ef2SChuck Lever 	if (cn->cpn_dst == NULL)
203909426ef2SChuck Lever 		return nfserr_jukebox;
204009426ef2SChuck Lever 
2041f9a953fbSChuck Lever 	status = nfsd4_decode_stateid4(argp, &cn->cpn_src_stateid);
204251911868SOlga Kornievskaia 	if (status)
204351911868SOlga Kornievskaia 		return status;
204409426ef2SChuck Lever 	return nfsd4_decode_nl4_server(argp, cn->cpn_dst);
204551911868SOlga Kornievskaia }
204651911868SOlga Kornievskaia 
204751911868SOlga Kornievskaia static __be32
nfsd4_decode_offload_status(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2048f9a953fbSChuck Lever nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp,
2049e78e274eSKees Cook 			    union nfsd4_op_u *u)
2050f9a953fbSChuck Lever {
2051e78e274eSKees Cook 	struct nfsd4_offload_status *os = &u->offload_status;
20523fdc5464SChuck Lever 	os->count = 0;
20533fdc5464SChuck Lever 	os->status = 0;
20542846bb05SChuck Lever 	return nfsd4_decode_stateid4(argp, &os->stateid);
2055f9a953fbSChuck Lever }
2056f9a953fbSChuck Lever 
2057f9a953fbSChuck Lever static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2058e78e274eSKees Cook nfsd4_decode_seek(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
205924bab491SAnna Schumaker {
2060e78e274eSKees Cook 	struct nfsd4_seek *seek = &u->seek;
20619d32b412SChuck Lever 	__be32 status;
206224bab491SAnna Schumaker 
20639d32b412SChuck Lever 	status = nfsd4_decode_stateid4(argp, &seek->seek_stateid);
206424bab491SAnna Schumaker 	if (status)
206524bab491SAnna Schumaker 		return status;
20669d32b412SChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &seek->seek_offset) < 0)
20679d32b412SChuck Lever 		return nfserr_bad_xdr;
20689d32b412SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &seek->seek_whence) < 0)
20699d32b412SChuck Lever 		return nfserr_bad_xdr;
207024bab491SAnna Schumaker 
20713fdc5464SChuck Lever 	seek->seek_eof = 0;
20723fdc5464SChuck Lever 	seek->seek_pos = 0;
20739d32b412SChuck Lever 	return nfs_ok;
207424bab491SAnna Schumaker }
207524bab491SAnna Schumaker 
20763dfd0b0eSChuck Lever static __be32
nfsd4_decode_clone(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)2077e78e274eSKees Cook nfsd4_decode_clone(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
20783dfd0b0eSChuck Lever {
2079e78e274eSKees Cook 	struct nfsd4_clone *clone = &u->clone;
20803dfd0b0eSChuck Lever 	__be32 status;
20813dfd0b0eSChuck Lever 
20823dfd0b0eSChuck Lever 	status = nfsd4_decode_stateid4(argp, &clone->cl_src_stateid);
20833dfd0b0eSChuck Lever 	if (status)
20843dfd0b0eSChuck Lever 		return status;
20853dfd0b0eSChuck Lever 	status = nfsd4_decode_stateid4(argp, &clone->cl_dst_stateid);
20863dfd0b0eSChuck Lever 	if (status)
20873dfd0b0eSChuck Lever 		return status;
20883dfd0b0eSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &clone->cl_src_pos) < 0)
20893dfd0b0eSChuck Lever 		return nfserr_bad_xdr;
20903dfd0b0eSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &clone->cl_dst_pos) < 0)
20913dfd0b0eSChuck Lever 		return nfserr_bad_xdr;
20923dfd0b0eSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &clone->cl_count) < 0)
20933dfd0b0eSChuck Lever 		return nfserr_bad_xdr;
20943dfd0b0eSChuck Lever 
20953dfd0b0eSChuck Lever 	return nfs_ok;
20963dfd0b0eSChuck Lever }
20973dfd0b0eSChuck Lever 
209823e50fe3SFrank van der Linden /*
209923e50fe3SFrank van der Linden  * XDR data that is more than PAGE_SIZE in size is normally part of a
210023e50fe3SFrank van der Linden  * read or write. However, the size of extended attributes is limited
210123e50fe3SFrank van der Linden  * by the maximum request size, and then further limited by the underlying
210223e50fe3SFrank van der Linden  * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
210323e50fe3SFrank van der Linden  * is 64k). Since there is no kvec- or page-based interface to xattrs,
210423e50fe3SFrank van der Linden  * and we're not dealing with contiguous pages, we need to do some copying.
210523e50fe3SFrank van der Linden  */
210623e50fe3SFrank van der Linden 
210723e50fe3SFrank van der Linden /*
2108c1346a12SChuck Lever  * Decode data into buffer.
210923e50fe3SFrank van der Linden  */
211023e50fe3SFrank van der Linden static __be32
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs * argp,struct xdr_buf * xdr,char ** bufp,u32 buflen)2111c1346a12SChuck Lever nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
2112c1346a12SChuck Lever 		       char **bufp, u32 buflen)
211323e50fe3SFrank van der Linden {
2114c1346a12SChuck Lever 	struct page **pages = xdr->pages;
2115c1346a12SChuck Lever 	struct kvec *head = xdr->head;
211623e50fe3SFrank van der Linden 	char *tmp, *dp;
211723e50fe3SFrank van der Linden 	u32 len;
211823e50fe3SFrank van der Linden 
211923e50fe3SFrank van der Linden 	if (buflen <= head->iov_len) {
212023e50fe3SFrank van der Linden 		/*
212123e50fe3SFrank van der Linden 		 * We're in luck, the head has enough space. Just return
212223e50fe3SFrank van der Linden 		 * the head, no need for copying.
212323e50fe3SFrank van der Linden 		 */
212423e50fe3SFrank van der Linden 		*bufp = head->iov_base;
212523e50fe3SFrank van der Linden 		return 0;
212623e50fe3SFrank van der Linden 	}
212723e50fe3SFrank van der Linden 
212823e50fe3SFrank van der Linden 	tmp = svcxdr_tmpalloc(argp, buflen);
212923e50fe3SFrank van der Linden 	if (tmp == NULL)
213023e50fe3SFrank van der Linden 		return nfserr_jukebox;
213123e50fe3SFrank van der Linden 
213223e50fe3SFrank van der Linden 	dp = tmp;
213323e50fe3SFrank van der Linden 	memcpy(dp, head->iov_base, head->iov_len);
213423e50fe3SFrank van der Linden 	buflen -= head->iov_len;
213523e50fe3SFrank van der Linden 	dp += head->iov_len;
213623e50fe3SFrank van der Linden 
213723e50fe3SFrank van der Linden 	while (buflen > 0) {
213823e50fe3SFrank van der Linden 		len = min_t(u32, buflen, PAGE_SIZE);
213923e50fe3SFrank van der Linden 		memcpy(dp, page_address(*pages), len);
214023e50fe3SFrank van der Linden 
214123e50fe3SFrank van der Linden 		buflen -= len;
214223e50fe3SFrank van der Linden 		dp += len;
214323e50fe3SFrank van der Linden 		pages++;
214423e50fe3SFrank van der Linden 	}
214523e50fe3SFrank van der Linden 
214623e50fe3SFrank van der Linden 	*bufp = tmp;
214723e50fe3SFrank van der Linden 	return 0;
214823e50fe3SFrank van der Linden }
214923e50fe3SFrank van der Linden 
215023e50fe3SFrank van der Linden /*
215123e50fe3SFrank van der Linden  * Get a user extended attribute name from the XDR buffer.
215223e50fe3SFrank van der Linden  * It will not have the "user." prefix, so prepend it.
215323e50fe3SFrank van der Linden  * Lastly, check for nul characters in the name.
215423e50fe3SFrank van der Linden  */
215523e50fe3SFrank van der Linden static __be32
nfsd4_decode_xattr_name(struct nfsd4_compoundargs * argp,char ** namep)215623e50fe3SFrank van der Linden nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
215723e50fe3SFrank van der Linden {
215823e50fe3SFrank van der Linden 	char *name, *sp, *dp;
215923e50fe3SFrank van der Linden 	u32 namelen, cnt;
2160830c7150SChuck Lever 	__be32 *p;
216123e50fe3SFrank van der Linden 
2162830c7150SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &namelen) < 0)
2163830c7150SChuck Lever 		return nfserr_bad_xdr;
216423e50fe3SFrank van der Linden 	if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
216523e50fe3SFrank van der Linden 		return nfserr_nametoolong;
216623e50fe3SFrank van der Linden 	if (namelen == 0)
2167830c7150SChuck Lever 		return nfserr_bad_xdr;
2168830c7150SChuck Lever 	p = xdr_inline_decode(argp->xdr, namelen);
2169830c7150SChuck Lever 	if (!p)
2170830c7150SChuck Lever 		return nfserr_bad_xdr;
217123e50fe3SFrank van der Linden 	name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
217223e50fe3SFrank van der Linden 	if (!name)
217323e50fe3SFrank van der Linden 		return nfserr_jukebox;
217423e50fe3SFrank van der Linden 	memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
217523e50fe3SFrank van der Linden 
217623e50fe3SFrank van der Linden 	/*
217723e50fe3SFrank van der Linden 	 * Copy the extended attribute name over while checking for 0
217823e50fe3SFrank van der Linden 	 * characters.
217923e50fe3SFrank van der Linden 	 */
218023e50fe3SFrank van der Linden 	sp = (char *)p;
218123e50fe3SFrank van der Linden 	dp = name + XATTR_USER_PREFIX_LEN;
218223e50fe3SFrank van der Linden 	cnt = namelen;
218323e50fe3SFrank van der Linden 
218423e50fe3SFrank van der Linden 	while (cnt-- > 0) {
218523e50fe3SFrank van der Linden 		if (*sp == '\0')
2186830c7150SChuck Lever 			return nfserr_bad_xdr;
218723e50fe3SFrank van der Linden 		*dp++ = *sp++;
218823e50fe3SFrank van der Linden 	}
218923e50fe3SFrank van der Linden 	*dp = '\0';
219023e50fe3SFrank van der Linden 
219123e50fe3SFrank van der Linden 	*namep = name;
219223e50fe3SFrank van der Linden 
2193830c7150SChuck Lever 	return nfs_ok;
219423e50fe3SFrank van der Linden }
219523e50fe3SFrank van der Linden 
219623e50fe3SFrank van der Linden /*
219723e50fe3SFrank van der Linden  * A GETXATTR op request comes without a length specifier. We just set the
219823e50fe3SFrank van der Linden  * maximum length for the reply based on XATTR_SIZE_MAX and the maximum
219923e50fe3SFrank van der Linden  * channel reply size. nfsd_getxattr will probe the length of the xattr,
220023e50fe3SFrank van der Linden  * check it against getxa_len, and allocate + return the value.
220123e50fe3SFrank van der Linden  */
220223e50fe3SFrank van der Linden static __be32
nfsd4_decode_getxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)220323e50fe3SFrank van der Linden nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
2204e78e274eSKees Cook 		      union nfsd4_op_u *u)
220523e50fe3SFrank van der Linden {
2206e78e274eSKees Cook 	struct nfsd4_getxattr *getxattr = &u->getxattr;
220723e50fe3SFrank van der Linden 	__be32 status;
220823e50fe3SFrank van der Linden 	u32 maxcount;
220923e50fe3SFrank van der Linden 
22103fdc5464SChuck Lever 	memset(getxattr, 0, sizeof(*getxattr));
221123e50fe3SFrank van der Linden 	status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
221223e50fe3SFrank van der Linden 	if (status)
221323e50fe3SFrank van der Linden 		return status;
221423e50fe3SFrank van der Linden 
221523e50fe3SFrank van der Linden 	maxcount = svc_max_payload(argp->rqstp);
221623e50fe3SFrank van der Linden 	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
221723e50fe3SFrank van der Linden 
221823e50fe3SFrank van der Linden 	getxattr->getxa_len = maxcount;
22193fdc5464SChuck Lever 	return nfs_ok;
222023e50fe3SFrank van der Linden }
222123e50fe3SFrank van der Linden 
222223e50fe3SFrank van der Linden static __be32
nfsd4_decode_setxattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)222323e50fe3SFrank van der Linden nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
2224e78e274eSKees Cook 		      union nfsd4_op_u *u)
222523e50fe3SFrank van der Linden {
2226e78e274eSKees Cook 	struct nfsd4_setxattr *setxattr = &u->setxattr;
222723e50fe3SFrank van der Linden 	u32 flags, maxcount, size;
2228403366a7SChuck Lever 	__be32 status;
222923e50fe3SFrank van der Linden 
22303fdc5464SChuck Lever 	memset(setxattr, 0, sizeof(*setxattr));
22313fdc5464SChuck Lever 
2232403366a7SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &flags) < 0)
2233403366a7SChuck Lever 		return nfserr_bad_xdr;
223423e50fe3SFrank van der Linden 
223523e50fe3SFrank van der Linden 	if (flags > SETXATTR4_REPLACE)
223623e50fe3SFrank van der Linden 		return nfserr_inval;
223723e50fe3SFrank van der Linden 	setxattr->setxa_flags = flags;
223823e50fe3SFrank van der Linden 
223923e50fe3SFrank van der Linden 	status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name);
224023e50fe3SFrank van der Linden 	if (status)
224123e50fe3SFrank van der Linden 		return status;
224223e50fe3SFrank van der Linden 
224323e50fe3SFrank van der Linden 	maxcount = svc_max_payload(argp->rqstp);
224423e50fe3SFrank van der Linden 	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
224523e50fe3SFrank van der Linden 
2246403366a7SChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &size) < 0)
2247403366a7SChuck Lever 		return nfserr_bad_xdr;
224823e50fe3SFrank van der Linden 	if (size > maxcount)
224923e50fe3SFrank van der Linden 		return nfserr_xattr2big;
225023e50fe3SFrank van der Linden 
225123e50fe3SFrank van der Linden 	setxattr->setxa_len = size;
225223e50fe3SFrank van der Linden 	if (size > 0) {
2253c1346a12SChuck Lever 		struct xdr_buf payload;
225423e50fe3SFrank van der Linden 
2255c1346a12SChuck Lever 		if (!xdr_stream_subsegment(argp->xdr, &payload, size))
2256403366a7SChuck Lever 			return nfserr_bad_xdr;
2257c1346a12SChuck Lever 		status = nfsd4_vbuf_from_vector(argp, &payload,
225823e50fe3SFrank van der Linden 						&setxattr->setxa_buf, size);
225923e50fe3SFrank van der Linden 	}
226023e50fe3SFrank van der Linden 
2261403366a7SChuck Lever 	return nfs_ok;
226223e50fe3SFrank van der Linden }
226323e50fe3SFrank van der Linden 
226423e50fe3SFrank van der Linden static __be32
nfsd4_decode_listxattrs(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)226523e50fe3SFrank van der Linden nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
2266e78e274eSKees Cook 			union nfsd4_op_u *u)
226723e50fe3SFrank van der Linden {
2268e78e274eSKees Cook 	struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
226923e50fe3SFrank van der Linden 	u32 maxcount;
227023e50fe3SFrank van der Linden 
22713fdc5464SChuck Lever 	memset(listxattrs, 0, sizeof(*listxattrs));
22723fdc5464SChuck Lever 
22732212036cSChuck Lever 	if (xdr_stream_decode_u64(argp->xdr, &listxattrs->lsxa_cookie) < 0)
22742212036cSChuck Lever 		return nfserr_bad_xdr;
227523e50fe3SFrank van der Linden 
227623e50fe3SFrank van der Linden 	/*
227723e50fe3SFrank van der Linden 	 * If the cookie  is too large to have even one user.x attribute
227823e50fe3SFrank van der Linden 	 * plus trailing '\0' left in a maximum size buffer, it's invalid.
227923e50fe3SFrank van der Linden 	 */
228023e50fe3SFrank van der Linden 	if (listxattrs->lsxa_cookie >=
228123e50fe3SFrank van der Linden 	    (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
228223e50fe3SFrank van der Linden 		return nfserr_badcookie;
228323e50fe3SFrank van der Linden 
22842212036cSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &maxcount) < 0)
22852212036cSChuck Lever 		return nfserr_bad_xdr;
228623e50fe3SFrank van der Linden 	if (maxcount < 8)
228723e50fe3SFrank van der Linden 		/* Always need at least 2 words (length and one character) */
228823e50fe3SFrank van der Linden 		return nfserr_inval;
228923e50fe3SFrank van der Linden 
229023e50fe3SFrank van der Linden 	maxcount = min(maxcount, svc_max_payload(argp->rqstp));
229123e50fe3SFrank van der Linden 	listxattrs->lsxa_maxcount = maxcount;
229223e50fe3SFrank van der Linden 
22932212036cSChuck Lever 	return nfs_ok;
229423e50fe3SFrank van der Linden }
229523e50fe3SFrank van der Linden 
229623e50fe3SFrank van der Linden static __be32
nfsd4_decode_removexattr(struct nfsd4_compoundargs * argp,union nfsd4_op_u * u)229723e50fe3SFrank van der Linden nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
2298e78e274eSKees Cook 			 union nfsd4_op_u *u)
229923e50fe3SFrank van der Linden {
2300e78e274eSKees Cook 	struct nfsd4_removexattr *removexattr = &u->removexattr;
23013fdc5464SChuck Lever 	memset(removexattr, 0, sizeof(*removexattr));
230223e50fe3SFrank van der Linden 	return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
230323e50fe3SFrank van der Linden }
230423e50fe3SFrank van der Linden 
230524bab491SAnna Schumaker static __be32
nfsd4_decode_noop(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)2306e78e274eSKees Cook nfsd4_decode_noop(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
2307347e0ad9SBenny Halevy {
2308347e0ad9SBenny Halevy 	return nfs_ok;
2309347e0ad9SBenny Halevy }
2310347e0ad9SBenny Halevy 
23113c375c6fSBenny Halevy static __be32
nfsd4_decode_notsupp(struct nfsd4_compoundargs * argp,union nfsd4_op_u * p)2312e78e274eSKees Cook nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
23133c375c6fSBenny Halevy {
23141e685ec2SBenny Halevy 	return nfserr_notsupp;
23153c375c6fSBenny Halevy }
23163c375c6fSBenny Halevy 
2317e78e274eSKees Cook typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u);
2318347e0ad9SBenny Halevy 
2319c1df609dSChuck Lever static const nfsd4_dec nfsd4_dec_ops[] = {
2320e78e274eSKees Cook 	[OP_ACCESS]		= nfsd4_decode_access,
2321e78e274eSKees Cook 	[OP_CLOSE]		= nfsd4_decode_close,
2322e78e274eSKees Cook 	[OP_COMMIT]		= nfsd4_decode_commit,
2323e78e274eSKees Cook 	[OP_CREATE]		= nfsd4_decode_create,
2324e78e274eSKees Cook 	[OP_DELEGPURGE]		= nfsd4_decode_notsupp,
2325e78e274eSKees Cook 	[OP_DELEGRETURN]	= nfsd4_decode_delegreturn,
2326e78e274eSKees Cook 	[OP_GETATTR]		= nfsd4_decode_getattr,
2327e78e274eSKees Cook 	[OP_GETFH]		= nfsd4_decode_noop,
2328e78e274eSKees Cook 	[OP_LINK]		= nfsd4_decode_link,
2329e78e274eSKees Cook 	[OP_LOCK]		= nfsd4_decode_lock,
2330e78e274eSKees Cook 	[OP_LOCKT]		= nfsd4_decode_lockt,
2331e78e274eSKees Cook 	[OP_LOCKU]		= nfsd4_decode_locku,
2332e78e274eSKees Cook 	[OP_LOOKUP]		= nfsd4_decode_lookup,
2333e78e274eSKees Cook 	[OP_LOOKUPP]		= nfsd4_decode_noop,
2334e78e274eSKees Cook 	[OP_NVERIFY]		= nfsd4_decode_verify,
2335e78e274eSKees Cook 	[OP_OPEN]		= nfsd4_decode_open,
2336e78e274eSKees Cook 	[OP_OPENATTR]		= nfsd4_decode_notsupp,
2337e78e274eSKees Cook 	[OP_OPEN_CONFIRM]	= nfsd4_decode_open_confirm,
2338e78e274eSKees Cook 	[OP_OPEN_DOWNGRADE]	= nfsd4_decode_open_downgrade,
2339e78e274eSKees Cook 	[OP_PUTFH]		= nfsd4_decode_putfh,
2340*6b17072cSChuck Lever 	[OP_PUTPUBFH]		= nfsd4_decode_noop,
2341e78e274eSKees Cook 	[OP_PUTROOTFH]		= nfsd4_decode_noop,
2342e78e274eSKees Cook 	[OP_READ]		= nfsd4_decode_read,
2343e78e274eSKees Cook 	[OP_READDIR]		= nfsd4_decode_readdir,
2344e78e274eSKees Cook 	[OP_READLINK]		= nfsd4_decode_noop,
2345e78e274eSKees Cook 	[OP_REMOVE]		= nfsd4_decode_remove,
2346e78e274eSKees Cook 	[OP_RENAME]		= nfsd4_decode_rename,
2347e78e274eSKees Cook 	[OP_RENEW]		= nfsd4_decode_renew,
2348e78e274eSKees Cook 	[OP_RESTOREFH]		= nfsd4_decode_noop,
2349e78e274eSKees Cook 	[OP_SAVEFH]		= nfsd4_decode_noop,
2350e78e274eSKees Cook 	[OP_SECINFO]		= nfsd4_decode_secinfo,
2351e78e274eSKees Cook 	[OP_SETATTR]		= nfsd4_decode_setattr,
2352e78e274eSKees Cook 	[OP_SETCLIENTID]	= nfsd4_decode_setclientid,
2353e78e274eSKees Cook 	[OP_SETCLIENTID_CONFIRM] = nfsd4_decode_setclientid_confirm,
2354e78e274eSKees Cook 	[OP_VERIFY]		= nfsd4_decode_verify,
2355e78e274eSKees Cook 	[OP_WRITE]		= nfsd4_decode_write,
2356e78e274eSKees Cook 	[OP_RELEASE_LOCKOWNER]	= nfsd4_decode_release_lockowner,
23572db134ebSAndy Adamson 
23582db134ebSAndy Adamson 	/* new operations for NFSv4.1 */
2359e78e274eSKees Cook 	[OP_BACKCHANNEL_CTL]	= nfsd4_decode_backchannel_ctl,
2360e78e274eSKees Cook 	[OP_BIND_CONN_TO_SESSION] = nfsd4_decode_bind_conn_to_session,
2361e78e274eSKees Cook 	[OP_EXCHANGE_ID]	= nfsd4_decode_exchange_id,
2362e78e274eSKees Cook 	[OP_CREATE_SESSION]	= nfsd4_decode_create_session,
2363e78e274eSKees Cook 	[OP_DESTROY_SESSION]	= nfsd4_decode_destroy_session,
2364e78e274eSKees Cook 	[OP_FREE_STATEID]	= nfsd4_decode_free_stateid,
2365e78e274eSKees Cook 	[OP_GET_DIR_DELEGATION]	= nfsd4_decode_notsupp,
23669cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
2367e78e274eSKees Cook 	[OP_GETDEVICEINFO]	= nfsd4_decode_getdeviceinfo,
2368e78e274eSKees Cook 	[OP_GETDEVICELIST]	= nfsd4_decode_notsupp,
2369e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_decode_layoutcommit,
2370e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_decode_layoutget,
2371e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_decode_layoutreturn,
23729cf514ccSChristoph Hellwig #else
2373e78e274eSKees Cook 	[OP_GETDEVICEINFO]	= nfsd4_decode_notsupp,
2374e78e274eSKees Cook 	[OP_GETDEVICELIST]	= nfsd4_decode_notsupp,
2375e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_decode_notsupp,
2376e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_decode_notsupp,
2377e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_decode_notsupp,
23789cf514ccSChristoph Hellwig #endif
2379e78e274eSKees Cook 	[OP_SECINFO_NO_NAME]	= nfsd4_decode_secinfo_no_name,
2380e78e274eSKees Cook 	[OP_SEQUENCE]		= nfsd4_decode_sequence,
2381e78e274eSKees Cook 	[OP_SET_SSV]		= nfsd4_decode_notsupp,
2382e78e274eSKees Cook 	[OP_TEST_STATEID]	= nfsd4_decode_test_stateid,
2383e78e274eSKees Cook 	[OP_WANT_DELEGATION]	= nfsd4_decode_notsupp,
2384e78e274eSKees Cook 	[OP_DESTROY_CLIENTID]	= nfsd4_decode_destroy_clientid,
2385e78e274eSKees Cook 	[OP_RECLAIM_COMPLETE]	= nfsd4_decode_reclaim_complete,
238687a15a80SAnna Schumaker 
238787a15a80SAnna Schumaker 	/* new operations for NFSv4.2 */
2388e78e274eSKees Cook 	[OP_ALLOCATE]		= nfsd4_decode_fallocate,
2389e78e274eSKees Cook 	[OP_COPY]		= nfsd4_decode_copy,
2390e78e274eSKees Cook 	[OP_COPY_NOTIFY]	= nfsd4_decode_copy_notify,
2391e78e274eSKees Cook 	[OP_DEALLOCATE]		= nfsd4_decode_fallocate,
2392e78e274eSKees Cook 	[OP_IO_ADVISE]		= nfsd4_decode_notsupp,
2393e78e274eSKees Cook 	[OP_LAYOUTERROR]	= nfsd4_decode_notsupp,
2394e78e274eSKees Cook 	[OP_LAYOUTSTATS]	= nfsd4_decode_notsupp,
2395e78e274eSKees Cook 	[OP_OFFLOAD_CANCEL]	= nfsd4_decode_offload_status,
2396e78e274eSKees Cook 	[OP_OFFLOAD_STATUS]	= nfsd4_decode_offload_status,
2397e78e274eSKees Cook 	[OP_READ_PLUS]		= nfsd4_decode_read,
2398e78e274eSKees Cook 	[OP_SEEK]		= nfsd4_decode_seek,
2399e78e274eSKees Cook 	[OP_WRITE_SAME]		= nfsd4_decode_notsupp,
2400e78e274eSKees Cook 	[OP_CLONE]		= nfsd4_decode_clone,
240123e50fe3SFrank van der Linden 	/* RFC 8276 extended atributes operations */
2402e78e274eSKees Cook 	[OP_GETXATTR]		= nfsd4_decode_getxattr,
2403e78e274eSKees Cook 	[OP_SETXATTR]		= nfsd4_decode_setxattr,
2404e78e274eSKees Cook 	[OP_LISTXATTRS]		= nfsd4_decode_listxattrs,
2405e78e274eSKees Cook 	[OP_REMOVEXATTR]	= nfsd4_decode_removexattr,
24062db134ebSAndy Adamson };
24072db134ebSAndy Adamson 
2408e1a90ebdSAnna Schumaker static inline bool
nfsd4_opnum_in_range(struct nfsd4_compoundargs * argp,struct nfsd4_op * op)2409e1a90ebdSAnna Schumaker nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
2410e1a90ebdSAnna Schumaker {
24118217d146SAnna Schumaker 	if (op->opnum < FIRST_NFS4_OP)
2412e1a90ebdSAnna Schumaker 		return false;
24138217d146SAnna Schumaker 	else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
2414e1a90ebdSAnna Schumaker 		return false;
24158217d146SAnna Schumaker 	else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
24168217d146SAnna Schumaker 		return false;
24178217d146SAnna Schumaker 	else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
2418e1a90ebdSAnna Schumaker 		return false;
2419e1a90ebdSAnna Schumaker 	return true;
2420e1a90ebdSAnna Schumaker }
2421f2feb96bSBenny Halevy 
2422c44b31c2SChuck Lever static bool
nfsd4_decode_compound(struct nfsd4_compoundargs * argp)24231da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
24241da177e4SLinus Torvalds {
24251da177e4SLinus Torvalds 	struct nfsd4_op *op;
24261091006cSJ. Bruce Fields 	bool cachethis = false;
2427a5cddc88SJ. Bruce Fields 	int auth_slack= argp->rqstp->rq_auth_slack;
2428a5cddc88SJ. Bruce Fields 	int max_reply = auth_slack + 8; /* opcnt, status */
2429b0e35fdaSJ. Bruce Fields 	int readcount = 0;
2430b0e35fdaSJ. Bruce Fields 	int readbytes = 0;
2431d9b74bdaSChuck Lever 	__be32 *p;
24321da177e4SLinus Torvalds 	int i;
24331da177e4SLinus Torvalds 
2434d9b74bdaSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &argp->taglen) < 0)
2435c44b31c2SChuck Lever 		return false;
2436d9b74bdaSChuck Lever 	max_reply += XDR_UNIT;
2437d9b74bdaSChuck Lever 	argp->tag = NULL;
2438d9b74bdaSChuck Lever 	if (unlikely(argp->taglen)) {
24391da177e4SLinus Torvalds 		if (argp->taglen > NFSD4_MAX_TAGLEN)
2440c44b31c2SChuck Lever 			return false;
2441d9b74bdaSChuck Lever 		p = xdr_inline_decode(argp->xdr, argp->taglen);
2442d9b74bdaSChuck Lever 		if (!p)
2443c44b31c2SChuck Lever 			return false;
24447b723008SChuck Lever 		argp->tag = svcxdr_savemem(argp, p, argp->taglen);
2445d9b74bdaSChuck Lever 		if (!argp->tag)
2446c44b31c2SChuck Lever 			return false;
2447d9b74bdaSChuck Lever 		max_reply += xdr_align_size(argp->taglen);
2448d9b74bdaSChuck Lever 	}
2449d9b74bdaSChuck Lever 
2450d9b74bdaSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &argp->minorversion) < 0)
2451c44b31c2SChuck Lever 		return false;
24527518a3dcSChuck Lever 	if (xdr_stream_decode_u32(argp->xdr, &argp->client_opcnt) < 0)
2453c44b31c2SChuck Lever 		return false;
24547518a3dcSChuck Lever 	argp->opcnt = min_t(u32, argp->client_opcnt,
24557518a3dcSChuck Lever 			    NFSD_MAX_OPS_PER_COMPOUND);
24561da177e4SLinus Torvalds 
2457e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
245880e591ceSChuck Lever 		argp->ops = vcalloc(argp->opcnt, sizeof(*argp->ops));
24591da177e4SLinus Torvalds 		if (!argp->ops) {
24601da177e4SLinus Torvalds 			argp->ops = argp->iops;
2461c44b31c2SChuck Lever 			return false;
24621da177e4SLinus Torvalds 		}
24631da177e4SLinus Torvalds 	}
24641da177e4SLinus Torvalds 
2465e1a90ebdSAnna Schumaker 	if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
246630cff1ffSBenny Halevy 		argp->opcnt = 0;
246730cff1ffSBenny Halevy 
24681da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
24691da177e4SLinus Torvalds 		op = &argp->ops[i];
24701da177e4SLinus Torvalds 		op->replay = NULL;
2471804d8e0aSChuck Lever 		op->opdesc = NULL;
24721da177e4SLinus Torvalds 
24733a237b4aSChuck Lever 		if (xdr_stream_decode_u32(argp->xdr, &op->opnum) < 0)
2474c44b31c2SChuck Lever 			return false;
247508281341SChuck Lever 		if (nfsd4_opnum_in_range(argp, op)) {
2476804d8e0aSChuck Lever 			op->opdesc = OPDESC(op);
2477e1a90ebdSAnna Schumaker 			op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
247808281341SChuck Lever 			if (op->status != nfs_ok)
247908281341SChuck Lever 				trace_nfsd_compound_decode_err(argp->rqstp,
248008281341SChuck Lever 							       argp->opcnt, i,
248108281341SChuck Lever 							       op->opnum,
248208281341SChuck Lever 							       op->status);
248308281341SChuck Lever 		} else {
24841da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
24851da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
24861da177e4SLinus Torvalds 		}
2487804d8e0aSChuck Lever 
24881091006cSJ. Bruce Fields 		/*
24891091006cSJ. Bruce Fields 		 * We'll try to cache the result in the DRC if any one
24901091006cSJ. Bruce Fields 		 * op in the compound wants to be cached:
24911091006cSJ. Bruce Fields 		 */
24921091006cSJ. Bruce Fields 		cachethis |= nfsd4_cache_this_op(op);
24936ff40decSJ. Bruce Fields 
2494528b8493SAnna Schumaker 		if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
2495b0e35fdaSJ. Bruce Fields 			readcount++;
2496b0e35fdaSJ. Bruce Fields 			readbytes += nfsd4_max_reply(argp->rqstp, op);
2497b0e35fdaSJ. Bruce Fields 		} else
24984f0cefbfSJ. Bruce Fields 			max_reply += nfsd4_max_reply(argp->rqstp, op);
2499f7b43d0cSJ. Bruce Fields 		/*
25007323f0d2SKinglong Mee 		 * OP_LOCK and OP_LOCKT may return a conflicting lock.
25017323f0d2SKinglong Mee 		 * (Special case because it will just skip encoding this
25027323f0d2SKinglong Mee 		 * if it runs out of xdr buffer space, and it is the only
25037323f0d2SKinglong Mee 		 * operation that behaves this way.)
2504f7b43d0cSJ. Bruce Fields 		 */
25057323f0d2SKinglong Mee 		if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT)
2506f7b43d0cSJ. Bruce Fields 			max_reply += NFS4_OPAQUE_LIMIT;
2507e372ba60SJ. Bruce Fields 
2508e372ba60SJ. Bruce Fields 		if (op->status) {
2509e372ba60SJ. Bruce Fields 			argp->opcnt = i+1;
2510e372ba60SJ. Bruce Fields 			break;
2511e372ba60SJ. Bruce Fields 		}
25121da177e4SLinus Torvalds 	}
25131091006cSJ. Bruce Fields 	/* Sessions make the DRC unnecessary: */
25141091006cSJ. Bruce Fields 	if (argp->minorversion)
25151091006cSJ. Bruce Fields 		cachethis = false;
2516b0e35fdaSJ. Bruce Fields 	svc_reserve(argp->rqstp, max_reply + readbytes);
25171091006cSJ. Bruce Fields 	argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
25181da177e4SLinus Torvalds 
2519a5cddc88SJ. Bruce Fields 	if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
25207827c81fSChuck Lever 		clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags);
2521b0e35fdaSJ. Bruce Fields 
2522c44b31c2SChuck Lever 	return true;
25231da177e4SLinus Torvalds }
25241da177e4SLinus Torvalds 
encode_change(__be32 * p,struct kstat * stat,struct inode * inode,struct svc_export * exp)2525b8800921SNeilBrown static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode,
2526b8800921SNeilBrown 			     struct svc_export *exp)
2527c654b8a9SJ. Bruce Fields {
2528b8800921SNeilBrown 	if (exp->ex_flags & NFSEXP_V4ROOT) {
2529b8800921SNeilBrown 		*p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time));
2530b8800921SNeilBrown 		*p++ = 0;
253170b87f77SJ. Bruce Fields 	} else
253239ca1bf6SAmir Goldstein 		p = xdr_encode_hyper(p, nfsd4_change_attribute(stat, inode));
2533d05d5744SJ. Bruce Fields 	return p;
2534c654b8a9SJ. Bruce Fields }
2535c654b8a9SJ. Bruce Fields 
nfsd4_encode_nfstime4(struct xdr_stream * xdr,struct timespec64 * tv)253626217679SChuck Lever static __be32 nfsd4_encode_nfstime4(struct xdr_stream *xdr,
253726217679SChuck Lever 				    struct timespec64 *tv)
253826217679SChuck Lever {
253926217679SChuck Lever 	__be32 *p;
254026217679SChuck Lever 
254126217679SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 3);
254226217679SChuck Lever 	if (!p)
254326217679SChuck Lever 		return nfserr_resource;
254426217679SChuck Lever 
254526217679SChuck Lever 	p = xdr_encode_hyper(p, (s64)tv->tv_sec);
254626217679SChuck Lever 	*p = cpu_to_be32(tv->tv_nsec);
254726217679SChuck Lever 	return nfs_ok;
254826217679SChuck Lever }
254926217679SChuck Lever 
255016945141SJ. Bruce Fields /*
255116945141SJ. Bruce Fields  * ctime (in NFSv4, time_metadata) is not writeable, and the client
255216945141SJ. Bruce Fields  * doesn't really care what resolution could theoretically be stored by
255316945141SJ. Bruce Fields  * the filesystem.
255416945141SJ. Bruce Fields  *
255516945141SJ. Bruce Fields  * The client cares how close together changes can be while still
255616945141SJ. Bruce Fields  * guaranteeing ctime changes.  For most filesystems (which have
255716945141SJ. Bruce Fields  * timestamps with nanosecond fields) that is limited by the resolution
255816945141SJ. Bruce Fields  * of the time returned from current_time() (which I'm assuming to be
255916945141SJ. Bruce Fields  * 1/HZ).
256016945141SJ. Bruce Fields  */
encode_time_delta(__be32 * p,struct inode * inode)256116945141SJ. Bruce Fields static __be32 *encode_time_delta(__be32 *p, struct inode *inode)
256216945141SJ. Bruce Fields {
2563e4598e38SArnd Bergmann 	struct timespec64 ts;
256416945141SJ. Bruce Fields 	u32 ns;
256516945141SJ. Bruce Fields 
256616945141SJ. Bruce Fields 	ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
2567e4598e38SArnd Bergmann 	ts = ns_to_timespec64(ns);
256816945141SJ. Bruce Fields 
256916945141SJ. Bruce Fields 	p = xdr_encode_hyper(p, ts.tv_sec);
257016945141SJ. Bruce Fields 	*p++ = cpu_to_be32(ts.tv_nsec);
257116945141SJ. Bruce Fields 
257216945141SJ. Bruce Fields 	return p;
257316945141SJ. Bruce Fields }
257416945141SJ. Bruce Fields 
257566a21db7SChuck Lever static __be32
nfsd4_encode_change_info4(struct xdr_stream * xdr,struct nfsd4_change_info * c)257666a21db7SChuck Lever nfsd4_encode_change_info4(struct xdr_stream *xdr, struct nfsd4_change_info *c)
2577c654b8a9SJ. Bruce Fields {
257866a21db7SChuck Lever 	if (xdr_stream_encode_bool(xdr, c->atomic) < 0)
257966a21db7SChuck Lever 		return nfserr_resource;
258066a21db7SChuck Lever 	if (xdr_stream_encode_u64(xdr, c->before_change) < 0)
258166a21db7SChuck Lever 		return nfserr_resource;
258266a21db7SChuck Lever 	if (xdr_stream_encode_u64(xdr, c->after_change) < 0)
258366a21db7SChuck Lever 		return nfserr_resource;
258466a21db7SChuck Lever 	return nfs_ok;
2585c654b8a9SJ. Bruce Fields }
25861da177e4SLinus Torvalds 
258781c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
2588e7a0444aSWeston Andros Adamson  * separated @sep, escaped with esc_enter and esc_exit.
258981c3f413SJ.Bruce Fields  */
nfsd4_encode_components_esc(struct xdr_stream * xdr,char sep,char * components,char esc_enter,char esc_exit)2590ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
2591ddd1ea56SJ. Bruce Fields 					  char *components, char esc_enter,
2592ddd1ea56SJ. Bruce Fields 					  char esc_exit)
259381c3f413SJ.Bruce Fields {
2594ddd1ea56SJ. Bruce Fields 	__be32 *p;
2595082d4bd7SJ. Bruce Fields 	__be32 pathlen;
2596082d4bd7SJ. Bruce Fields 	int pathlen_offset;
259781c3f413SJ.Bruce Fields 	int strlen, count=0;
2598e7a0444aSWeston Andros Adamson 	char *str, *end, *next;
259981c3f413SJ.Bruce Fields 
260081c3f413SJ.Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
2601082d4bd7SJ. Bruce Fields 
2602082d4bd7SJ. Bruce Fields 	pathlen_offset = xdr->buf->len;
2603ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2604ddd1ea56SJ. Bruce Fields 	if (!p)
260581c3f413SJ.Bruce Fields 		return nfserr_resource;
2606082d4bd7SJ. Bruce Fields 	p++; /* We will fill this in with @count later */
2607082d4bd7SJ. Bruce Fields 
260881c3f413SJ.Bruce Fields 	end = str = components;
260981c3f413SJ.Bruce Fields 	while (*end) {
2610e7a0444aSWeston Andros Adamson 		bool found_esc = false;
2611e7a0444aSWeston Andros Adamson 
2612e7a0444aSWeston Andros Adamson 		/* try to parse as esc_start, ..., esc_end, sep */
2613e7a0444aSWeston Andros Adamson 		if (*str == esc_enter) {
2614e7a0444aSWeston Andros Adamson 			for (; *end && (*end != esc_exit); end++)
2615e7a0444aSWeston Andros Adamson 				/* find esc_exit or end of string */;
2616e7a0444aSWeston Andros Adamson 			next = end + 1;
2617e7a0444aSWeston Andros Adamson 			if (*end && (!*next || *next == sep)) {
2618e7a0444aSWeston Andros Adamson 				str++;
2619e7a0444aSWeston Andros Adamson 				found_esc = true;
2620e7a0444aSWeston Andros Adamson 			}
2621e7a0444aSWeston Andros Adamson 		}
2622e7a0444aSWeston Andros Adamson 
2623e7a0444aSWeston Andros Adamson 		if (!found_esc)
262481c3f413SJ.Bruce Fields 			for (; *end && (*end != sep); end++)
2625e7a0444aSWeston Andros Adamson 				/* find sep or end of string */;
2626e7a0444aSWeston Andros Adamson 
262781c3f413SJ.Bruce Fields 		strlen = end - str;
262881c3f413SJ.Bruce Fields 		if (strlen) {
2629ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, strlen + 4);
2630ddd1ea56SJ. Bruce Fields 			if (!p)
263181c3f413SJ.Bruce Fields 				return nfserr_resource;
26320c0c267bSJ. Bruce Fields 			p = xdr_encode_opaque(p, str, strlen);
263381c3f413SJ.Bruce Fields 			count++;
263481c3f413SJ.Bruce Fields 		}
263581c3f413SJ.Bruce Fields 		else
263681c3f413SJ.Bruce Fields 			end++;
26375a64e569SBenjamin Coddington 		if (found_esc)
26385a64e569SBenjamin Coddington 			end = next;
26395a64e569SBenjamin Coddington 
264081c3f413SJ.Bruce Fields 		str = end;
264181c3f413SJ.Bruce Fields 	}
2642bf7491f1SBenjamin Coddington 	pathlen = htonl(count);
2643082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
264481c3f413SJ.Bruce Fields 	return 0;
264581c3f413SJ.Bruce Fields }
264681c3f413SJ.Bruce Fields 
2647e7a0444aSWeston Andros Adamson /* Encode as an array of strings the string given with components
2648e7a0444aSWeston Andros Adamson  * separated @sep.
2649e7a0444aSWeston Andros Adamson  */
nfsd4_encode_components(struct xdr_stream * xdr,char sep,char * components)2650ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
2651ddd1ea56SJ. Bruce Fields 				      char *components)
2652e7a0444aSWeston Andros Adamson {
2653ddd1ea56SJ. Bruce Fields 	return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
2654e7a0444aSWeston Andros Adamson }
2655e7a0444aSWeston Andros Adamson 
265681c3f413SJ.Bruce Fields /*
265781c3f413SJ.Bruce Fields  * encode a location element of a fs_locations structure
265881c3f413SJ.Bruce Fields  */
nfsd4_encode_fs_location4(struct xdr_stream * xdr,struct nfsd4_fs_location * location)2659ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
2660ddd1ea56SJ. Bruce Fields 					struct nfsd4_fs_location *location)
266181c3f413SJ.Bruce Fields {
2662b37ad28bSAl Viro 	__be32 status;
266381c3f413SJ.Bruce Fields 
2664ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
2665e7a0444aSWeston Andros Adamson 						'[', ']');
266681c3f413SJ.Bruce Fields 	if (status)
266781c3f413SJ.Bruce Fields 		return status;
2668ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components(xdr, '/', location->path);
266981c3f413SJ.Bruce Fields 	if (status)
267081c3f413SJ.Bruce Fields 		return status;
267181c3f413SJ.Bruce Fields 	return 0;
267281c3f413SJ.Bruce Fields }
267381c3f413SJ.Bruce Fields 
267481c3f413SJ.Bruce Fields /*
2675ed748aacSTrond Myklebust  * Encode a path in RFC3530 'pathname4' format
267681c3f413SJ.Bruce Fields  */
nfsd4_encode_path(struct xdr_stream * xdr,const struct path * root,const struct path * path)2677ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_path(struct xdr_stream *xdr,
2678ddd1ea56SJ. Bruce Fields 				const struct path *root,
2679ddd1ea56SJ. Bruce Fields 				const struct path *path)
268081c3f413SJ.Bruce Fields {
2681301f0268SAl Viro 	struct path cur = *path;
2682ddd1ea56SJ. Bruce Fields 	__be32 *p;
2683ed748aacSTrond Myklebust 	struct dentry **components = NULL;
2684ed748aacSTrond Myklebust 	unsigned int ncomponents = 0;
2685ed748aacSTrond Myklebust 	__be32 err = nfserr_jukebox;
268681c3f413SJ.Bruce Fields 
2687ed748aacSTrond Myklebust 	dprintk("nfsd4_encode_components(");
268881c3f413SJ.Bruce Fields 
2689ed748aacSTrond Myklebust 	path_get(&cur);
2690ed748aacSTrond Myklebust 	/* First walk the path up to the nfsd root, and store the
2691ed748aacSTrond Myklebust 	 * dentries/path components in an array.
2692ed748aacSTrond Myklebust 	 */
2693ed748aacSTrond Myklebust 	for (;;) {
2694b77a4b2eSKinglong Mee 		if (path_equal(&cur, root))
2695ed748aacSTrond Myklebust 			break;
2696ed748aacSTrond Myklebust 		if (cur.dentry == cur.mnt->mnt_root) {
2697ed748aacSTrond Myklebust 			if (follow_up(&cur))
2698ed748aacSTrond Myklebust 				continue;
2699ed748aacSTrond Myklebust 			goto out_free;
270081c3f413SJ.Bruce Fields 		}
2701ed748aacSTrond Myklebust 		if ((ncomponents & 15) == 0) {
2702ed748aacSTrond Myklebust 			struct dentry **new;
2703ed748aacSTrond Myklebust 			new = krealloc(components,
2704ed748aacSTrond Myklebust 					sizeof(*new) * (ncomponents + 16),
2705ed748aacSTrond Myklebust 					GFP_KERNEL);
2706ed748aacSTrond Myklebust 			if (!new)
2707ed748aacSTrond Myklebust 				goto out_free;
2708ed748aacSTrond Myklebust 			components = new;
2709ed748aacSTrond Myklebust 		}
2710ed748aacSTrond Myklebust 		components[ncomponents++] = cur.dentry;
2711ed748aacSTrond Myklebust 		cur.dentry = dget_parent(cur.dentry);
2712ed748aacSTrond Myklebust 	}
2713ddd1ea56SJ. Bruce Fields 	err = nfserr_resource;
2714ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2715ddd1ea56SJ. Bruce Fields 	if (!p)
2716ed748aacSTrond Myklebust 		goto out_free;
2717c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(ncomponents);
2718ed748aacSTrond Myklebust 
2719ed748aacSTrond Myklebust 	while (ncomponents) {
2720ed748aacSTrond Myklebust 		struct dentry *dentry = components[ncomponents - 1];
2721301f0268SAl Viro 		unsigned int len;
2722ed748aacSTrond Myklebust 
2723301f0268SAl Viro 		spin_lock(&dentry->d_lock);
2724301f0268SAl Viro 		len = dentry->d_name.len;
2725ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, len + 4);
2726ddd1ea56SJ. Bruce Fields 		if (!p) {
2727301f0268SAl Viro 			spin_unlock(&dentry->d_lock);
2728ed748aacSTrond Myklebust 			goto out_free;
2729301f0268SAl Viro 		}
27300c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, dentry->d_name.name, len);
2731a455589fSAl Viro 		dprintk("/%pd", dentry);
2732301f0268SAl Viro 		spin_unlock(&dentry->d_lock);
2733ed748aacSTrond Myklebust 		dput(dentry);
2734ed748aacSTrond Myklebust 		ncomponents--;
2735ed748aacSTrond Myklebust 	}
2736ed748aacSTrond Myklebust 
2737ed748aacSTrond Myklebust 	err = 0;
2738ed748aacSTrond Myklebust out_free:
2739ed748aacSTrond Myklebust 	dprintk(")\n");
2740ed748aacSTrond Myklebust 	while (ncomponents)
2741ed748aacSTrond Myklebust 		dput(components[--ncomponents]);
2742ed748aacSTrond Myklebust 	kfree(components);
2743ed748aacSTrond Myklebust 	path_put(&cur);
2744ed748aacSTrond Myklebust 	return err;
2745ed748aacSTrond Myklebust }
2746ed748aacSTrond Myklebust 
nfsd4_encode_fsloc_fsroot(struct xdr_stream * xdr,struct svc_rqst * rqstp,const struct path * path)2747ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr,
2748ddd1ea56SJ. Bruce Fields 			struct svc_rqst *rqstp, const struct path *path)
2749ed748aacSTrond Myklebust {
2750ed748aacSTrond Myklebust 	struct svc_export *exp_ps;
2751ed748aacSTrond Myklebust 	__be32 res;
2752ed748aacSTrond Myklebust 
2753ed748aacSTrond Myklebust 	exp_ps = rqst_find_fsidzero_export(rqstp);
2754ed748aacSTrond Myklebust 	if (IS_ERR(exp_ps))
2755ed748aacSTrond Myklebust 		return nfserrno(PTR_ERR(exp_ps));
2756ddd1ea56SJ. Bruce Fields 	res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
2757ed748aacSTrond Myklebust 	exp_put(exp_ps);
2758ed748aacSTrond Myklebust 	return res;
275981c3f413SJ.Bruce Fields }
276081c3f413SJ.Bruce Fields 
276181c3f413SJ.Bruce Fields /*
276281c3f413SJ.Bruce Fields  *  encode a fs_locations structure
276381c3f413SJ.Bruce Fields  */
nfsd4_encode_fs_locations(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct svc_export * exp)2764ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr,
2765ddd1ea56SJ. Bruce Fields 			struct svc_rqst *rqstp, struct svc_export *exp)
276681c3f413SJ.Bruce Fields {
2767b37ad28bSAl Viro 	__be32 status;
2768cc45f017SAl Viro 	int i;
2769ddd1ea56SJ. Bruce Fields 	__be32 *p;
277081c3f413SJ.Bruce Fields 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
277181c3f413SJ.Bruce Fields 
2772ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
277381c3f413SJ.Bruce Fields 	if (status)
277481c3f413SJ.Bruce Fields 		return status;
2775ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2776ddd1ea56SJ. Bruce Fields 	if (!p)
277781c3f413SJ.Bruce Fields 		return nfserr_resource;
2778c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(fslocs->locations_count);
277981c3f413SJ.Bruce Fields 	for (i=0; i<fslocs->locations_count; i++) {
2780ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
278181c3f413SJ.Bruce Fields 		if (status)
278281c3f413SJ.Bruce Fields 			return status;
278381c3f413SJ.Bruce Fields 	}
278481c3f413SJ.Bruce Fields 	return 0;
278581c3f413SJ.Bruce Fields }
27861da177e4SLinus Torvalds 
nfs4_file_type(umode_t mode)27873d2544b1SJ. Bruce Fields static u32 nfs4_file_type(umode_t mode)
27883d2544b1SJ. Bruce Fields {
27893d2544b1SJ. Bruce Fields 	switch (mode & S_IFMT) {
27903d2544b1SJ. Bruce Fields 	case S_IFIFO:	return NF4FIFO;
27913d2544b1SJ. Bruce Fields 	case S_IFCHR:	return NF4CHR;
27923d2544b1SJ. Bruce Fields 	case S_IFDIR:	return NF4DIR;
27933d2544b1SJ. Bruce Fields 	case S_IFBLK:	return NF4BLK;
27943d2544b1SJ. Bruce Fields 	case S_IFLNK:	return NF4LNK;
27953d2544b1SJ. Bruce Fields 	case S_IFREG:	return NF4REG;
27963d2544b1SJ. Bruce Fields 	case S_IFSOCK:	return NF4SOCK;
27973d2544b1SJ. Bruce Fields 	default:	return NF4BAD;
279825fef48bSTom Rix 	}
27993d2544b1SJ. Bruce Fields }
28001da177e4SLinus Torvalds 
2801b37ad28bSAl Viro static inline __be32
nfsd4_encode_aclname(struct xdr_stream * xdr,struct svc_rqst * rqstp,struct nfs4_ace * ace)2802ddd1ea56SJ. Bruce Fields nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2803ddd1ea56SJ. Bruce Fields 		     struct nfs4_ace *ace)
28041da177e4SLinus Torvalds {
28053554116dSJ. Bruce Fields 	if (ace->whotype != NFS4_ACL_WHO_NAMED)
2806ddd1ea56SJ. Bruce Fields 		return nfs4_acl_write_who(xdr, ace->whotype);
28073554116dSJ. Bruce Fields 	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
2808ddd1ea56SJ. Bruce Fields 		return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
2809ab8e4aeeSEric W. Biederman 	else
2810ddd1ea56SJ. Bruce Fields 		return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
28111da177e4SLinus Torvalds }
28121da177e4SLinus Torvalds 
28136896f15aSKinglong Mee static inline __be32
nfsd4_encode_layout_types(struct xdr_stream * xdr,u32 layout_types)28148a4c3926SJeff Layton nfsd4_encode_layout_types(struct xdr_stream *xdr, u32 layout_types)
28156896f15aSKinglong Mee {
28166896f15aSKinglong Mee 	__be32		*p;
28178a4c3926SJeff Layton 	unsigned long	i = hweight_long(layout_types);
28186896f15aSKinglong Mee 
28198a4c3926SJeff Layton 	p = xdr_reserve_space(xdr, 4 + 4 * i);
28206896f15aSKinglong Mee 	if (!p)
28216896f15aSKinglong Mee 		return nfserr_resource;
28228a4c3926SJeff Layton 
28238a4c3926SJeff Layton 	*p++ = cpu_to_be32(i);
28248a4c3926SJeff Layton 
28258a4c3926SJeff Layton 	for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
28268a4c3926SJeff Layton 		if (layout_types & (1 << i))
28278a4c3926SJeff Layton 			*p++ = cpu_to_be32(i);
28286896f15aSKinglong Mee 
28296896f15aSKinglong Mee 	return 0;
28306896f15aSKinglong Mee }
28316896f15aSKinglong Mee 
283242ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
283342ca0993SJ.Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
283442ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
2835c2227a39SKinglong Mee #define WORD2_ABSENT_FS_ATTRS 0
283642ca0993SJ.Bruce Fields 
283718032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
283818032ca0SDavid Quigley static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)2839ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2840ddd1ea56SJ. Bruce Fields 			    void *context, int len)
284118032ca0SDavid Quigley {
2842ddd1ea56SJ. Bruce Fields 	__be32 *p;
284318032ca0SDavid Quigley 
2844ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
2845ddd1ea56SJ. Bruce Fields 	if (!p)
284618032ca0SDavid Quigley 		return nfserr_resource;
284718032ca0SDavid Quigley 
284818032ca0SDavid Quigley 	/*
284918032ca0SDavid Quigley 	 * For now we use a 0 here to indicate the null translation; in
285018032ca0SDavid Quigley 	 * the future we may place a call to translation code here.
285118032ca0SDavid Quigley 	 */
2852c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* lfs */
2853c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* pi */
285418032ca0SDavid Quigley 	p = xdr_encode_opaque(p, context, len);
285518032ca0SDavid Quigley 	return 0;
285618032ca0SDavid Quigley }
285718032ca0SDavid Quigley #else
285818032ca0SDavid Quigley static inline __be32
nfsd4_encode_security_label(struct xdr_stream * xdr,struct svc_rqst * rqstp,void * context,int len)2859ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2860ddd1ea56SJ. Bruce Fields 			    void *context, int len)
286118032ca0SDavid Quigley { return 0; }
286218032ca0SDavid Quigley #endif
286318032ca0SDavid Quigley 
fattr_handle_absent_fs(u32 * bmval0,u32 * bmval1,u32 * bmval2,u32 * rdattr_err)2864c2227a39SKinglong Mee static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
286542ca0993SJ.Bruce Fields {
286642ca0993SJ.Bruce Fields 	/* As per referral draft:  */
286742ca0993SJ.Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
286842ca0993SJ.Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
286942ca0993SJ.Bruce Fields 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
287042ca0993SJ.Bruce Fields 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
287142ca0993SJ.Bruce Fields 			*rdattr_err = NFSERR_MOVED;
287242ca0993SJ.Bruce Fields 		else
287342ca0993SJ.Bruce Fields 			return nfserr_moved;
287442ca0993SJ.Bruce Fields 	}
287542ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
287642ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
2877c2227a39SKinglong Mee 	*bmval2 &= WORD2_ABSENT_FS_ATTRS;
287842ca0993SJ.Bruce Fields 	return 0;
287942ca0993SJ.Bruce Fields }
28801da177e4SLinus Torvalds 
2881ae7095a7SJ. Bruce Fields 
nfsd4_get_mounted_on_ino(struct svc_export * exp,u64 * pino)28826106d911SJeff Layton static int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino)
2883ae7095a7SJ. Bruce Fields {
2884ae7095a7SJ. Bruce Fields 	struct path path = exp->ex_path;
28856106d911SJeff Layton 	struct kstat stat;
2886ae7095a7SJ. Bruce Fields 	int err;
2887ae7095a7SJ. Bruce Fields 
2888ae7095a7SJ. Bruce Fields 	path_get(&path);
2889ae7095a7SJ. Bruce Fields 	while (follow_up(&path)) {
2890ae7095a7SJ. Bruce Fields 		if (path.dentry != path.mnt->mnt_root)
2891ae7095a7SJ. Bruce Fields 			break;
2892ae7095a7SJ. Bruce Fields 	}
28936106d911SJeff Layton 	err = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT);
2894ae7095a7SJ. Bruce Fields 	path_put(&path);
28956106d911SJeff Layton 	if (!err)
28966106d911SJeff Layton 		*pino = stat.ino;
2897ae7095a7SJ. Bruce Fields 	return err;
2898ae7095a7SJ. Bruce Fields }
2899ae7095a7SJ. Bruce Fields 
290075976de6SKinglong Mee static __be32
nfsd4_encode_bitmap(struct xdr_stream * xdr,u32 bmval0,u32 bmval1,u32 bmval2)290175976de6SKinglong Mee nfsd4_encode_bitmap(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2)
290275976de6SKinglong Mee {
290375976de6SKinglong Mee 	__be32 *p;
290475976de6SKinglong Mee 
290575976de6SKinglong Mee 	if (bmval2) {
290675976de6SKinglong Mee 		p = xdr_reserve_space(xdr, 16);
290775976de6SKinglong Mee 		if (!p)
290875976de6SKinglong Mee 			goto out_resource;
290975976de6SKinglong Mee 		*p++ = cpu_to_be32(3);
291075976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
291175976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval1);
291275976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval2);
291375976de6SKinglong Mee 	} else if (bmval1) {
291475976de6SKinglong Mee 		p = xdr_reserve_space(xdr, 12);
291575976de6SKinglong Mee 		if (!p)
291675976de6SKinglong Mee 			goto out_resource;
291775976de6SKinglong Mee 		*p++ = cpu_to_be32(2);
291875976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
291975976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval1);
292075976de6SKinglong Mee 	} else {
292175976de6SKinglong Mee 		p = xdr_reserve_space(xdr, 8);
292275976de6SKinglong Mee 		if (!p)
292375976de6SKinglong Mee 			goto out_resource;
292475976de6SKinglong Mee 		*p++ = cpu_to_be32(1);
292575976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
292675976de6SKinglong Mee 	}
292775976de6SKinglong Mee 
292875976de6SKinglong Mee 	return 0;
292975976de6SKinglong Mee out_resource:
293075976de6SKinglong Mee 	return nfserr_resource;
293175976de6SKinglong Mee }
293275976de6SKinglong Mee 
29331da177e4SLinus Torvalds /*
29341da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
29351da177e4SLinus Torvalds  * ourselves.
29361da177e4SLinus Torvalds  */
2937da2ebce6SJeff Layton static __be32
nfsd4_encode_fattr(struct xdr_stream * xdr,struct svc_fh * fhp,struct svc_export * exp,struct dentry * dentry,u32 * bmval,struct svc_rqst * rqstp,int ignore_crossmnt)2938d5184658SJ. Bruce Fields nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
2939d5184658SJ. Bruce Fields 		struct svc_export *exp,
2940d5184658SJ. Bruce Fields 		struct dentry *dentry, u32 *bmval,
2941406a7ea9SFrank Filz 		struct svc_rqst *rqstp, int ignore_crossmnt)
29421da177e4SLinus Torvalds {
29431da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
29441da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
29457e705706SAndy Adamson 	u32 bmval2 = bmval[2];
29461da177e4SLinus Torvalds 	struct kstat stat;
2947d50e6136SJ. Bruce Fields 	struct svc_fh *tempfh = NULL;
29481da177e4SLinus Torvalds 	struct kstatfs statfs;
2949ab04de60SChuck Lever 	__be32 *p, *attrlen_p;
29501fcea5b2SJ. Bruce Fields 	int starting_len = xdr->buf->len;
2951082d4bd7SJ. Bruce Fields 	int attrlen_offset;
29521da177e4SLinus Torvalds 	u32 dummy;
29531da177e4SLinus Torvalds 	u64 dummy64;
295442ca0993SJ.Bruce Fields 	u32 rdattr_err = 0;
2955b37ad28bSAl Viro 	__be32 status;
2956b8dd7b9aSAl Viro 	int err;
29571da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
29580ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
295918032ca0SDavid Quigley 	void *context = NULL;
296018032ca0SDavid Quigley 	int contextlen;
29610ab88ca4SArnd Bergmann #endif
296218032ca0SDavid Quigley 	bool contextsupport = false;
29637e705706SAndy Adamson 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
29647e705706SAndy Adamson 	u32 minorversion = resp->cstate.minorversion;
2965ebabe9a9SChristoph Hellwig 	struct path path = {
2966ebabe9a9SChristoph Hellwig 		.mnt	= exp->ex_path.mnt,
2967ebabe9a9SChristoph Hellwig 		.dentry	= dentry,
2968ebabe9a9SChristoph Hellwig 	};
29693d733711SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
29701da177e4SLinus Torvalds 
29711da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
2972916d2d84SJ. Bruce Fields 	BUG_ON(!nfsd_attrs_supported(minorversion, bmval));
29731da177e4SLinus Torvalds 
297442ca0993SJ.Bruce Fields 	if (exp->ex_fslocs.migrated) {
2975c2227a39SKinglong Mee 		status = fattr_handle_absent_fs(&bmval0, &bmval1, &bmval2, &rdattr_err);
297642ca0993SJ.Bruce Fields 		if (status)
297742ca0993SJ.Bruce Fields 			goto out;
297842ca0993SJ.Bruce Fields 	}
2979fd19ca36SDai Ngo 	if (bmval0 & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
2980fd19ca36SDai Ngo 		status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry));
2981fd19ca36SDai Ngo 		if (status)
2982fd19ca36SDai Ngo 			goto out;
2983fd19ca36SDai Ngo 	}
298442ca0993SJ.Bruce Fields 
2985638e3e7dSJeff Layton 	err = vfs_getattr(&path, &stat,
2986638e3e7dSJeff Layton 			  STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
2987638e3e7dSJeff Layton 			  AT_STATX_SYNC_AS_STAT);
2988b8dd7b9aSAl Viro 	if (err)
29891da177e4SLinus Torvalds 		goto out_nfserr;
2990e377a3e6SOndrej Valousek 	if (!(stat.result_mask & STATX_BTIME))
2991e377a3e6SOndrej Valousek 		/* underlying FS does not offer btime so we can't share it */
2992e377a3e6SOndrej Valousek 		bmval1 &= ~FATTR4_WORD1_TIME_CREATE;
299312337901SChristoph Hellwig 	if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
299412337901SChristoph Hellwig 			FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
29951da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
29961da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
2997ebabe9a9SChristoph Hellwig 		err = vfs_statfs(&path, &statfs);
2998b8dd7b9aSAl Viro 		if (err)
29991da177e4SLinus Torvalds 			goto out_nfserr;
30001da177e4SLinus Torvalds 	}
30011da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
3002d50e6136SJ. Bruce Fields 		tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
3003d50e6136SJ. Bruce Fields 		status = nfserr_jukebox;
3004d50e6136SJ. Bruce Fields 		if (!tempfh)
3005d50e6136SJ. Bruce Fields 			goto out;
3006d50e6136SJ. Bruce Fields 		fh_init(tempfh, NFS4_FHSIZE);
3007d50e6136SJ. Bruce Fields 		status = fh_compose(tempfh, exp, dentry, NULL);
30081da177e4SLinus Torvalds 		if (status)
30091da177e4SLinus Torvalds 			goto out;
3010d50e6136SJ. Bruce Fields 		fhp = tempfh;
30111da177e4SLinus Torvalds 	}
30121da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
30130c9d65e7SAndreas Gruenbacher 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
3014b8dd7b9aSAl Viro 		if (err == -EOPNOTSUPP)
30151da177e4SLinus Torvalds 			bmval0 &= ~FATTR4_WORD0_ACL;
3016b8dd7b9aSAl Viro 		else if (err == -EINVAL) {
30171da177e4SLinus Torvalds 			status = nfserr_attrnotsupp;
30181da177e4SLinus Torvalds 			goto out;
3019b8dd7b9aSAl Viro 		} else if (err != 0)
30201da177e4SLinus Torvalds 			goto out_nfserr;
30211da177e4SLinus Torvalds 	}
30222b44f1baSBenny Halevy 
302318032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
3024c2227a39SKinglong Mee 	if ((bmval2 & FATTR4_WORD2_SECURITY_LABEL) ||
3025c2227a39SKinglong Mee 	     bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
302632ddd944SJ. Bruce Fields 		if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
30272b0143b5SDavid Howells 			err = security_inode_getsecctx(d_inode(dentry),
302818032ca0SDavid Quigley 						&context, &contextlen);
302932ddd944SJ. Bruce Fields 		else
303032ddd944SJ. Bruce Fields 			err = -EOPNOTSUPP;
303118032ca0SDavid Quigley 		contextsupport = (err == 0);
303218032ca0SDavid Quigley 		if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
303318032ca0SDavid Quigley 			if (err == -EOPNOTSUPP)
303418032ca0SDavid Quigley 				bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
303518032ca0SDavid Quigley 			else if (err)
303618032ca0SDavid Quigley 				goto out_nfserr;
303718032ca0SDavid Quigley 		}
303818032ca0SDavid Quigley 	}
303918032ca0SDavid Quigley #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
304018032ca0SDavid Quigley 
304175976de6SKinglong Mee 	status = nfsd4_encode_bitmap(xdr, bmval0, bmval1, bmval2);
304275976de6SKinglong Mee 	if (status)
304375976de6SKinglong Mee 		goto out;
3044082d4bd7SJ. Bruce Fields 
3045082d4bd7SJ. Bruce Fields 	attrlen_offset = xdr->buf->len;
3046ab04de60SChuck Lever 	attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
3047ab04de60SChuck Lever 	if (!attrlen_p)
3048ddd1ea56SJ. Bruce Fields 		goto out_resource;
30491da177e4SLinus Torvalds 
30501da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
3051dcd20869SJ. Bruce Fields 		u32 supp[3];
3052dcd20869SJ. Bruce Fields 
3053dcd20869SJ. Bruce Fields 		memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
30547e705706SAndy Adamson 
30550c9d65e7SAndreas Gruenbacher 		if (!IS_POSIXACL(dentry->d_inode))
3056916d2d84SJ. Bruce Fields 			supp[0] &= ~FATTR4_WORD0_ACL;
305718032ca0SDavid Quigley 		if (!contextsupport)
3058916d2d84SJ. Bruce Fields 			supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
3059916d2d84SJ. Bruce Fields 		if (!supp[2]) {
3060ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 12);
3061ddd1ea56SJ. Bruce Fields 			if (!p)
30622b44f1baSBenny Halevy 				goto out_resource;
3063c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(2);
3064916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[0]);
3065916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[1]);
30667e705706SAndy Adamson 		} else {
3067ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 16);
3068ddd1ea56SJ. Bruce Fields 			if (!p)
30692b44f1baSBenny Halevy 				goto out_resource;
3070c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(3);
3071916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[0]);
3072916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[1]);
3073916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[2]);
30747e705706SAndy Adamson 		}
30751da177e4SLinus Torvalds 	}
30761da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
3077ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3078ddd1ea56SJ. Bruce Fields 		if (!p)
30791da177e4SLinus Torvalds 			goto out_resource;
30803d2544b1SJ. Bruce Fields 		dummy = nfs4_file_type(stat.mode);
30816b6d8137SJ. Bruce Fields 		if (dummy == NF4BAD) {
30826b6d8137SJ. Bruce Fields 			status = nfserr_serverfault;
30836b6d8137SJ. Bruce Fields 			goto out;
30846b6d8137SJ. Bruce Fields 		}
3085c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(dummy);
30861da177e4SLinus Torvalds 	}
30871da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
3088ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3089ddd1ea56SJ. Bruce Fields 		if (!p)
30901da177e4SLinus Torvalds 			goto out_resource;
309149640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
3092c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_FH_PERSISTENT);
309349640001SNeilBrown 		else
3094c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_FH_PERSISTENT|
3095c373b0a4SJ. Bruce Fields 						NFS4_FH_VOL_RENAME);
30961da177e4SLinus Torvalds 	}
30971da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
3098ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3099ddd1ea56SJ. Bruce Fields 		if (!p)
31001da177e4SLinus Torvalds 			goto out_resource;
3101b8800921SNeilBrown 		p = encode_change(p, &stat, d_inode(dentry), exp);
31021da177e4SLinus Torvalds 	}
31031da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
3104ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3105ddd1ea56SJ. Bruce Fields 		if (!p)
31061da177e4SLinus Torvalds 			goto out_resource;
3107b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.size);
31081da177e4SLinus Torvalds 	}
31091da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
3110ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3111ddd1ea56SJ. Bruce Fields 		if (!p)
31121da177e4SLinus Torvalds 			goto out_resource;
3113c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
31141da177e4SLinus Torvalds 	}
31151da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
3116ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3117ddd1ea56SJ. Bruce Fields 		if (!p)
31181da177e4SLinus Torvalds 			goto out_resource;
3119c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
31201da177e4SLinus Torvalds 	}
31211da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
3122ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3123ddd1ea56SJ. Bruce Fields 		if (!p)
31241da177e4SLinus Torvalds 			goto out_resource;
3125c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
31261da177e4SLinus Torvalds 	}
31271da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
3128ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
3129ddd1ea56SJ. Bruce Fields 		if (!p)
31301da177e4SLinus Torvalds 			goto out_resource;
313142ca0993SJ.Bruce Fields 		if (exp->ex_fslocs.migrated) {
3132b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
3133b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
3134af6a4e28SNeilBrown 		} else switch(fsid_source(fhp)) {
3135af6a4e28SNeilBrown 		case FSIDSOURCE_FSID:
3136b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
3137b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, (u64)0);
3138af6a4e28SNeilBrown 			break;
3139af6a4e28SNeilBrown 		case FSIDSOURCE_DEV:
3140c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
3141c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(MAJOR(stat.dev));
3142c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
3143c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(MINOR(stat.dev));
3144af6a4e28SNeilBrown 			break;
3145af6a4e28SNeilBrown 		case FSIDSOURCE_UUID:
314694eb3689SKinglong Mee 			p = xdr_encode_opaque_fixed(p, exp->ex_uuid,
314794eb3689SKinglong Mee 								EX_UUID_LEN);
3148af6a4e28SNeilBrown 			break;
31491da177e4SLinus Torvalds 		}
31501da177e4SLinus Torvalds 	}
31511da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
3152ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3153ddd1ea56SJ. Bruce Fields 		if (!p)
31541da177e4SLinus Torvalds 			goto out_resource;
3155c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
31561da177e4SLinus Torvalds 	}
31571da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
3158ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3159ddd1ea56SJ. Bruce Fields 		if (!p)
31601da177e4SLinus Torvalds 			goto out_resource;
3161c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(nn->nfsd4_lease);
31621da177e4SLinus Torvalds 	}
31631da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
3164ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3165ddd1ea56SJ. Bruce Fields 		if (!p)
31661da177e4SLinus Torvalds 			goto out_resource;
3167c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(rdattr_err);
31681da177e4SLinus Torvalds 	}
31691da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
31701da177e4SLinus Torvalds 		struct nfs4_ace *ace;
31711da177e4SLinus Torvalds 
31721da177e4SLinus Torvalds 		if (acl == NULL) {
3173ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
3174ddd1ea56SJ. Bruce Fields 			if (!p)
31751da177e4SLinus Torvalds 				goto out_resource;
31761da177e4SLinus Torvalds 
3177c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
31781da177e4SLinus Torvalds 			goto out_acl;
31791da177e4SLinus Torvalds 		}
3180ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3181ddd1ea56SJ. Bruce Fields 		if (!p)
31821da177e4SLinus Torvalds 			goto out_resource;
3183c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(acl->naces);
31841da177e4SLinus Torvalds 
318528e05dd8SJ. Bruce Fields 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
3186ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4*3);
3187ddd1ea56SJ. Bruce Fields 			if (!p)
31881da177e4SLinus Torvalds 				goto out_resource;
3189c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->type);
3190c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->flag);
3191c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->access_mask &
3192c373b0a4SJ. Bruce Fields 							NFS4_ACE_MASK_ALL);
3193ddd1ea56SJ. Bruce Fields 			status = nfsd4_encode_aclname(xdr, rqstp, ace);
31941da177e4SLinus Torvalds 			if (status)
31951da177e4SLinus Torvalds 				goto out;
31961da177e4SLinus Torvalds 		}
31971da177e4SLinus Torvalds 	}
31981da177e4SLinus Torvalds out_acl:
31991da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
3200ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3201ddd1ea56SJ. Bruce Fields 		if (!p)
32021da177e4SLinus Torvalds 			goto out_resource;
32030c9d65e7SAndreas Gruenbacher 		*p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ?
32041da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
32051da177e4SLinus Torvalds 	}
32061da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
3207ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3208ddd1ea56SJ. Bruce Fields 		if (!p)
32091da177e4SLinus Torvalds 			goto out_resource;
3210c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
32111da177e4SLinus Torvalds 	}
32121da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
3213ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3214ddd1ea56SJ. Bruce Fields 		if (!p)
32151da177e4SLinus Torvalds 			goto out_resource;
3216c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
32171da177e4SLinus Torvalds 	}
32181da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
3219ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3220ddd1ea56SJ. Bruce Fields 		if (!p)
32211da177e4SLinus Torvalds 			goto out_resource;
3222c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
32231da177e4SLinus Torvalds 	}
32241da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
3225ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3226ddd1ea56SJ. Bruce Fields 		if (!p)
32271da177e4SLinus Torvalds 			goto out_resource;
3228c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
32291da177e4SLinus Torvalds 	}
32301da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
3231ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4);
3232ddd1ea56SJ. Bruce Fields 		if (!p)
32331da177e4SLinus Torvalds 			goto out_resource;
3234d8b26071SNeilBrown 		p = xdr_encode_opaque(p, &fhp->fh_handle.fh_raw,
32350c0c267bSJ. Bruce Fields 					fhp->fh_handle.fh_size);
32361da177e4SLinus Torvalds 	}
32371da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
3238ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3239ddd1ea56SJ. Bruce Fields 		if (!p)
32401da177e4SLinus Torvalds 			goto out_resource;
3241b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.ino);
32421da177e4SLinus Torvalds 	}
32431da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
3244ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3245ddd1ea56SJ. Bruce Fields 		if (!p)
32461da177e4SLinus Torvalds 			goto out_resource;
3247b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
32481da177e4SLinus Torvalds 	}
32491da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
3250ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3251ddd1ea56SJ. Bruce Fields 		if (!p)
32521da177e4SLinus Torvalds 			goto out_resource;
3253b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
32541da177e4SLinus Torvalds 	}
32551da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
3256ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3257ddd1ea56SJ. Bruce Fields 		if (!p)
32581da177e4SLinus Torvalds 			goto out_resource;
3259b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_files);
32601da177e4SLinus Torvalds 	}
326181c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
3262ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
326381c3f413SJ.Bruce Fields 		if (status)
326481c3f413SJ.Bruce Fields 			goto out;
326581c3f413SJ.Bruce Fields 	}
32661da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
3267ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3268ddd1ea56SJ. Bruce Fields 		if (!p)
32691da177e4SLinus Torvalds 			goto out_resource;
3270c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
32711da177e4SLinus Torvalds 	}
32721da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
3273ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3274ddd1ea56SJ. Bruce Fields 		if (!p)
32751da177e4SLinus Torvalds 			goto out_resource;
3276b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes);
32771da177e4SLinus Torvalds 	}
32781da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
3279ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3280ddd1ea56SJ. Bruce Fields 		if (!p)
32811da177e4SLinus Torvalds 			goto out_resource;
3282c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(255);
32831da177e4SLinus Torvalds 	}
32841da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
3285ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3286ddd1ea56SJ. Bruce Fields 		if (!p)
32871da177e4SLinus Torvalds 			goto out_resource;
3288c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(statfs.f_namelen);
32891da177e4SLinus Torvalds 	}
32901da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
3291ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3292ddd1ea56SJ. Bruce Fields 		if (!p)
32931da177e4SLinus Torvalds 			goto out_resource;
3294b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
32951da177e4SLinus Torvalds 	}
32961da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
3297ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3298ddd1ea56SJ. Bruce Fields 		if (!p)
32991da177e4SLinus Torvalds 			goto out_resource;
3300b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
33011da177e4SLinus Torvalds 	}
33021da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
3303ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3304ddd1ea56SJ. Bruce Fields 		if (!p)
33051da177e4SLinus Torvalds 			goto out_resource;
3306c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.mode & S_IALLUGO);
33071da177e4SLinus Torvalds 	}
33081da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
3309ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3310ddd1ea56SJ. Bruce Fields 		if (!p)
33111da177e4SLinus Torvalds 			goto out_resource;
3312c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
33131da177e4SLinus Torvalds 	}
33141da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
3315ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3316ddd1ea56SJ. Bruce Fields 		if (!p)
33171da177e4SLinus Torvalds 			goto out_resource;
3318c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.nlink);
33191da177e4SLinus Torvalds 	}
33201da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
3321ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_user(xdr, rqstp, stat.uid);
33221da177e4SLinus Torvalds 		if (status)
33231da177e4SLinus Torvalds 			goto out;
33241da177e4SLinus Torvalds 	}
33251da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
3326ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_group(xdr, rqstp, stat.gid);
33271da177e4SLinus Torvalds 		if (status)
33281da177e4SLinus Torvalds 			goto out;
33291da177e4SLinus Torvalds 	}
33301da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
3331ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3332ddd1ea56SJ. Bruce Fields 		if (!p)
33331da177e4SLinus Torvalds 			goto out_resource;
3334c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32((u32) MAJOR(stat.rdev));
3335c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32((u32) MINOR(stat.rdev));
33361da177e4SLinus Torvalds 	}
33371da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
3338ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3339ddd1ea56SJ. Bruce Fields 		if (!p)
33401da177e4SLinus Torvalds 			goto out_resource;
33411da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
3342b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
33431da177e4SLinus Torvalds 	}
33441da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
3345ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3346ddd1ea56SJ. Bruce Fields 		if (!p)
33471da177e4SLinus Torvalds 			goto out_resource;
33481da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
3349b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
33501da177e4SLinus Torvalds 	}
33511da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
3352ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3353ddd1ea56SJ. Bruce Fields 		if (!p)
33541da177e4SLinus Torvalds 			goto out_resource;
33551da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
3356b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
33571da177e4SLinus Torvalds 	}
33581da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
3359ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3360ddd1ea56SJ. Bruce Fields 		if (!p)
33611da177e4SLinus Torvalds 			goto out_resource;
33621da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
3363b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
33641da177e4SLinus Torvalds 	}
33651da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
336626217679SChuck Lever 		status = nfsd4_encode_nfstime4(xdr, &stat.atime);
336726217679SChuck Lever 		if (status)
336826217679SChuck Lever 			goto out;
33691da177e4SLinus Torvalds 	}
3370d7dbed45STavian Barnes 	if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
3371d7dbed45STavian Barnes 		status = nfsd4_encode_nfstime4(xdr, &stat.btime);
3372d7dbed45STavian Barnes 		if (status)
3373d7dbed45STavian Barnes 			goto out;
3374d7dbed45STavian Barnes 	}
33751da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
3376ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
3377ddd1ea56SJ. Bruce Fields 		if (!p)
33781da177e4SLinus Torvalds 			goto out_resource;
337916945141SJ. Bruce Fields 		p = encode_time_delta(p, d_inode(dentry));
33801da177e4SLinus Torvalds 	}
33811da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
338226217679SChuck Lever 		status = nfsd4_encode_nfstime4(xdr, &stat.ctime);
338326217679SChuck Lever 		if (status)
338426217679SChuck Lever 			goto out;
33851da177e4SLinus Torvalds 	}
33861da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
338726217679SChuck Lever 		status = nfsd4_encode_nfstime4(xdr, &stat.mtime);
338826217679SChuck Lever 		if (status)
338926217679SChuck Lever 			goto out;
33901da177e4SLinus Torvalds 	}
33911da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
33920a2050d7SKinglong Mee 		u64 ino = stat.ino;
33930a2050d7SKinglong Mee 
3394ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3395ddd1ea56SJ. Bruce Fields 		if (!p)
33961da177e4SLinus Torvalds                 	goto out_resource;
3397406a7ea9SFrank Filz 		/*
33986106d911SJeff Layton 		 * Get ino of mountpoint in parent filesystem, if not ignoring
33996106d911SJeff Layton 		 * crossmount and this is the root of a cross-mounted
34006106d911SJeff Layton 		 * filesystem.
3401406a7ea9SFrank Filz 		 */
3402406a7ea9SFrank Filz 		if (ignore_crossmnt == 0 &&
34030a2050d7SKinglong Mee 		    dentry == exp->ex_path.mnt->mnt_root) {
34046106d911SJeff Layton 			err = nfsd4_get_mounted_on_ino(exp, &ino);
34050a2050d7SKinglong Mee 			if (err)
34060a2050d7SKinglong Mee 				goto out_nfserr;
34070a2050d7SKinglong Mee 		}
34080a2050d7SKinglong Mee 		p = xdr_encode_hyper(p, ino);
34091da177e4SLinus Torvalds 	}
34109cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
34116896f15aSKinglong Mee 	if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) {
34128a4c3926SJeff Layton 		status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
34136896f15aSKinglong Mee 		if (status)
34146896f15aSKinglong Mee 			goto out;
34159cf514ccSChristoph Hellwig 	}
34166896f15aSKinglong Mee 
34176896f15aSKinglong Mee 	if (bmval2 & FATTR4_WORD2_LAYOUT_TYPES) {
34188a4c3926SJeff Layton 		status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
34196896f15aSKinglong Mee 		if (status)
34206896f15aSKinglong Mee 			goto out;
34219cf514ccSChristoph Hellwig 	}
34229cf514ccSChristoph Hellwig 
34239cf514ccSChristoph Hellwig 	if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) {
34249cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4);
34259cf514ccSChristoph Hellwig 		if (!p)
34269cf514ccSChristoph Hellwig 			goto out_resource;
34279cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(stat.blksize);
34289cf514ccSChristoph Hellwig 	}
34299cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
34308c18f205SBenny Halevy 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
3431b26b78cbSTrond Myklebust 		u32 supp[3];
3432b26b78cbSTrond Myklebust 
3433b26b78cbSTrond Myklebust 		memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
3434b26b78cbSTrond Myklebust 		supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0;
3435b26b78cbSTrond Myklebust 		supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1;
3436b26b78cbSTrond Myklebust 		supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2;
3437b26b78cbSTrond Myklebust 
3438b26b78cbSTrond Myklebust 		status = nfsd4_encode_bitmap(xdr, supp[0], supp[1], supp[2]);
343975976de6SKinglong Mee 		if (status)
344075976de6SKinglong Mee 			goto out;
34418c18f205SBenny Halevy 	}
34427e705706SAndy Adamson 
34430ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
34447d580722SKinglong Mee 	if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
34457d580722SKinglong Mee 		status = nfsd4_encode_security_label(xdr, rqstp, context,
34467d580722SKinglong Mee 								contextlen);
34477d580722SKinglong Mee 		if (status)
34487d580722SKinglong Mee 			goto out;
34497d580722SKinglong Mee 	}
34500ab88ca4SArnd Bergmann #endif
34517d580722SKinglong Mee 
34520e885e84SFrank van der Linden 	if (bmval2 & FATTR4_WORD2_XATTR_SUPPORT) {
34530e885e84SFrank van der Linden 		p = xdr_reserve_space(xdr, 4);
34540e885e84SFrank van der Linden 		if (!p)
34550e885e84SFrank van der Linden 			goto out_resource;
3456831be973SChristian Brauner 		err = xattr_supports_user_prefix(d_inode(dentry));
34570e885e84SFrank van der Linden 		*p++ = cpu_to_be32(err == 0);
34580e885e84SFrank van der Linden 	}
34590e885e84SFrank van der Linden 
3460ab04de60SChuck Lever 	*attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
34611da177e4SLinus Torvalds 	status = nfs_ok;
34621da177e4SLinus Torvalds 
34631da177e4SLinus Torvalds out:
3464ba4e55bbSJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
346518032ca0SDavid Quigley 	if (context)
346618032ca0SDavid Quigley 		security_release_secctx(context, contextlen);
3467ba4e55bbSJ. Bruce Fields #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
346828e05dd8SJ. Bruce Fields 	kfree(acl);
346918df11d0SYan, Zheng 	if (tempfh) {
3470d50e6136SJ. Bruce Fields 		fh_put(tempfh);
347118df11d0SYan, Zheng 		kfree(tempfh);
347218df11d0SYan, Zheng 	}
34731fcea5b2SJ. Bruce Fields 	if (status)
34741fcea5b2SJ. Bruce Fields 		xdr_truncate_encode(xdr, starting_len);
34751da177e4SLinus Torvalds 	return status;
34761da177e4SLinus Torvalds out_nfserr:
3477b8dd7b9aSAl Viro 	status = nfserrno(err);
34781da177e4SLinus Torvalds 	goto out;
34791da177e4SLinus Torvalds out_resource:
34801da177e4SLinus Torvalds 	status = nfserr_resource;
34811da177e4SLinus Torvalds 	goto out;
34821da177e4SLinus Torvalds }
34831da177e4SLinus Torvalds 
svcxdr_init_encode_from_buffer(struct xdr_stream * xdr,struct xdr_buf * buf,__be32 * p,int bytes)34842825a7f9SJ. Bruce Fields static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
34852825a7f9SJ. Bruce Fields 				struct xdr_buf *buf, __be32 *p, int bytes)
34862825a7f9SJ. Bruce Fields {
34872825a7f9SJ. Bruce Fields 	xdr->scratch.iov_len = 0;
34882825a7f9SJ. Bruce Fields 	memset(buf, 0, sizeof(struct xdr_buf));
34892825a7f9SJ. Bruce Fields 	buf->head[0].iov_base = p;
34902825a7f9SJ. Bruce Fields 	buf->head[0].iov_len = 0;
34912825a7f9SJ. Bruce Fields 	buf->len = 0;
34922825a7f9SJ. Bruce Fields 	xdr->buf = buf;
34932825a7f9SJ. Bruce Fields 	xdr->iov = buf->head;
34942825a7f9SJ. Bruce Fields 	xdr->p = p;
34952825a7f9SJ. Bruce Fields 	xdr->end = (void *)p + bytes;
34962825a7f9SJ. Bruce Fields 	buf->buflen = bytes;
34972825a7f9SJ. Bruce Fields }
34982825a7f9SJ. 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)3499d5184658SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
3500d5184658SJ. Bruce Fields 			struct svc_fh *fhp, struct svc_export *exp,
3501d5184658SJ. Bruce Fields 			struct dentry *dentry, u32 *bmval,
3502d5184658SJ. Bruce Fields 			struct svc_rqst *rqstp, int ignore_crossmnt)
3503d5184658SJ. Bruce Fields {
35042825a7f9SJ. Bruce Fields 	struct xdr_buf dummy;
3505d5184658SJ. Bruce Fields 	struct xdr_stream xdr;
3506d5184658SJ. Bruce Fields 	__be32 ret;
3507d5184658SJ. Bruce Fields 
35082825a7f9SJ. Bruce Fields 	svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
3509d5184658SJ. Bruce Fields 	ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
3510d5184658SJ. Bruce Fields 							ignore_crossmnt);
3511d5184658SJ. Bruce Fields 	*p = xdr.p;
3512d5184658SJ. Bruce Fields 	return ret;
3513d5184658SJ. Bruce Fields }
3514d5184658SJ. Bruce Fields 
attributes_need_mount(u32 * bmval)3515c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
3516c0ce6ec8SJ. Bruce Fields {
3517c0ce6ec8SJ. Bruce Fields 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
3518c0ce6ec8SJ. Bruce Fields 		return 1;
3519c0ce6ec8SJ. Bruce Fields 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
3520c0ce6ec8SJ. Bruce Fields 		return 1;
3521c0ce6ec8SJ. Bruce Fields 	return 0;
3522c0ce6ec8SJ. Bruce Fields }
3523c0ce6ec8SJ. Bruce Fields 
3524b37ad28bSAl Viro static __be32
nfsd4_encode_dirent_fattr(struct xdr_stream * xdr,struct nfsd4_readdir * cd,const char * name,int namlen)3525561f0ed4SJ. Bruce Fields nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
3526561f0ed4SJ. Bruce Fields 			const char *name, int namlen)
35271da177e4SLinus Torvalds {
35281da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
35291da177e4SLinus Torvalds 	struct dentry *dentry;
3530b37ad28bSAl Viro 	__be32 nfserr;
3531406a7ea9SFrank Filz 	int ignore_crossmnt = 0;
35321da177e4SLinus Torvalds 
35336c2d4798SAl Viro 	dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
35341da177e4SLinus Torvalds 	if (IS_ERR(dentry))
35351da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
35361da177e4SLinus Torvalds 
35371da177e4SLinus Torvalds 	exp_get(exp);
3538406a7ea9SFrank Filz 	/*
3539406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
3540406a7ea9SFrank Filz 	 * attributes that are only properties of the underlying filesystem
3541406a7ea9SFrank Filz 	 * as opposed to the cross-mounted file system. In such a case,
3542406a7ea9SFrank Filz 	 * we will not follow the cross mount and will fill the attribtutes
3543406a7ea9SFrank Filz 	 * directly from the mountpoint dentry.
3544406a7ea9SFrank Filz 	 */
35453227fa41SJ. Bruce Fields 	if (nfsd_mountpoint(dentry, exp)) {
3546021d3a72SJ.Bruce Fields 		int err;
3547021d3a72SJ.Bruce Fields 
35483227fa41SJ. Bruce Fields 		if (!(exp->ex_flags & NFSEXP_V4ROOT)
35493227fa41SJ. Bruce Fields 				&& !attributes_need_mount(cd->rd_bmval)) {
35503227fa41SJ. Bruce Fields 			ignore_crossmnt = 1;
35513227fa41SJ. Bruce Fields 			goto out_encode;
35523227fa41SJ. Bruce Fields 		}
3553dcb488a3SAndy Adamson 		/*
3554dcb488a3SAndy Adamson 		 * Why the heck aren't we just using nfsd_lookup??
3555dcb488a3SAndy Adamson 		 * Different "."/".." handling?  Something else?
3556dcb488a3SAndy Adamson 		 * At least, add a comment here to explain....
3557dcb488a3SAndy Adamson 		 */
3558021d3a72SJ.Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
3559021d3a72SJ.Bruce Fields 		if (err) {
3560021d3a72SJ.Bruce Fields 			nfserr = nfserrno(err);
35611da177e4SLinus Torvalds 			goto out_put;
35621da177e4SLinus Torvalds 		}
3563dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
3564dcb488a3SAndy Adamson 		if (nfserr)
3565dcb488a3SAndy Adamson 			goto out_put;
35661da177e4SLinus Torvalds 
35671da177e4SLinus Torvalds 	}
35683227fa41SJ. Bruce Fields out_encode:
3569561f0ed4SJ. Bruce Fields 	nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval,
3570406a7ea9SFrank Filz 					cd->rd_rqstp, ignore_crossmnt);
35711da177e4SLinus Torvalds out_put:
35721da177e4SLinus Torvalds 	dput(dentry);
35731da177e4SLinus Torvalds 	exp_put(exp);
35741da177e4SLinus Torvalds 	return nfserr;
35751da177e4SLinus Torvalds }
35761da177e4SLinus Torvalds 
35772ebbc012SAl Viro static __be32 *
nfsd4_encode_rdattr_error(struct xdr_stream * xdr,__be32 nfserr)3578561f0ed4SJ. Bruce Fields nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
35791da177e4SLinus Torvalds {
3580561f0ed4SJ. Bruce Fields 	__be32 *p;
3581561f0ed4SJ. Bruce Fields 
3582c3a45617SKinglong Mee 	p = xdr_reserve_space(xdr, 20);
3583561f0ed4SJ. Bruce Fields 	if (!p)
35841da177e4SLinus Torvalds 		return NULL;
35851da177e4SLinus Torvalds 	*p++ = htonl(2);
35861da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
35871da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
35881da177e4SLinus Torvalds 
358987915c64SJ. Bruce Fields 	*p++ = htonl(4);     /* attribute length */
35901da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
35911da177e4SLinus Torvalds 	return p;
35921da177e4SLinus Torvalds }
35931da177e4SLinus Torvalds 
35941da177e4SLinus Torvalds static int
nfsd4_encode_dirent(void * ccdv,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)3595a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
3596a0ad13efSNeilBrown 		    loff_t offset, u64 ino, unsigned int d_type)
35971da177e4SLinus Torvalds {
3598a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
35991da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
3600561f0ed4SJ. Bruce Fields 	struct xdr_stream *xdr = cd->xdr;
3601561f0ed4SJ. Bruce Fields 	int start_offset = xdr->buf->len;
3602561f0ed4SJ. Bruce Fields 	int cookie_offset;
3603aee37764SJ. Bruce Fields 	u32 name_and_cookie;
3604561f0ed4SJ. Bruce Fields 	int entry_bytes;
3605b37ad28bSAl Viro 	__be32 nfserr = nfserr_toosmall;
3606561f0ed4SJ. Bruce Fields 	__be64 wire_offset;
3607561f0ed4SJ. Bruce Fields 	__be32 *p;
36081da177e4SLinus Torvalds 
36091da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
36101da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
36111da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
36121da177e4SLinus Torvalds 		return 0;
36131da177e4SLinus Torvalds 	}
36141da177e4SLinus Torvalds 
3615561f0ed4SJ. Bruce Fields 	if (cd->cookie_offset) {
3616561f0ed4SJ. Bruce Fields 		wire_offset = cpu_to_be64(offset);
3617561f0ed4SJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset,
3618561f0ed4SJ. Bruce Fields 							&wire_offset, 8);
3619561f0ed4SJ. Bruce Fields 	}
36201da177e4SLinus Torvalds 
3621561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
3622561f0ed4SJ. Bruce Fields 	if (!p)
36231da177e4SLinus Torvalds 		goto fail;
36241da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
3625561f0ed4SJ. Bruce Fields 	cookie_offset = xdr->buf->len;
3626561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 3*4 + namlen);
3627561f0ed4SJ. Bruce Fields 	if (!p)
3628561f0ed4SJ. Bruce Fields 		goto fail;
3629c306d737SChuck Lever 	p = xdr_encode_hyper(p, OFFSET_MAX);        /* offset of next entry */
36301da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
36311da177e4SLinus Torvalds 
3632561f0ed4SJ. Bruce Fields 	nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
36331da177e4SLinus Torvalds 	switch (nfserr) {
36341da177e4SLinus Torvalds 	case nfs_ok:
36351da177e4SLinus Torvalds 		break;
36361da177e4SLinus Torvalds 	case nfserr_resource:
36371da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
36381da177e4SLinus Torvalds 		goto fail;
3639b2c0cea6SJ. Bruce Fields 	case nfserr_noent:
3640f41c5ad2SKinglong Mee 		xdr_truncate_encode(xdr, start_offset);
3641b2c0cea6SJ. Bruce Fields 		goto skip_entry;
3642cad85337SJeff Layton 	case nfserr_jukebox:
3643cad85337SJeff Layton 		/*
3644cad85337SJeff Layton 		 * The pseudoroot should only display dentries that lead to
3645cad85337SJeff Layton 		 * exports. If we get EJUKEBOX here, then we can't tell whether
3646cad85337SJeff Layton 		 * this entry should be included. Just fail the whole READDIR
3647cad85337SJeff Layton 		 * with NFS4ERR_DELAY in that case, and hope that the situation
3648cad85337SJeff Layton 		 * will resolve itself by the client's next attempt.
3649cad85337SJeff Layton 		 */
3650cad85337SJeff Layton 		if (cd->rd_fhp->fh_export->ex_flags & NFSEXP_V4ROOT)
3651cad85337SJeff Layton 			goto fail;
3652cad85337SJeff Layton 		fallthrough;
36531da177e4SLinus Torvalds 	default:
36541da177e4SLinus Torvalds 		/*
36551da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
36561da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
36571da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
36581da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
36591da177e4SLinus Torvalds 		 * entire READDIR operation(!)
36601da177e4SLinus Torvalds 		 */
36611da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
36621da177e4SLinus Torvalds 			goto fail;
3663561f0ed4SJ. Bruce Fields 		p = nfsd4_encode_rdattr_error(xdr, nfserr);
366434081efcSFred Isaman 		if (p == NULL) {
366534081efcSFred Isaman 			nfserr = nfserr_toosmall;
36661da177e4SLinus Torvalds 			goto fail;
36671da177e4SLinus Torvalds 		}
366834081efcSFred Isaman 	}
3669561f0ed4SJ. Bruce Fields 	nfserr = nfserr_toosmall;
3670561f0ed4SJ. Bruce Fields 	entry_bytes = xdr->buf->len - start_offset;
3671561f0ed4SJ. Bruce Fields 	if (entry_bytes > cd->rd_maxcount)
3672561f0ed4SJ. Bruce Fields 		goto fail;
3673561f0ed4SJ. Bruce Fields 	cd->rd_maxcount -= entry_bytes;
3674aee37764SJ. Bruce Fields 	/*
3675f2e717d6STrond Myklebust 	 * RFC 3530 14.2.24 describes rd_dircount as only a "hint", and
3676f2e717d6STrond Myklebust 	 * notes that it could be zero. If it is zero, then the server
3677f2e717d6STrond Myklebust 	 * should enforce only the rd_maxcount value.
3678aee37764SJ. Bruce Fields 	 */
3679f2e717d6STrond Myklebust 	if (cd->rd_dircount) {
36800ec016e3SJ. Bruce Fields 		name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8;
3681aee37764SJ. Bruce Fields 		if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
3682aee37764SJ. Bruce Fields 			goto fail;
3683aee37764SJ. Bruce Fields 		cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
3684f2e717d6STrond Myklebust 		if (!cd->rd_dircount)
3685f2e717d6STrond Myklebust 			cd->rd_maxcount = 0;
3686f2e717d6STrond Myklebust 	}
36870ec016e3SJ. Bruce Fields 
3688561f0ed4SJ. Bruce Fields 	cd->cookie_offset = cookie_offset;
3689b2c0cea6SJ. Bruce Fields skip_entry:
36901da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
36911da177e4SLinus Torvalds 	return 0;
36921da177e4SLinus Torvalds fail:
3693561f0ed4SJ. Bruce Fields 	xdr_truncate_encode(xdr, start_offset);
36941da177e4SLinus Torvalds 	cd->common.err = nfserr;
36951da177e4SLinus Torvalds 	return -EINVAL;
36961da177e4SLinus Torvalds }
36971da177e4SLinus Torvalds 
3698d0a381ddSJ. Bruce Fields static __be32
nfsd4_encode_verifier4(struct xdr_stream * xdr,const nfs4_verifier * verf)3699adaa7a50SChuck Lever nfsd4_encode_verifier4(struct xdr_stream *xdr, const nfs4_verifier *verf)
3700adaa7a50SChuck Lever {
3701adaa7a50SChuck Lever 	__be32 *p;
3702adaa7a50SChuck Lever 
3703adaa7a50SChuck Lever 	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
3704adaa7a50SChuck Lever 	if (!p)
3705adaa7a50SChuck Lever 		return nfserr_resource;
3706adaa7a50SChuck Lever 	memcpy(p, verf->data, sizeof(verf->data));
3707adaa7a50SChuck Lever 	return nfs_ok;
3708adaa7a50SChuck Lever }
3709adaa7a50SChuck Lever 
3710adaa7a50SChuck Lever static __be32
nfsd4_encode_clientid4(struct xdr_stream * xdr,const clientid_t * clientid)3711adaa7a50SChuck Lever nfsd4_encode_clientid4(struct xdr_stream *xdr, const clientid_t *clientid)
3712adaa7a50SChuck Lever {
3713adaa7a50SChuck Lever 	__be32 *p;
3714adaa7a50SChuck Lever 
3715adaa7a50SChuck Lever 	p = xdr_reserve_space(xdr, sizeof(__be64));
3716adaa7a50SChuck Lever 	if (!p)
3717adaa7a50SChuck Lever 		return nfserr_resource;
3718adaa7a50SChuck Lever 	memcpy(p, clientid, sizeof(*clientid));
3719adaa7a50SChuck Lever 	return nfs_ok;
3720adaa7a50SChuck Lever }
3721adaa7a50SChuck Lever 
3722adaa7a50SChuck Lever static __be32
nfsd4_encode_stateid(struct xdr_stream * xdr,stateid_t * sid)3723d0a381ddSJ. Bruce Fields nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
3724e2f282b9SBenny Halevy {
3725bc749ca4SJ. Bruce Fields 	__be32 *p;
3726e2f282b9SBenny Halevy 
3727d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, sizeof(stateid_t));
3728d0a381ddSJ. Bruce Fields 	if (!p)
3729d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3730c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sid->si_generation);
37310c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, &sid->si_opaque,
37320c0c267bSJ. Bruce Fields 					sizeof(stateid_opaque_t));
3733d0a381ddSJ. Bruce Fields 	return 0;
3734e2f282b9SBenny Halevy }
3735e2f282b9SBenny Halevy 
3736695e12f8SBenny Halevy static __be32
nfsd4_encode_access(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3737e78e274eSKees Cook nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr,
3738e78e274eSKees Cook 		    union nfsd4_op_u *u)
37391da177e4SLinus Torvalds {
3740e78e274eSKees Cook 	struct nfsd4_access *access = &u->access;
3741bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3742bc749ca4SJ. Bruce Fields 	__be32 *p;
37431da177e4SLinus Torvalds 
3744d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
3745d0a381ddSJ. Bruce Fields 	if (!p)
3746d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3747c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(access->ac_supported);
3748c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(access->ac_resp_access);
3749bac966d6SJ. Bruce Fields 	return 0;
37501da177e4SLinus Torvalds }
37511da177e4SLinus Torvalds 
nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3752e78e274eSKees Cook static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr,
3753e78e274eSKees Cook 						union nfsd4_op_u *u)
37541d1bc8f2SJ. Bruce Fields {
3755e78e274eSKees Cook 	struct nfsd4_bind_conn_to_session *bcts = &u->bind_conn_to_session;
3756bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
37571d1bc8f2SJ. Bruce Fields 	__be32 *p;
37581d1bc8f2SJ. Bruce Fields 
3759d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8);
3760d0a381ddSJ. Bruce Fields 	if (!p)
3761d0a381ddSJ. Bruce Fields 		return nfserr_resource;
37620c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, bcts->sessionid.data,
37630c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
3764c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(bcts->dir);
37654ce85c8cSChuck Lever 	/* Upshifting from TCP to RDMA is not supported */
3766c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);
3767bac966d6SJ. Bruce Fields 	return 0;
37681d1bc8f2SJ. Bruce Fields }
37691d1bc8f2SJ. Bruce Fields 
3770695e12f8SBenny Halevy static __be32
nfsd4_encode_close(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3771e78e274eSKees Cook nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr,
3772e78e274eSKees Cook 		   union nfsd4_op_u *u)
37731da177e4SLinus Torvalds {
3774e78e274eSKees Cook 	struct nfsd4_close *close = &u->close;
3775bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3776d0a381ddSJ. Bruce Fields 
3777bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &close->cl_stateid);
37781da177e4SLinus Torvalds }
37791da177e4SLinus Torvalds 
37801da177e4SLinus Torvalds 
3781695e12f8SBenny Halevy static __be32
nfsd4_encode_commit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3782e78e274eSKees Cook nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr,
3783e78e274eSKees Cook 		    union nfsd4_op_u *u)
37841da177e4SLinus Torvalds {
3785e78e274eSKees Cook 	struct nfsd4_commit *commit = &u->commit;
37861da177e4SLinus Torvalds 
3787adaa7a50SChuck Lever 	return nfsd4_encode_verifier4(resp->xdr, &commit->co_verf);
37881da177e4SLinus Torvalds }
37891da177e4SLinus Torvalds 
3790695e12f8SBenny Halevy static __be32
nfsd4_encode_create(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3791e78e274eSKees Cook nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr,
3792e78e274eSKees Cook 		    union nfsd4_op_u *u)
37931da177e4SLinus Torvalds {
3794e78e274eSKees Cook 	struct nfsd4_create *create = &u->create;
3795bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
37961da177e4SLinus Torvalds 
379766a21db7SChuck Lever 	nfserr = nfsd4_encode_change_info4(xdr, &create->cr_cinfo);
379866a21db7SChuck Lever 	if (nfserr)
379966a21db7SChuck Lever 		return nfserr;
3800b96811cdSTrond Myklebust 	return nfsd4_encode_bitmap(xdr, create->cr_bmval[0],
380175976de6SKinglong Mee 			create->cr_bmval[1], create->cr_bmval[2]);
38021da177e4SLinus Torvalds }
38031da177e4SLinus Torvalds 
3804b37ad28bSAl Viro static __be32
nfsd4_encode_getattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3805e78e274eSKees Cook nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr,
3806e78e274eSKees Cook 		     union nfsd4_op_u *u)
38071da177e4SLinus Torvalds {
3808e78e274eSKees Cook 	struct nfsd4_getattr *getattr = &u->getattr;
38091da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
3810bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
38111da177e4SLinus Torvalds 
3812bac966d6SJ. Bruce Fields 	return nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry,
3813bac966d6SJ. Bruce Fields 				    getattr->ga_bmval, resp->rqstp, 0);
38141da177e4SLinus Torvalds }
38151da177e4SLinus Torvalds 
3816695e12f8SBenny Halevy static __be32
nfsd4_encode_getfh(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3817e78e274eSKees Cook nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr,
3818e78e274eSKees Cook 		   union nfsd4_op_u *u)
38191da177e4SLinus Torvalds {
3820e78e274eSKees Cook 	struct svc_fh **fhpp = &u->getfh;
3821bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3822695e12f8SBenny Halevy 	struct svc_fh *fhp = *fhpp;
38231da177e4SLinus Torvalds 	unsigned int len;
3824bc749ca4SJ. Bruce Fields 	__be32 *p;
38251da177e4SLinus Torvalds 
38261da177e4SLinus Torvalds 	len = fhp->fh_handle.fh_size;
3827d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, len + 4);
3828d0a381ddSJ. Bruce Fields 	if (!p)
3829d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3830d8b26071SNeilBrown 	p = xdr_encode_opaque(p, &fhp->fh_handle.fh_raw, len);
3831bac966d6SJ. Bruce Fields 	return 0;
38321da177e4SLinus Torvalds }
38331da177e4SLinus Torvalds 
38341da177e4SLinus Torvalds /*
38351da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
38361da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
38371da177e4SLinus Torvalds */
3838d0a381ddSJ. Bruce Fields static __be32
nfsd4_encode_lock_denied(struct xdr_stream * xdr,struct nfsd4_lock_denied * ld)3839d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
38401da177e4SLinus Torvalds {
38417c13f344SJ. Bruce Fields 	struct xdr_netobj *conf = &ld->ld_owner;
3842bc749ca4SJ. Bruce Fields 	__be32 *p;
38431da177e4SLinus Torvalds 
38448c7424cfSJ. Bruce Fields again:
3845d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
38468c7424cfSJ. Bruce Fields 	if (!p) {
38478c7424cfSJ. Bruce Fields 		/*
38488c7424cfSJ. Bruce Fields 		 * Don't fail to return the result just because we can't
38498c7424cfSJ. Bruce Fields 		 * return the conflicting open:
38508c7424cfSJ. Bruce Fields 		 */
38518c7424cfSJ. Bruce Fields 		if (conf->len) {
3852f98bac5aSKinglong Mee 			kfree(conf->data);
38538c7424cfSJ. Bruce Fields 			conf->len = 0;
38548c7424cfSJ. Bruce Fields 			conf->data = NULL;
38558c7424cfSJ. Bruce Fields 			goto again;
38568c7424cfSJ. Bruce Fields 		}
3857d0a381ddSJ. Bruce Fields 		return nfserr_resource;
38588c7424cfSJ. Bruce Fields 	}
3859b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, ld->ld_start);
3860b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, ld->ld_length);
3861c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(ld->ld_type);
38627c13f344SJ. Bruce Fields 	if (conf->len) {
38630c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8);
38640c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, conf->data, conf->len);
3865f98bac5aSKinglong Mee 		kfree(conf->data);
38661da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
3867b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64)0); /* clientid */
3868c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0); /* length of owner name */
38691da177e4SLinus Torvalds 	}
3870d0a381ddSJ. Bruce Fields 	return nfserr_denied;
38711da177e4SLinus Torvalds }
38721da177e4SLinus Torvalds 
3873695e12f8SBenny Halevy static __be32
nfsd4_encode_lock(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3874e78e274eSKees Cook nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr,
3875e78e274eSKees Cook 		  union nfsd4_op_u *u)
38761da177e4SLinus Torvalds {
3877e78e274eSKees Cook 	struct nfsd4_lock *lock = &u->lock;
3878bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3879d0a381ddSJ. Bruce Fields 
3880e2f282b9SBenny Halevy 	if (!nfserr)
3881d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
3882e2f282b9SBenny Halevy 	else if (nfserr == nfserr_denied)
3883d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
3884f98bac5aSKinglong Mee 
3885695e12f8SBenny Halevy 	return nfserr;
38861da177e4SLinus Torvalds }
38871da177e4SLinus Torvalds 
3888695e12f8SBenny Halevy static __be32
nfsd4_encode_lockt(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3889e78e274eSKees Cook nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr,
3890e78e274eSKees Cook 		   union nfsd4_op_u *u)
38911da177e4SLinus Torvalds {
3892e78e274eSKees Cook 	struct nfsd4_lockt *lockt = &u->lockt;
3893bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3894d0a381ddSJ. Bruce Fields 
38951da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
3896d0a381ddSJ. Bruce Fields 		nfsd4_encode_lock_denied(xdr, &lockt->lt_denied);
3897695e12f8SBenny Halevy 	return nfserr;
38981da177e4SLinus Torvalds }
38991da177e4SLinus Torvalds 
3900695e12f8SBenny Halevy static __be32
nfsd4_encode_locku(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3901e78e274eSKees Cook nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr,
3902e78e274eSKees Cook 		   union nfsd4_op_u *u)
39031da177e4SLinus Torvalds {
3904e78e274eSKees Cook 	struct nfsd4_locku *locku = &u->locku;
3905bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3906d0a381ddSJ. Bruce Fields 
3907bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &locku->lu_stateid);
39081da177e4SLinus Torvalds }
39091da177e4SLinus Torvalds 
39101da177e4SLinus Torvalds 
3911695e12f8SBenny Halevy static __be32
nfsd4_encode_link(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3912e78e274eSKees Cook nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr,
3913e78e274eSKees Cook 		  union nfsd4_op_u *u)
39141da177e4SLinus Torvalds {
3915e78e274eSKees Cook 	struct nfsd4_link *link = &u->link;
3916bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
39171da177e4SLinus Torvalds 
391866a21db7SChuck Lever 	return nfsd4_encode_change_info4(xdr, &link->li_cinfo);
39191da177e4SLinus Torvalds }
39201da177e4SLinus Torvalds 
39211da177e4SLinus Torvalds 
3922695e12f8SBenny Halevy static __be32
nfsd4_encode_open(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)3923e78e274eSKees Cook nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr,
3924e78e274eSKees Cook 		  union nfsd4_op_u *u)
39251da177e4SLinus Torvalds {
3926e78e274eSKees Cook 	struct nfsd4_open *open = &u->open;
3927bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
3928bc749ca4SJ. Bruce Fields 	__be32 *p;
39291da177e4SLinus Torvalds 
3930d0a381ddSJ. Bruce Fields 	nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid);
3931d0a381ddSJ. Bruce Fields 	if (nfserr)
3932bac966d6SJ. Bruce Fields 		return nfserr;
393366a21db7SChuck Lever 	nfserr = nfsd4_encode_change_info4(xdr, &open->op_cinfo);
393466a21db7SChuck Lever 	if (nfserr)
393566a21db7SChuck Lever 		return nfserr;
393666a21db7SChuck Lever 	if (xdr_stream_encode_u32(xdr, open->op_rflags) < 0)
3937d0a381ddSJ. Bruce Fields 		return nfserr_resource;
39381da177e4SLinus Torvalds 
393975976de6SKinglong Mee 	nfserr = nfsd4_encode_bitmap(xdr, open->op_bmval[0], open->op_bmval[1],
394075976de6SKinglong Mee 					open->op_bmval[2]);
394175976de6SKinglong Mee 	if (nfserr)
3942bac966d6SJ. Bruce Fields 		return nfserr;
394375976de6SKinglong Mee 
394475976de6SKinglong Mee 	p = xdr_reserve_space(xdr, 4);
394575976de6SKinglong Mee 	if (!p)
394675976de6SKinglong Mee 		return nfserr_resource;
394775976de6SKinglong Mee 
394875976de6SKinglong Mee 	*p++ = cpu_to_be32(open->op_delegate_type);
39491da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
39501da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
39511da177e4SLinus Torvalds 		break;
39521da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
3953d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3954d0a381ddSJ. Bruce Fields 		if (nfserr)
3955d0a381ddSJ. Bruce Fields 			return nfserr;
3956d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 20);
3957d0a381ddSJ. Bruce Fields 		if (!p)
3958d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3959c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(open->op_recall);
39601da177e4SLinus Torvalds 
39611da177e4SLinus Torvalds 		/*
39621da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
39631da177e4SLinus Torvalds 		 */
3964c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3965c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3966c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3967c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);   /* XXX: is NULL principal ok? */
39681da177e4SLinus Torvalds 		break;
39691da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
3970d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3971d0a381ddSJ. Bruce Fields 		if (nfserr)
3972d0a381ddSJ. Bruce Fields 			return nfserr;
397350bce06fSChuck Lever 
397450bce06fSChuck Lever 		p = xdr_reserve_space(xdr, XDR_UNIT * 8);
3975d0a381ddSJ. Bruce Fields 		if (!p)
3976d0a381ddSJ. Bruce Fields 			return nfserr_resource;
397758f5d894SDai Ngo 		*p++ = cpu_to_be32(open->op_recall);
39781da177e4SLinus Torvalds 
39791da177e4SLinus Torvalds 		/*
398050bce06fSChuck Lever 		 * Always flush on close
398150bce06fSChuck Lever 		 *
39821da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
39831da177e4SLinus Torvalds 		 */
3984c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_LIMIT_SIZE);
398550bce06fSChuck Lever 		*p++ = xdr_zero;
398650bce06fSChuck Lever 		*p++ = xdr_zero;
39871da177e4SLinus Torvalds 
39881da177e4SLinus Torvalds 		/*
39891da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
39901da177e4SLinus Torvalds 		 */
3991c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3992c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3993c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3994c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);   /* XXX: is NULL principal ok? */
39951da177e4SLinus Torvalds 		break;
3996d24433cdSBenny Halevy 	case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
3997d24433cdSBenny Halevy 		switch (open->op_why_no_deleg) {
3998d24433cdSBenny Halevy 		case WND4_CONTENTION:
3999d24433cdSBenny Halevy 		case WND4_RESOURCE:
4000d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 8);
4001d0a381ddSJ. Bruce Fields 			if (!p)
4002d0a381ddSJ. Bruce Fields 				return nfserr_resource;
4003c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(open->op_why_no_deleg);
4004c373b0a4SJ. Bruce Fields 			/* deleg signaling not supported yet: */
4005c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
4006d24433cdSBenny Halevy 			break;
4007d24433cdSBenny Halevy 		default:
4008d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
4009d0a381ddSJ. Bruce Fields 			if (!p)
4010d0a381ddSJ. Bruce Fields 				return nfserr_resource;
4011c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(open->op_why_no_deleg);
4012d24433cdSBenny Halevy 		}
4013d24433cdSBenny Halevy 		break;
40141da177e4SLinus Torvalds 	default:
40151da177e4SLinus Torvalds 		BUG();
40161da177e4SLinus Torvalds 	}
40171da177e4SLinus Torvalds 	/* XXX save filehandle here */
4018bac966d6SJ. Bruce Fields 	return 0;
40191da177e4SLinus Torvalds }
40201da177e4SLinus Torvalds 
4021695e12f8SBenny Halevy static __be32
nfsd4_encode_open_confirm(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4022e78e274eSKees Cook nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr,
4023e78e274eSKees Cook 			  union nfsd4_op_u *u)
40241da177e4SLinus Torvalds {
4025e78e274eSKees Cook 	struct nfsd4_open_confirm *oc = &u->open_confirm;
4026bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4027d0a381ddSJ. Bruce Fields 
4028bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid);
40291da177e4SLinus Torvalds }
40301da177e4SLinus Torvalds 
4031695e12f8SBenny Halevy static __be32
nfsd4_encode_open_downgrade(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4032e78e274eSKees Cook nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr,
4033e78e274eSKees Cook 			    union nfsd4_op_u *u)
40341da177e4SLinus Torvalds {
4035e78e274eSKees Cook 	struct nfsd4_open_downgrade *od = &u->open_downgrade;
4036bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4037d0a381ddSJ. Bruce Fields 
4038bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &od->od_stateid);
40391da177e4SLinus Torvalds }
40401da177e4SLinus Torvalds 
4041ba21e20bSChuck Lever /*
4042ba21e20bSChuck Lever  * The operation of this function assumes that this is the only
4043ba21e20bSChuck Lever  * READ operation in the COMPOUND. If there are multiple READs,
4044ba21e20bSChuck Lever  * we use nfsd4_encode_readv().
4045ba21e20bSChuck Lever  */
nfsd4_encode_splice_read(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)4046dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_splice_read(
4047dc97618dSJ. Bruce Fields 				struct nfsd4_compoundres *resp,
4048dc97618dSJ. Bruce Fields 				struct nfsd4_read *read,
4049dc97618dSJ. Bruce Fields 				struct file *file, unsigned long maxcount)
40501da177e4SLinus Torvalds {
4051bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
405234a78b48SJ. Bruce Fields 	struct xdr_buf *buf = xdr->buf;
405376e5492bSChuck Lever 	int status, space_left;
4054dc97618dSJ. Bruce Fields 	__be32 nfserr;
4055dc97618dSJ. Bruce Fields 
4056ba21e20bSChuck Lever 	/*
4057ba21e20bSChuck Lever 	 * Make sure there is room at the end of buf->head for
4058ba21e20bSChuck Lever 	 * svcxdr_encode_opaque_pages() to create a tail buffer
4059ba21e20bSChuck Lever 	 * to XDR-pad the payload.
4060ba21e20bSChuck Lever 	 */
4061ba21e20bSChuck Lever 	if (xdr->iov != xdr->buf->head || xdr->end - xdr->p < 1)
4062dc97618dSJ. Bruce Fields 		return nfserr_resource;
4063dc97618dSJ. Bruce Fields 
406487c5942eSChuck Lever 	nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
406524c7fb85SChuck Lever 				  file, read->rd_offset, &maxcount,
406624c7fb85SChuck Lever 				  &read->rd_eof);
406787c5942eSChuck Lever 	read->rd_length = maxcount;
406876e5492bSChuck Lever 	if (nfserr)
406976e5492bSChuck Lever 		goto out_err;
4070ba21e20bSChuck Lever 	svcxdr_encode_opaque_pages(read->rd_rqstp, xdr, buf->pages,
4071ba21e20bSChuck Lever 				   buf->page_base, maxcount);
407276e5492bSChuck Lever 	status = svc_encode_result_payload(read->rd_rqstp,
407376e5492bSChuck Lever 					   buf->head[0].iov_len, maxcount);
407476e5492bSChuck Lever 	if (status) {
407576e5492bSChuck Lever 		nfserr = nfserrno(status);
407676e5492bSChuck Lever 		goto out_err;
4077dc97618dSJ. Bruce Fields 	}
4078dc97618dSJ. Bruce Fields 
4079ba21e20bSChuck Lever 	/*
4080ba21e20bSChuck Lever 	 * Prepare to encode subsequent operations.
4081ba21e20bSChuck Lever 	 *
4082ba21e20bSChuck Lever 	 * xdr_truncate_encode() is not safe to use after a successful
4083ba21e20bSChuck Lever 	 * splice read has been done, so the following stream
4084ba21e20bSChuck Lever 	 * manipulations are open-coded.
4085ba21e20bSChuck Lever 	 */
4086dc97618dSJ. Bruce Fields 	space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
408734a78b48SJ. Bruce Fields 				buf->buflen - buf->len);
408834a78b48SJ. Bruce Fields 	buf->buflen = buf->len + space_left;
4089dc97618dSJ. Bruce Fields 	xdr->end = (__be32 *)((void *)xdr->end + space_left);
4090dc97618dSJ. Bruce Fields 
4091ba21e20bSChuck Lever 	return nfs_ok;
409276e5492bSChuck Lever 
409376e5492bSChuck Lever out_err:
409476e5492bSChuck Lever 	/*
409576e5492bSChuck Lever 	 * nfsd_splice_actor may have already messed with the
409676e5492bSChuck Lever 	 * page length; reset it so as not to confuse
409776e5492bSChuck Lever 	 * xdr_truncate_encode in our caller.
409876e5492bSChuck Lever 	 */
409976e5492bSChuck Lever 	buf->page_len = 0;
410076e5492bSChuck Lever 	return nfserr;
4101dc97618dSJ. Bruce Fields }
4102dc97618dSJ. Bruce Fields 
nfsd4_encode_readv(struct nfsd4_compoundres * resp,struct nfsd4_read * read,struct file * file,unsigned long maxcount)4103dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
4104dc97618dSJ. Bruce Fields 				 struct nfsd4_read *read,
4105dc97618dSJ. Bruce Fields 				 struct file *file, unsigned long maxcount)
4106dc97618dSJ. Bruce Fields {
4107bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
41080d32a6bbSChuck Lever 	unsigned int base = xdr->buf->page_len & ~PAGE_MASK;
4109071ae99fSChuck Lever 	unsigned int starting_len = xdr->buf->len;
41105e64d85cSChuck Lever 	__be32 zero = xdr_zero;
4111dc97618dSJ. Bruce Fields 	__be32 nfserr;
41121da177e4SLinus Torvalds 
4113703d7521SChuck Lever 	if (xdr_reserve_space_vec(xdr, maxcount) < 0)
4114403217f3SAnna Schumaker 		return nfserr_resource;
41151da177e4SLinus Torvalds 
4116703d7521SChuck Lever 	nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file,
41170d32a6bbSChuck Lever 				read->rd_offset, &maxcount, base,
411824c7fb85SChuck Lever 				&read->rd_eof);
411987c5942eSChuck Lever 	read->rd_length = maxcount;
4120dc97618dSJ. Bruce Fields 	if (nfserr)
41211da177e4SLinus Torvalds 		return nfserr;
4122071ae99fSChuck Lever 	if (svc_encode_result_payload(resp->rqstp, starting_len, maxcount))
412341205539SChuck Lever 		return nfserr_io;
4124071ae99fSChuck Lever 	xdr_truncate_encode(xdr, starting_len + xdr_align_size(maxcount));
4125dc97618dSJ. Bruce Fields 
41265e64d85cSChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, starting_len + maxcount, &zero,
41275e64d85cSChuck Lever 			       xdr_pad_size(maxcount));
41285e64d85cSChuck Lever 	return nfs_ok;
4129dc97618dSJ. Bruce Fields }
4130dc97618dSJ. Bruce Fields 
4131dc97618dSJ. Bruce Fields static __be32
nfsd4_encode_read(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4132dc97618dSJ. Bruce Fields nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
4133e78e274eSKees Cook 		  union nfsd4_op_u *u)
4134dc97618dSJ. Bruce Fields {
4135e78e274eSKees Cook 	struct nfsd4_read *read = &u->read;
4136c738b218SChuck Lever 	bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags);
4137dc97618dSJ. Bruce Fields 	unsigned long maxcount;
4138bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
41395c4583b2SJeff Layton 	struct file *file;
4140dc97618dSJ. Bruce Fields 	int starting_len = xdr->buf->len;
4141dc97618dSJ. Bruce Fields 	__be32 *p;
4142dc97618dSJ. Bruce Fields 
41435c4583b2SJeff Layton 	if (nfserr)
41445c4583b2SJeff Layton 		return nfserr;
41455c4583b2SJeff Layton 	file = read->rd_nf->nf_file;
41465c4583b2SJeff Layton 
4147dc97618dSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
4148dc97618dSJ. Bruce Fields 	if (!p) {
4149c738b218SChuck Lever 		WARN_ON_ONCE(splice_ok);
4150bac966d6SJ. Bruce Fields 		return nfserr_resource;
4151dc97618dSJ. Bruce Fields 	}
4152c738b218SChuck Lever 	if (resp->xdr->buf->page_len && splice_ok) {
4153b0420980SJ. Bruce Fields 		WARN_ON_ONCE(1);
415406981d56SAnna Schumaker 		return nfserr_serverfault;
4155dc97618dSJ. Bruce Fields 	}
4156dc97618dSJ. Bruce Fields 	xdr_commit_encode(xdr);
4157dc97618dSJ. Bruce Fields 
41580cb4d23aSChuck Lever 	maxcount = min_t(unsigned long, read->rd_length,
415968e8bb03SChristoph Hellwig 			 (xdr->buf->buflen - xdr->buf->len));
4160dc97618dSJ. Bruce Fields 
4161c738b218SChuck Lever 	if (file->f_op->splice_read && splice_ok)
416296bcad50SChristoph Hellwig 		nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
4163dc97618dSJ. Bruce Fields 	else
416496bcad50SChristoph Hellwig 		nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
416528d5bc46SChuck Lever 	if (nfserr) {
4166dc97618dSJ. Bruce Fields 		xdr_truncate_encode(xdr, starting_len);
416796bcad50SChristoph Hellwig 		return nfserr;
41681da177e4SLinus Torvalds 	}
41691da177e4SLinus Torvalds 
417028d5bc46SChuck Lever 	p = xdr_encode_bool(p, read->rd_eof);
417128d5bc46SChuck Lever 	*p = cpu_to_be32(read->rd_length);
417228d5bc46SChuck Lever 	return nfs_ok;
417328d5bc46SChuck Lever }
417428d5bc46SChuck Lever 
4175b37ad28bSAl Viro static __be32
nfsd4_encode_readlink(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4176e78e274eSKees Cook nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr,
4177e78e274eSKees Cook 		      union nfsd4_op_u *u)
41781da177e4SLinus Torvalds {
4179e78e274eSKees Cook 	struct nfsd4_readlink *readlink = &u->readlink;
418099b002a1SChuck Lever 	__be32 *p, *maxcount_p, zero = xdr_zero;
4181bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
41821fcea5b2SJ. Bruce Fields 	int length_offset = xdr->buf->len;
418399b002a1SChuck Lever 	int maxcount, status;
41841da177e4SLinus Torvalds 
418599b002a1SChuck Lever 	maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
418699b002a1SChuck Lever 	if (!maxcount_p)
41872825a7f9SJ. Bruce Fields 		return nfserr_resource;
41881da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
4189d0a381ddSJ. Bruce Fields 
4190476a7b1fSJ. Bruce Fields 	p = xdr_reserve_space(xdr, maxcount);
4191476a7b1fSJ. Bruce Fields 	if (!p)
41924e21ac4bSJ. Bruce Fields 		return nfserr_resource;
41931da177e4SLinus Torvalds 	/*
4194fd4a0edfSMiklos Szeredi 	 * XXX: By default, vfs_readlink() will truncate symlinks if they
4195fd4a0edfSMiklos Szeredi 	 * would overflow the buffer.  Is this kosher in NFSv4?  If not, one
4196fd4a0edfSMiklos Szeredi 	 * easy fix is: if vfs_readlink() precisely fills the buffer, assume
4197fd4a0edfSMiklos Szeredi 	 * that truncation occurred, and return NFS4ERR_RESOURCE.
41981da177e4SLinus Torvalds 	 */
4199476a7b1fSJ. Bruce Fields 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
4200476a7b1fSJ. Bruce Fields 						(char *)p, &maxcount);
42011da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
4202d3f627c8SJ. Bruce Fields 		nfserr = nfserr_inval;
420376e5492bSChuck Lever 	if (nfserr)
420476e5492bSChuck Lever 		goto out_err;
420576e5492bSChuck Lever 	status = svc_encode_result_payload(readlink->rl_rqstp, length_offset,
420676e5492bSChuck Lever 					   maxcount);
420776e5492bSChuck Lever 	if (status) {
420876e5492bSChuck Lever 		nfserr = nfserrno(status);
420976e5492bSChuck Lever 		goto out_err;
4210d3f627c8SJ. Bruce Fields 	}
421199b002a1SChuck Lever 	*maxcount_p = cpu_to_be32(maxcount);
421299b002a1SChuck Lever 	xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
421399b002a1SChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
421499b002a1SChuck Lever 			       xdr_pad_size(maxcount));
421599b002a1SChuck Lever 	return nfs_ok;
421676e5492bSChuck Lever 
421776e5492bSChuck Lever out_err:
421876e5492bSChuck Lever 	xdr_truncate_encode(xdr, length_offset);
421976e5492bSChuck Lever 	return nfserr;
42201da177e4SLinus Torvalds }
42211da177e4SLinus Torvalds 
4222b37ad28bSAl Viro static __be32
nfsd4_encode_readdir(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4223e78e274eSKees Cook nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr,
4224e78e274eSKees Cook 		     union nfsd4_op_u *u)
42251da177e4SLinus Torvalds {
4226e78e274eSKees Cook 	struct nfsd4_readdir *readdir = &u->readdir;
42271da177e4SLinus Torvalds 	int maxcount;
4228561f0ed4SJ. Bruce Fields 	int bytes_left;
42291da177e4SLinus Torvalds 	loff_t offset;
4230561f0ed4SJ. Bruce Fields 	__be64 wire_offset;
4231bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
42321fcea5b2SJ. Bruce Fields 	int starting_len = xdr->buf->len;
4233bc749ca4SJ. Bruce Fields 	__be32 *p;
42341da177e4SLinus Torvalds 
4235adaa7a50SChuck Lever 	nfserr = nfsd4_encode_verifier4(xdr, &readdir->rd_verf);
4236adaa7a50SChuck Lever 	if (nfserr != nfs_ok)
4237adaa7a50SChuck Lever 		return nfserr;
42381da177e4SLinus Torvalds 
42391da177e4SLinus Torvalds 	/*
4240561f0ed4SJ. Bruce Fields 	 * Number of bytes left for directory entries allowing for the
4241561f0ed4SJ. Bruce Fields 	 * final 8 bytes of the readdir and a following failed op:
42421da177e4SLinus Torvalds 	 */
4243561f0ed4SJ. Bruce Fields 	bytes_left = xdr->buf->buflen - xdr->buf->len
4244561f0ed4SJ. Bruce Fields 			- COMPOUND_ERR_SLACK_SPACE - 8;
4245561f0ed4SJ. Bruce Fields 	if (bytes_left < 0) {
4246561f0ed4SJ. Bruce Fields 		nfserr = nfserr_resource;
4247561f0ed4SJ. Bruce Fields 		goto err_no_verf;
4248561f0ed4SJ. Bruce Fields 	}
42499c2ece6eSScott Mayhew 	maxcount = svc_max_payload(resp->rqstp);
42509c2ece6eSScott Mayhew 	maxcount = min_t(u32, readdir->rd_maxcount, maxcount);
4251561f0ed4SJ. Bruce Fields 	/*
4252561f0ed4SJ. Bruce Fields 	 * Note the rfc defines rd_maxcount as the size of the
4253561f0ed4SJ. Bruce Fields 	 * READDIR4resok structure, which includes the verifier above
4254561f0ed4SJ. Bruce Fields 	 * and the 8 bytes encoded at the end of this function:
4255561f0ed4SJ. Bruce Fields 	 */
4256561f0ed4SJ. Bruce Fields 	if (maxcount < 16) {
42571da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
42581da177e4SLinus Torvalds 		goto err_no_verf;
42591da177e4SLinus Torvalds 	}
4260561f0ed4SJ. Bruce Fields 	maxcount = min_t(int, maxcount-16, bytes_left);
42611da177e4SLinus Torvalds 
4262aee37764SJ. Bruce Fields 	/* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
4263aee37764SJ. Bruce Fields 	if (!readdir->rd_dircount)
42649c2ece6eSScott Mayhew 		readdir->rd_dircount = svc_max_payload(resp->rqstp);
4265aee37764SJ. Bruce Fields 
4266561f0ed4SJ. Bruce Fields 	readdir->xdr = xdr;
4267561f0ed4SJ. Bruce Fields 	readdir->rd_maxcount = maxcount;
42681da177e4SLinus Torvalds 	readdir->common.err = 0;
4269561f0ed4SJ. Bruce Fields 	readdir->cookie_offset = 0;
42701da177e4SLinus Torvalds 
42711da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
42721da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
42731da177e4SLinus Torvalds 			      &offset,
42741da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
42751da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
42761da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
4277561f0ed4SJ. Bruce Fields 	    xdr->buf->len == starting_len + 8) {
4278561f0ed4SJ. Bruce Fields 		/* nothing encoded; which limit did we hit?: */
4279561f0ed4SJ. Bruce Fields 		if (maxcount - 16 < bytes_left)
4280561f0ed4SJ. Bruce Fields 			/* It was the fault of rd_maxcount: */
42811da177e4SLinus Torvalds 			nfserr = nfserr_toosmall;
4282561f0ed4SJ. Bruce Fields 		else
4283561f0ed4SJ. Bruce Fields 			/* We ran out of buffer space: */
4284561f0ed4SJ. Bruce Fields 			nfserr = nfserr_resource;
4285561f0ed4SJ. Bruce Fields 	}
42861da177e4SLinus Torvalds 	if (nfserr)
42871da177e4SLinus Torvalds 		goto err_no_verf;
42881da177e4SLinus Torvalds 
4289561f0ed4SJ. Bruce Fields 	if (readdir->cookie_offset) {
4290561f0ed4SJ. Bruce Fields 		wire_offset = cpu_to_be64(offset);
4291561f0ed4SJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset,
4292561f0ed4SJ. Bruce Fields 							&wire_offset, 8);
4293561f0ed4SJ. Bruce Fields 	}
42941da177e4SLinus Torvalds 
4295561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
4296561f0ed4SJ. Bruce Fields 	if (!p) {
4297561f0ed4SJ. Bruce Fields 		WARN_ON_ONCE(1);
4298561f0ed4SJ. Bruce Fields 		goto err_no_verf;
4299561f0ed4SJ. Bruce Fields 	}
43001da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
43011da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
43021da177e4SLinus Torvalds 
43031da177e4SLinus Torvalds 	return 0;
43041da177e4SLinus Torvalds err_no_verf:
43051fcea5b2SJ. Bruce Fields 	xdr_truncate_encode(xdr, starting_len);
43061da177e4SLinus Torvalds 	return nfserr;
43071da177e4SLinus Torvalds }
43081da177e4SLinus Torvalds 
4309695e12f8SBenny Halevy static __be32
nfsd4_encode_remove(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4310e78e274eSKees Cook nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr,
4311e78e274eSKees Cook 		    union nfsd4_op_u *u)
43121da177e4SLinus Torvalds {
4313e78e274eSKees Cook 	struct nfsd4_remove *remove = &u->remove;
4314bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
43151da177e4SLinus Torvalds 
431666a21db7SChuck Lever 	return nfsd4_encode_change_info4(xdr, &remove->rm_cinfo);
43171da177e4SLinus Torvalds }
43181da177e4SLinus Torvalds 
4319695e12f8SBenny Halevy static __be32
nfsd4_encode_rename(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4320e78e274eSKees Cook nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr,
4321e78e274eSKees Cook 		    union nfsd4_op_u *u)
43221da177e4SLinus Torvalds {
4323e78e274eSKees Cook 	struct nfsd4_rename *rename = &u->rename;
4324bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
43251da177e4SLinus Torvalds 
432666a21db7SChuck Lever 	nfserr = nfsd4_encode_change_info4(xdr, &rename->rn_sinfo);
432766a21db7SChuck Lever 	if (nfserr)
432866a21db7SChuck Lever 		return nfserr;
432966a21db7SChuck Lever 	return nfsd4_encode_change_info4(xdr, &rename->rn_tinfo);
43301da177e4SLinus Torvalds }
43311da177e4SLinus Torvalds 
4332695e12f8SBenny Halevy static __be32
nfsd4_do_encode_secinfo(struct xdr_stream * xdr,struct svc_export * exp)4333bac966d6SJ. Bruce Fields nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
4334dcb488a3SAndy Adamson {
4335676e4ebdSChuck Lever 	u32 i, nflavs, supported;
43364796f457SJ. Bruce Fields 	struct exp_flavor_info *flavs;
43374796f457SJ. Bruce Fields 	struct exp_flavor_info def_flavs[2];
4338676e4ebdSChuck Lever 	__be32 *p, *flavorsp;
4339676e4ebdSChuck Lever 	static bool report = true;
4340dcb488a3SAndy Adamson 
43414796f457SJ. Bruce Fields 	if (exp->ex_nflavors) {
43424796f457SJ. Bruce Fields 		flavs = exp->ex_flavors;
43434796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
43444796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
43454796f457SJ. Bruce Fields 		flavs = def_flavs;
43464796f457SJ. Bruce Fields 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
43474796f457SJ. Bruce Fields 			nflavs = 2;
43484796f457SJ. Bruce Fields 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
43494796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
43504796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
43514796f457SJ. Bruce Fields 			nflavs = 1;
43524796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
43534796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
43544796f457SJ. Bruce Fields 		} else {
43554796f457SJ. Bruce Fields 			nflavs = 1;
43564796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
43574796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
43584796f457SJ. Bruce Fields 		}
43594796f457SJ. Bruce Fields 	}
43604796f457SJ. Bruce Fields 
4361676e4ebdSChuck Lever 	supported = 0;
4362d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
4363d0a381ddSJ. Bruce Fields 	if (!p)
4364bac966d6SJ. Bruce Fields 		return nfserr_resource;
4365676e4ebdSChuck Lever 	flavorsp = p++;		/* to be backfilled later */
4366676e4ebdSChuck Lever 
43674796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
4368676e4ebdSChuck Lever 		rpc_authflavor_t pf = flavs[i].pseudoflavor;
4369a77c806fSChuck Lever 		struct rpcsec_gss_info info;
4370dcb488a3SAndy Adamson 
4371676e4ebdSChuck Lever 		if (rpcauth_get_gssinfo(pf, &info) == 0) {
4372676e4ebdSChuck Lever 			supported++;
4373d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4 + 4 +
4374d0a381ddSJ. Bruce Fields 					      XDR_LEN(info.oid.len) + 4 + 4);
4375d0a381ddSJ. Bruce Fields 			if (!p)
4376bac966d6SJ. Bruce Fields 				return nfserr_resource;
4377c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(RPC_AUTH_GSS);
43780c0c267bSJ. Bruce Fields 			p = xdr_encode_opaque(p,  info.oid.data, info.oid.len);
4379c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(info.qop);
4380c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(info.service);
4381676e4ebdSChuck Lever 		} else if (pf < RPC_AUTH_MAXFLAVOR) {
4382676e4ebdSChuck Lever 			supported++;
4383d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
4384d0a381ddSJ. Bruce Fields 			if (!p)
4385bac966d6SJ. Bruce Fields 				return nfserr_resource;
4386c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(pf);
4387676e4ebdSChuck Lever 		} else {
4388676e4ebdSChuck Lever 			if (report)
4389676e4ebdSChuck Lever 				pr_warn("NFS: SECINFO: security flavor %u "
4390676e4ebdSChuck Lever 					"is not supported\n", pf);
4391dcb488a3SAndy Adamson 		}
4392dcb488a3SAndy Adamson 	}
4393a77c806fSChuck Lever 
4394676e4ebdSChuck Lever 	if (nflavs != supported)
4395676e4ebdSChuck Lever 		report = false;
4396676e4ebdSChuck Lever 	*flavorsp = htonl(supported);
4397bac966d6SJ. Bruce Fields 	return 0;
4398dcb488a3SAndy Adamson }
4399dcb488a3SAndy Adamson 
440022b6dee8SMi Jinlong static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)440122b6dee8SMi Jinlong nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
4402e78e274eSKees Cook 		     union nfsd4_op_u *u)
440322b6dee8SMi Jinlong {
4404e78e274eSKees Cook 	struct nfsd4_secinfo *secinfo = &u->secinfo;
4405bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4406d0a381ddSJ. Bruce Fields 
4407bac966d6SJ. Bruce Fields 	return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
440822b6dee8SMi Jinlong }
440922b6dee8SMi Jinlong 
441022b6dee8SMi Jinlong static __be32
nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)441122b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
4412e78e274eSKees Cook 		     union nfsd4_op_u *u)
441322b6dee8SMi Jinlong {
4414e78e274eSKees Cook 	struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
4415bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4416d0a381ddSJ. Bruce Fields 
4417bac966d6SJ. Bruce Fields 	return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
441822b6dee8SMi Jinlong }
441922b6dee8SMi Jinlong 
44201da177e4SLinus Torvalds /*
44211da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
44221da177e4SLinus Torvalds  * regardless of the error status.
44231da177e4SLinus Torvalds  */
4424695e12f8SBenny Halevy static __be32
nfsd4_encode_setattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4425e78e274eSKees Cook nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr,
4426e78e274eSKees Cook 		     union nfsd4_op_u *u)
44271da177e4SLinus Torvalds {
4428e78e274eSKees Cook 	struct nfsd4_setattr *setattr = &u->setattr;
4429bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4430bc749ca4SJ. Bruce Fields 	__be32 *p;
44311da177e4SLinus Torvalds 
4432d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 16);
4433d0a381ddSJ. Bruce Fields 	if (!p)
4434d0a381ddSJ. Bruce Fields 		return nfserr_resource;
44351da177e4SLinus Torvalds 	if (nfserr) {
4436c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
4437c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
4438c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
4439c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
44401da177e4SLinus Torvalds 	}
44411da177e4SLinus Torvalds 	else {
4442c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
4443c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[0]);
4444c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[1]);
4445c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[2]);
44461da177e4SLinus Torvalds 	}
4447695e12f8SBenny Halevy 	return nfserr;
44481da177e4SLinus Torvalds }
44491da177e4SLinus Torvalds 
4450695e12f8SBenny Halevy static __be32
nfsd4_encode_setclientid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4451e78e274eSKees Cook nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr,
4452e78e274eSKees Cook 			 union nfsd4_op_u *u)
44531da177e4SLinus Torvalds {
4454e78e274eSKees Cook 	struct nfsd4_setclientid *scd = &u->setclientid;
4455bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
44561da177e4SLinus Torvalds 
44571da177e4SLinus Torvalds 	if (!nfserr) {
4458adaa7a50SChuck Lever 		nfserr = nfsd4_encode_clientid4(xdr, &scd->se_clientid);
4459adaa7a50SChuck Lever 		if (nfserr != nfs_ok)
4460adaa7a50SChuck Lever 			goto out;
4461adaa7a50SChuck Lever 		nfserr = nfsd4_encode_verifier4(xdr, &scd->se_confirm);
4462adaa7a50SChuck Lever 	} else if (nfserr == nfserr_clid_inuse) {
4463adaa7a50SChuck Lever 		/* empty network id */
4464adaa7a50SChuck Lever 		if (xdr_stream_encode_u32(xdr, 0) < 0) {
4465adaa7a50SChuck Lever 			nfserr = nfserr_resource;
4466adaa7a50SChuck Lever 			goto out;
44671da177e4SLinus Torvalds 		}
4468adaa7a50SChuck Lever 		/* empty universal address */
4469adaa7a50SChuck Lever 		if (xdr_stream_encode_u32(xdr, 0) < 0) {
4470adaa7a50SChuck Lever 			nfserr = nfserr_resource;
4471adaa7a50SChuck Lever 			goto out;
44721da177e4SLinus Torvalds 		}
4473adaa7a50SChuck Lever 	}
4474adaa7a50SChuck Lever out:
4475695e12f8SBenny Halevy 	return nfserr;
44761da177e4SLinus Torvalds }
44771da177e4SLinus Torvalds 
4478695e12f8SBenny Halevy static __be32
nfsd4_encode_write(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4479e78e274eSKees Cook nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr,
4480e78e274eSKees Cook 		   union nfsd4_op_u *u)
44811da177e4SLinus Torvalds {
4482e78e274eSKees Cook 	struct nfsd4_write *write = &u->write;
44831da177e4SLinus Torvalds 
4484adaa7a50SChuck Lever 	if (xdr_stream_encode_u32(resp->xdr, write->wr_bytes_written) < 0)
4485d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4486adaa7a50SChuck Lever 	if (xdr_stream_encode_u32(resp->xdr, write->wr_how_written) < 0)
4487adaa7a50SChuck Lever 		return nfserr_resource;
4488adaa7a50SChuck Lever 	return nfsd4_encode_verifier4(resp->xdr, &write->wr_verifier);
44891da177e4SLinus Torvalds }
44901da177e4SLinus Torvalds 
4491695e12f8SBenny Halevy static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)449257b7b43bSJ. Bruce Fields nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
4493e78e274eSKees Cook 			 union nfsd4_op_u *u)
44942db134ebSAndy Adamson {
4495e78e274eSKees Cook 	struct nfsd4_exchange_id *exid = &u->exchange_id;
4496bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4497bc749ca4SJ. Bruce Fields 	__be32 *p;
44980733d213SAndy Adamson 	char *major_id;
44990733d213SAndy Adamson 	char *server_scope;
45000733d213SAndy Adamson 	int major_id_sz;
45010733d213SAndy Adamson 	int server_scope_sz;
45020733d213SAndy Adamson 	uint64_t minor_id = 0;
45037627d7dcSScott Mayhew 	struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id);
45040733d213SAndy Adamson 
45057627d7dcSScott Mayhew 	major_id = nn->nfsd_name;
45067627d7dcSScott Mayhew 	major_id_sz = strlen(nn->nfsd_name);
45077627d7dcSScott Mayhew 	server_scope = nn->nfsd_name;
45087627d7dcSScott Mayhew 	server_scope_sz = strlen(nn->nfsd_name);
45090733d213SAndy Adamson 
4510adaa7a50SChuck Lever 	if (nfsd4_encode_clientid4(xdr, &exid->clientid) != nfs_ok)
4511adaa7a50SChuck Lever 		return nfserr_resource;
4512adaa7a50SChuck Lever 	if (xdr_stream_encode_u32(xdr, exid->seqid) < 0)
4513adaa7a50SChuck Lever 		return nfserr_resource;
4514adaa7a50SChuck Lever 	if (xdr_stream_encode_u32(xdr, exid->flags) < 0)
4515d0a381ddSJ. Bruce Fields 		return nfserr_resource;
45160733d213SAndy Adamson 
4517adaa7a50SChuck Lever 	if (xdr_stream_encode_u32(xdr, exid->spa_how) < 0)
4518adaa7a50SChuck Lever 		return nfserr_resource;
451957266a6eSJ. Bruce Fields 	switch (exid->spa_how) {
452057266a6eSJ. Bruce Fields 	case SP4_NONE:
452157266a6eSJ. Bruce Fields 		break;
452257266a6eSJ. Bruce Fields 	case SP4_MACH_CRED:
452357266a6eSJ. Bruce Fields 		/* spo_must_enforce bitmap: */
4524bac966d6SJ. Bruce Fields 		nfserr = nfsd4_encode_bitmap(xdr,
4525ed941643SAndrew Elble 					exid->spo_must_enforce[0],
4526ed941643SAndrew Elble 					exid->spo_must_enforce[1],
4527ed941643SAndrew Elble 					exid->spo_must_enforce[2]);
4528bac966d6SJ. Bruce Fields 		if (nfserr)
4529bac966d6SJ. Bruce Fields 			return nfserr;
4530ed941643SAndrew Elble 		/* spo_must_allow bitmap: */
4531bac966d6SJ. Bruce Fields 		nfserr = nfsd4_encode_bitmap(xdr,
4532ed941643SAndrew Elble 					exid->spo_must_allow[0],
4533ed941643SAndrew Elble 					exid->spo_must_allow[1],
4534ed941643SAndrew Elble 					exid->spo_must_allow[2]);
4535bac966d6SJ. Bruce Fields 		if (nfserr)
4536bac966d6SJ. Bruce Fields 			return nfserr;
453757266a6eSJ. Bruce Fields 		break;
453857266a6eSJ. Bruce Fields 	default:
453957266a6eSJ. Bruce Fields 		WARN_ON_ONCE(1);
454057266a6eSJ. Bruce Fields 	}
45410733d213SAndy Adamson 
4542d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr,
4543a8bb84bcSKinglong Mee 		8 /* so_minor_id */ +
4544a8bb84bcSKinglong Mee 		4 /* so_major_id.len */ +
4545a8bb84bcSKinglong Mee 		(XDR_QUADLEN(major_id_sz) * 4) +
4546a8bb84bcSKinglong Mee 		4 /* eir_server_scope.len */ +
4547a8bb84bcSKinglong Mee 		(XDR_QUADLEN(server_scope_sz) * 4) +
4548a8bb84bcSKinglong Mee 		4 /* eir_server_impl_id.count (0) */);
4549d0a381ddSJ. Bruce Fields 	if (!p)
4550d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4551a8bb84bcSKinglong Mee 
45520733d213SAndy Adamson 	/* The server_owner struct */
4553b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, minor_id);      /* Minor id */
45540733d213SAndy Adamson 	/* major id */
45550c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, major_id, major_id_sz);
45560733d213SAndy Adamson 
45570733d213SAndy Adamson 	/* Server scope */
45580c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, server_scope, server_scope_sz);
45590733d213SAndy Adamson 
45600733d213SAndy Adamson 	/* Implementation id */
4561c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);	/* zero length nfs_impl_id4 array */
45620733d213SAndy Adamson 	return 0;
45632db134ebSAndy Adamson }
45642db134ebSAndy Adamson 
45652db134ebSAndy Adamson static __be32
nfsd4_encode_create_session(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)456657b7b43bSJ. Bruce Fields nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
4567e78e274eSKees Cook 			    union nfsd4_op_u *u)
45682db134ebSAndy Adamson {
4569e78e274eSKees Cook 	struct nfsd4_create_session *sess = &u->create_session;
4570bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4571bc749ca4SJ. Bruce Fields 	__be32 *p;
4572ec6b5d7bSAndy Adamson 
4573d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 24);
4574d0a381ddSJ. Bruce Fields 	if (!p)
4575d0a381ddSJ. Bruce Fields 		return nfserr_resource;
45760c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, sess->sessionid.data,
45770c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
4578c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->seqid);
4579c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->flags);
4580ec6b5d7bSAndy Adamson 
4581d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 28);
4582d0a381ddSJ. Bruce Fields 	if (!p)
4583d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4584c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* headerpadsz */
4585c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxreq_sz);
4586c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxresp_sz);
4587c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxresp_cached);
4588c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxops);
4589c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxreqs);
4590c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs);
4591ec6b5d7bSAndy Adamson 
4592ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs) {
4593d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
4594d0a381ddSJ. Bruce Fields 		if (!p)
4595d0a381ddSJ. Bruce Fields 			return nfserr_resource;
4596c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(sess->fore_channel.rdma_attrs);
4597ec6b5d7bSAndy Adamson 	}
4598ec6b5d7bSAndy Adamson 
4599d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 28);
4600d0a381ddSJ. Bruce Fields 	if (!p)
4601d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4602c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* headerpadsz */
4603c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxreq_sz);
4604c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxresp_sz);
4605c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxresp_cached);
4606c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxops);
4607c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxreqs);
4608c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs);
4609ec6b5d7bSAndy Adamson 
4610ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs) {
4611d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
4612d0a381ddSJ. Bruce Fields 		if (!p)
4613d0a381ddSJ. Bruce Fields 			return nfserr_resource;
4614c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(sess->back_channel.rdma_attrs);
4615ec6b5d7bSAndy Adamson 	}
4616ec6b5d7bSAndy Adamson 	return 0;
46172db134ebSAndy Adamson }
46182db134ebSAndy Adamson 
46192db134ebSAndy Adamson static __be32
nfsd4_encode_sequence(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)462057b7b43bSJ. Bruce Fields nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
4621e78e274eSKees Cook 		      union nfsd4_op_u *u)
46222db134ebSAndy Adamson {
4623e78e274eSKees Cook 	struct nfsd4_sequence *seq = &u->sequence;
4624bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4625bc749ca4SJ. Bruce Fields 	__be32 *p;
4626b85d4c01SBenny Halevy 
4627d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20);
4628d0a381ddSJ. Bruce Fields 	if (!p)
4629d0a381ddSJ. Bruce Fields 		return nfserr_resource;
46300c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, seq->sessionid.data,
46310c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
4632c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->seqid);
4633c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->slotid);
4634b7d7ca35SJ. Bruce Fields 	/* Note slotid's are numbered from zero: */
4635c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */
4636c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */
4637c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->status_flags);
4638b85d4c01SBenny Halevy 
4639f5236013SJ. Bruce Fields 	resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
4640b85d4c01SBenny Halevy 	return 0;
46412db134ebSAndy Adamson }
46422db134ebSAndy Adamson 
46432355c596SJ. Bruce Fields static __be32
nfsd4_encode_test_stateid(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)464457b7b43bSJ. Bruce Fields nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
4645e78e274eSKees Cook 			  union nfsd4_op_u *u)
464617456804SBryan Schumaker {
4647e78e274eSKees Cook 	struct nfsd4_test_stateid *test_stateid = &u->test_stateid;
4648bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
464903cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid, *next;
465017456804SBryan Schumaker 	__be32 *p;
465117456804SBryan Schumaker 
4652d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids));
4653d0a381ddSJ. Bruce Fields 	if (!p)
4654d0a381ddSJ. Bruce Fields 		return nfserr_resource;
465517456804SBryan Schumaker 	*p++ = htonl(test_stateid->ts_num_ids);
465617456804SBryan Schumaker 
465703cfb420SBryan Schumaker 	list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
465802f5fde5SAl Viro 		*p++ = stateid->ts_id_status;
465917456804SBryan Schumaker 	}
466017456804SBryan Schumaker 
4661bac966d6SJ. Bruce Fields 	return 0;
466217456804SBryan Schumaker }
466317456804SBryan Schumaker 
46649cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
46659cf514ccSChristoph Hellwig static __be32
nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)46669cf514ccSChristoph Hellwig nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
4667e78e274eSKees Cook 		union nfsd4_op_u *u)
46689cf514ccSChristoph Hellwig {
4669e78e274eSKees Cook 	struct nfsd4_getdeviceinfo *gdev = &u->getdeviceinfo;
4670bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4671f961e3f2SJ. Bruce Fields 	const struct nfsd4_layout_ops *ops;
46729cf514ccSChristoph Hellwig 	u32 starting_len = xdr->buf->len, needed_len;
46739cf514ccSChristoph Hellwig 	__be32 *p;
46749cf514ccSChristoph Hellwig 
46759cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
46769cf514ccSChristoph Hellwig 	if (!p)
4677bac966d6SJ. Bruce Fields 		return nfserr_resource;
46789cf514ccSChristoph Hellwig 
46799cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(gdev->gd_layout_type);
46809cf514ccSChristoph Hellwig 
4681f961e3f2SJ. Bruce Fields 	ops = nfsd4_layout_ops[gdev->gd_layout_type];
46829cf514ccSChristoph Hellwig 	nfserr = ops->encode_getdeviceinfo(xdr, gdev);
46839cf514ccSChristoph Hellwig 	if (nfserr) {
46849cf514ccSChristoph Hellwig 		/*
46859cf514ccSChristoph Hellwig 		 * We don't bother to burden the layout drivers with
46869cf514ccSChristoph Hellwig 		 * enforcing gd_maxcount, just tell the client to
46879cf514ccSChristoph Hellwig 		 * come back with a bigger buffer if it's not enough.
46889cf514ccSChristoph Hellwig 		 */
46899cf514ccSChristoph Hellwig 		if (xdr->buf->len + 4 > gdev->gd_maxcount)
46909cf514ccSChristoph Hellwig 			goto toosmall;
4691bac966d6SJ. Bruce Fields 		return nfserr;
46929cf514ccSChristoph Hellwig 	}
46939cf514ccSChristoph Hellwig 
46949cf514ccSChristoph Hellwig 	if (gdev->gd_notify_types) {
46959cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4 + 4);
46969cf514ccSChristoph Hellwig 		if (!p)
4697bac966d6SJ. Bruce Fields 			return nfserr_resource;
46989cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(1);			/* bitmap length */
46999cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(gdev->gd_notify_types);
47009cf514ccSChristoph Hellwig 	} else {
47019cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4);
47029cf514ccSChristoph Hellwig 		if (!p)
4703bac966d6SJ. Bruce Fields 			return nfserr_resource;
47049cf514ccSChristoph Hellwig 		*p++ = 0;
47059cf514ccSChristoph Hellwig 	}
47069cf514ccSChristoph Hellwig 
4707bac966d6SJ. Bruce Fields 	return 0;
47089cf514ccSChristoph Hellwig toosmall:
47099cf514ccSChristoph Hellwig 	dprintk("%s: maxcount too small\n", __func__);
47109cf514ccSChristoph Hellwig 	needed_len = xdr->buf->len + 4 /* notifications */;
47119cf514ccSChristoph Hellwig 	xdr_truncate_encode(xdr, starting_len);
47129cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
4713bac966d6SJ. Bruce Fields 	if (!p)
4714bac966d6SJ. Bruce Fields 		return nfserr_resource;
47159cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(needed_len);
4716bac966d6SJ. Bruce Fields 	return nfserr_toosmall;
47179cf514ccSChristoph Hellwig }
47189cf514ccSChristoph Hellwig 
47199cf514ccSChristoph Hellwig static __be32
nfsd4_encode_layoutget(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)47209cf514ccSChristoph Hellwig nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
4721e78e274eSKees Cook 		union nfsd4_op_u *u)
47229cf514ccSChristoph Hellwig {
4723e78e274eSKees Cook 	struct nfsd4_layoutget *lgp = &u->layoutget;
4724bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4725f961e3f2SJ. Bruce Fields 	const struct nfsd4_layout_ops *ops;
47269cf514ccSChristoph Hellwig 	__be32 *p;
47279cf514ccSChristoph Hellwig 
47289cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 36 + sizeof(stateid_opaque_t));
47299cf514ccSChristoph Hellwig 	if (!p)
4730bac966d6SJ. Bruce Fields 		return nfserr_resource;
47319cf514ccSChristoph Hellwig 
47329cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(1);	/* we always set return-on-close */
47339cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_sid.si_generation);
47349cf514ccSChristoph Hellwig 	p = xdr_encode_opaque_fixed(p, &lgp->lg_sid.si_opaque,
47359cf514ccSChristoph Hellwig 				    sizeof(stateid_opaque_t));
47369cf514ccSChristoph Hellwig 
47379cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(1);	/* we always return a single layout */
47389cf514ccSChristoph Hellwig 	p = xdr_encode_hyper(p, lgp->lg_seg.offset);
47399cf514ccSChristoph Hellwig 	p = xdr_encode_hyper(p, lgp->lg_seg.length);
47409cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_seg.iomode);
47419cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_layout_type);
47429cf514ccSChristoph Hellwig 
4743f961e3f2SJ. Bruce Fields 	ops = nfsd4_layout_ops[lgp->lg_layout_type];
4744bac966d6SJ. Bruce Fields 	return ops->encode_layoutget(xdr, lgp);
47459cf514ccSChristoph Hellwig }
47469cf514ccSChristoph Hellwig 
47479cf514ccSChristoph Hellwig static __be32
nfsd4_encode_layoutcommit(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)47489cf514ccSChristoph Hellwig nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr,
4749e78e274eSKees Cook 			  union nfsd4_op_u *u)
47509cf514ccSChristoph Hellwig {
4751e78e274eSKees Cook 	struct nfsd4_layoutcommit *lcp = &u->layoutcommit;
4752bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
47539cf514ccSChristoph Hellwig 	__be32 *p;
47549cf514ccSChristoph Hellwig 
47559cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
47569cf514ccSChristoph Hellwig 	if (!p)
47579cf514ccSChristoph Hellwig 		return nfserr_resource;
47589cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lcp->lc_size_chg);
47599cf514ccSChristoph Hellwig 	if (lcp->lc_size_chg) {
47609cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 8);
47619cf514ccSChristoph Hellwig 		if (!p)
47629cf514ccSChristoph Hellwig 			return nfserr_resource;
47639cf514ccSChristoph Hellwig 		p = xdr_encode_hyper(p, lcp->lc_newsize);
47649cf514ccSChristoph Hellwig 	}
47659cf514ccSChristoph Hellwig 
4766bac966d6SJ. Bruce Fields 	return 0;
47679cf514ccSChristoph Hellwig }
47689cf514ccSChristoph Hellwig 
47699cf514ccSChristoph Hellwig static __be32
nfsd4_encode_layoutreturn(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)47709cf514ccSChristoph Hellwig nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
4771e78e274eSKees Cook 		union nfsd4_op_u *u)
47729cf514ccSChristoph Hellwig {
4773e78e274eSKees Cook 	struct nfsd4_layoutreturn *lrp = &u->layoutreturn;
4774bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
47759cf514ccSChristoph Hellwig 	__be32 *p;
47769cf514ccSChristoph Hellwig 
47779cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
47789cf514ccSChristoph Hellwig 	if (!p)
47799cf514ccSChristoph Hellwig 		return nfserr_resource;
47809cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lrp->lrs_present);
47819cf514ccSChristoph Hellwig 	if (lrp->lrs_present)
4782376675daSKinglong Mee 		return nfsd4_encode_stateid(xdr, &lrp->lr_sid);
4783bac966d6SJ. Bruce Fields 	return 0;
47849cf514ccSChristoph Hellwig }
47859cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
47869cf514ccSChristoph Hellwig 
47872db134ebSAndy Adamson static __be32
nfsd42_encode_write_res(struct nfsd4_compoundres * resp,struct nfsd42_write_res * write,bool sync)4788e0639dc5SOlga Kornievskaia nfsd42_encode_write_res(struct nfsd4_compoundres *resp,
4789e0639dc5SOlga Kornievskaia 		struct nfsd42_write_res *write, bool sync)
479029ae7f9dSAnna Schumaker {
479129ae7f9dSAnna Schumaker 	__be32 *p;
4792bddfdbcdSChuck Lever 	p = xdr_reserve_space(resp->xdr, 4);
479329ae7f9dSAnna Schumaker 	if (!p)
479429ae7f9dSAnna Schumaker 		return nfserr_resource;
479529ae7f9dSAnna Schumaker 
4796e0639dc5SOlga Kornievskaia 	if (sync)
479729ae7f9dSAnna Schumaker 		*p++ = cpu_to_be32(0);
4798e0639dc5SOlga Kornievskaia 	else {
4799e0639dc5SOlga Kornievskaia 		__be32 nfserr;
4800e0639dc5SOlga Kornievskaia 		*p++ = cpu_to_be32(1);
4801bddfdbcdSChuck Lever 		nfserr = nfsd4_encode_stateid(resp->xdr, &write->cb_stateid);
4802e0639dc5SOlga Kornievskaia 		if (nfserr)
4803e0639dc5SOlga Kornievskaia 			return nfserr;
4804e0639dc5SOlga Kornievskaia 	}
4805bddfdbcdSChuck Lever 	p = xdr_reserve_space(resp->xdr, 8 + 4 + NFS4_VERIFIER_SIZE);
4806e0639dc5SOlga Kornievskaia 	if (!p)
4807e0639dc5SOlga Kornievskaia 		return nfserr_resource;
4808e0639dc5SOlga Kornievskaia 
480929ae7f9dSAnna Schumaker 	p = xdr_encode_hyper(p, write->wr_bytes_written);
481029ae7f9dSAnna Schumaker 	*p++ = cpu_to_be32(write->wr_stable_how);
481129ae7f9dSAnna Schumaker 	p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
481229ae7f9dSAnna Schumaker 				    NFS4_VERIFIER_SIZE);
481329ae7f9dSAnna Schumaker 	return nfs_ok;
481429ae7f9dSAnna Schumaker }
481529ae7f9dSAnna Schumaker 
481629ae7f9dSAnna Schumaker static __be32
nfsd42_encode_nl4_server(struct nfsd4_compoundres * resp,struct nl4_server * ns)481751911868SOlga Kornievskaia nfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns)
481851911868SOlga Kornievskaia {
4819bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
482051911868SOlga Kornievskaia 	struct nfs42_netaddr *addr;
482151911868SOlga Kornievskaia 	__be32 *p;
482251911868SOlga Kornievskaia 
482351911868SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 4);
482451911868SOlga Kornievskaia 	*p++ = cpu_to_be32(ns->nl4_type);
482551911868SOlga Kornievskaia 
482651911868SOlga Kornievskaia 	switch (ns->nl4_type) {
482751911868SOlga Kornievskaia 	case NL4_NETADDR:
482851911868SOlga Kornievskaia 		addr = &ns->u.nl4_addr;
482951911868SOlga Kornievskaia 
483051911868SOlga Kornievskaia 		/* netid_len, netid, uaddr_len, uaddr (port included
483151911868SOlga Kornievskaia 		 * in RPCBIND_MAXUADDRLEN)
483251911868SOlga Kornievskaia 		 */
483351911868SOlga Kornievskaia 		p = xdr_reserve_space(xdr,
483451911868SOlga Kornievskaia 			4 /* netid len */ +
483551911868SOlga Kornievskaia 			(XDR_QUADLEN(addr->netid_len) * 4) +
483651911868SOlga Kornievskaia 			4 /* uaddr len */ +
483751911868SOlga Kornievskaia 			(XDR_QUADLEN(addr->addr_len) * 4));
483851911868SOlga Kornievskaia 		if (!p)
483951911868SOlga Kornievskaia 			return nfserr_resource;
484051911868SOlga Kornievskaia 
484151911868SOlga Kornievskaia 		*p++ = cpu_to_be32(addr->netid_len);
484251911868SOlga Kornievskaia 		p = xdr_encode_opaque_fixed(p, addr->netid,
484351911868SOlga Kornievskaia 					    addr->netid_len);
484451911868SOlga Kornievskaia 		*p++ = cpu_to_be32(addr->addr_len);
484551911868SOlga Kornievskaia 		p = xdr_encode_opaque_fixed(p, addr->addr,
484651911868SOlga Kornievskaia 					addr->addr_len);
484751911868SOlga Kornievskaia 		break;
484851911868SOlga Kornievskaia 	default:
484951911868SOlga Kornievskaia 		WARN_ON_ONCE(ns->nl4_type != NL4_NETADDR);
485051911868SOlga Kornievskaia 		return nfserr_inval;
485151911868SOlga Kornievskaia 	}
485251911868SOlga Kornievskaia 
485351911868SOlga Kornievskaia 	return 0;
485451911868SOlga Kornievskaia }
485551911868SOlga Kornievskaia 
485651911868SOlga Kornievskaia static __be32
nfsd4_encode_copy(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)485729ae7f9dSAnna Schumaker nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
4858e78e274eSKees Cook 		  union nfsd4_op_u *u)
485929ae7f9dSAnna Schumaker {
4860e78e274eSKees Cook 	struct nfsd4_copy *copy = &u->copy;
486129ae7f9dSAnna Schumaker 	__be32 *p;
486229ae7f9dSAnna Schumaker 
4863e0639dc5SOlga Kornievskaia 	nfserr = nfsd42_encode_write_res(resp, &copy->cp_res,
48641913cdf5SChuck Lever 					 nfsd4_copy_is_sync(copy));
486529ae7f9dSAnna Schumaker 	if (nfserr)
486629ae7f9dSAnna Schumaker 		return nfserr;
486729ae7f9dSAnna Schumaker 
4868bddfdbcdSChuck Lever 	p = xdr_reserve_space(resp->xdr, 4 + 4);
4869edcc8452SJ. Bruce Fields 	*p++ = xdr_one; /* cr_consecutive */
48701913cdf5SChuck Lever 	*p = nfsd4_copy_is_sync(copy) ? xdr_one : xdr_zero;
4871bac966d6SJ. Bruce Fields 	return 0;
487229ae7f9dSAnna Schumaker }
487329ae7f9dSAnna Schumaker 
487429ae7f9dSAnna Schumaker static __be32
nfsd4_encode_offload_status(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)48756308bc98SOlga Kornievskaia nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
4876e78e274eSKees Cook 			    union nfsd4_op_u *u)
48776308bc98SOlga Kornievskaia {
4878e78e274eSKees Cook 	struct nfsd4_offload_status *os = &u->offload_status;
4879bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
48806308bc98SOlga Kornievskaia 	__be32 *p;
48816308bc98SOlga Kornievskaia 
48826308bc98SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 8 + 4);
48836308bc98SOlga Kornievskaia 	if (!p)
48846308bc98SOlga Kornievskaia 		return nfserr_resource;
48856308bc98SOlga Kornievskaia 	p = xdr_encode_hyper(p, os->count);
48866308bc98SOlga Kornievskaia 	*p++ = cpu_to_be32(0);
4887528b8493SAnna Schumaker 	return nfserr;
4888528b8493SAnna Schumaker }
4889528b8493SAnna Schumaker 
4890528b8493SAnna Schumaker static __be32
nfsd4_encode_read_plus_data(struct nfsd4_compoundres * resp,struct nfsd4_read * read)4891528b8493SAnna Schumaker nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
4892eeadcb75SAnna Schumaker 			    struct nfsd4_read *read)
4893528b8493SAnna Schumaker {
4894eeadcb75SAnna Schumaker 	bool splice_ok = test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags);
4895528b8493SAnna Schumaker 	struct file *file = read->rd_nf->nf_file;
4896eeadcb75SAnna Schumaker 	struct xdr_stream *xdr = resp->xdr;
4897eeadcb75SAnna Schumaker 	unsigned long maxcount;
4898eeadcb75SAnna Schumaker 	__be32 nfserr, *p;
4899528b8493SAnna Schumaker 
4900528b8493SAnna Schumaker 	/* Content type, offset, byte count */
4901528b8493SAnna Schumaker 	p = xdr_reserve_space(xdr, 4 + 8 + 4);
4902528b8493SAnna Schumaker 	if (!p)
4903eeadcb75SAnna Schumaker 		return nfserr_io;
4904eeadcb75SAnna Schumaker 	if (resp->xdr->buf->page_len && splice_ok) {
4905eeadcb75SAnna Schumaker 		WARN_ON_ONCE(splice_ok);
4906eeadcb75SAnna Schumaker 		return nfserr_serverfault;
4907528b8493SAnna Schumaker 	}
4908528b8493SAnna Schumaker 
4909eeadcb75SAnna Schumaker 	maxcount = min_t(unsigned long, read->rd_length,
4910eeadcb75SAnna Schumaker 			 (xdr->buf->buflen - xdr->buf->len));
49112db27992SAnna Schumaker 
4912eeadcb75SAnna Schumaker 	if (file->f_op->splice_read && splice_ok)
4913eeadcb75SAnna Schumaker 		nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
4914eeadcb75SAnna Schumaker 	else
4915eeadcb75SAnna Schumaker 		nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
4916eeadcb75SAnna Schumaker 	if (nfserr)
4917eeadcb75SAnna Schumaker 		return nfserr;
4918278765eaSAnna Schumaker 
4919eeadcb75SAnna Schumaker 	*p++ = cpu_to_be32(NFS4_CONTENT_DATA);
49202db27992SAnna Schumaker 	p = xdr_encode_hyper(p, read->rd_offset);
4921eeadcb75SAnna Schumaker 	*p = cpu_to_be32(read->rd_length);
49222db27992SAnna Schumaker 
49232db27992SAnna Schumaker 	return nfs_ok;
49242db27992SAnna Schumaker }
49252db27992SAnna Schumaker 
49262db27992SAnna Schumaker static __be32
nfsd4_encode_read_plus(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)4927528b8493SAnna Schumaker nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
4928e78e274eSKees Cook 		       union nfsd4_op_u *u)
4929528b8493SAnna Schumaker {
4930e78e274eSKees Cook 	struct nfsd4_read *read = &u->read;
4931eeadcb75SAnna Schumaker 	struct file *file = read->rd_nf->nf_file;
4932bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
4933528b8493SAnna Schumaker 	int starting_len = xdr->buf->len;
4934eeadcb75SAnna Schumaker 	u32 segments = 0;
4935eeadcb75SAnna Schumaker 	__be32 *p;
4936528b8493SAnna Schumaker 
4937528b8493SAnna Schumaker 	if (nfserr)
4938528b8493SAnna Schumaker 		return nfserr;
4939528b8493SAnna Schumaker 
4940528b8493SAnna Schumaker 	/* eof flag, segment count */
4941528b8493SAnna Schumaker 	p = xdr_reserve_space(xdr, 4 + 4);
4942528b8493SAnna Schumaker 	if (!p)
4943eeadcb75SAnna Schumaker 		return nfserr_io;
4944528b8493SAnna Schumaker 	xdr_commit_encode(xdr);
4945528b8493SAnna Schumaker 
4946eeadcb75SAnna Schumaker 	read->rd_eof = read->rd_offset >= i_size_read(file_inode(file));
4947eeadcb75SAnna Schumaker 	if (read->rd_eof)
49482db27992SAnna Schumaker 		goto out;
49492db27992SAnna Schumaker 
4950eeadcb75SAnna Schumaker 	nfserr = nfsd4_encode_read_plus_data(resp, read);
4951eeadcb75SAnna Schumaker 	if (nfserr) {
4952eeadcb75SAnna Schumaker 		xdr_truncate_encode(xdr, starting_len);
4953eeadcb75SAnna Schumaker 		return nfserr;
4954528b8493SAnna Schumaker 	}
4955528b8493SAnna Schumaker 
4956eeadcb75SAnna Schumaker 	segments++;
4957eeadcb75SAnna Schumaker 
49582db27992SAnna Schumaker out:
4959eeadcb75SAnna Schumaker 	p = xdr_encode_bool(p, read->rd_eof);
4960eeadcb75SAnna Schumaker 	*p = cpu_to_be32(segments);
49616308bc98SOlga Kornievskaia 	return nfserr;
49626308bc98SOlga Kornievskaia }
49636308bc98SOlga Kornievskaia 
49646308bc98SOlga Kornievskaia static __be32
nfsd4_encode_copy_notify(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)496551911868SOlga Kornievskaia nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
4966e78e274eSKees Cook 			 union nfsd4_op_u *u)
496751911868SOlga Kornievskaia {
4968e78e274eSKees Cook 	struct nfsd4_copy_notify *cn = &u->copy_notify;
4969bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
497051911868SOlga Kornievskaia 	__be32 *p;
497151911868SOlga Kornievskaia 
497251911868SOlga Kornievskaia 	if (nfserr)
497351911868SOlga Kornievskaia 		return nfserr;
497451911868SOlga Kornievskaia 
497551911868SOlga Kornievskaia 	/* 8 sec, 4 nsec */
497651911868SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 12);
497751911868SOlga Kornievskaia 	if (!p)
497851911868SOlga Kornievskaia 		return nfserr_resource;
497951911868SOlga Kornievskaia 
498051911868SOlga Kornievskaia 	/* cnr_lease_time */
498151911868SOlga Kornievskaia 	p = xdr_encode_hyper(p, cn->cpn_sec);
498251911868SOlga Kornievskaia 	*p++ = cpu_to_be32(cn->cpn_nsec);
498351911868SOlga Kornievskaia 
498451911868SOlga Kornievskaia 	/* cnr_stateid */
498551911868SOlga Kornievskaia 	nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid);
498651911868SOlga Kornievskaia 	if (nfserr)
498751911868SOlga Kornievskaia 		return nfserr;
498851911868SOlga Kornievskaia 
498951911868SOlga Kornievskaia 	/* cnr_src.nl_nsvr */
499051911868SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 4);
499151911868SOlga Kornievskaia 	if (!p)
499251911868SOlga Kornievskaia 		return nfserr_resource;
499351911868SOlga Kornievskaia 
499451911868SOlga Kornievskaia 	*p++ = cpu_to_be32(1);
499551911868SOlga Kornievskaia 
499609426ef2SChuck Lever 	nfserr = nfsd42_encode_nl4_server(resp, cn->cpn_src);
499709426ef2SChuck Lever 	return nfserr;
499851911868SOlga Kornievskaia }
499951911868SOlga Kornievskaia 
500051911868SOlga Kornievskaia static __be32
nfsd4_encode_seek(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)500124bab491SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
5002e78e274eSKees Cook 		  union nfsd4_op_u *u)
500324bab491SAnna Schumaker {
5004e78e274eSKees Cook 	struct nfsd4_seek *seek = &u->seek;
500524bab491SAnna Schumaker 	__be32 *p;
500624bab491SAnna Schumaker 
5007bddfdbcdSChuck Lever 	p = xdr_reserve_space(resp->xdr, 4 + 8);
500824bab491SAnna Schumaker 	*p++ = cpu_to_be32(seek->seek_eof);
500924bab491SAnna Schumaker 	p = xdr_encode_hyper(p, seek->seek_pos);
501024bab491SAnna Schumaker 
5011bac966d6SJ. Bruce Fields 	return 0;
501224bab491SAnna Schumaker }
501324bab491SAnna Schumaker 
501424bab491SAnna Schumaker static __be32
nfsd4_encode_noop(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * p)5015e78e274eSKees Cook nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr,
5016e78e274eSKees Cook 		  union nfsd4_op_u *p)
5017695e12f8SBenny Halevy {
5018695e12f8SBenny Halevy 	return nfserr;
5019695e12f8SBenny Halevy }
5020695e12f8SBenny Halevy 
502123e50fe3SFrank van der Linden /*
502223e50fe3SFrank van der Linden  * Encode kmalloc-ed buffer in to XDR stream.
502323e50fe3SFrank van der Linden  */
5024b9a49237SChuck Lever static __be32
nfsd4_vbuf_to_stream(struct xdr_stream * xdr,char * buf,u32 buflen)502523e50fe3SFrank van der Linden nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
502623e50fe3SFrank van der Linden {
502723e50fe3SFrank van der Linden 	u32 cplen;
502823e50fe3SFrank van der Linden 	__be32 *p;
502923e50fe3SFrank van der Linden 
503023e50fe3SFrank van der Linden 	cplen = min_t(unsigned long, buflen,
503123e50fe3SFrank van der Linden 		      ((void *)xdr->end - (void *)xdr->p));
503223e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, cplen);
503323e50fe3SFrank van der Linden 	if (!p)
503423e50fe3SFrank van der Linden 		return nfserr_resource;
503523e50fe3SFrank van der Linden 
503623e50fe3SFrank van der Linden 	memcpy(p, buf, cplen);
503723e50fe3SFrank van der Linden 	buf += cplen;
503823e50fe3SFrank van der Linden 	buflen -= cplen;
503923e50fe3SFrank van der Linden 
504023e50fe3SFrank van der Linden 	while (buflen) {
504123e50fe3SFrank van der Linden 		cplen = min_t(u32, buflen, PAGE_SIZE);
504223e50fe3SFrank van der Linden 		p = xdr_reserve_space(xdr, cplen);
504323e50fe3SFrank van der Linden 		if (!p)
504423e50fe3SFrank van der Linden 			return nfserr_resource;
504523e50fe3SFrank van der Linden 
504623e50fe3SFrank van der Linden 		memcpy(p, buf, cplen);
504723e50fe3SFrank van der Linden 
504823e50fe3SFrank van der Linden 		if (cplen < PAGE_SIZE) {
504923e50fe3SFrank van der Linden 			/*
505023e50fe3SFrank van der Linden 			 * We're done, with a length that wasn't page
505123e50fe3SFrank van der Linden 			 * aligned, so possibly not word aligned. Pad
505223e50fe3SFrank van der Linden 			 * any trailing bytes with 0.
505323e50fe3SFrank van der Linden 			 */
505423e50fe3SFrank van der Linden 			xdr_encode_opaque_fixed(p, NULL, cplen);
505523e50fe3SFrank van der Linden 			break;
505623e50fe3SFrank van der Linden 		}
505723e50fe3SFrank van der Linden 
505823e50fe3SFrank van der Linden 		buflen -= PAGE_SIZE;
505923e50fe3SFrank van der Linden 		buf += PAGE_SIZE;
506023e50fe3SFrank van der Linden 	}
506123e50fe3SFrank van der Linden 
506223e50fe3SFrank van der Linden 	return 0;
506323e50fe3SFrank van der Linden }
506423e50fe3SFrank van der Linden 
506523e50fe3SFrank van der Linden static __be32
nfsd4_encode_getxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)506623e50fe3SFrank van der Linden nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
5067e78e274eSKees Cook 		      union nfsd4_op_u *u)
506823e50fe3SFrank van der Linden {
5069e78e274eSKees Cook 	struct nfsd4_getxattr *getxattr = &u->getxattr;
5070bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
507123e50fe3SFrank van der Linden 	__be32 *p, err;
507223e50fe3SFrank van der Linden 
507323e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 4);
507423e50fe3SFrank van der Linden 	if (!p)
507523e50fe3SFrank van der Linden 		return nfserr_resource;
507623e50fe3SFrank van der Linden 
507723e50fe3SFrank van der Linden 	*p = cpu_to_be32(getxattr->getxa_len);
507823e50fe3SFrank van der Linden 
507923e50fe3SFrank van der Linden 	if (getxattr->getxa_len == 0)
508023e50fe3SFrank van der Linden 		return 0;
508123e50fe3SFrank van der Linden 
508223e50fe3SFrank van der Linden 	err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
508323e50fe3SFrank van der Linden 				    getxattr->getxa_len);
508423e50fe3SFrank van der Linden 
508523e50fe3SFrank van der Linden 	kvfree(getxattr->getxa_buf);
508623e50fe3SFrank van der Linden 
508723e50fe3SFrank van der Linden 	return err;
508823e50fe3SFrank van der Linden }
508923e50fe3SFrank van der Linden 
509023e50fe3SFrank van der Linden static __be32
nfsd4_encode_setxattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)509123e50fe3SFrank van der Linden nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
5092e78e274eSKees Cook 		      union nfsd4_op_u *u)
509323e50fe3SFrank van der Linden {
5094e78e274eSKees Cook 	struct nfsd4_setxattr *setxattr = &u->setxattr;
5095bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
509623e50fe3SFrank van der Linden 
509766a21db7SChuck Lever 	return nfsd4_encode_change_info4(xdr, &setxattr->setxa_cinfo);
509823e50fe3SFrank van der Linden }
509923e50fe3SFrank van der Linden 
510023e50fe3SFrank van der Linden /*
510123e50fe3SFrank van der Linden  * See if there are cookie values that can be rejected outright.
510223e50fe3SFrank van der Linden  */
510323e50fe3SFrank van der Linden static __be32
nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs * listxattrs,u32 * offsetp)510423e50fe3SFrank van der Linden nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
510523e50fe3SFrank van der Linden 				u32 *offsetp)
510623e50fe3SFrank van der Linden {
510723e50fe3SFrank van der Linden 	u64 cookie = listxattrs->lsxa_cookie;
510823e50fe3SFrank van der Linden 
510923e50fe3SFrank van der Linden 	/*
511023e50fe3SFrank van der Linden 	 * If the cookie is larger than the maximum number we can fit
511123e50fe3SFrank van der Linden 	 * in either the buffer we just got back from vfs_listxattr, or,
511223e50fe3SFrank van der Linden 	 * XDR-encoded, in the return buffer, it's invalid.
511323e50fe3SFrank van der Linden 	 */
511423e50fe3SFrank van der Linden 	if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
511523e50fe3SFrank van der Linden 		return nfserr_badcookie;
511623e50fe3SFrank van der Linden 
511723e50fe3SFrank van der Linden 	if (cookie > (listxattrs->lsxa_maxcount /
511823e50fe3SFrank van der Linden 		      (XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4)))
511923e50fe3SFrank van der Linden 		return nfserr_badcookie;
512023e50fe3SFrank van der Linden 
512123e50fe3SFrank van der Linden 	*offsetp = (u32)cookie;
512223e50fe3SFrank van der Linden 	return 0;
512323e50fe3SFrank van der Linden }
512423e50fe3SFrank van der Linden 
512523e50fe3SFrank van der Linden static __be32
nfsd4_encode_listxattrs(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)512623e50fe3SFrank van der Linden nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
5127e78e274eSKees Cook 			union nfsd4_op_u *u)
512823e50fe3SFrank van der Linden {
5129e78e274eSKees Cook 	struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
5130bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
513123e50fe3SFrank van der Linden 	u32 cookie_offset, count_offset, eof;
513223e50fe3SFrank van der Linden 	u32 left, xdrleft, slen, count;
513323e50fe3SFrank van der Linden 	u32 xdrlen, offset;
513423e50fe3SFrank van der Linden 	u64 cookie;
513523e50fe3SFrank van der Linden 	char *sp;
5136b9a49237SChuck Lever 	__be32 status, tmp;
513723e50fe3SFrank van der Linden 	__be32 *p;
513823e50fe3SFrank van der Linden 	u32 nuser;
513923e50fe3SFrank van der Linden 
514023e50fe3SFrank van der Linden 	eof = 1;
514123e50fe3SFrank van der Linden 
514223e50fe3SFrank van der Linden 	status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
514323e50fe3SFrank van der Linden 	if (status)
514423e50fe3SFrank van der Linden 		goto out;
514523e50fe3SFrank van der Linden 
514623e50fe3SFrank van der Linden 	/*
514723e50fe3SFrank van der Linden 	 * Reserve space for the cookie and the name array count. Record
514823e50fe3SFrank van der Linden 	 * the offsets to save them later.
514923e50fe3SFrank van der Linden 	 */
515023e50fe3SFrank van der Linden 	cookie_offset = xdr->buf->len;
515123e50fe3SFrank van der Linden 	count_offset = cookie_offset + 8;
515223e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 12);
515323e50fe3SFrank van der Linden 	if (!p) {
515423e50fe3SFrank van der Linden 		status = nfserr_resource;
515523e50fe3SFrank van der Linden 		goto out;
515623e50fe3SFrank van der Linden 	}
515723e50fe3SFrank van der Linden 
515823e50fe3SFrank van der Linden 	count = 0;
515923e50fe3SFrank van der Linden 	left = listxattrs->lsxa_len;
516023e50fe3SFrank van der Linden 	sp = listxattrs->lsxa_buf;
516123e50fe3SFrank van der Linden 	nuser = 0;
516223e50fe3SFrank van der Linden 
516323e50fe3SFrank van der Linden 	xdrleft = listxattrs->lsxa_maxcount;
516423e50fe3SFrank van der Linden 
516523e50fe3SFrank van der Linden 	while (left > 0 && xdrleft > 0) {
516623e50fe3SFrank van der Linden 		slen = strlen(sp);
516723e50fe3SFrank van der Linden 
516823e50fe3SFrank van der Linden 		/*
51694cce11faSAlex Dewar 		 * Check if this is a "user." attribute, skip it if not.
517023e50fe3SFrank van der Linden 		 */
517123e50fe3SFrank van der Linden 		if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
517223e50fe3SFrank van der Linden 			goto contloop;
517323e50fe3SFrank van der Linden 
517423e50fe3SFrank van der Linden 		slen -= XATTR_USER_PREFIX_LEN;
517523e50fe3SFrank van der Linden 		xdrlen = 4 + ((slen + 3) & ~3);
517623e50fe3SFrank van der Linden 		if (xdrlen > xdrleft) {
517723e50fe3SFrank van der Linden 			if (count == 0) {
517823e50fe3SFrank van der Linden 				/*
517923e50fe3SFrank van der Linden 				 * Can't even fit the first attribute name.
518023e50fe3SFrank van der Linden 				 */
518123e50fe3SFrank van der Linden 				status = nfserr_toosmall;
518223e50fe3SFrank van der Linden 				goto out;
518323e50fe3SFrank van der Linden 			}
518423e50fe3SFrank van der Linden 			eof = 0;
518523e50fe3SFrank van der Linden 			goto wreof;
518623e50fe3SFrank van der Linden 		}
518723e50fe3SFrank van der Linden 
518823e50fe3SFrank van der Linden 		left -= XATTR_USER_PREFIX_LEN;
518923e50fe3SFrank van der Linden 		sp += XATTR_USER_PREFIX_LEN;
519023e50fe3SFrank van der Linden 		if (nuser++ < offset)
519123e50fe3SFrank van der Linden 			goto contloop;
519223e50fe3SFrank van der Linden 
519323e50fe3SFrank van der Linden 
519423e50fe3SFrank van der Linden 		p = xdr_reserve_space(xdr, xdrlen);
519523e50fe3SFrank van der Linden 		if (!p) {
519623e50fe3SFrank van der Linden 			status = nfserr_resource;
519723e50fe3SFrank van der Linden 			goto out;
519823e50fe3SFrank van der Linden 		}
519923e50fe3SFrank van der Linden 
5200e2a1840eSAlex Dewar 		xdr_encode_opaque(p, sp, slen);
520123e50fe3SFrank van der Linden 
520223e50fe3SFrank van der Linden 		xdrleft -= xdrlen;
520323e50fe3SFrank van der Linden 		count++;
520423e50fe3SFrank van der Linden contloop:
520523e50fe3SFrank van der Linden 		sp += slen + 1;
520623e50fe3SFrank van der Linden 		left -= slen + 1;
520723e50fe3SFrank van der Linden 	}
520823e50fe3SFrank van der Linden 
520923e50fe3SFrank van der Linden 	/*
521023e50fe3SFrank van der Linden 	 * If there were user attributes to copy, but we didn't copy
521123e50fe3SFrank van der Linden 	 * any, the offset was too large (e.g. the cookie was invalid).
521223e50fe3SFrank van der Linden 	 */
521323e50fe3SFrank van der Linden 	if (nuser > 0 && count == 0) {
521423e50fe3SFrank van der Linden 		status = nfserr_badcookie;
521523e50fe3SFrank van der Linden 		goto out;
521623e50fe3SFrank van der Linden 	}
521723e50fe3SFrank van der Linden 
521823e50fe3SFrank van der Linden wreof:
521923e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 4);
522023e50fe3SFrank van der Linden 	if (!p) {
522123e50fe3SFrank van der Linden 		status = nfserr_resource;
522223e50fe3SFrank van der Linden 		goto out;
522323e50fe3SFrank van der Linden 	}
522423e50fe3SFrank van der Linden 	*p = cpu_to_be32(eof);
522523e50fe3SFrank van der Linden 
522623e50fe3SFrank van der Linden 	cookie = offset + count;
522723e50fe3SFrank van der Linden 
522823e50fe3SFrank van der Linden 	write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8);
5229b9a49237SChuck Lever 	tmp = cpu_to_be32(count);
5230b9a49237SChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4);
523123e50fe3SFrank van der Linden out:
523223e50fe3SFrank van der Linden 	if (listxattrs->lsxa_len)
523323e50fe3SFrank van der Linden 		kvfree(listxattrs->lsxa_buf);
523423e50fe3SFrank van der Linden 	return status;
523523e50fe3SFrank van der Linden }
523623e50fe3SFrank van der Linden 
523723e50fe3SFrank van der Linden static __be32
nfsd4_encode_removexattr(struct nfsd4_compoundres * resp,__be32 nfserr,union nfsd4_op_u * u)523823e50fe3SFrank van der Linden nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
5239e78e274eSKees Cook 			 union nfsd4_op_u *u)
524023e50fe3SFrank van der Linden {
5241e78e274eSKees Cook 	struct nfsd4_removexattr *removexattr = &u->removexattr;
5242bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
524323e50fe3SFrank van der Linden 
524466a21db7SChuck Lever 	return nfsd4_encode_change_info4(xdr, &removexattr->rmxa_cinfo);
524523e50fe3SFrank van der Linden }
524623e50fe3SFrank van der Linden 
5247e78e274eSKees Cook typedef __be32(*nfsd4_enc)(struct nfsd4_compoundres *, __be32, union nfsd4_op_u *u);
5248695e12f8SBenny Halevy 
52492db134ebSAndy Adamson /*
52502db134ebSAndy Adamson  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
52512db134ebSAndy Adamson  * since we don't need to filter out obsolete ops as this is
52522db134ebSAndy Adamson  * done in the decoding phase.
52532db134ebSAndy Adamson  */
5254c1df609dSChuck Lever static const nfsd4_enc nfsd4_enc_ops[] = {
5255e78e274eSKees Cook 	[OP_ACCESS]		= nfsd4_encode_access,
5256e78e274eSKees Cook 	[OP_CLOSE]		= nfsd4_encode_close,
5257e78e274eSKees Cook 	[OP_COMMIT]		= nfsd4_encode_commit,
5258e78e274eSKees Cook 	[OP_CREATE]		= nfsd4_encode_create,
5259e78e274eSKees Cook 	[OP_DELEGPURGE]		= nfsd4_encode_noop,
5260e78e274eSKees Cook 	[OP_DELEGRETURN]	= nfsd4_encode_noop,
5261e78e274eSKees Cook 	[OP_GETATTR]		= nfsd4_encode_getattr,
5262e78e274eSKees Cook 	[OP_GETFH]		= nfsd4_encode_getfh,
5263e78e274eSKees Cook 	[OP_LINK]		= nfsd4_encode_link,
5264e78e274eSKees Cook 	[OP_LOCK]		= nfsd4_encode_lock,
5265e78e274eSKees Cook 	[OP_LOCKT]		= nfsd4_encode_lockt,
5266e78e274eSKees Cook 	[OP_LOCKU]		= nfsd4_encode_locku,
5267e78e274eSKees Cook 	[OP_LOOKUP]		= nfsd4_encode_noop,
5268e78e274eSKees Cook 	[OP_LOOKUPP]		= nfsd4_encode_noop,
5269e78e274eSKees Cook 	[OP_NVERIFY]		= nfsd4_encode_noop,
5270e78e274eSKees Cook 	[OP_OPEN]		= nfsd4_encode_open,
5271e78e274eSKees Cook 	[OP_OPENATTR]		= nfsd4_encode_noop,
5272e78e274eSKees Cook 	[OP_OPEN_CONFIRM]	= nfsd4_encode_open_confirm,
5273e78e274eSKees Cook 	[OP_OPEN_DOWNGRADE]	= nfsd4_encode_open_downgrade,
5274e78e274eSKees Cook 	[OP_PUTFH]		= nfsd4_encode_noop,
5275e78e274eSKees Cook 	[OP_PUTPUBFH]		= nfsd4_encode_noop,
5276e78e274eSKees Cook 	[OP_PUTROOTFH]		= nfsd4_encode_noop,
5277e78e274eSKees Cook 	[OP_READ]		= nfsd4_encode_read,
5278e78e274eSKees Cook 	[OP_READDIR]		= nfsd4_encode_readdir,
5279e78e274eSKees Cook 	[OP_READLINK]		= nfsd4_encode_readlink,
5280e78e274eSKees Cook 	[OP_REMOVE]		= nfsd4_encode_remove,
5281e78e274eSKees Cook 	[OP_RENAME]		= nfsd4_encode_rename,
5282e78e274eSKees Cook 	[OP_RENEW]		= nfsd4_encode_noop,
5283e78e274eSKees Cook 	[OP_RESTOREFH]		= nfsd4_encode_noop,
5284e78e274eSKees Cook 	[OP_SAVEFH]		= nfsd4_encode_noop,
5285e78e274eSKees Cook 	[OP_SECINFO]		= nfsd4_encode_secinfo,
5286e78e274eSKees Cook 	[OP_SETATTR]		= nfsd4_encode_setattr,
5287e78e274eSKees Cook 	[OP_SETCLIENTID]	= nfsd4_encode_setclientid,
5288e78e274eSKees Cook 	[OP_SETCLIENTID_CONFIRM] = nfsd4_encode_noop,
5289e78e274eSKees Cook 	[OP_VERIFY]		= nfsd4_encode_noop,
5290e78e274eSKees Cook 	[OP_WRITE]		= nfsd4_encode_write,
5291e78e274eSKees Cook 	[OP_RELEASE_LOCKOWNER]	= nfsd4_encode_noop,
52922db134ebSAndy Adamson 
52932db134ebSAndy Adamson 	/* NFSv4.1 operations */
5294e78e274eSKees Cook 	[OP_BACKCHANNEL_CTL]	= nfsd4_encode_noop,
5295e78e274eSKees Cook 	[OP_BIND_CONN_TO_SESSION] = nfsd4_encode_bind_conn_to_session,
5296e78e274eSKees Cook 	[OP_EXCHANGE_ID]	= nfsd4_encode_exchange_id,
5297e78e274eSKees Cook 	[OP_CREATE_SESSION]	= nfsd4_encode_create_session,
5298e78e274eSKees Cook 	[OP_DESTROY_SESSION]	= nfsd4_encode_noop,
5299e78e274eSKees Cook 	[OP_FREE_STATEID]	= nfsd4_encode_noop,
5300e78e274eSKees Cook 	[OP_GET_DIR_DELEGATION]	= nfsd4_encode_noop,
53019cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
5302e78e274eSKees Cook 	[OP_GETDEVICEINFO]	= nfsd4_encode_getdeviceinfo,
5303e78e274eSKees Cook 	[OP_GETDEVICELIST]	= nfsd4_encode_noop,
5304e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_encode_layoutcommit,
5305e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_encode_layoutget,
5306e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_encode_layoutreturn,
53079cf514ccSChristoph Hellwig #else
5308e78e274eSKees Cook 	[OP_GETDEVICEINFO]	= nfsd4_encode_noop,
5309e78e274eSKees Cook 	[OP_GETDEVICELIST]	= nfsd4_encode_noop,
5310e78e274eSKees Cook 	[OP_LAYOUTCOMMIT]	= nfsd4_encode_noop,
5311e78e274eSKees Cook 	[OP_LAYOUTGET]		= nfsd4_encode_noop,
5312e78e274eSKees Cook 	[OP_LAYOUTRETURN]	= nfsd4_encode_noop,
53139cf514ccSChristoph Hellwig #endif
5314e78e274eSKees Cook 	[OP_SECINFO_NO_NAME]	= nfsd4_encode_secinfo_no_name,
5315e78e274eSKees Cook 	[OP_SEQUENCE]		= nfsd4_encode_sequence,
5316e78e274eSKees Cook 	[OP_SET_SSV]		= nfsd4_encode_noop,
5317e78e274eSKees Cook 	[OP_TEST_STATEID]	= nfsd4_encode_test_stateid,
5318e78e274eSKees Cook 	[OP_WANT_DELEGATION]	= nfsd4_encode_noop,
5319e78e274eSKees Cook 	[OP_DESTROY_CLIENTID]	= nfsd4_encode_noop,
5320e78e274eSKees Cook 	[OP_RECLAIM_COMPLETE]	= nfsd4_encode_noop,
532187a15a80SAnna Schumaker 
532287a15a80SAnna Schumaker 	/* NFSv4.2 operations */
5323e78e274eSKees Cook 	[OP_ALLOCATE]		= nfsd4_encode_noop,
5324e78e274eSKees Cook 	[OP_COPY]		= nfsd4_encode_copy,
5325e78e274eSKees Cook 	[OP_COPY_NOTIFY]	= nfsd4_encode_copy_notify,
5326e78e274eSKees Cook 	[OP_DEALLOCATE]		= nfsd4_encode_noop,
5327e78e274eSKees Cook 	[OP_IO_ADVISE]		= nfsd4_encode_noop,
5328e78e274eSKees Cook 	[OP_LAYOUTERROR]	= nfsd4_encode_noop,
5329e78e274eSKees Cook 	[OP_LAYOUTSTATS]	= nfsd4_encode_noop,
5330e78e274eSKees Cook 	[OP_OFFLOAD_CANCEL]	= nfsd4_encode_noop,
5331e78e274eSKees Cook 	[OP_OFFLOAD_STATUS]	= nfsd4_encode_offload_status,
5332e78e274eSKees Cook 	[OP_READ_PLUS]		= nfsd4_encode_read_plus,
5333e78e274eSKees Cook 	[OP_SEEK]		= nfsd4_encode_seek,
5334e78e274eSKees Cook 	[OP_WRITE_SAME]		= nfsd4_encode_noop,
5335e78e274eSKees Cook 	[OP_CLONE]		= nfsd4_encode_noop,
533623e50fe3SFrank van der Linden 
533723e50fe3SFrank van der Linden 	/* RFC 8276 extended atributes operations */
5338e78e274eSKees Cook 	[OP_GETXATTR]		= nfsd4_encode_getxattr,
5339e78e274eSKees Cook 	[OP_SETXATTR]		= nfsd4_encode_setxattr,
5340e78e274eSKees Cook 	[OP_LISTXATTRS]		= nfsd4_encode_listxattrs,
5341e78e274eSKees Cook 	[OP_REMOVEXATTR]	= nfsd4_encode_removexattr,
5342695e12f8SBenny Halevy };
5343695e12f8SBenny Halevy 
5344496c262cSAndy Adamson /*
5345a8095f7eSJ. Bruce Fields  * Calculate whether we still have space to encode repsize bytes.
5346a8095f7eSJ. Bruce Fields  * There are two considerations:
5347a8095f7eSJ. Bruce Fields  *     - For NFS versions >=4.1, the size of the reply must stay within
5348a8095f7eSJ. Bruce Fields  *       session limits
5349a8095f7eSJ. Bruce Fields  *     - For all NFS versions, we must stay within limited preallocated
5350a8095f7eSJ. Bruce Fields  *       buffer space.
5351496c262cSAndy Adamson  *
5352a8095f7eSJ. Bruce Fields  * This is called before the operation is processed, so can only provide
5353a8095f7eSJ. Bruce Fields  * an upper estimate.  For some nonidempotent operations (such as
5354a8095f7eSJ. Bruce Fields  * getattr), it's not necessarily a problem if that estimate is wrong,
5355a8095f7eSJ. Bruce Fields  * as we can fail it after processing without significant side effects.
5356496c262cSAndy Adamson  */
nfsd4_check_resp_size(struct nfsd4_compoundres * resp,u32 respsize)5357a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
5358496c262cSAndy Adamson {
535967492c99SJ. Bruce Fields 	struct xdr_buf *buf = &resp->rqstp->rq_res;
5360a8095f7eSJ. Bruce Fields 	struct nfsd4_slot *slot = resp->cstate.slot;
5361496c262cSAndy Adamson 
536247ee5298SJ. Bruce Fields 	if (buf->len + respsize <= buf->buflen)
536347ee5298SJ. Bruce Fields 		return nfs_ok;
536447ee5298SJ. Bruce Fields 	if (!nfsd4_has_session(&resp->cstate))
536547ee5298SJ. Bruce Fields 		return nfserr_resource;
536647ee5298SJ. Bruce Fields 	if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
536747ee5298SJ. Bruce Fields 		WARN_ON_ONCE(1);
5368496c262cSAndy Adamson 		return nfserr_rep_too_big_to_cache;
5369ea8d7720SJ. Bruce Fields 	}
537047ee5298SJ. Bruce Fields 	return nfserr_rep_too_big;
5371496c262cSAndy Adamson }
5372496c262cSAndy Adamson 
53731da177e4SLinus Torvalds void
nfsd4_encode_operation(struct nfsd4_compoundres * resp,struct nfsd4_op * op)53741da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
53751da177e4SLinus Torvalds {
5376bddfdbcdSChuck Lever 	struct xdr_stream *xdr = resp->xdr;
53779411b1d4SJ. Bruce Fields 	struct nfs4_stateowner *so = resp->cstate.replay_owner;
53785f4ab945SJ. Bruce Fields 	struct svc_rqst *rqstp = resp->rqstp;
537934b1744cSJ. Bruce Fields 	const struct nfsd4_operation *opdesc = op->opdesc;
5380082d4bd7SJ. Bruce Fields 	int post_err_offset;
538107d1f802SJ. Bruce Fields 	nfsd4_enc encoder;
5382bc749ca4SJ. Bruce Fields 	__be32 *p;
53831da177e4SLinus Torvalds 
5384d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
538515a8b55dSJeff Layton 	if (!p)
538615a8b55dSJeff Layton 		goto release;
5387c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(op->opnum);
5388082d4bd7SJ. Bruce Fields 	post_err_offset = xdr->buf->len;
53891da177e4SLinus Torvalds 
5390695e12f8SBenny Halevy 	if (op->opnum == OP_ILLEGAL)
5391695e12f8SBenny Halevy 		goto status;
5392b7571e4cSJ. Bruce Fields 	if (op->status && opdesc &&
5393b7571e4cSJ. Bruce Fields 			!(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE))
5394b7571e4cSJ. Bruce Fields 		goto status;
53953a237b4aSChuck Lever 	BUG_ON(op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
5396695e12f8SBenny Halevy 	       !nfsd4_enc_ops[op->opnum]);
539707d1f802SJ. Bruce Fields 	encoder = nfsd4_enc_ops[op->opnum];
539807d1f802SJ. Bruce Fields 	op->status = encoder(resp, op->status, &op->u);
539908281341SChuck Lever 	if (op->status)
540008281341SChuck Lever 		trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status);
54012825a7f9SJ. Bruce Fields 	xdr_commit_encode(xdr);
54022825a7f9SJ. Bruce Fields 
5403067e1aceSJ. Bruce Fields 	/* nfsd4_check_resp_size guarantees enough room for error status */
54045f4ab945SJ. Bruce Fields 	if (!op->status) {
54055f4ab945SJ. Bruce Fields 		int space_needed = 0;
54065f4ab945SJ. Bruce Fields 		if (!nfsd4_last_compound_op(rqstp))
54075f4ab945SJ. Bruce Fields 			space_needed = COMPOUND_ERR_SLACK_SPACE;
54085f4ab945SJ. Bruce Fields 		op->status = nfsd4_check_resp_size(resp, space_needed);
54095f4ab945SJ. Bruce Fields 	}
5410c8f13d97SJ. Bruce Fields 	if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
5411c8f13d97SJ. Bruce Fields 		struct nfsd4_slot *slot = resp->cstate.slot;
5412c8f13d97SJ. Bruce Fields 
5413c8f13d97SJ. Bruce Fields 		if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
5414c8f13d97SJ. Bruce Fields 			op->status = nfserr_rep_too_big_to_cache;
5415c8f13d97SJ. Bruce Fields 		else
5416c8f13d97SJ. Bruce Fields 			op->status = nfserr_rep_too_big;
5417c8f13d97SJ. Bruce Fields 	}
541807d1f802SJ. Bruce Fields 	if (op->status == nfserr_resource ||
541907d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big ||
542007d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big_to_cache) {
542107d1f802SJ. Bruce Fields 		/*
542207d1f802SJ. Bruce Fields 		 * The operation may have already been encoded or
542307d1f802SJ. Bruce Fields 		 * partially encoded.  No op returns anything additional
542407d1f802SJ. Bruce Fields 		 * in the case of one of these three errors, so we can
542507d1f802SJ. Bruce Fields 		 * just truncate back to after the status.  But it's a
542607d1f802SJ. Bruce Fields 		 * bug if we had to do this on a non-idempotent op:
542707d1f802SJ. Bruce Fields 		 */
542807d1f802SJ. Bruce Fields 		warn_on_nonidempotent_op(op);
5429082d4bd7SJ. Bruce Fields 		xdr_truncate_encode(xdr, post_err_offset);
543007d1f802SJ. Bruce Fields 	}
54319411b1d4SJ. Bruce Fields 	if (so) {
5432082d4bd7SJ. Bruce Fields 		int len = xdr->buf->len - post_err_offset;
5433082d4bd7SJ. Bruce Fields 
54349411b1d4SJ. Bruce Fields 		so->so_replay.rp_status = op->status;
5435082d4bd7SJ. Bruce Fields 		so->so_replay.rp_buflen = len;
5436082d4bd7SJ. Bruce Fields 		read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
5437082d4bd7SJ. Bruce Fields 						so->so_replay.rp_buf, len);
54389411b1d4SJ. Bruce Fields 	}
5439695e12f8SBenny Halevy status:
5440095a764bSChuck Lever 	*p = op->status;
544115a8b55dSJeff Layton release:
544215a8b55dSJeff Layton 	if (opdesc && opdesc->op_release)
544315a8b55dSJeff Layton 		opdesc->op_release(&op->u);
5444ed4a567aSChuck Lever 
5445ed4a567aSChuck Lever 	/*
5446ed4a567aSChuck Lever 	 * Account for pages consumed while encoding this operation.
5447ed4a567aSChuck Lever 	 * The xdr_stream primitives don't manage rq_next_page.
5448ed4a567aSChuck Lever 	 */
5449ed4a567aSChuck Lever 	rqstp->rq_next_page = xdr->page_ptr + 1;
54501da177e4SLinus Torvalds }
54511da177e4SLinus Torvalds 
54521da177e4SLinus Torvalds /*
54531da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
54541da177e4SLinus Torvalds  *
54551da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
54561da177e4SLinus Torvalds  * previously sent already encoded operation.
54571da177e4SLinus Torvalds  */
54581da177e4SLinus Torvalds void
nfsd4_encode_replay(struct xdr_stream * xdr,struct nfsd4_op * op)5459d0a381ddSJ. Bruce Fields nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
54601da177e4SLinus Torvalds {
5461bc749ca4SJ. Bruce Fields 	__be32 *p;
54621da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
54631da177e4SLinus Torvalds 
5464d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
5465d0a381ddSJ. Bruce Fields 	if (!p) {
5466d0a381ddSJ. Bruce Fields 		WARN_ON_ONCE(1);
5467d0a381ddSJ. Bruce Fields 		return;
5468d0a381ddSJ. Bruce Fields 	}
5469c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(op->opnum);
54701da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
54711da177e4SLinus Torvalds 
54720c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen);
54731da177e4SLinus Torvalds }
54741da177e4SLinus Torvalds 
nfsd4_release_compoundargs(struct svc_rqst * rqstp)54758537488bSChristoph Hellwig void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
54761da177e4SLinus Torvalds {
54773e98abffSJ. Bruce Fields 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
54783e98abffSJ. Bruce Fields 
54791da177e4SLinus Torvalds 	if (args->ops != args->iops) {
548080e591ceSChuck Lever 		vfree(args->ops);
54811da177e4SLinus Torvalds 		args->ops = args->iops;
54821da177e4SLinus Torvalds 	}
54831da177e4SLinus Torvalds 	while (args->to_free) {
5484d5e23383SJ. Bruce Fields 		struct svcxdr_tmpbuf *tb = args->to_free;
54851da177e4SLinus Torvalds 		args->to_free = tb->next;
54861da177e4SLinus Torvalds 		kfree(tb);
54871da177e4SLinus Torvalds 	}
54881da177e4SLinus Torvalds }
54891da177e4SLinus Torvalds 
5490c44b31c2SChuck Lever bool
nfs4svc_decode_compoundargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)549116c66364SChuck Lever nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
54921da177e4SLinus Torvalds {
5493026fec7eSChristoph Hellwig 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
5494026fec7eSChristoph Hellwig 
5495c1346a12SChuck Lever 	/* svcxdr_tmp_alloc */
54961da177e4SLinus Torvalds 	args->to_free = NULL;
5497c1346a12SChuck Lever 
549816c66364SChuck Lever 	args->xdr = xdr;
54991da177e4SLinus Torvalds 	args->ops = args->iops;
55001da177e4SLinus Torvalds 	args->rqstp = rqstp;
55011da177e4SLinus Torvalds 
5502d9b74bdaSChuck Lever 	return nfsd4_decode_compound(args);
55031da177e4SLinus Torvalds }
55041da177e4SLinus Torvalds 
5505130e2054SChuck Lever bool
nfs4svc_encode_compoundres(struct svc_rqst * rqstp,struct xdr_stream * xdr)5506fda49441SChuck Lever nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
55071da177e4SLinus Torvalds {
550863f8de37SChristoph Hellwig 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
5509fda49441SChuck Lever 	__be32 *p;
55106ac90391SJ. Bruce Fields 
55113b0ebb25SChuck Lever 	/*
55123b0ebb25SChuck Lever 	 * Send buffer space for the following items is reserved
55133b0ebb25SChuck Lever 	 * at the top of nfsd4_proc_compound().
55143b0ebb25SChuck Lever 	 */
55153b0ebb25SChuck Lever 	p = resp->statusp;
5516cc028a10SChuck Lever 
55173b0ebb25SChuck Lever 	*p++ = resp->cstate.status;
55181da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
55191da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
55201da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
55211da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
55221da177e4SLinus Torvalds 
5523b607664eSTrond Myklebust 	nfsd4_sequence_done(resp);
5524130e2054SChuck Lever 	return true;
55251da177e4SLinus Torvalds }
5526