xref: /linux/fs/nfsd/nfs4xdr.c (revision 08281341be8ebc97ee47999812bcf411942baa1e)
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>
4523e50fe3SFrank van der Linden #include <uapi/linux/xattr.h>
469a74af21SBoaz Harrosh 
472ca72e17SJ. Bruce Fields #include "idmap.h"
482ca72e17SJ. Bruce Fields #include "acl.h"
499a74af21SBoaz Harrosh #include "xdr4.h"
500a3adadeSJ. Bruce Fields #include "vfs.h"
5117456804SBryan Schumaker #include "state.h"
521091006cSJ. Bruce Fields #include "cache.h"
533d733711SStanislav Kinsbursky #include "netns.h"
549cf514ccSChristoph Hellwig #include "pnfs.h"
555c4583b2SJeff Layton #include "filecache.h"
562ca72e17SJ. Bruce Fields 
57*08281341SChuck Lever #include "trace.h"
58*08281341SChuck Lever 
5918032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
6018032ca0SDavid Quigley #include <linux/security.h>
6118032ca0SDavid Quigley #endif
6218032ca0SDavid Quigley 
6318032ca0SDavid Quigley 
641da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
651da177e4SLinus Torvalds 
665cf23dbbSJ. Bruce Fields const u32 nfsd_suppattrs[3][3] = {
67916d2d84SJ. Bruce Fields 	{NFSD4_SUPPORTED_ATTRS_WORD0,
68916d2d84SJ. Bruce Fields 	 NFSD4_SUPPORTED_ATTRS_WORD1,
69916d2d84SJ. Bruce Fields 	 NFSD4_SUPPORTED_ATTRS_WORD2},
70916d2d84SJ. Bruce Fields 
71916d2d84SJ. Bruce Fields 	{NFSD4_1_SUPPORTED_ATTRS_WORD0,
72916d2d84SJ. Bruce Fields 	 NFSD4_1_SUPPORTED_ATTRS_WORD1,
73916d2d84SJ. Bruce Fields 	 NFSD4_1_SUPPORTED_ATTRS_WORD2},
74916d2d84SJ. Bruce Fields 
75916d2d84SJ. Bruce Fields 	{NFSD4_1_SUPPORTED_ATTRS_WORD0,
76916d2d84SJ. Bruce Fields 	 NFSD4_1_SUPPORTED_ATTRS_WORD1,
77916d2d84SJ. Bruce Fields 	 NFSD4_2_SUPPORTED_ATTRS_WORD2},
78916d2d84SJ. Bruce Fields };
79916d2d84SJ. Bruce Fields 
8042ca0993SJ.Bruce Fields /*
8142ca0993SJ.Bruce Fields  * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
8242ca0993SJ.Bruce Fields  * directory in order to indicate to the client that a filesystem boundary is present
8342ca0993SJ.Bruce Fields  * We use a fixed fsid for a referral
8442ca0993SJ.Bruce Fields  */
8542ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
8642ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL
8742ca0993SJ.Bruce Fields 
88b37ad28bSAl Viro static __be32
89a36b1725SJ. Bruce Fields check_filename(char *str, int len)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	int i;
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	if (len == 0)
941da177e4SLinus Torvalds 		return nfserr_inval;
951da177e4SLinus Torvalds 	if (isdotent(str, len))
96a36b1725SJ. Bruce Fields 		return nfserr_badname;
971da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
981da177e4SLinus Torvalds 		if (str[i] == '/')
99a36b1725SJ. Bruce Fields 			return nfserr_badname;
1001da177e4SLinus Torvalds 	return 0;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds #define DECODE_HEAD				\
1042ebbc012SAl Viro 	__be32 *p;				\
105b37ad28bSAl Viro 	__be32 status
1061da177e4SLinus Torvalds #define DECODE_TAIL				\
1071da177e4SLinus Torvalds 	status = 0;				\
1081da177e4SLinus Torvalds out:						\
1091da177e4SLinus Torvalds 	return status;				\
1101da177e4SLinus Torvalds xdr_error:					\
111817cb9d4SChuck Lever 	dprintk("NFSD: xdr error (%s:%d)\n",	\
112817cb9d4SChuck Lever 			__FILE__, __LINE__);	\
1131da177e4SLinus Torvalds 	status = nfserr_bad_xdr;		\
1141da177e4SLinus Torvalds 	goto out
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds #define READMEM(x,nbytes) do {			\
1171da177e4SLinus Torvalds 	x = (char *)p;				\
1181da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1191da177e4SLinus Torvalds } while (0)
1201da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do {			\
1211da177e4SLinus Torvalds 	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
1221da177e4SLinus Torvalds  		savemem(argp, p, nbytes) :	\
1231da177e4SLinus Torvalds  		(char *)p)) {			\
124817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
125817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1261da177e4SLinus Torvalds 		goto xdr_error;			\
1271da177e4SLinus Torvalds 		}				\
1281da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1291da177e4SLinus Torvalds } while (0)
1301da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do {			\
1311da177e4SLinus Torvalds 	memcpy((x), p, nbytes);			\
1321da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1331da177e4SLinus Torvalds } while (0)
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
1361da177e4SLinus Torvalds #define READ_BUF(nbytes)  do {			\
1371da177e4SLinus Torvalds 	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
1381da177e4SLinus Torvalds 		p = argp->p;			\
1391da177e4SLinus Torvalds 		argp->p += XDR_QUADLEN(nbytes);	\
1401da177e4SLinus Torvalds 	} else if (!(p = read_buf(argp, nbytes))) { \
141817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
142817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1431da177e4SLinus Torvalds 		goto xdr_error;			\
1441da177e4SLinus Torvalds 	}					\
1451da177e4SLinus Torvalds } while (0)
1461da177e4SLinus Torvalds 
147590b7431SJ. Bruce Fields static void next_decode_page(struct nfsd4_compoundargs *argp)
148590b7431SJ. Bruce Fields {
149590b7431SJ. Bruce Fields 	argp->p = page_address(argp->pagelist[0]);
150365da4adSJ. Bruce Fields 	argp->pagelist++;
151590b7431SJ. Bruce Fields 	if (argp->pagelen < PAGE_SIZE) {
152fc788f64SChuck Lever 		argp->end = argp->p + XDR_QUADLEN(argp->pagelen);
153590b7431SJ. Bruce Fields 		argp->pagelen = 0;
154590b7431SJ. Bruce Fields 	} else {
155590b7431SJ. Bruce Fields 		argp->end = argp->p + (PAGE_SIZE>>2);
156590b7431SJ. Bruce Fields 		argp->pagelen -= PAGE_SIZE;
157590b7431SJ. Bruce Fields 	}
158590b7431SJ. Bruce Fields }
159590b7431SJ. Bruce Fields 
160ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds 	/* We want more bytes than seem to be available.
1631da177e4SLinus Torvalds 	 * Maybe we need a new page, maybe we have just run out
1641da177e4SLinus Torvalds 	 */
165ca2a05aaSJ. Bruce Fields 	unsigned int avail = (char *)argp->end - (char *)argp->p;
1662ebbc012SAl Viro 	__be32 *p;
167eae03e2aSChuck Lever 
168eae03e2aSChuck Lever 	if (argp->pagelen == 0) {
169eae03e2aSChuck Lever 		struct kvec *vec = &argp->rqstp->rq_arg.tail[0];
170eae03e2aSChuck Lever 
171eae03e2aSChuck Lever 		if (!argp->tail) {
172eae03e2aSChuck Lever 			argp->tail = true;
173eae03e2aSChuck Lever 			avail = vec->iov_len;
174eae03e2aSChuck Lever 			argp->p = vec->iov_base;
175eae03e2aSChuck Lever 			argp->end = vec->iov_base + avail;
176eae03e2aSChuck Lever 		}
177eae03e2aSChuck Lever 
178eae03e2aSChuck Lever 		if (avail < nbytes)
179eae03e2aSChuck Lever 			return NULL;
180eae03e2aSChuck Lever 
181eae03e2aSChuck Lever 		p = argp->p;
182eae03e2aSChuck Lever 		argp->p += XDR_QUADLEN(nbytes);
183eae03e2aSChuck Lever 		return p;
184eae03e2aSChuck Lever 	}
185eae03e2aSChuck Lever 
1861da177e4SLinus Torvalds 	if (avail + argp->pagelen < nbytes)
1871da177e4SLinus Torvalds 		return NULL;
1881da177e4SLinus Torvalds 	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
1891da177e4SLinus Torvalds 		return NULL;
1901da177e4SLinus Torvalds 	/* ok, we can do it with the current plus the next page */
1911da177e4SLinus Torvalds 	if (nbytes <= sizeof(argp->tmp))
1921da177e4SLinus Torvalds 		p = argp->tmp;
1931da177e4SLinus Torvalds 	else {
1941da177e4SLinus Torvalds 		kfree(argp->tmpp);
1951da177e4SLinus Torvalds 		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
1961da177e4SLinus Torvalds 		if (!p)
1971da177e4SLinus Torvalds 			return NULL;
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	}
200ca2a05aaSJ. Bruce Fields 	/*
201ca2a05aaSJ. Bruce Fields 	 * The following memcpy is safe because read_buf is always
202ca2a05aaSJ. Bruce Fields 	 * called with nbytes > avail, and the two cases above both
203ca2a05aaSJ. Bruce Fields 	 * guarantee p points to at least nbytes bytes.
204ca2a05aaSJ. Bruce Fields 	 */
2051da177e4SLinus Torvalds 	memcpy(p, argp->p, avail);
206590b7431SJ. Bruce Fields 	next_decode_page(argp);
2071da177e4SLinus Torvalds 	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
2081da177e4SLinus Torvalds 	argp->p += XDR_QUADLEN(nbytes - avail);
2091da177e4SLinus Torvalds 	return p;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
2122b86e3aaSJ. Bruce Fields static unsigned int compoundargs_bytes_left(struct nfsd4_compoundargs *argp)
2132b86e3aaSJ. Bruce Fields {
2142b86e3aaSJ. Bruce Fields 	unsigned int this = (char *)argp->end - (char *)argp->p;
2152b86e3aaSJ. Bruce Fields 
2162b86e3aaSJ. Bruce Fields 	return this + argp->pagelen;
2172b86e3aaSJ. Bruce Fields }
2182b86e3aaSJ. Bruce Fields 
21960adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
22060adfc50SAndy Adamson {
22160adfc50SAndy Adamson 	return (clid->cl_boot == 0) && (clid->cl_id == 0);
22260adfc50SAndy Adamson }
22360adfc50SAndy Adamson 
2242d8498dbSChristoph Hellwig /**
225d5e23383SJ. Bruce Fields  * svcxdr_tmpalloc - allocate memory to be freed after compound processing
226ce043ac8SJ. Bruce Fields  * @argp: NFSv4 compound argument structure
227ed992753STrond Myklebust  * @len: length of buffer to allocate
2282d8498dbSChristoph Hellwig  *
229ed992753STrond Myklebust  * Allocates a buffer of size @len to be freed when processing the compound
230ed992753STrond Myklebust  * operation described in @argp finishes.
2312d8498dbSChristoph Hellwig  */
232d5e23383SJ. Bruce Fields static void *
233d5e23383SJ. Bruce Fields svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)
2341da177e4SLinus Torvalds {
235d5e23383SJ. Bruce Fields 	struct svcxdr_tmpbuf *tb;
2361da177e4SLinus Torvalds 
237d5e23383SJ. Bruce Fields 	tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL);
2381da177e4SLinus Torvalds 	if (!tb)
239d5e23383SJ. Bruce Fields 		return NULL;
2401da177e4SLinus Torvalds 	tb->next = argp->to_free;
2411da177e4SLinus Torvalds 	argp->to_free = tb;
242d5e23383SJ. Bruce Fields 	return tb->buf;
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds 
24529c353b3SJ. Bruce Fields /*
24629c353b3SJ. Bruce Fields  * For xdr strings that need to be passed to other kernel api's
24729c353b3SJ. Bruce Fields  * as null-terminated strings.
24829c353b3SJ. Bruce Fields  *
24929c353b3SJ. Bruce Fields  * Note null-terminating in place usually isn't safe since the
25029c353b3SJ. Bruce Fields  * buffer might end on a page boundary.
25129c353b3SJ. Bruce Fields  */
25229c353b3SJ. Bruce Fields static char *
25329c353b3SJ. Bruce Fields svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
25429c353b3SJ. Bruce Fields {
255d5e23383SJ. Bruce Fields 	char *p = svcxdr_tmpalloc(argp, len + 1);
25629c353b3SJ. Bruce Fields 
25729c353b3SJ. Bruce Fields 	if (!p)
25829c353b3SJ. Bruce Fields 		return NULL;
25929c353b3SJ. Bruce Fields 	memcpy(p, buf, len);
26029c353b3SJ. Bruce Fields 	p[len] = '\0';
26129c353b3SJ. Bruce Fields 	return p;
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
264874c7b8eSFrank van der Linden static __be32
265874c7b8eSFrank van der Linden svcxdr_construct_vector(struct nfsd4_compoundargs *argp, struct kvec *head,
266874c7b8eSFrank van der Linden 			struct page ***pagelist, u32 buflen)
267874c7b8eSFrank van der Linden {
268874c7b8eSFrank van der Linden 	int avail;
269874c7b8eSFrank van der Linden 	int len;
270874c7b8eSFrank van der Linden 	int pages;
271874c7b8eSFrank van der Linden 
272874c7b8eSFrank van der Linden 	/* Sorry .. no magic macros for this.. *
273874c7b8eSFrank van der Linden 	 * READ_BUF(write->wr_buflen);
274874c7b8eSFrank van der Linden 	 * SAVEMEM(write->wr_buf, write->wr_buflen);
275874c7b8eSFrank van der Linden 	 */
276874c7b8eSFrank van der Linden 	avail = (char *)argp->end - (char *)argp->p;
277874c7b8eSFrank van der Linden 	if (avail + argp->pagelen < buflen) {
278874c7b8eSFrank van der Linden 		dprintk("NFSD: xdr error (%s:%d)\n",
279874c7b8eSFrank van der Linden 			       __FILE__, __LINE__);
280874c7b8eSFrank van der Linden 		return nfserr_bad_xdr;
281874c7b8eSFrank van der Linden 	}
282874c7b8eSFrank van der Linden 	head->iov_base = argp->p;
283874c7b8eSFrank van der Linden 	head->iov_len = avail;
284874c7b8eSFrank van der Linden 	*pagelist = argp->pagelist;
285874c7b8eSFrank van der Linden 
286874c7b8eSFrank van der Linden 	len = XDR_QUADLEN(buflen) << 2;
287874c7b8eSFrank van der Linden 	if (len >= avail) {
288874c7b8eSFrank van der Linden 		len -= avail;
289874c7b8eSFrank van der Linden 
290874c7b8eSFrank van der Linden 		pages = len >> PAGE_SHIFT;
291874c7b8eSFrank van der Linden 		argp->pagelist += pages;
292874c7b8eSFrank van der Linden 		argp->pagelen -= pages * PAGE_SIZE;
293874c7b8eSFrank van der Linden 		len -= pages * PAGE_SIZE;
294874c7b8eSFrank van der Linden 
295874c7b8eSFrank van der Linden 		next_decode_page(argp);
296874c7b8eSFrank van der Linden 	}
297874c7b8eSFrank van der Linden 	argp->p += XDR_QUADLEN(len);
298874c7b8eSFrank van der Linden 
299874c7b8eSFrank van der Linden 	return 0;
300874c7b8eSFrank van der Linden }
301874c7b8eSFrank van der Linden 
3022d8498dbSChristoph Hellwig /**
3032d8498dbSChristoph Hellwig  * savemem - duplicate a chunk of memory for later processing
3042d8498dbSChristoph Hellwig  * @argp: NFSv4 compound argument structure to be freed with
3052d8498dbSChristoph Hellwig  * @p: pointer to be duplicated
3062d8498dbSChristoph Hellwig  * @nbytes: length to be duplicated
3072d8498dbSChristoph Hellwig  *
3082d8498dbSChristoph Hellwig  * Returns a pointer to a copy of @nbytes bytes of memory at @p
3092d8498dbSChristoph Hellwig  * that are preserved until processing of the NFSv4 compound
3102d8498dbSChristoph Hellwig  * operation described by @argp finishes.
3112d8498dbSChristoph Hellwig  */
3122ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
3131da177e4SLinus Torvalds {
314d5e23383SJ. Bruce Fields 	void *ret;
315d5e23383SJ. Bruce Fields 
316d5e23383SJ. Bruce Fields 	ret = svcxdr_tmpalloc(argp, nbytes);
317d5e23383SJ. Bruce Fields 	if (!ret)
318a4db5fe5SJ. Bruce Fields 		return NULL;
319d5e23383SJ. Bruce Fields 	memcpy(ret, p, nbytes);
320d5e23383SJ. Bruce Fields 	return ret;
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds 
3234c94e13eSChristoph Hellwig static __be32
324bdba5368SJ. Bruce Fields nfsd4_decode_time(struct nfsd4_compoundargs *argp, struct timespec64 *tv)
3254c94e13eSChristoph Hellwig {
3264c94e13eSChristoph Hellwig 	DECODE_HEAD;
3274c94e13eSChristoph Hellwig 
3284c94e13eSChristoph Hellwig 	READ_BUF(12);
329bdba5368SJ. Bruce Fields 	p = xdr_decode_hyper(p, &tv->tv_sec);
3304c94e13eSChristoph Hellwig 	tv->tv_nsec = be32_to_cpup(p++);
3314c94e13eSChristoph Hellwig 	if (tv->tv_nsec >= (u32)1000000000)
3324c94e13eSChristoph Hellwig 		return nfserr_inval;
3334c94e13eSChristoph Hellwig 
3344c94e13eSChristoph Hellwig 	DECODE_TAIL;
3354c94e13eSChristoph Hellwig }
3364c94e13eSChristoph Hellwig 
337b37ad28bSAl Viro static __be32
3381da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds 	u32 bmlen;
3411da177e4SLinus Torvalds 	DECODE_HEAD;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	bmval[0] = 0;
3441da177e4SLinus Torvalds 	bmval[1] = 0;
3457e705706SAndy Adamson 	bmval[2] = 0;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	READ_BUF(4);
34806553991SJ. Bruce Fields 	bmlen = be32_to_cpup(p++);
3491da177e4SLinus Torvalds 	if (bmlen > 1000)
3501da177e4SLinus Torvalds 		goto xdr_error;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	READ_BUF(bmlen << 2);
3531da177e4SLinus Torvalds 	if (bmlen > 0)
35406553991SJ. Bruce Fields 		bmval[0] = be32_to_cpup(p++);
3551da177e4SLinus Torvalds 	if (bmlen > 1)
35606553991SJ. Bruce Fields 		bmval[1] = be32_to_cpup(p++);
3577e705706SAndy Adamson 	if (bmlen > 2)
35806553991SJ. Bruce Fields 		bmval[2] = be32_to_cpup(p++);
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	DECODE_TAIL;
3611da177e4SLinus Torvalds }
3621da177e4SLinus Torvalds 
363b37ad28bSAl Viro static __be32
3643c8e0316SYu Zhiguo nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
36518032ca0SDavid Quigley 		   struct iattr *iattr, struct nfs4_acl **acl,
36647057abdSAndreas Gruenbacher 		   struct xdr_netobj *label, int *umask)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	int expected_len, len = 0;
3691da177e4SLinus Torvalds 	u32 dummy32;
3701da177e4SLinus Torvalds 	char *buf;
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	DECODE_HEAD;
3731da177e4SLinus Torvalds 	iattr->ia_valid = 0;
3741da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
3751da177e4SLinus Torvalds 		return status;
3761da177e4SLinus Torvalds 
377e864c189SJ. Bruce Fields 	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
378e864c189SJ. Bruce Fields 	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
379e864c189SJ. Bruce Fields 	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) {
380e864c189SJ. Bruce Fields 		if (nfsd_attrs_supported(argp->minorversion, bmval))
381e864c189SJ. Bruce Fields 			return nfserr_inval;
382e864c189SJ. Bruce Fields 		return nfserr_attrnotsupp;
383e864c189SJ. Bruce Fields 	}
384e864c189SJ. Bruce Fields 
3851da177e4SLinus Torvalds 	READ_BUF(4);
38606553991SJ. Bruce Fields 	expected_len = be32_to_cpup(p++);
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
3891da177e4SLinus Torvalds 		READ_BUF(8);
3901da177e4SLinus Torvalds 		len += 8;
391542d1ab3SJ. Bruce Fields 		p = xdr_decode_hyper(p, &iattr->ia_size);
3921da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
3931da177e4SLinus Torvalds 	}
3941da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
39564a817cfSJ. Bruce Fields 		u32 nace;
39628e05dd8SJ. Bruce Fields 		struct nfs4_ace *ace;
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 		READ_BUF(4); len += 4;
39906553991SJ. Bruce Fields 		nace = be32_to_cpup(p++);
4001da177e4SLinus Torvalds 
4012b86e3aaSJ. Bruce Fields 		if (nace > compoundargs_bytes_left(argp)/20)
4022b86e3aaSJ. Bruce Fields 			/*
4032b86e3aaSJ. Bruce Fields 			 * Even with 4-byte names there wouldn't be
4042b86e3aaSJ. Bruce Fields 			 * space for that many aces; something fishy is
4052b86e3aaSJ. Bruce Fields 			 * going on:
4062b86e3aaSJ. Bruce Fields 			 */
407798df338SJ. Bruce Fields 			return nfserr_fbig;
40828e05dd8SJ. Bruce Fields 
409d5e23383SJ. Bruce Fields 		*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
410eba1c99cSKinglong Mee 		if (*acl == NULL)
411eba1c99cSKinglong Mee 			return nfserr_jukebox;
412eba1c99cSKinglong Mee 
41328e05dd8SJ. Bruce Fields 		(*acl)->naces = nace;
41428e05dd8SJ. Bruce Fields 		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
4151da177e4SLinus Torvalds 			READ_BUF(16); len += 16;
41606553991SJ. Bruce Fields 			ace->type = be32_to_cpup(p++);
41706553991SJ. Bruce Fields 			ace->flag = be32_to_cpup(p++);
41806553991SJ. Bruce Fields 			ace->access_mask = be32_to_cpup(p++);
41906553991SJ. Bruce Fields 			dummy32 = be32_to_cpup(p++);
4201da177e4SLinus Torvalds 			READ_BUF(dummy32);
4211da177e4SLinus Torvalds 			len += XDR_QUADLEN(dummy32) << 2;
4221da177e4SLinus Torvalds 			READMEM(buf, dummy32);
42328e05dd8SJ. Bruce Fields 			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
4243c726023SJ. Bruce Fields 			status = nfs_ok;
42528e05dd8SJ. Bruce Fields 			if (ace->whotype != NFS4_ACL_WHO_NAMED)
426ab8e4aeeSEric W. Biederman 				;
42728e05dd8SJ. Bruce Fields 			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
4283c726023SJ. Bruce Fields 				status = nfsd_map_name_to_gid(argp->rqstp,
429ab8e4aeeSEric W. Biederman 						buf, dummy32, &ace->who_gid);
4301da177e4SLinus Torvalds 			else
4313c726023SJ. Bruce Fields 				status = nfsd_map_name_to_uid(argp->rqstp,
432ab8e4aeeSEric W. Biederman 						buf, dummy32, &ace->who_uid);
4333c726023SJ. Bruce Fields 			if (status)
4343c726023SJ. Bruce Fields 				return status;
4351da177e4SLinus Torvalds 		}
4361da177e4SLinus Torvalds 	} else
4371da177e4SLinus Torvalds 		*acl = NULL;
4381da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
4391da177e4SLinus Torvalds 		READ_BUF(4);
4401da177e4SLinus Torvalds 		len += 4;
44106553991SJ. Bruce Fields 		iattr->ia_mode = be32_to_cpup(p++);
4421da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
4431da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
4461da177e4SLinus Torvalds 		READ_BUF(4);
4471da177e4SLinus Torvalds 		len += 4;
44806553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
4491da177e4SLinus Torvalds 		READ_BUF(dummy32);
4501da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
4511da177e4SLinus Torvalds 		READMEM(buf, dummy32);
45247c85291SNeilBrown 		if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
45347c85291SNeilBrown 			return status;
4541da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
4551da177e4SLinus Torvalds 	}
4561da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
4571da177e4SLinus Torvalds 		READ_BUF(4);
4581da177e4SLinus Torvalds 		len += 4;
45906553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
4601da177e4SLinus Torvalds 		READ_BUF(dummy32);
4611da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
4621da177e4SLinus Torvalds 		READMEM(buf, dummy32);
46347c85291SNeilBrown 		if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
46447c85291SNeilBrown 			return status;
4651da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
4661da177e4SLinus Torvalds 	}
4671da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
4681da177e4SLinus Torvalds 		READ_BUF(4);
4691da177e4SLinus Torvalds 		len += 4;
47006553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
4711da177e4SLinus Torvalds 		switch (dummy32) {
4721da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
4731da177e4SLinus Torvalds 			len += 12;
474bdba5368SJ. Bruce Fields 			status = nfsd4_decode_time(argp, &iattr->ia_atime);
4754c94e13eSChristoph Hellwig 			if (status)
4764c94e13eSChristoph Hellwig 				return status;
4771da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
4781da177e4SLinus Torvalds 			break;
4791da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
4801da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
4811da177e4SLinus Torvalds 			break;
4821da177e4SLinus Torvalds 		default:
4831da177e4SLinus Torvalds 			goto xdr_error;
4841da177e4SLinus Torvalds 		}
4851da177e4SLinus Torvalds 	}
4861da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
4871da177e4SLinus Torvalds 		READ_BUF(4);
4881da177e4SLinus Torvalds 		len += 4;
48906553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
4901da177e4SLinus Torvalds 		switch (dummy32) {
4911da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
4921da177e4SLinus Torvalds 			len += 12;
493bdba5368SJ. Bruce Fields 			status = nfsd4_decode_time(argp, &iattr->ia_mtime);
4944c94e13eSChristoph Hellwig 			if (status)
4954c94e13eSChristoph Hellwig 				return status;
4961da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
4971da177e4SLinus Torvalds 			break;
4981da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
4991da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
5001da177e4SLinus Torvalds 			break;
5011da177e4SLinus Torvalds 		default:
5021da177e4SLinus Torvalds 			goto xdr_error;
5031da177e4SLinus Torvalds 		}
5041da177e4SLinus Torvalds 	}
50518032ca0SDavid Quigley 
50618032ca0SDavid Quigley 	label->len = 0;
5072285ae76SArnd Bergmann 	if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) &&
5082285ae76SArnd Bergmann 	    bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
50918032ca0SDavid Quigley 		READ_BUF(4);
51018032ca0SDavid Quigley 		len += 4;
51106553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++); /* lfs: we don't use it */
51218032ca0SDavid Quigley 		READ_BUF(4);
51318032ca0SDavid Quigley 		len += 4;
51406553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++); /* pi: we don't use it either */
51518032ca0SDavid Quigley 		READ_BUF(4);
51618032ca0SDavid Quigley 		len += 4;
51706553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
51818032ca0SDavid Quigley 		READ_BUF(dummy32);
5191ec8c0c4SKinglong Mee 		if (dummy32 > NFS4_MAXLABELLEN)
52018032ca0SDavid Quigley 			return nfserr_badlabel;
52118032ca0SDavid Quigley 		len += (XDR_QUADLEN(dummy32) << 2);
52218032ca0SDavid Quigley 		READMEM(buf, dummy32);
52329c353b3SJ. Bruce Fields 		label->len = dummy32;
52429c353b3SJ. Bruce Fields 		label->data = svcxdr_dupstr(argp, buf, dummy32);
52518032ca0SDavid Quigley 		if (!label->data)
52618032ca0SDavid Quigley 			return nfserr_jukebox;
52718032ca0SDavid Quigley 	}
52847057abdSAndreas Gruenbacher 	if (bmval[2] & FATTR4_WORD2_MODE_UMASK) {
52947057abdSAndreas Gruenbacher 		if (!umask)
53047057abdSAndreas Gruenbacher 			goto xdr_error;
53147057abdSAndreas Gruenbacher 		READ_BUF(8);
53247057abdSAndreas Gruenbacher 		len += 8;
53347057abdSAndreas Gruenbacher 		dummy32 = be32_to_cpup(p++);
53447057abdSAndreas Gruenbacher 		iattr->ia_mode = dummy32 & (S_IFMT | S_IALLUGO);
53547057abdSAndreas Gruenbacher 		dummy32 = be32_to_cpup(p++);
53647057abdSAndreas Gruenbacher 		*umask = dummy32 & S_IRWXUGO;
53747057abdSAndreas Gruenbacher 		iattr->ia_valid |= ATTR_MODE;
53847057abdSAndreas Gruenbacher 	}
539e864c189SJ. Bruce Fields 	if (len != expected_len)
5401da177e4SLinus Torvalds 		goto xdr_error;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	DECODE_TAIL;
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds 
545b37ad28bSAl Viro static __be32
546e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
547e31a1b66SBenny Halevy {
548e31a1b66SBenny Halevy 	DECODE_HEAD;
549e31a1b66SBenny Halevy 
550e31a1b66SBenny Halevy 	READ_BUF(sizeof(stateid_t));
55106553991SJ. Bruce Fields 	sid->si_generation = be32_to_cpup(p++);
552e31a1b66SBenny Halevy 	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
553e31a1b66SBenny Halevy 
554e31a1b66SBenny Halevy 	DECODE_TAIL;
555e31a1b66SBenny Halevy }
556e31a1b66SBenny Halevy 
557e31a1b66SBenny Halevy static __be32
5581da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
5591da177e4SLinus Torvalds {
5601da177e4SLinus Torvalds 	DECODE_HEAD;
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds 	READ_BUF(4);
56306553991SJ. Bruce Fields 	access->ac_req_access = be32_to_cpup(p++);
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	DECODE_TAIL;
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds 
568acb2887eSJ. Bruce Fields static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
569acb2887eSJ. Bruce Fields {
570acb2887eSJ. Bruce Fields 	DECODE_HEAD;
571e45d1a18STrond Myklebust 	struct user_namespace *userns = nfsd_user_namespace(argp->rqstp);
57212fc3e92SJ. Bruce Fields 	u32 dummy, uid, gid;
573acb2887eSJ. Bruce Fields 	char *machine_name;
574acb2887eSJ. Bruce Fields 	int i;
575acb2887eSJ. Bruce Fields 	int nr_secflavs;
576acb2887eSJ. Bruce Fields 
577acb2887eSJ. Bruce Fields 	/* callback_sec_params4 */
578acb2887eSJ. Bruce Fields 	READ_BUF(4);
57906553991SJ. Bruce Fields 	nr_secflavs = be32_to_cpup(p++);
58057569a70SJ. Bruce Fields 	if (nr_secflavs)
58112fc3e92SJ. Bruce Fields 		cbs->flavor = (u32)(-1);
58257569a70SJ. Bruce Fields 	else
58357569a70SJ. Bruce Fields 		/* Is this legal? Be generous, take it to mean AUTH_NONE: */
58457569a70SJ. Bruce Fields 		cbs->flavor = 0;
585acb2887eSJ. Bruce Fields 	for (i = 0; i < nr_secflavs; ++i) {
586acb2887eSJ. Bruce Fields 		READ_BUF(4);
58706553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
588acb2887eSJ. Bruce Fields 		switch (dummy) {
589acb2887eSJ. Bruce Fields 		case RPC_AUTH_NULL:
590acb2887eSJ. Bruce Fields 			/* Nothing to read */
59112fc3e92SJ. Bruce Fields 			if (cbs->flavor == (u32)(-1))
59212fc3e92SJ. Bruce Fields 				cbs->flavor = RPC_AUTH_NULL;
593acb2887eSJ. Bruce Fields 			break;
594acb2887eSJ. Bruce Fields 		case RPC_AUTH_UNIX:
595acb2887eSJ. Bruce Fields 			READ_BUF(8);
596acb2887eSJ. Bruce Fields 			/* stamp */
59706553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
598acb2887eSJ. Bruce Fields 
599acb2887eSJ. Bruce Fields 			/* machine name */
60006553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
601acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
602acb2887eSJ. Bruce Fields 			SAVEMEM(machine_name, dummy);
603acb2887eSJ. Bruce Fields 
604acb2887eSJ. Bruce Fields 			/* uid, gid */
605acb2887eSJ. Bruce Fields 			READ_BUF(8);
60606553991SJ. Bruce Fields 			uid = be32_to_cpup(p++);
60706553991SJ. Bruce Fields 			gid = be32_to_cpup(p++);
608acb2887eSJ. Bruce Fields 
609acb2887eSJ. Bruce Fields 			/* more gids */
610acb2887eSJ. Bruce Fields 			READ_BUF(4);
61106553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
612acb2887eSJ. Bruce Fields 			READ_BUF(dummy * 4);
61312fc3e92SJ. Bruce Fields 			if (cbs->flavor == (u32)(-1)) {
614e45d1a18STrond Myklebust 				kuid_t kuid = make_kuid(userns, uid);
615e45d1a18STrond Myklebust 				kgid_t kgid = make_kgid(userns, gid);
61603bc6d1cSEric W. Biederman 				if (uid_valid(kuid) && gid_valid(kgid)) {
61703bc6d1cSEric W. Biederman 					cbs->uid = kuid;
61803bc6d1cSEric W. Biederman 					cbs->gid = kgid;
61912fc3e92SJ. Bruce Fields 					cbs->flavor = RPC_AUTH_UNIX;
62003bc6d1cSEric W. Biederman 				} else {
62103bc6d1cSEric W. Biederman 					dprintk("RPC_AUTH_UNIX with invalid"
62203bc6d1cSEric W. Biederman 						"uid or gid ignoring!\n");
62303bc6d1cSEric W. Biederman 				}
62412fc3e92SJ. Bruce Fields 			}
625acb2887eSJ. Bruce Fields 			break;
626acb2887eSJ. Bruce Fields 		case RPC_AUTH_GSS:
627acb2887eSJ. Bruce Fields 			dprintk("RPC_AUTH_GSS callback secflavor "
628acb2887eSJ. Bruce Fields 				"not supported!\n");
629acb2887eSJ. Bruce Fields 			READ_BUF(8);
630acb2887eSJ. Bruce Fields 			/* gcbp_service */
63106553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
632acb2887eSJ. Bruce Fields 			/* gcbp_handle_from_server */
63306553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
634acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
635acb2887eSJ. Bruce Fields 			p += XDR_QUADLEN(dummy);
636acb2887eSJ. Bruce Fields 			/* gcbp_handle_from_client */
637acb2887eSJ. Bruce Fields 			READ_BUF(4);
63806553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
639acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
640acb2887eSJ. Bruce Fields 			break;
641acb2887eSJ. Bruce Fields 		default:
642acb2887eSJ. Bruce Fields 			dprintk("Illegal callback secflavor\n");
643acb2887eSJ. Bruce Fields 			return nfserr_inval;
644acb2887eSJ. Bruce Fields 		}
645acb2887eSJ. Bruce Fields 	}
646acb2887eSJ. Bruce Fields 	DECODE_TAIL;
647acb2887eSJ. Bruce Fields }
648acb2887eSJ. Bruce Fields 
649cb73a9f4SJ. Bruce Fields static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc)
650cb73a9f4SJ. Bruce Fields {
651cb73a9f4SJ. Bruce Fields 	DECODE_HEAD;
652cb73a9f4SJ. Bruce Fields 
653cb73a9f4SJ. Bruce Fields 	READ_BUF(4);
65406553991SJ. Bruce Fields 	bc->bc_cb_program = be32_to_cpup(p++);
655cb73a9f4SJ. Bruce Fields 	nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
656cb73a9f4SJ. Bruce Fields 
657cb73a9f4SJ. Bruce Fields 	DECODE_TAIL;
658cb73a9f4SJ. Bruce Fields }
659cb73a9f4SJ. Bruce Fields 
6601d1bc8f2SJ. Bruce Fields static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts)
6611d1bc8f2SJ. Bruce Fields {
6621d1bc8f2SJ. Bruce Fields 	DECODE_HEAD;
6631d1bc8f2SJ. Bruce Fields 
6641d1bc8f2SJ. Bruce Fields 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 8);
6651d1bc8f2SJ. Bruce Fields 	COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
66606553991SJ. Bruce Fields 	bcts->dir = be32_to_cpup(p++);
6676ce2357fSBryan Schumaker 	/* XXX: skipping ctsa_use_conn_in_rdma_mode.  Perhaps Tom Tucker
6686ce2357fSBryan Schumaker 	 * could help us figure out we should be using it. */
6691d1bc8f2SJ. Bruce Fields 	DECODE_TAIL;
6701d1bc8f2SJ. Bruce Fields }
6711d1bc8f2SJ. Bruce Fields 
672b37ad28bSAl Viro static __be32
6731da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	DECODE_HEAD;
6761da177e4SLinus Torvalds 
677e31a1b66SBenny Halevy 	READ_BUF(4);
67806553991SJ. Bruce Fields 	close->cl_seqid = be32_to_cpup(p++);
679e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &close->cl_stateid);
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	DECODE_TAIL;
6821da177e4SLinus Torvalds }
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 
685b37ad28bSAl Viro static __be32
6861da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
6871da177e4SLinus Torvalds {
6881da177e4SLinus Torvalds 	DECODE_HEAD;
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	READ_BUF(12);
691542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &commit->co_offset);
69206553991SJ. Bruce Fields 	commit->co_count = be32_to_cpup(p++);
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	DECODE_TAIL;
6951da177e4SLinus Torvalds }
6961da177e4SLinus Torvalds 
697b37ad28bSAl Viro static __be32
6981da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
6991da177e4SLinus Torvalds {
7001da177e4SLinus Torvalds 	DECODE_HEAD;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	READ_BUF(4);
70306553991SJ. Bruce Fields 	create->cr_type = be32_to_cpup(p++);
7041da177e4SLinus Torvalds 	switch (create->cr_type) {
7051da177e4SLinus Torvalds 	case NF4LNK:
7061da177e4SLinus Torvalds 		READ_BUF(4);
7077fb84306SJ. Bruce Fields 		create->cr_datalen = be32_to_cpup(p++);
7087fb84306SJ. Bruce Fields 		READ_BUF(create->cr_datalen);
70929c353b3SJ. Bruce Fields 		create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen);
7107fb84306SJ. Bruce Fields 		if (!create->cr_data)
71176f47128SJ. Bruce Fields 			return nfserr_jukebox;
7121da177e4SLinus Torvalds 		break;
7131da177e4SLinus Torvalds 	case NF4BLK:
7141da177e4SLinus Torvalds 	case NF4CHR:
7151da177e4SLinus Torvalds 		READ_BUF(8);
71606553991SJ. Bruce Fields 		create->cr_specdata1 = be32_to_cpup(p++);
71706553991SJ. Bruce Fields 		create->cr_specdata2 = be32_to_cpup(p++);
7181da177e4SLinus Torvalds 		break;
7191da177e4SLinus Torvalds 	case NF4SOCK:
7201da177e4SLinus Torvalds 	case NF4FIFO:
7211da177e4SLinus Torvalds 	case NF4DIR:
7221da177e4SLinus Torvalds 	default:
7231da177e4SLinus Torvalds 		break;
7241da177e4SLinus Torvalds 	}
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	READ_BUF(4);
72706553991SJ. Bruce Fields 	create->cr_namelen = be32_to_cpup(p++);
7281da177e4SLinus Torvalds 	READ_BUF(create->cr_namelen);
7291da177e4SLinus Torvalds 	SAVEMEM(create->cr_name, create->cr_namelen);
730a36b1725SJ. Bruce Fields 	if ((status = check_filename(create->cr_name, create->cr_namelen)))
7311da177e4SLinus Torvalds 		return status;
7321da177e4SLinus Torvalds 
7333c8e0316SYu Zhiguo 	status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
73447057abdSAndreas Gruenbacher 				    &create->cr_acl, &create->cr_label,
735880a3a53SJ. Bruce Fields 				    &create->cr_umask);
736c0d6fc8aSBenny Halevy 	if (status)
7371da177e4SLinus Torvalds 		goto out;
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 	DECODE_TAIL;
7401da177e4SLinus Torvalds }
7411da177e4SLinus Torvalds 
742b37ad28bSAl Viro static inline __be32
7431da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
7441da177e4SLinus Torvalds {
745e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
7461da177e4SLinus Torvalds }
7471da177e4SLinus Torvalds 
748b37ad28bSAl Viro static inline __be32
7491da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
7501da177e4SLinus Torvalds {
7511da177e4SLinus Torvalds 	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds 
754b37ad28bSAl Viro static __be32
7551da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
7561da177e4SLinus Torvalds {
7571da177e4SLinus Torvalds 	DECODE_HEAD;
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	READ_BUF(4);
76006553991SJ. Bruce Fields 	link->li_namelen = be32_to_cpup(p++);
7611da177e4SLinus Torvalds 	READ_BUF(link->li_namelen);
7621da177e4SLinus Torvalds 	SAVEMEM(link->li_name, link->li_namelen);
763a36b1725SJ. Bruce Fields 	if ((status = check_filename(link->li_name, link->li_namelen)))
7641da177e4SLinus Torvalds 		return status;
7651da177e4SLinus Torvalds 
7661da177e4SLinus Torvalds 	DECODE_TAIL;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds 
769b37ad28bSAl Viro static __be32
7701da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
7711da177e4SLinus Torvalds {
7721da177e4SLinus Torvalds 	DECODE_HEAD;
7731da177e4SLinus Torvalds 
7741da177e4SLinus Torvalds 	/*
7751da177e4SLinus Torvalds 	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
7761da177e4SLinus Torvalds 	*/
7771da177e4SLinus Torvalds 	READ_BUF(28);
77806553991SJ. Bruce Fields 	lock->lk_type = be32_to_cpup(p++);
7791da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
7801da177e4SLinus Torvalds 		goto xdr_error;
78106553991SJ. Bruce Fields 	lock->lk_reclaim = be32_to_cpup(p++);
782542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lock->lk_offset);
783542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lock->lk_length);
78406553991SJ. Bruce Fields 	lock->lk_is_new = be32_to_cpup(p++);
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds 	if (lock->lk_is_new) {
787e31a1b66SBenny Halevy 		READ_BUF(4);
78806553991SJ. Bruce Fields 		lock->lk_new_open_seqid = be32_to_cpup(p++);
789e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
790e31a1b66SBenny Halevy 		if (status)
791e31a1b66SBenny Halevy 			return status;
792e31a1b66SBenny Halevy 		READ_BUF(8 + sizeof(clientid_t));
79306553991SJ. Bruce Fields 		lock->lk_new_lock_seqid = be32_to_cpup(p++);
7941da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
79506553991SJ. Bruce Fields 		lock->lk_new_owner.len = be32_to_cpup(p++);
7961da177e4SLinus Torvalds 		READ_BUF(lock->lk_new_owner.len);
7971da177e4SLinus Torvalds 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
7981da177e4SLinus Torvalds 	} else {
799e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
800e31a1b66SBenny Halevy 		if (status)
801e31a1b66SBenny Halevy 			return status;
802e31a1b66SBenny Halevy 		READ_BUF(4);
80306553991SJ. Bruce Fields 		lock->lk_old_lock_seqid = be32_to_cpup(p++);
8041da177e4SLinus Torvalds 	}
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds 	DECODE_TAIL;
8071da177e4SLinus Torvalds }
8081da177e4SLinus Torvalds 
809b37ad28bSAl Viro static __be32
8101da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
8111da177e4SLinus Torvalds {
8121da177e4SLinus Torvalds 	DECODE_HEAD;
8131da177e4SLinus Torvalds 
8141da177e4SLinus Torvalds 	READ_BUF(32);
81506553991SJ. Bruce Fields 	lockt->lt_type = be32_to_cpup(p++);
8161da177e4SLinus Torvalds 	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
8171da177e4SLinus Torvalds 		goto xdr_error;
818542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lockt->lt_offset);
819542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lockt->lt_length);
8201da177e4SLinus Torvalds 	COPYMEM(&lockt->lt_clientid, 8);
82106553991SJ. Bruce Fields 	lockt->lt_owner.len = be32_to_cpup(p++);
8221da177e4SLinus Torvalds 	READ_BUF(lockt->lt_owner.len);
8231da177e4SLinus Torvalds 	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 	DECODE_TAIL;
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
828b37ad28bSAl Viro static __be32
8291da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
8301da177e4SLinus Torvalds {
8311da177e4SLinus Torvalds 	DECODE_HEAD;
8321da177e4SLinus Torvalds 
833e31a1b66SBenny Halevy 	READ_BUF(8);
83406553991SJ. Bruce Fields 	locku->lu_type = be32_to_cpup(p++);
8351da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
8361da177e4SLinus Torvalds 		goto xdr_error;
83706553991SJ. Bruce Fields 	locku->lu_seqid = be32_to_cpup(p++);
838e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
839e31a1b66SBenny Halevy 	if (status)
840e31a1b66SBenny Halevy 		return status;
841e31a1b66SBenny Halevy 	READ_BUF(16);
842542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &locku->lu_offset);
843542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &locku->lu_length);
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds 	DECODE_TAIL;
8461da177e4SLinus Torvalds }
8471da177e4SLinus Torvalds 
848b37ad28bSAl Viro static __be32
8491da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	DECODE_HEAD;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	READ_BUF(4);
85406553991SJ. Bruce Fields 	lookup->lo_len = be32_to_cpup(p++);
8551da177e4SLinus Torvalds 	READ_BUF(lookup->lo_len);
8561da177e4SLinus Torvalds 	SAVEMEM(lookup->lo_name, lookup->lo_len);
857a36b1725SJ. Bruce Fields 	if ((status = check_filename(lookup->lo_name, lookup->lo_len)))
8581da177e4SLinus Torvalds 		return status;
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 	DECODE_TAIL;
8611da177e4SLinus Torvalds }
8621da177e4SLinus Torvalds 
8632c8bd7e0SBenny Halevy static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when)
86404f9e664SJ. Bruce Fields {
86504f9e664SJ. Bruce Fields 	__be32 *p;
86604f9e664SJ. Bruce Fields 	u32 w;
86704f9e664SJ. Bruce Fields 
86804f9e664SJ. Bruce Fields 	READ_BUF(4);
86906553991SJ. Bruce Fields 	w = be32_to_cpup(p++);
8702c8bd7e0SBenny Halevy 	*share_access = w & NFS4_SHARE_ACCESS_MASK;
8712c8bd7e0SBenny Halevy 	*deleg_want = w & NFS4_SHARE_WANT_MASK;
8722c8bd7e0SBenny Halevy 	if (deleg_when)
8732c8bd7e0SBenny Halevy 		*deleg_when = w & NFS4_SHARE_WHEN_MASK;
8742c8bd7e0SBenny Halevy 
87504f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_ACCESS_MASK) {
87604f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_READ:
87704f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_WRITE:
87804f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_BOTH:
87904f9e664SJ. Bruce Fields 		break;
88004f9e664SJ. Bruce Fields 	default:
88104f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
88204f9e664SJ. Bruce Fields 	}
883fc0d14feSBenny Halevy 	w &= ~NFS4_SHARE_ACCESS_MASK;
88404f9e664SJ. Bruce Fields 	if (!w)
88504f9e664SJ. Bruce Fields 		return nfs_ok;
88604f9e664SJ. Bruce Fields 	if (!argp->minorversion)
88704f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
88804f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_WANT_MASK) {
88904f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_PREFERENCE:
89004f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_READ_DELEG:
89104f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_WRITE_DELEG:
89204f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_ANY_DELEG:
89304f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_DELEG:
89404f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_CANCEL:
89504f9e664SJ. Bruce Fields 		break;
89604f9e664SJ. Bruce Fields 	default:
89704f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
89804f9e664SJ. Bruce Fields 	}
89992bac8c5SBenny Halevy 	w &= ~NFS4_SHARE_WANT_MASK;
90004f9e664SJ. Bruce Fields 	if (!w)
90104f9e664SJ. Bruce Fields 		return nfs_ok;
9022c8bd7e0SBenny Halevy 
9032c8bd7e0SBenny Halevy 	if (!deleg_when)	/* open_downgrade */
9042c8bd7e0SBenny Halevy 		return nfserr_inval;
90504f9e664SJ. Bruce Fields 	switch (w) {
90604f9e664SJ. Bruce Fields 	case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
90704f9e664SJ. Bruce Fields 	case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
908c668fc6dSBenny Halevy 	case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
909c668fc6dSBenny Halevy 	      NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
91004f9e664SJ. Bruce Fields 		return nfs_ok;
91104f9e664SJ. Bruce Fields 	}
91204f9e664SJ. Bruce Fields xdr_error:
91304f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
91404f9e664SJ. Bruce Fields }
91504f9e664SJ. Bruce Fields 
91604f9e664SJ. Bruce Fields static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
91704f9e664SJ. Bruce Fields {
91804f9e664SJ. Bruce Fields 	__be32 *p;
91904f9e664SJ. Bruce Fields 
92004f9e664SJ. Bruce Fields 	READ_BUF(4);
92106553991SJ. Bruce Fields 	*x = be32_to_cpup(p++);
92204f9e664SJ. Bruce Fields 	/* Note: unlinke access bits, deny bits may be zero. */
92301cd4afaSDan Carpenter 	if (*x & ~NFS4_SHARE_DENY_BOTH)
92404f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
92504f9e664SJ. Bruce Fields 	return nfs_ok;
92604f9e664SJ. Bruce Fields xdr_error:
92704f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
92804f9e664SJ. Bruce Fields }
92904f9e664SJ. Bruce Fields 
930a084daf5SJ. Bruce Fields static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
931a084daf5SJ. Bruce Fields {
932a084daf5SJ. Bruce Fields 	__be32 *p;
933a084daf5SJ. Bruce Fields 
934a084daf5SJ. Bruce Fields 	READ_BUF(4);
93506553991SJ. Bruce Fields 	o->len = be32_to_cpup(p++);
936a084daf5SJ. Bruce Fields 
937a084daf5SJ. Bruce Fields 	if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT)
938a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
939a084daf5SJ. Bruce Fields 
940a084daf5SJ. Bruce Fields 	READ_BUF(o->len);
941a084daf5SJ. Bruce Fields 	SAVEMEM(o->data, o->len);
942a084daf5SJ. Bruce Fields 	return nfs_ok;
943a084daf5SJ. Bruce Fields xdr_error:
944a084daf5SJ. Bruce Fields 	return nfserr_bad_xdr;
945a084daf5SJ. Bruce Fields }
946a084daf5SJ. Bruce Fields 
947b37ad28bSAl Viro static __be32
9481da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
9491da177e4SLinus Torvalds {
9501da177e4SLinus Torvalds 	DECODE_HEAD;
9512c8bd7e0SBenny Halevy 	u32 dummy;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	memset(open->op_bmval, 0, sizeof(open->op_bmval));
9541da177e4SLinus Torvalds 	open->op_iattr.ia_valid = 0;
955fe0750e5SJ. Bruce Fields 	open->op_openowner = NULL;
9561da177e4SLinus Torvalds 
9579d313b17SJ. Bruce Fields 	open->op_xdr_error = 0;
9581da177e4SLinus Torvalds 	/* seqid, share_access, share_deny, clientid, ownerlen */
95904f9e664SJ. Bruce Fields 	READ_BUF(4);
96006553991SJ. Bruce Fields 	open->op_seqid = be32_to_cpup(p++);
9612c8bd7e0SBenny Halevy 	/* decode, yet ignore deleg_when until supported */
9622c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open->op_share_access,
9632c8bd7e0SBenny Halevy 					   &open->op_deleg_want, &dummy);
96404f9e664SJ. Bruce Fields 	if (status)
96504f9e664SJ. Bruce Fields 		goto xdr_error;
96604f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
96704f9e664SJ. Bruce Fields 	if (status)
96804f9e664SJ. Bruce Fields 		goto xdr_error;
969a084daf5SJ. Bruce Fields 	READ_BUF(sizeof(clientid_t));
9701da177e4SLinus Torvalds 	COPYMEM(&open->op_clientid, sizeof(clientid_t));
971a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &open->op_owner);
972a084daf5SJ. Bruce Fields 	if (status)
973a084daf5SJ. Bruce Fields 		goto xdr_error;
974a084daf5SJ. Bruce Fields 	READ_BUF(4);
97506553991SJ. Bruce Fields 	open->op_create = be32_to_cpup(p++);
9761da177e4SLinus Torvalds 	switch (open->op_create) {
9771da177e4SLinus Torvalds 	case NFS4_OPEN_NOCREATE:
9781da177e4SLinus Torvalds 		break;
9791da177e4SLinus Torvalds 	case NFS4_OPEN_CREATE:
9801da177e4SLinus Torvalds 		READ_BUF(4);
98106553991SJ. Bruce Fields 		open->op_createmode = be32_to_cpup(p++);
9821da177e4SLinus Torvalds 		switch (open->op_createmode) {
9831da177e4SLinus Torvalds 		case NFS4_CREATE_UNCHECKED:
9841da177e4SLinus Torvalds 		case NFS4_CREATE_GUARDED:
985c0d6fc8aSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
98647057abdSAndreas Gruenbacher 				&open->op_iattr, &open->op_acl, &open->op_label,
987880a3a53SJ. Bruce Fields 				&open->op_umask);
988c0d6fc8aSBenny Halevy 			if (status)
9891da177e4SLinus Torvalds 				goto out;
9901da177e4SLinus Torvalds 			break;
9911da177e4SLinus Torvalds 		case NFS4_CREATE_EXCLUSIVE:
992ab4684d1SChuck Lever 			READ_BUF(NFS4_VERIFIER_SIZE);
993ab4684d1SChuck Lever 			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
9941da177e4SLinus Torvalds 			break;
99579fb54abSBenny Halevy 		case NFS4_CREATE_EXCLUSIVE4_1:
99679fb54abSBenny Halevy 			if (argp->minorversion < 1)
99779fb54abSBenny Halevy 				goto xdr_error;
998ab4684d1SChuck Lever 			READ_BUF(NFS4_VERIFIER_SIZE);
999ab4684d1SChuck Lever 			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
100079fb54abSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
100147057abdSAndreas Gruenbacher 				&open->op_iattr, &open->op_acl, &open->op_label,
1002880a3a53SJ. Bruce Fields 				&open->op_umask);
100379fb54abSBenny Halevy 			if (status)
100479fb54abSBenny Halevy 				goto out;
100579fb54abSBenny Halevy 			break;
10061da177e4SLinus Torvalds 		default:
10071da177e4SLinus Torvalds 			goto xdr_error;
10081da177e4SLinus Torvalds 		}
10091da177e4SLinus Torvalds 		break;
10101da177e4SLinus Torvalds 	default:
10111da177e4SLinus Torvalds 		goto xdr_error;
10121da177e4SLinus Torvalds 	}
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 	/* open_claim */
10151da177e4SLinus Torvalds 	READ_BUF(4);
101606553991SJ. Bruce Fields 	open->op_claim_type = be32_to_cpup(p++);
10171da177e4SLinus Torvalds 	switch (open->op_claim_type) {
10181da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_NULL:
10191da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
10201da177e4SLinus Torvalds 		READ_BUF(4);
102106553991SJ. Bruce Fields 		open->op_fname.len = be32_to_cpup(p++);
10221da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
10231da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
1024a36b1725SJ. Bruce Fields 		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
10251da177e4SLinus Torvalds 			return status;
10261da177e4SLinus Torvalds 		break;
10271da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_PREVIOUS:
10281da177e4SLinus Torvalds 		READ_BUF(4);
102906553991SJ. Bruce Fields 		open->op_delegate_type = be32_to_cpup(p++);
10301da177e4SLinus Torvalds 		break;
10311da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
1032e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
1033e31a1b66SBenny Halevy 		if (status)
1034e31a1b66SBenny Halevy 			return status;
1035e31a1b66SBenny Halevy 		READ_BUF(4);
103606553991SJ. Bruce Fields 		open->op_fname.len = be32_to_cpup(p++);
10371da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
10381da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
1039a36b1725SJ. Bruce Fields 		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
10401da177e4SLinus Torvalds 			return status;
10411da177e4SLinus Torvalds 		break;
10428b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_FH:
10438b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
10448b289b2cSJ. Bruce Fields 		if (argp->minorversion < 1)
10458b289b2cSJ. Bruce Fields 			goto xdr_error;
10468b289b2cSJ. Bruce Fields 		/* void */
10478b289b2cSJ. Bruce Fields 		break;
10488b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
10498b289b2cSJ. Bruce Fields 		if (argp->minorversion < 1)
10508b289b2cSJ. Bruce Fields 			goto xdr_error;
10518b289b2cSJ. Bruce Fields 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
10528b289b2cSJ. Bruce Fields 		if (status)
10538b289b2cSJ. Bruce Fields 			return status;
10548b289b2cSJ. Bruce Fields 		break;
10551da177e4SLinus Torvalds 	default:
10561da177e4SLinus Torvalds 		goto xdr_error;
10571da177e4SLinus Torvalds 	}
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	DECODE_TAIL;
10601da177e4SLinus Torvalds }
10611da177e4SLinus Torvalds 
1062b37ad28bSAl Viro static __be32
10631da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
10641da177e4SLinus Torvalds {
10651da177e4SLinus Torvalds 	DECODE_HEAD;
10661da177e4SLinus Torvalds 
1067e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1068e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1069e1a90ebdSAnna Schumaker 
1070e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
1071e31a1b66SBenny Halevy 	if (status)
1072e31a1b66SBenny Halevy 		return status;
1073e31a1b66SBenny Halevy 	READ_BUF(4);
107406553991SJ. Bruce Fields 	open_conf->oc_seqid = be32_to_cpup(p++);
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	DECODE_TAIL;
10771da177e4SLinus Torvalds }
10781da177e4SLinus Torvalds 
1079b37ad28bSAl Viro static __be32
10801da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
10811da177e4SLinus Torvalds {
10821da177e4SLinus Torvalds 	DECODE_HEAD;
10831da177e4SLinus Torvalds 
1084e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
1085e31a1b66SBenny Halevy 	if (status)
1086e31a1b66SBenny Halevy 		return status;
108704f9e664SJ. Bruce Fields 	READ_BUF(4);
108806553991SJ. Bruce Fields 	open_down->od_seqid = be32_to_cpup(p++);
10892c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
10902c8bd7e0SBenny Halevy 					   &open_down->od_deleg_want, NULL);
109104f9e664SJ. Bruce Fields 	if (status)
109204f9e664SJ. Bruce Fields 		return status;
109304f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
109404f9e664SJ. Bruce Fields 	if (status)
109504f9e664SJ. Bruce Fields 		return status;
10961da177e4SLinus Torvalds 	DECODE_TAIL;
10971da177e4SLinus Torvalds }
10981da177e4SLinus Torvalds 
1099b37ad28bSAl Viro static __be32
11001da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
11011da177e4SLinus Torvalds {
11021da177e4SLinus Torvalds 	DECODE_HEAD;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	READ_BUF(4);
110506553991SJ. Bruce Fields 	putfh->pf_fhlen = be32_to_cpup(p++);
11061da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
11071da177e4SLinus Torvalds 		goto xdr_error;
11081da177e4SLinus Torvalds 	READ_BUF(putfh->pf_fhlen);
11091da177e4SLinus Torvalds 	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 	DECODE_TAIL;
11121da177e4SLinus Torvalds }
11131da177e4SLinus Torvalds 
1114b37ad28bSAl Viro static __be32
1115e1a90ebdSAnna Schumaker nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, void *p)
1116e1a90ebdSAnna Schumaker {
1117e1a90ebdSAnna Schumaker 	if (argp->minorversion == 0)
1118e1a90ebdSAnna Schumaker 		return nfs_ok;
1119e1a90ebdSAnna Schumaker 	return nfserr_notsupp;
1120e1a90ebdSAnna Schumaker }
1121e1a90ebdSAnna Schumaker 
1122e1a90ebdSAnna Schumaker static __be32
11231da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
11241da177e4SLinus Torvalds {
11251da177e4SLinus Torvalds 	DECODE_HEAD;
11261da177e4SLinus Torvalds 
1127e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
1128e31a1b66SBenny Halevy 	if (status)
1129e31a1b66SBenny Halevy 		return status;
1130e31a1b66SBenny Halevy 	READ_BUF(12);
1131542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &read->rd_offset);
113206553991SJ. Bruce Fields 	read->rd_length = be32_to_cpup(p++);
11331da177e4SLinus Torvalds 
11341da177e4SLinus Torvalds 	DECODE_TAIL;
11351da177e4SLinus Torvalds }
11361da177e4SLinus Torvalds 
1137b37ad28bSAl Viro static __be32
11381da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
11391da177e4SLinus Torvalds {
11401da177e4SLinus Torvalds 	DECODE_HEAD;
11411da177e4SLinus Torvalds 
11421da177e4SLinus Torvalds 	READ_BUF(24);
1143542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &readdir->rd_cookie);
11441da177e4SLinus Torvalds 	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
114506553991SJ. Bruce Fields 	readdir->rd_dircount = be32_to_cpup(p++);
114606553991SJ. Bruce Fields 	readdir->rd_maxcount = be32_to_cpup(p++);
11471da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
11481da177e4SLinus Torvalds 		goto out;
11491da177e4SLinus Torvalds 
11501da177e4SLinus Torvalds 	DECODE_TAIL;
11511da177e4SLinus Torvalds }
11521da177e4SLinus Torvalds 
1153b37ad28bSAl Viro static __be32
11541da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
11551da177e4SLinus Torvalds {
11561da177e4SLinus Torvalds 	DECODE_HEAD;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	READ_BUF(4);
115906553991SJ. Bruce Fields 	remove->rm_namelen = be32_to_cpup(p++);
11601da177e4SLinus Torvalds 	READ_BUF(remove->rm_namelen);
11611da177e4SLinus Torvalds 	SAVEMEM(remove->rm_name, remove->rm_namelen);
1162a36b1725SJ. Bruce Fields 	if ((status = check_filename(remove->rm_name, remove->rm_namelen)))
11631da177e4SLinus Torvalds 		return status;
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds 	DECODE_TAIL;
11661da177e4SLinus Torvalds }
11671da177e4SLinus Torvalds 
1168b37ad28bSAl Viro static __be32
11691da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
11701da177e4SLinus Torvalds {
11711da177e4SLinus Torvalds 	DECODE_HEAD;
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 	READ_BUF(4);
117406553991SJ. Bruce Fields 	rename->rn_snamelen = be32_to_cpup(p++);
11754aed9c46SJ. Bruce Fields 	READ_BUF(rename->rn_snamelen);
11761da177e4SLinus Torvalds 	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
11774aed9c46SJ. Bruce Fields 	READ_BUF(4);
117806553991SJ. Bruce Fields 	rename->rn_tnamelen = be32_to_cpup(p++);
11791da177e4SLinus Torvalds 	READ_BUF(rename->rn_tnamelen);
11801da177e4SLinus Torvalds 	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
1181a36b1725SJ. Bruce Fields 	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen)))
11821da177e4SLinus Torvalds 		return status;
1183a36b1725SJ. Bruce Fields 	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen)))
11841da177e4SLinus Torvalds 		return status;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 	DECODE_TAIL;
11871da177e4SLinus Torvalds }
11881da177e4SLinus Torvalds 
1189b37ad28bSAl Viro static __be32
11901da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
11911da177e4SLinus Torvalds {
11921da177e4SLinus Torvalds 	DECODE_HEAD;
11931da177e4SLinus Torvalds 
1194e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1195e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1196e1a90ebdSAnna Schumaker 
11971da177e4SLinus Torvalds 	READ_BUF(sizeof(clientid_t));
11981da177e4SLinus Torvalds 	COPYMEM(clientid, sizeof(clientid_t));
11991da177e4SLinus Torvalds 
12001da177e4SLinus Torvalds 	DECODE_TAIL;
12011da177e4SLinus Torvalds }
12021da177e4SLinus Torvalds 
1203b37ad28bSAl Viro static __be32
1204dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1205dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
1206dcb488a3SAndy Adamson {
1207dcb488a3SAndy Adamson 	DECODE_HEAD;
1208dcb488a3SAndy Adamson 
1209dcb488a3SAndy Adamson 	READ_BUF(4);
121006553991SJ. Bruce Fields 	secinfo->si_namelen = be32_to_cpup(p++);
1211dcb488a3SAndy Adamson 	READ_BUF(secinfo->si_namelen);
1212dcb488a3SAndy Adamson 	SAVEMEM(secinfo->si_name, secinfo->si_namelen);
1213a36b1725SJ. Bruce Fields 	status = check_filename(secinfo->si_name, secinfo->si_namelen);
1214dcb488a3SAndy Adamson 	if (status)
1215dcb488a3SAndy Adamson 		return status;
1216dcb488a3SAndy Adamson 	DECODE_TAIL;
1217dcb488a3SAndy Adamson }
1218dcb488a3SAndy Adamson 
1219dcb488a3SAndy Adamson static __be32
122004f4ad16SJ. Bruce Fields nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
122104f4ad16SJ. Bruce Fields 		     struct nfsd4_secinfo_no_name *sin)
122204f4ad16SJ. Bruce Fields {
122304f4ad16SJ. Bruce Fields 	DECODE_HEAD;
122404f4ad16SJ. Bruce Fields 
122504f4ad16SJ. Bruce Fields 	READ_BUF(4);
122606553991SJ. Bruce Fields 	sin->sin_style = be32_to_cpup(p++);
122704f4ad16SJ. Bruce Fields 	DECODE_TAIL;
122804f4ad16SJ. Bruce Fields }
122904f4ad16SJ. Bruce Fields 
123004f4ad16SJ. Bruce Fields static __be32
12311da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
12321da177e4SLinus Torvalds {
1233e31a1b66SBenny Halevy 	__be32 status;
12341da177e4SLinus Torvalds 
1235e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
1236e31a1b66SBenny Halevy 	if (status)
1237e31a1b66SBenny Halevy 		return status;
12383c8e0316SYu Zhiguo 	return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
123947057abdSAndreas Gruenbacher 				  &setattr->sa_acl, &setattr->sa_label, NULL);
12401da177e4SLinus Torvalds }
12411da177e4SLinus Torvalds 
1242b37ad28bSAl Viro static __be32
12431da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
12441da177e4SLinus Torvalds {
12451da177e4SLinus Torvalds 	DECODE_HEAD;
12461da177e4SLinus Torvalds 
1247e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1248e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1249e1a90ebdSAnna Schumaker 
1250ab4684d1SChuck Lever 	READ_BUF(NFS4_VERIFIER_SIZE);
1251ab4684d1SChuck Lever 	COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE);
12521da177e4SLinus Torvalds 
1253a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &setclientid->se_name);
1254a084daf5SJ. Bruce Fields 	if (status)
1255a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
1256a084daf5SJ. Bruce Fields 	READ_BUF(8);
125706553991SJ. Bruce Fields 	setclientid->se_callback_prog = be32_to_cpup(p++);
125806553991SJ. Bruce Fields 	setclientid->se_callback_netid_len = be32_to_cpup(p++);
12594aed9c46SJ. Bruce Fields 	READ_BUF(setclientid->se_callback_netid_len);
12601da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
12614aed9c46SJ. Bruce Fields 	READ_BUF(4);
126206553991SJ. Bruce Fields 	setclientid->se_callback_addr_len = be32_to_cpup(p++);
12631da177e4SLinus Torvalds 
12644aed9c46SJ. Bruce Fields 	READ_BUF(setclientid->se_callback_addr_len);
12651da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
12664aed9c46SJ. Bruce Fields 	READ_BUF(4);
126706553991SJ. Bruce Fields 	setclientid->se_callback_ident = be32_to_cpup(p++);
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	DECODE_TAIL;
12701da177e4SLinus Torvalds }
12711da177e4SLinus Torvalds 
1272b37ad28bSAl Viro static __be32
12731da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
12741da177e4SLinus Torvalds {
12751da177e4SLinus Torvalds 	DECODE_HEAD;
12761da177e4SLinus Torvalds 
1277e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1278e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1279e1a90ebdSAnna Schumaker 
1280ab4684d1SChuck Lever 	READ_BUF(8 + NFS4_VERIFIER_SIZE);
12811da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_clientid, 8);
1282ab4684d1SChuck Lever 	COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE);
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds 	DECODE_TAIL;
12851da177e4SLinus Torvalds }
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds /* Also used for NVERIFY */
1288b37ad28bSAl Viro static __be32
12891da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
12901da177e4SLinus Torvalds {
12911da177e4SLinus Torvalds 	DECODE_HEAD;
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
12941da177e4SLinus Torvalds 		goto out;
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
1297e5f95703SJ. Bruce Fields 	 * nfsd4_proc_verify */
1298e5f95703SJ. Bruce Fields 
12991da177e4SLinus Torvalds 	READ_BUF(4);
130006553991SJ. Bruce Fields 	verify->ve_attrlen = be32_to_cpup(p++);
13011da177e4SLinus Torvalds 	READ_BUF(verify->ve_attrlen);
13021da177e4SLinus Torvalds 	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	DECODE_TAIL;
13051da177e4SLinus Torvalds }
13061da177e4SLinus Torvalds 
1307b37ad28bSAl Viro static __be32
13081da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
13091da177e4SLinus Torvalds {
13101da177e4SLinus Torvalds 	DECODE_HEAD;
13111da177e4SLinus Torvalds 
1312e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
1313e31a1b66SBenny Halevy 	if (status)
1314e31a1b66SBenny Halevy 		return status;
1315e31a1b66SBenny Halevy 	READ_BUF(16);
1316542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &write->wr_offset);
131706553991SJ. Bruce Fields 	write->wr_stable_how = be32_to_cpup(p++);
131854bbb7d2SKinglong Mee 	if (write->wr_stable_how > NFS_FILE_SYNC)
13191da177e4SLinus Torvalds 		goto xdr_error;
132006553991SJ. Bruce Fields 	write->wr_buflen = be32_to_cpup(p++);
13211da177e4SLinus Torvalds 
1322874c7b8eSFrank van der Linden 	status = svcxdr_construct_vector(argp, &write->wr_head,
1323874c7b8eSFrank van der Linden 					 &write->wr_pagelist, write->wr_buflen);
1324874c7b8eSFrank van der Linden 	if (status)
1325874c7b8eSFrank van der Linden 		return status;
13261da177e4SLinus Torvalds 
13271da177e4SLinus Torvalds 	DECODE_TAIL;
13281da177e4SLinus Torvalds }
13291da177e4SLinus Torvalds 
1330b37ad28bSAl Viro static __be32
13311da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
13321da177e4SLinus Torvalds {
13331da177e4SLinus Torvalds 	DECODE_HEAD;
13341da177e4SLinus Torvalds 
1335e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1336e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1337e1a90ebdSAnna Schumaker 
13381da177e4SLinus Torvalds 	READ_BUF(12);
13391da177e4SLinus Torvalds 	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
134006553991SJ. Bruce Fields 	rlockowner->rl_owner.len = be32_to_cpup(p++);
13411da177e4SLinus Torvalds 	READ_BUF(rlockowner->rl_owner.len);
13421da177e4SLinus Torvalds 	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
13431da177e4SLinus Torvalds 
134460adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
134560adfc50SAndy Adamson 		return nfserr_inval;
13461da177e4SLinus Torvalds 	DECODE_TAIL;
13471da177e4SLinus Torvalds }
13481da177e4SLinus Torvalds 
1349b37ad28bSAl Viro static __be32
13502db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
13510733d213SAndy Adamson 			 struct nfsd4_exchange_id *exid)
13522db134ebSAndy Adamson {
13535afa040bSMi Jinlong 	int dummy, tmp;
13540733d213SAndy Adamson 	DECODE_HEAD;
13550733d213SAndy Adamson 
13560733d213SAndy Adamson 	READ_BUF(NFS4_VERIFIER_SIZE);
13570733d213SAndy Adamson 	COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE);
13580733d213SAndy Adamson 
1359a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &exid->clname);
1360a084daf5SJ. Bruce Fields 	if (status)
1361a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
13620733d213SAndy Adamson 
13630733d213SAndy Adamson 	READ_BUF(4);
136406553991SJ. Bruce Fields 	exid->flags = be32_to_cpup(p++);
13650733d213SAndy Adamson 
13660733d213SAndy Adamson 	/* Ignore state_protect4_a */
13670733d213SAndy Adamson 	READ_BUF(4);
136806553991SJ. Bruce Fields 	exid->spa_how = be32_to_cpup(p++);
13690733d213SAndy Adamson 	switch (exid->spa_how) {
13700733d213SAndy Adamson 	case SP4_NONE:
13710733d213SAndy Adamson 		break;
13720733d213SAndy Adamson 	case SP4_MACH_CRED:
13730733d213SAndy Adamson 		/* spo_must_enforce */
1374ed941643SAndrew Elble 		status = nfsd4_decode_bitmap(argp,
1375ed941643SAndrew Elble 					exid->spo_must_enforce);
1376ed941643SAndrew Elble 		if (status)
1377ed941643SAndrew Elble 			goto out;
13780733d213SAndy Adamson 		/* spo_must_allow */
1379ed941643SAndrew Elble 		status = nfsd4_decode_bitmap(argp, exid->spo_must_allow);
1380ed941643SAndrew Elble 		if (status)
1381ed941643SAndrew Elble 			goto out;
13820733d213SAndy Adamson 		break;
13830733d213SAndy Adamson 	case SP4_SSV:
13840733d213SAndy Adamson 		/* ssp_ops */
13850733d213SAndy Adamson 		READ_BUF(4);
138606553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13870733d213SAndy Adamson 		READ_BUF(dummy * 4);
13880733d213SAndy Adamson 		p += dummy;
13890733d213SAndy Adamson 
13900733d213SAndy Adamson 		READ_BUF(4);
139106553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13920733d213SAndy Adamson 		READ_BUF(dummy * 4);
13930733d213SAndy Adamson 		p += dummy;
13940733d213SAndy Adamson 
13950733d213SAndy Adamson 		/* ssp_hash_algs<> */
13960733d213SAndy Adamson 		READ_BUF(4);
139706553991SJ. Bruce Fields 		tmp = be32_to_cpup(p++);
13985afa040bSMi Jinlong 		while (tmp--) {
13990733d213SAndy Adamson 			READ_BUF(4);
140006553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
14010733d213SAndy Adamson 			READ_BUF(dummy);
14020733d213SAndy Adamson 			p += XDR_QUADLEN(dummy);
14035afa040bSMi Jinlong 		}
14045afa040bSMi Jinlong 
14055afa040bSMi Jinlong 		/* ssp_encr_algs<> */
14065afa040bSMi Jinlong 		READ_BUF(4);
140706553991SJ. Bruce Fields 		tmp = be32_to_cpup(p++);
14085afa040bSMi Jinlong 		while (tmp--) {
14095afa040bSMi Jinlong 			READ_BUF(4);
141006553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
14115afa040bSMi Jinlong 			READ_BUF(dummy);
14125afa040bSMi Jinlong 			p += XDR_QUADLEN(dummy);
14135afa040bSMi Jinlong 		}
14140733d213SAndy Adamson 
14155ed96bc5Snixiaoming 		/* ignore ssp_window and ssp_num_gss_handles: */
14160733d213SAndy Adamson 		READ_BUF(8);
14170733d213SAndy Adamson 		break;
14180733d213SAndy Adamson 	default:
14190733d213SAndy Adamson 		goto xdr_error;
14200733d213SAndy Adamson 	}
14210733d213SAndy Adamson 
14220733d213SAndy Adamson 	READ_BUF(4);    /* nfs_impl_id4 array length */
142306553991SJ. Bruce Fields 	dummy = be32_to_cpup(p++);
14240733d213SAndy Adamson 
14250733d213SAndy Adamson 	if (dummy > 1)
14260733d213SAndy Adamson 		goto xdr_error;
14270733d213SAndy Adamson 
14280733d213SAndy Adamson 	if (dummy == 1) {
142979123444SJ. Bruce Fields 		status = nfsd4_decode_opaque(argp, &exid->nii_domain);
143079123444SJ. Bruce Fields 		if (status)
143179123444SJ. Bruce Fields 			goto xdr_error;
14320733d213SAndy Adamson 
14330733d213SAndy Adamson 		/* nii_name */
143479123444SJ. Bruce Fields 		status = nfsd4_decode_opaque(argp, &exid->nii_name);
143579123444SJ. Bruce Fields 		if (status)
143679123444SJ. Bruce Fields 			goto xdr_error;
14370733d213SAndy Adamson 
14380733d213SAndy Adamson 		/* nii_date */
143979123444SJ. Bruce Fields 		status = nfsd4_decode_time(argp, &exid->nii_time);
144079123444SJ. Bruce Fields 		if (status)
144179123444SJ. Bruce Fields 			goto xdr_error;
14420733d213SAndy Adamson 	}
14430733d213SAndy Adamson 	DECODE_TAIL;
14442db134ebSAndy Adamson }
14452db134ebSAndy Adamson 
14462db134ebSAndy Adamson static __be32
14472db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
14482db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
14492db134ebSAndy Adamson {
1450ec6b5d7bSAndy Adamson 	DECODE_HEAD;
1451ec6b5d7bSAndy Adamson 
1452ec6b5d7bSAndy Adamson 	READ_BUF(16);
1453ec6b5d7bSAndy Adamson 	COPYMEM(&sess->clientid, 8);
145406553991SJ. Bruce Fields 	sess->seqid = be32_to_cpup(p++);
145506553991SJ. Bruce Fields 	sess->flags = be32_to_cpup(p++);
1456ec6b5d7bSAndy Adamson 
1457ec6b5d7bSAndy Adamson 	/* Fore channel attrs */
1458ec6b5d7bSAndy Adamson 	READ_BUF(28);
1459b96811cdSTrond Myklebust 	p++; /* headerpadsz is always 0 */
146006553991SJ. Bruce Fields 	sess->fore_channel.maxreq_sz = be32_to_cpup(p++);
146106553991SJ. Bruce Fields 	sess->fore_channel.maxresp_sz = be32_to_cpup(p++);
146206553991SJ. Bruce Fields 	sess->fore_channel.maxresp_cached = be32_to_cpup(p++);
146306553991SJ. Bruce Fields 	sess->fore_channel.maxops = be32_to_cpup(p++);
146406553991SJ. Bruce Fields 	sess->fore_channel.maxreqs = be32_to_cpup(p++);
146506553991SJ. Bruce Fields 	sess->fore_channel.nr_rdma_attrs = be32_to_cpup(p++);
1466ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs == 1) {
1467ec6b5d7bSAndy Adamson 		READ_BUF(4);
146806553991SJ. Bruce Fields 		sess->fore_channel.rdma_attrs = be32_to_cpup(p++);
1469ec6b5d7bSAndy Adamson 	} else if (sess->fore_channel.nr_rdma_attrs > 1) {
1470ec6b5d7bSAndy Adamson 		dprintk("Too many fore channel attr bitmaps!\n");
1471ec6b5d7bSAndy Adamson 		goto xdr_error;
1472ec6b5d7bSAndy Adamson 	}
1473ec6b5d7bSAndy Adamson 
1474ec6b5d7bSAndy Adamson 	/* Back channel attrs */
1475ec6b5d7bSAndy Adamson 	READ_BUF(28);
1476b96811cdSTrond Myklebust 	p++; /* headerpadsz is always 0 */
147706553991SJ. Bruce Fields 	sess->back_channel.maxreq_sz = be32_to_cpup(p++);
147806553991SJ. Bruce Fields 	sess->back_channel.maxresp_sz = be32_to_cpup(p++);
147906553991SJ. Bruce Fields 	sess->back_channel.maxresp_cached = be32_to_cpup(p++);
148006553991SJ. Bruce Fields 	sess->back_channel.maxops = be32_to_cpup(p++);
148106553991SJ. Bruce Fields 	sess->back_channel.maxreqs = be32_to_cpup(p++);
148206553991SJ. Bruce Fields 	sess->back_channel.nr_rdma_attrs = be32_to_cpup(p++);
1483ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs == 1) {
1484ec6b5d7bSAndy Adamson 		READ_BUF(4);
148506553991SJ. Bruce Fields 		sess->back_channel.rdma_attrs = be32_to_cpup(p++);
1486ec6b5d7bSAndy Adamson 	} else if (sess->back_channel.nr_rdma_attrs > 1) {
1487ec6b5d7bSAndy Adamson 		dprintk("Too many back channel attr bitmaps!\n");
1488ec6b5d7bSAndy Adamson 		goto xdr_error;
1489ec6b5d7bSAndy Adamson 	}
1490ec6b5d7bSAndy Adamson 
1491acb2887eSJ. Bruce Fields 	READ_BUF(4);
149206553991SJ. Bruce Fields 	sess->callback_prog = be32_to_cpup(p++);
1493acb2887eSJ. Bruce Fields 	nfsd4_decode_cb_sec(argp, &sess->cb_sec);
1494ec6b5d7bSAndy Adamson 	DECODE_TAIL;
14952db134ebSAndy Adamson }
14962db134ebSAndy Adamson 
14972db134ebSAndy Adamson static __be32
14982db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
14992db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
15002db134ebSAndy Adamson {
1501e10e0cfcSBenny Halevy 	DECODE_HEAD;
1502e10e0cfcSBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN);
1503e10e0cfcSBenny Halevy 	COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1504e10e0cfcSBenny Halevy 
1505e10e0cfcSBenny Halevy 	DECODE_TAIL;
15062db134ebSAndy Adamson }
15072db134ebSAndy Adamson 
15082db134ebSAndy Adamson static __be32
1509e1ca12dfSBryan Schumaker nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1510e1ca12dfSBryan Schumaker 			  struct nfsd4_free_stateid *free_stateid)
1511e1ca12dfSBryan Schumaker {
1512e1ca12dfSBryan Schumaker 	DECODE_HEAD;
1513e1ca12dfSBryan Schumaker 
1514e1ca12dfSBryan Schumaker 	READ_BUF(sizeof(stateid_t));
151506553991SJ. Bruce Fields 	free_stateid->fr_stateid.si_generation = be32_to_cpup(p++);
1516e1ca12dfSBryan Schumaker 	COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
1517e1ca12dfSBryan Schumaker 
1518e1ca12dfSBryan Schumaker 	DECODE_TAIL;
1519e1ca12dfSBryan Schumaker }
1520e1ca12dfSBryan Schumaker 
1521e1ca12dfSBryan Schumaker static __be32
15222db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
15232db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
15242db134ebSAndy Adamson {
1525b85d4c01SBenny Halevy 	DECODE_HEAD;
1526b85d4c01SBenny Halevy 
1527b85d4c01SBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
1528b85d4c01SBenny Halevy 	COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
152906553991SJ. Bruce Fields 	seq->seqid = be32_to_cpup(p++);
153006553991SJ. Bruce Fields 	seq->slotid = be32_to_cpup(p++);
153106553991SJ. Bruce Fields 	seq->maxslots = be32_to_cpup(p++);
153206553991SJ. Bruce Fields 	seq->cachethis = be32_to_cpup(p++);
1533b85d4c01SBenny Halevy 
1534b85d4c01SBenny Halevy 	DECODE_TAIL;
15352db134ebSAndy Adamson }
15362db134ebSAndy Adamson 
153717456804SBryan Schumaker static __be32
153817456804SBryan Schumaker nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
153917456804SBryan Schumaker {
154017456804SBryan Schumaker 	int i;
154103cfb420SBryan Schumaker 	__be32 *p, status;
154203cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid;
154317456804SBryan Schumaker 
154417456804SBryan Schumaker 	READ_BUF(4);
154517456804SBryan Schumaker 	test_stateid->ts_num_ids = ntohl(*p++);
154617456804SBryan Schumaker 
154703cfb420SBryan Schumaker 	INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
154817456804SBryan Schumaker 
154917456804SBryan Schumaker 	for (i = 0; i < test_stateid->ts_num_ids; i++) {
1550d5e23383SJ. Bruce Fields 		stateid = svcxdr_tmpalloc(argp, sizeof(*stateid));
155103cfb420SBryan Schumaker 		if (!stateid) {
1552afcf6792SAl Viro 			status = nfserrno(-ENOMEM);
155303cfb420SBryan Schumaker 			goto out;
155403cfb420SBryan Schumaker 		}
155503cfb420SBryan Schumaker 
155603cfb420SBryan Schumaker 		INIT_LIST_HEAD(&stateid->ts_id_list);
155703cfb420SBryan Schumaker 		list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
155803cfb420SBryan Schumaker 
155903cfb420SBryan Schumaker 		status = nfsd4_decode_stateid(argp, &stateid->ts_id_stateid);
156017456804SBryan Schumaker 		if (status)
156103cfb420SBryan Schumaker 			goto out;
156217456804SBryan Schumaker 	}
156317456804SBryan Schumaker 
156417456804SBryan Schumaker 	status = 0;
156517456804SBryan Schumaker out:
156617456804SBryan Schumaker 	return status;
156717456804SBryan Schumaker xdr_error:
156817456804SBryan Schumaker 	dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
156917456804SBryan Schumaker 	status = nfserr_bad_xdr;
157017456804SBryan Schumaker 	goto out;
157117456804SBryan Schumaker }
157217456804SBryan Schumaker 
1573345c2842SMi Jinlong static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, struct nfsd4_destroy_clientid *dc)
1574345c2842SMi Jinlong {
1575345c2842SMi Jinlong 	DECODE_HEAD;
1576345c2842SMi Jinlong 
1577345c2842SMi Jinlong 	READ_BUF(8);
1578345c2842SMi Jinlong 	COPYMEM(&dc->clientid, 8);
1579345c2842SMi Jinlong 
1580345c2842SMi Jinlong 	DECODE_TAIL;
1581345c2842SMi Jinlong }
1582345c2842SMi Jinlong 
15834dc6ec00SJ. Bruce Fields static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
15844dc6ec00SJ. Bruce Fields {
15854dc6ec00SJ. Bruce Fields 	DECODE_HEAD;
15864dc6ec00SJ. Bruce Fields 
15874dc6ec00SJ. Bruce Fields 	READ_BUF(4);
158806553991SJ. Bruce Fields 	rc->rca_one_fs = be32_to_cpup(p++);
15894dc6ec00SJ. Bruce Fields 
15904dc6ec00SJ. Bruce Fields 	DECODE_TAIL;
15914dc6ec00SJ. Bruce Fields }
15924dc6ec00SJ. Bruce Fields 
15939cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
15949cf514ccSChristoph Hellwig static __be32
15959cf514ccSChristoph Hellwig nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
15969cf514ccSChristoph Hellwig 		struct nfsd4_getdeviceinfo *gdev)
15979cf514ccSChristoph Hellwig {
15989cf514ccSChristoph Hellwig 	DECODE_HEAD;
15999cf514ccSChristoph Hellwig 	u32 num, i;
16009cf514ccSChristoph Hellwig 
16019cf514ccSChristoph Hellwig 	READ_BUF(sizeof(struct nfsd4_deviceid) + 3 * 4);
16029cf514ccSChristoph Hellwig 	COPYMEM(&gdev->gd_devid, sizeof(struct nfsd4_deviceid));
16039cf514ccSChristoph Hellwig 	gdev->gd_layout_type = be32_to_cpup(p++);
16049cf514ccSChristoph Hellwig 	gdev->gd_maxcount = be32_to_cpup(p++);
16059cf514ccSChristoph Hellwig 	num = be32_to_cpup(p++);
16069cf514ccSChristoph Hellwig 	if (num) {
16073171822fSScott Mayhew 		if (num > 1000)
16083171822fSScott Mayhew 			goto xdr_error;
16099cf514ccSChristoph Hellwig 		READ_BUF(4 * num);
16109cf514ccSChristoph Hellwig 		gdev->gd_notify_types = be32_to_cpup(p++);
16119cf514ccSChristoph Hellwig 		for (i = 1; i < num; i++) {
16129cf514ccSChristoph Hellwig 			if (be32_to_cpup(p++)) {
16139cf514ccSChristoph Hellwig 				status = nfserr_inval;
16149cf514ccSChristoph Hellwig 				goto out;
16159cf514ccSChristoph Hellwig 			}
16169cf514ccSChristoph Hellwig 		}
16179cf514ccSChristoph Hellwig 	}
16189cf514ccSChristoph Hellwig 	DECODE_TAIL;
16199cf514ccSChristoph Hellwig }
16209cf514ccSChristoph Hellwig 
16219cf514ccSChristoph Hellwig static __be32
16229cf514ccSChristoph Hellwig nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
16239cf514ccSChristoph Hellwig 		struct nfsd4_layoutget *lgp)
16249cf514ccSChristoph Hellwig {
16259cf514ccSChristoph Hellwig 	DECODE_HEAD;
16269cf514ccSChristoph Hellwig 
16279cf514ccSChristoph Hellwig 	READ_BUF(36);
16289cf514ccSChristoph Hellwig 	lgp->lg_signal = be32_to_cpup(p++);
16299cf514ccSChristoph Hellwig 	lgp->lg_layout_type = be32_to_cpup(p++);
16309cf514ccSChristoph Hellwig 	lgp->lg_seg.iomode = be32_to_cpup(p++);
16319cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lgp->lg_seg.offset);
16329cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lgp->lg_seg.length);
16339cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lgp->lg_minlength);
1634db59c0efSKinglong Mee 
1635db59c0efSKinglong Mee 	status = nfsd4_decode_stateid(argp, &lgp->lg_sid);
1636db59c0efSKinglong Mee 	if (status)
1637db59c0efSKinglong Mee 		return status;
1638db59c0efSKinglong Mee 
16399cf514ccSChristoph Hellwig 	READ_BUF(4);
16409cf514ccSChristoph Hellwig 	lgp->lg_maxcount = be32_to_cpup(p++);
16419cf514ccSChristoph Hellwig 
16429cf514ccSChristoph Hellwig 	DECODE_TAIL;
16439cf514ccSChristoph Hellwig }
16449cf514ccSChristoph Hellwig 
16459cf514ccSChristoph Hellwig static __be32
16469cf514ccSChristoph Hellwig nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp,
16479cf514ccSChristoph Hellwig 		struct nfsd4_layoutcommit *lcp)
16489cf514ccSChristoph Hellwig {
16499cf514ccSChristoph Hellwig 	DECODE_HEAD;
16509cf514ccSChristoph Hellwig 	u32 timechange;
16519cf514ccSChristoph Hellwig 
16529cf514ccSChristoph Hellwig 	READ_BUF(20);
16539cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lcp->lc_seg.offset);
16549cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lcp->lc_seg.length);
16559cf514ccSChristoph Hellwig 	lcp->lc_reclaim = be32_to_cpup(p++);
1656db59c0efSKinglong Mee 
1657db59c0efSKinglong Mee 	status = nfsd4_decode_stateid(argp, &lcp->lc_sid);
1658db59c0efSKinglong Mee 	if (status)
1659db59c0efSKinglong Mee 		return status;
1660db59c0efSKinglong Mee 
16619cf514ccSChristoph Hellwig 	READ_BUF(4);
16629cf514ccSChristoph Hellwig 	lcp->lc_newoffset = be32_to_cpup(p++);
16639cf514ccSChristoph Hellwig 	if (lcp->lc_newoffset) {
16649cf514ccSChristoph Hellwig 		READ_BUF(8);
16659cf514ccSChristoph Hellwig 		p = xdr_decode_hyper(p, &lcp->lc_last_wr);
16669cf514ccSChristoph Hellwig 	} else
16679cf514ccSChristoph Hellwig 		lcp->lc_last_wr = 0;
16689cf514ccSChristoph Hellwig 	READ_BUF(4);
16699cf514ccSChristoph Hellwig 	timechange = be32_to_cpup(p++);
16709cf514ccSChristoph Hellwig 	if (timechange) {
16719cf514ccSChristoph Hellwig 		status = nfsd4_decode_time(argp, &lcp->lc_mtime);
16729cf514ccSChristoph Hellwig 		if (status)
16739cf514ccSChristoph Hellwig 			return status;
16749cf514ccSChristoph Hellwig 	} else {
16759cf514ccSChristoph Hellwig 		lcp->lc_mtime.tv_nsec = UTIME_NOW;
16769cf514ccSChristoph Hellwig 	}
16779cf514ccSChristoph Hellwig 	READ_BUF(8);
16789cf514ccSChristoph Hellwig 	lcp->lc_layout_type = be32_to_cpup(p++);
16799cf514ccSChristoph Hellwig 
16809cf514ccSChristoph Hellwig 	/*
16819cf514ccSChristoph Hellwig 	 * Save the layout update in XDR format and let the layout driver deal
16829cf514ccSChristoph Hellwig 	 * with it later.
16839cf514ccSChristoph Hellwig 	 */
16849cf514ccSChristoph Hellwig 	lcp->lc_up_len = be32_to_cpup(p++);
16859cf514ccSChristoph Hellwig 	if (lcp->lc_up_len > 0) {
16869cf514ccSChristoph Hellwig 		READ_BUF(lcp->lc_up_len);
16879cf514ccSChristoph Hellwig 		READMEM(lcp->lc_up_layout, lcp->lc_up_len);
16889cf514ccSChristoph Hellwig 	}
16899cf514ccSChristoph Hellwig 
16909cf514ccSChristoph Hellwig 	DECODE_TAIL;
16919cf514ccSChristoph Hellwig }
16929cf514ccSChristoph Hellwig 
16939cf514ccSChristoph Hellwig static __be32
16949cf514ccSChristoph Hellwig nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp,
16959cf514ccSChristoph Hellwig 		struct nfsd4_layoutreturn *lrp)
16969cf514ccSChristoph Hellwig {
16979cf514ccSChristoph Hellwig 	DECODE_HEAD;
16989cf514ccSChristoph Hellwig 
16999cf514ccSChristoph Hellwig 	READ_BUF(16);
17009cf514ccSChristoph Hellwig 	lrp->lr_reclaim = be32_to_cpup(p++);
17019cf514ccSChristoph Hellwig 	lrp->lr_layout_type = be32_to_cpup(p++);
17029cf514ccSChristoph Hellwig 	lrp->lr_seg.iomode = be32_to_cpup(p++);
17039cf514ccSChristoph Hellwig 	lrp->lr_return_type = be32_to_cpup(p++);
17049cf514ccSChristoph Hellwig 	if (lrp->lr_return_type == RETURN_FILE) {
17059cf514ccSChristoph Hellwig 		READ_BUF(16);
17069cf514ccSChristoph Hellwig 		p = xdr_decode_hyper(p, &lrp->lr_seg.offset);
17079cf514ccSChristoph Hellwig 		p = xdr_decode_hyper(p, &lrp->lr_seg.length);
1708db59c0efSKinglong Mee 
1709db59c0efSKinglong Mee 		status = nfsd4_decode_stateid(argp, &lrp->lr_sid);
1710db59c0efSKinglong Mee 		if (status)
1711db59c0efSKinglong Mee 			return status;
1712db59c0efSKinglong Mee 
17139cf514ccSChristoph Hellwig 		READ_BUF(4);
17149cf514ccSChristoph Hellwig 		lrp->lrf_body_len = be32_to_cpup(p++);
17159cf514ccSChristoph Hellwig 		if (lrp->lrf_body_len > 0) {
17169cf514ccSChristoph Hellwig 			READ_BUF(lrp->lrf_body_len);
17179cf514ccSChristoph Hellwig 			READMEM(lrp->lrf_body, lrp->lrf_body_len);
17189cf514ccSChristoph Hellwig 		}
17199cf514ccSChristoph Hellwig 	} else {
17209cf514ccSChristoph Hellwig 		lrp->lr_seg.offset = 0;
17219cf514ccSChristoph Hellwig 		lrp->lr_seg.length = NFS4_MAX_UINT64;
17229cf514ccSChristoph Hellwig 	}
17239cf514ccSChristoph Hellwig 
17249cf514ccSChristoph Hellwig 	DECODE_TAIL;
17259cf514ccSChristoph Hellwig }
17269cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
17279cf514ccSChristoph Hellwig 
17282db134ebSAndy Adamson static __be32
172995d871f0SAnna Schumaker nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
173095d871f0SAnna Schumaker 		       struct nfsd4_fallocate *fallocate)
173195d871f0SAnna Schumaker {
173295d871f0SAnna Schumaker 	DECODE_HEAD;
173395d871f0SAnna Schumaker 
173495d871f0SAnna Schumaker 	status = nfsd4_decode_stateid(argp, &fallocate->falloc_stateid);
173595d871f0SAnna Schumaker 	if (status)
173695d871f0SAnna Schumaker 		return status;
173795d871f0SAnna Schumaker 
173895d871f0SAnna Schumaker 	READ_BUF(16);
173995d871f0SAnna Schumaker 	p = xdr_decode_hyper(p, &fallocate->falloc_offset);
174095d871f0SAnna Schumaker 	xdr_decode_hyper(p, &fallocate->falloc_length);
174195d871f0SAnna Schumaker 
174295d871f0SAnna Schumaker 	DECODE_TAIL;
174395d871f0SAnna Schumaker }
174495d871f0SAnna Schumaker 
174595d871f0SAnna Schumaker static __be32
1746ffa0160aSChristoph Hellwig nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
1747ffa0160aSChristoph Hellwig {
1748ffa0160aSChristoph Hellwig 	DECODE_HEAD;
1749ffa0160aSChristoph Hellwig 
1750ffa0160aSChristoph Hellwig 	status = nfsd4_decode_stateid(argp, &clone->cl_src_stateid);
1751ffa0160aSChristoph Hellwig 	if (status)
1752ffa0160aSChristoph Hellwig 		return status;
1753ffa0160aSChristoph Hellwig 	status = nfsd4_decode_stateid(argp, &clone->cl_dst_stateid);
1754ffa0160aSChristoph Hellwig 	if (status)
1755ffa0160aSChristoph Hellwig 		return status;
1756ffa0160aSChristoph Hellwig 
1757ffa0160aSChristoph Hellwig 	READ_BUF(8 + 8 + 8);
1758ffa0160aSChristoph Hellwig 	p = xdr_decode_hyper(p, &clone->cl_src_pos);
1759ffa0160aSChristoph Hellwig 	p = xdr_decode_hyper(p, &clone->cl_dst_pos);
1760ffa0160aSChristoph Hellwig 	p = xdr_decode_hyper(p, &clone->cl_count);
1761ffa0160aSChristoph Hellwig 	DECODE_TAIL;
1762ffa0160aSChristoph Hellwig }
1763ffa0160aSChristoph Hellwig 
176484e1b21dSOlga Kornievskaia static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
176584e1b21dSOlga Kornievskaia 				      struct nl4_server *ns)
176684e1b21dSOlga Kornievskaia {
176784e1b21dSOlga Kornievskaia 	DECODE_HEAD;
176884e1b21dSOlga Kornievskaia 	struct nfs42_netaddr *naddr;
176984e1b21dSOlga Kornievskaia 
177084e1b21dSOlga Kornievskaia 	READ_BUF(4);
177184e1b21dSOlga Kornievskaia 	ns->nl4_type = be32_to_cpup(p++);
177284e1b21dSOlga Kornievskaia 
177384e1b21dSOlga Kornievskaia 	/* currently support for 1 inter-server source server */
177484e1b21dSOlga Kornievskaia 	switch (ns->nl4_type) {
177584e1b21dSOlga Kornievskaia 	case NL4_NETADDR:
177684e1b21dSOlga Kornievskaia 		naddr = &ns->u.nl4_addr;
177784e1b21dSOlga Kornievskaia 
177884e1b21dSOlga Kornievskaia 		READ_BUF(4);
177984e1b21dSOlga Kornievskaia 		naddr->netid_len = be32_to_cpup(p++);
178084e1b21dSOlga Kornievskaia 		if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
178184e1b21dSOlga Kornievskaia 			goto xdr_error;
178284e1b21dSOlga Kornievskaia 
178384e1b21dSOlga Kornievskaia 		READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
178484e1b21dSOlga Kornievskaia 		COPYMEM(naddr->netid, naddr->netid_len);
178584e1b21dSOlga Kornievskaia 
178684e1b21dSOlga Kornievskaia 		naddr->addr_len = be32_to_cpup(p++);
178784e1b21dSOlga Kornievskaia 		if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
178884e1b21dSOlga Kornievskaia 			goto xdr_error;
178984e1b21dSOlga Kornievskaia 
179084e1b21dSOlga Kornievskaia 		READ_BUF(naddr->addr_len);
179184e1b21dSOlga Kornievskaia 		COPYMEM(naddr->addr, naddr->addr_len);
179284e1b21dSOlga Kornievskaia 		break;
179384e1b21dSOlga Kornievskaia 	default:
179484e1b21dSOlga Kornievskaia 		goto xdr_error;
179584e1b21dSOlga Kornievskaia 	}
179684e1b21dSOlga Kornievskaia 	DECODE_TAIL;
179784e1b21dSOlga Kornievskaia }
179884e1b21dSOlga Kornievskaia 
1799ffa0160aSChristoph Hellwig static __be32
180029ae7f9dSAnna Schumaker nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
180129ae7f9dSAnna Schumaker {
180229ae7f9dSAnna Schumaker 	DECODE_HEAD;
180384e1b21dSOlga Kornievskaia 	struct nl4_server *ns_dummy;
180484e1b21dSOlga Kornievskaia 	int i, count;
180529ae7f9dSAnna Schumaker 
180629ae7f9dSAnna Schumaker 	status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
180729ae7f9dSAnna Schumaker 	if (status)
180829ae7f9dSAnna Schumaker 		return status;
180929ae7f9dSAnna Schumaker 	status = nfsd4_decode_stateid(argp, &copy->cp_dst_stateid);
181029ae7f9dSAnna Schumaker 	if (status)
181129ae7f9dSAnna Schumaker 		return status;
181229ae7f9dSAnna Schumaker 
181329ae7f9dSAnna Schumaker 	READ_BUF(8 + 8 + 8 + 4 + 4 + 4);
181429ae7f9dSAnna Schumaker 	p = xdr_decode_hyper(p, &copy->cp_src_pos);
181529ae7f9dSAnna Schumaker 	p = xdr_decode_hyper(p, &copy->cp_dst_pos);
181629ae7f9dSAnna Schumaker 	p = xdr_decode_hyper(p, &copy->cp_count);
1817edcc8452SJ. Bruce Fields 	p++; /* ca_consecutive: we always do consecutive copies */
181829ae7f9dSAnna Schumaker 	copy->cp_synchronous = be32_to_cpup(p++);
181984e1b21dSOlga Kornievskaia 
182084e1b21dSOlga Kornievskaia 	count = be32_to_cpup(p++);
182184e1b21dSOlga Kornievskaia 
182284e1b21dSOlga Kornievskaia 	copy->cp_intra = false;
182384e1b21dSOlga Kornievskaia 	if (count == 0) { /* intra-server copy */
182484e1b21dSOlga Kornievskaia 		copy->cp_intra = true;
182584e1b21dSOlga Kornievskaia 		goto intra;
182684e1b21dSOlga Kornievskaia 	}
182784e1b21dSOlga Kornievskaia 
182884e1b21dSOlga Kornievskaia 	/* decode all the supplied server addresses but use first */
182984e1b21dSOlga Kornievskaia 	status = nfsd4_decode_nl4_server(argp, &copy->cp_src);
183084e1b21dSOlga Kornievskaia 	if (status)
183184e1b21dSOlga Kornievskaia 		return status;
183284e1b21dSOlga Kornievskaia 
183384e1b21dSOlga Kornievskaia 	ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
183484e1b21dSOlga Kornievskaia 	if (ns_dummy == NULL)
183584e1b21dSOlga Kornievskaia 		return nfserrno(-ENOMEM);
183684e1b21dSOlga Kornievskaia 	for (i = 0; i < count - 1; i++) {
183784e1b21dSOlga Kornievskaia 		status = nfsd4_decode_nl4_server(argp, ns_dummy);
183884e1b21dSOlga Kornievskaia 		if (status) {
183984e1b21dSOlga Kornievskaia 			kfree(ns_dummy);
184084e1b21dSOlga Kornievskaia 			return status;
184184e1b21dSOlga Kornievskaia 		}
184284e1b21dSOlga Kornievskaia 	}
184384e1b21dSOlga Kornievskaia 	kfree(ns_dummy);
184484e1b21dSOlga Kornievskaia intra:
184529ae7f9dSAnna Schumaker 
184629ae7f9dSAnna Schumaker 	DECODE_TAIL;
184729ae7f9dSAnna Schumaker }
184829ae7f9dSAnna Schumaker 
184929ae7f9dSAnna Schumaker static __be32
18506308bc98SOlga Kornievskaia nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp,
18516308bc98SOlga Kornievskaia 			    struct nfsd4_offload_status *os)
18526308bc98SOlga Kornievskaia {
18536308bc98SOlga Kornievskaia 	return nfsd4_decode_stateid(argp, &os->stateid);
18546308bc98SOlga Kornievskaia }
18556308bc98SOlga Kornievskaia 
18566308bc98SOlga Kornievskaia static __be32
185751911868SOlga Kornievskaia nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
185851911868SOlga Kornievskaia 			 struct nfsd4_copy_notify *cn)
185951911868SOlga Kornievskaia {
18605aff7d08SChuck Lever 	__be32 status;
186151911868SOlga Kornievskaia 
186251911868SOlga Kornievskaia 	status = nfsd4_decode_stateid(argp, &cn->cpn_src_stateid);
186351911868SOlga Kornievskaia 	if (status)
186451911868SOlga Kornievskaia 		return status;
186551911868SOlga Kornievskaia 	return nfsd4_decode_nl4_server(argp, &cn->cpn_dst);
186651911868SOlga Kornievskaia }
186751911868SOlga Kornievskaia 
186851911868SOlga Kornievskaia static __be32
186924bab491SAnna Schumaker nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
187024bab491SAnna Schumaker {
187124bab491SAnna Schumaker 	DECODE_HEAD;
187224bab491SAnna Schumaker 
187324bab491SAnna Schumaker 	status = nfsd4_decode_stateid(argp, &seek->seek_stateid);
187424bab491SAnna Schumaker 	if (status)
187524bab491SAnna Schumaker 		return status;
187624bab491SAnna Schumaker 
187724bab491SAnna Schumaker 	READ_BUF(8 + 4);
187824bab491SAnna Schumaker 	p = xdr_decode_hyper(p, &seek->seek_offset);
187924bab491SAnna Schumaker 	seek->seek_whence = be32_to_cpup(p);
188024bab491SAnna Schumaker 
188124bab491SAnna Schumaker 	DECODE_TAIL;
188224bab491SAnna Schumaker }
188324bab491SAnna Schumaker 
188423e50fe3SFrank van der Linden /*
188523e50fe3SFrank van der Linden  * XDR data that is more than PAGE_SIZE in size is normally part of a
188623e50fe3SFrank van der Linden  * read or write. However, the size of extended attributes is limited
188723e50fe3SFrank van der Linden  * by the maximum request size, and then further limited by the underlying
188823e50fe3SFrank van der Linden  * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
188923e50fe3SFrank van der Linden  * is 64k). Since there is no kvec- or page-based interface to xattrs,
189023e50fe3SFrank van der Linden  * and we're not dealing with contiguous pages, we need to do some copying.
189123e50fe3SFrank van der Linden  */
189223e50fe3SFrank van der Linden 
189323e50fe3SFrank van der Linden /*
189423e50fe3SFrank van der Linden  * Decode data into buffer. Uses head and pages constructed by
189523e50fe3SFrank van der Linden  * svcxdr_construct_vector.
189623e50fe3SFrank van der Linden  */
189723e50fe3SFrank van der Linden static __be32
189823e50fe3SFrank van der Linden nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct kvec *head,
189923e50fe3SFrank van der Linden 		       struct page **pages, char **bufp, u32 buflen)
190023e50fe3SFrank van der Linden {
190123e50fe3SFrank van der Linden 	char *tmp, *dp;
190223e50fe3SFrank van der Linden 	u32 len;
190323e50fe3SFrank van der Linden 
190423e50fe3SFrank van der Linden 	if (buflen <= head->iov_len) {
190523e50fe3SFrank van der Linden 		/*
190623e50fe3SFrank van der Linden 		 * We're in luck, the head has enough space. Just return
190723e50fe3SFrank van der Linden 		 * the head, no need for copying.
190823e50fe3SFrank van der Linden 		 */
190923e50fe3SFrank van der Linden 		*bufp = head->iov_base;
191023e50fe3SFrank van der Linden 		return 0;
191123e50fe3SFrank van der Linden 	}
191223e50fe3SFrank van der Linden 
191323e50fe3SFrank van der Linden 	tmp = svcxdr_tmpalloc(argp, buflen);
191423e50fe3SFrank van der Linden 	if (tmp == NULL)
191523e50fe3SFrank van der Linden 		return nfserr_jukebox;
191623e50fe3SFrank van der Linden 
191723e50fe3SFrank van der Linden 	dp = tmp;
191823e50fe3SFrank van der Linden 	memcpy(dp, head->iov_base, head->iov_len);
191923e50fe3SFrank van der Linden 	buflen -= head->iov_len;
192023e50fe3SFrank van der Linden 	dp += head->iov_len;
192123e50fe3SFrank van der Linden 
192223e50fe3SFrank van der Linden 	while (buflen > 0) {
192323e50fe3SFrank van der Linden 		len = min_t(u32, buflen, PAGE_SIZE);
192423e50fe3SFrank van der Linden 		memcpy(dp, page_address(*pages), len);
192523e50fe3SFrank van der Linden 
192623e50fe3SFrank van der Linden 		buflen -= len;
192723e50fe3SFrank van der Linden 		dp += len;
192823e50fe3SFrank van der Linden 		pages++;
192923e50fe3SFrank van der Linden 	}
193023e50fe3SFrank van der Linden 
193123e50fe3SFrank van der Linden 	*bufp = tmp;
193223e50fe3SFrank van der Linden 	return 0;
193323e50fe3SFrank van der Linden }
193423e50fe3SFrank van der Linden 
193523e50fe3SFrank van der Linden /*
193623e50fe3SFrank van der Linden  * Get a user extended attribute name from the XDR buffer.
193723e50fe3SFrank van der Linden  * It will not have the "user." prefix, so prepend it.
193823e50fe3SFrank van der Linden  * Lastly, check for nul characters in the name.
193923e50fe3SFrank van der Linden  */
194023e50fe3SFrank van der Linden static __be32
194123e50fe3SFrank van der Linden nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
194223e50fe3SFrank van der Linden {
194323e50fe3SFrank van der Linden 	DECODE_HEAD;
194423e50fe3SFrank van der Linden 	char *name, *sp, *dp;
194523e50fe3SFrank van der Linden 	u32 namelen, cnt;
194623e50fe3SFrank van der Linden 
194723e50fe3SFrank van der Linden 	READ_BUF(4);
194823e50fe3SFrank van der Linden 	namelen = be32_to_cpup(p++);
194923e50fe3SFrank van der Linden 
195023e50fe3SFrank van der Linden 	if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
195123e50fe3SFrank van der Linden 		return nfserr_nametoolong;
195223e50fe3SFrank van der Linden 
195323e50fe3SFrank van der Linden 	if (namelen == 0)
195423e50fe3SFrank van der Linden 		goto xdr_error;
195523e50fe3SFrank van der Linden 
195623e50fe3SFrank van der Linden 	READ_BUF(namelen);
195723e50fe3SFrank van der Linden 
195823e50fe3SFrank van der Linden 	name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
195923e50fe3SFrank van der Linden 	if (!name)
196023e50fe3SFrank van der Linden 		return nfserr_jukebox;
196123e50fe3SFrank van der Linden 
196223e50fe3SFrank van der Linden 	memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
196323e50fe3SFrank van der Linden 
196423e50fe3SFrank van der Linden 	/*
196523e50fe3SFrank van der Linden 	 * Copy the extended attribute name over while checking for 0
196623e50fe3SFrank van der Linden 	 * characters.
196723e50fe3SFrank van der Linden 	 */
196823e50fe3SFrank van der Linden 	sp = (char *)p;
196923e50fe3SFrank van der Linden 	dp = name + XATTR_USER_PREFIX_LEN;
197023e50fe3SFrank van der Linden 	cnt = namelen;
197123e50fe3SFrank van der Linden 
197223e50fe3SFrank van der Linden 	while (cnt-- > 0) {
197323e50fe3SFrank van der Linden 		if (*sp == '\0')
197423e50fe3SFrank van der Linden 			goto xdr_error;
197523e50fe3SFrank van der Linden 		*dp++ = *sp++;
197623e50fe3SFrank van der Linden 	}
197723e50fe3SFrank van der Linden 	*dp = '\0';
197823e50fe3SFrank van der Linden 
197923e50fe3SFrank van der Linden 	*namep = name;
198023e50fe3SFrank van der Linden 
198123e50fe3SFrank van der Linden 	DECODE_TAIL;
198223e50fe3SFrank van der Linden }
198323e50fe3SFrank van der Linden 
198423e50fe3SFrank van der Linden /*
198523e50fe3SFrank van der Linden  * A GETXATTR op request comes without a length specifier. We just set the
198623e50fe3SFrank van der Linden  * maximum length for the reply based on XATTR_SIZE_MAX and the maximum
198723e50fe3SFrank van der Linden  * channel reply size. nfsd_getxattr will probe the length of the xattr,
198823e50fe3SFrank van der Linden  * check it against getxa_len, and allocate + return the value.
198923e50fe3SFrank van der Linden  */
199023e50fe3SFrank van der Linden static __be32
199123e50fe3SFrank van der Linden nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
199223e50fe3SFrank van der Linden 		      struct nfsd4_getxattr *getxattr)
199323e50fe3SFrank van der Linden {
199423e50fe3SFrank van der Linden 	__be32 status;
199523e50fe3SFrank van der Linden 	u32 maxcount;
199623e50fe3SFrank van der Linden 
199723e50fe3SFrank van der Linden 	status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
199823e50fe3SFrank van der Linden 	if (status)
199923e50fe3SFrank van der Linden 		return status;
200023e50fe3SFrank van der Linden 
200123e50fe3SFrank van der Linden 	maxcount = svc_max_payload(argp->rqstp);
200223e50fe3SFrank van der Linden 	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
200323e50fe3SFrank van der Linden 
200423e50fe3SFrank van der Linden 	getxattr->getxa_len = maxcount;
200523e50fe3SFrank van der Linden 
200623e50fe3SFrank van der Linden 	return status;
200723e50fe3SFrank van der Linden }
200823e50fe3SFrank van der Linden 
200923e50fe3SFrank van der Linden static __be32
201023e50fe3SFrank van der Linden nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
201123e50fe3SFrank van der Linden 		      struct nfsd4_setxattr *setxattr)
201223e50fe3SFrank van der Linden {
201323e50fe3SFrank van der Linden 	DECODE_HEAD;
201423e50fe3SFrank van der Linden 	u32 flags, maxcount, size;
201523e50fe3SFrank van der Linden 	struct kvec head;
201623e50fe3SFrank van der Linden 	struct page **pagelist;
201723e50fe3SFrank van der Linden 
201823e50fe3SFrank van der Linden 	READ_BUF(4);
201923e50fe3SFrank van der Linden 	flags = be32_to_cpup(p++);
202023e50fe3SFrank van der Linden 
202123e50fe3SFrank van der Linden 	if (flags > SETXATTR4_REPLACE)
202223e50fe3SFrank van der Linden 		return nfserr_inval;
202323e50fe3SFrank van der Linden 	setxattr->setxa_flags = flags;
202423e50fe3SFrank van der Linden 
202523e50fe3SFrank van der Linden 	status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name);
202623e50fe3SFrank van der Linden 	if (status)
202723e50fe3SFrank van der Linden 		return status;
202823e50fe3SFrank van der Linden 
202923e50fe3SFrank van der Linden 	maxcount = svc_max_payload(argp->rqstp);
203023e50fe3SFrank van der Linden 	maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
203123e50fe3SFrank van der Linden 
203223e50fe3SFrank van der Linden 	READ_BUF(4);
203323e50fe3SFrank van der Linden 	size = be32_to_cpup(p++);
203423e50fe3SFrank van der Linden 	if (size > maxcount)
203523e50fe3SFrank van der Linden 		return nfserr_xattr2big;
203623e50fe3SFrank van der Linden 
203723e50fe3SFrank van der Linden 	setxattr->setxa_len = size;
203823e50fe3SFrank van der Linden 	if (size > 0) {
203923e50fe3SFrank van der Linden 		status = svcxdr_construct_vector(argp, &head, &pagelist, size);
204023e50fe3SFrank van der Linden 		if (status)
204123e50fe3SFrank van der Linden 			return status;
204223e50fe3SFrank van der Linden 
204323e50fe3SFrank van der Linden 		status = nfsd4_vbuf_from_vector(argp, &head, pagelist,
204423e50fe3SFrank van der Linden 		    &setxattr->setxa_buf, size);
204523e50fe3SFrank van der Linden 	}
204623e50fe3SFrank van der Linden 
204723e50fe3SFrank van der Linden 	DECODE_TAIL;
204823e50fe3SFrank van der Linden }
204923e50fe3SFrank van der Linden 
205023e50fe3SFrank van der Linden static __be32
205123e50fe3SFrank van der Linden nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
205223e50fe3SFrank van der Linden 			struct nfsd4_listxattrs *listxattrs)
205323e50fe3SFrank van der Linden {
205423e50fe3SFrank van der Linden 	DECODE_HEAD;
205523e50fe3SFrank van der Linden 	u32 maxcount;
205623e50fe3SFrank van der Linden 
205723e50fe3SFrank van der Linden 	READ_BUF(12);
205823e50fe3SFrank van der Linden 	p = xdr_decode_hyper(p, &listxattrs->lsxa_cookie);
205923e50fe3SFrank van der Linden 
206023e50fe3SFrank van der Linden 	/*
206123e50fe3SFrank van der Linden 	 * If the cookie  is too large to have even one user.x attribute
206223e50fe3SFrank van der Linden 	 * plus trailing '\0' left in a maximum size buffer, it's invalid.
206323e50fe3SFrank van der Linden 	 */
206423e50fe3SFrank van der Linden 	if (listxattrs->lsxa_cookie >=
206523e50fe3SFrank van der Linden 	    (XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
206623e50fe3SFrank van der Linden 		return nfserr_badcookie;
206723e50fe3SFrank van der Linden 
206823e50fe3SFrank van der Linden 	maxcount = be32_to_cpup(p++);
206923e50fe3SFrank van der Linden 	if (maxcount < 8)
207023e50fe3SFrank van der Linden 		/* Always need at least 2 words (length and one character) */
207123e50fe3SFrank van der Linden 		return nfserr_inval;
207223e50fe3SFrank van der Linden 
207323e50fe3SFrank van der Linden 	maxcount = min(maxcount, svc_max_payload(argp->rqstp));
207423e50fe3SFrank van der Linden 	listxattrs->lsxa_maxcount = maxcount;
207523e50fe3SFrank van der Linden 
207623e50fe3SFrank van der Linden 	DECODE_TAIL;
207723e50fe3SFrank van der Linden }
207823e50fe3SFrank van der Linden 
207923e50fe3SFrank van der Linden static __be32
208023e50fe3SFrank van der Linden nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
208123e50fe3SFrank van der Linden 			 struct nfsd4_removexattr *removexattr)
208223e50fe3SFrank van der Linden {
208323e50fe3SFrank van der Linden 	return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
208423e50fe3SFrank van der Linden }
208523e50fe3SFrank van der Linden 
208624bab491SAnna Schumaker static __be32
2087347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
2088347e0ad9SBenny Halevy {
2089347e0ad9SBenny Halevy 	return nfs_ok;
2090347e0ad9SBenny Halevy }
2091347e0ad9SBenny Halevy 
20923c375c6fSBenny Halevy static __be32
20933c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p)
20943c375c6fSBenny Halevy {
20951e685ec2SBenny Halevy 	return nfserr_notsupp;
20963c375c6fSBenny Halevy }
20973c375c6fSBenny Halevy 
2098347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *);
2099347e0ad9SBenny Halevy 
2100c1df609dSChuck Lever static const nfsd4_dec nfsd4_dec_ops[] = {
2101ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
2102ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
2103ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
2104ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
2105ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
2106ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
2107ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
2108ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
2109ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
2110ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
2111ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
2112ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
2113ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
2114ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
2115ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
2116ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
2117ad1060c8SJ. Bruce Fields 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
2118ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_open_confirm,
2119ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
2120ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
2121e1a90ebdSAnna Schumaker 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_putpubfh,
2122ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
2123ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
2124ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
2125ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
2126ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
2127ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
2128ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_renew,
2129ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
2130ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
2131ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
2132ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
2133ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_setclientid,
2134ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm,
2135ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
2136ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
2137ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_release_lockowner,
21382db134ebSAndy Adamson 
21392db134ebSAndy Adamson 	/* new operations for NFSv4.1 */
2140cb73a9f4SJ. Bruce Fields 	[OP_BACKCHANNEL_CTL]	= (nfsd4_dec)nfsd4_decode_backchannel_ctl,
21411d1bc8f2SJ. Bruce Fields 	[OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session,
21429064caaeSRandy Dunlap 	[OP_EXCHANGE_ID]	= (nfsd4_dec)nfsd4_decode_exchange_id,
21439064caaeSRandy Dunlap 	[OP_CREATE_SESSION]	= (nfsd4_dec)nfsd4_decode_create_session,
21449064caaeSRandy Dunlap 	[OP_DESTROY_SESSION]	= (nfsd4_dec)nfsd4_decode_destroy_session,
2145e1ca12dfSBryan Schumaker 	[OP_FREE_STATEID]	= (nfsd4_dec)nfsd4_decode_free_stateid,
21469064caaeSRandy Dunlap 	[OP_GET_DIR_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
21479cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
21489cf514ccSChristoph Hellwig 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_getdeviceinfo,
21499cf514ccSChristoph Hellwig 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
21509cf514ccSChristoph Hellwig 	[OP_LAYOUTCOMMIT]	= (nfsd4_dec)nfsd4_decode_layoutcommit,
21519cf514ccSChristoph Hellwig 	[OP_LAYOUTGET]		= (nfsd4_dec)nfsd4_decode_layoutget,
21529cf514ccSChristoph Hellwig 	[OP_LAYOUTRETURN]	= (nfsd4_dec)nfsd4_decode_layoutreturn,
21539cf514ccSChristoph Hellwig #else
21549064caaeSRandy Dunlap 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_notsupp,
21559064caaeSRandy Dunlap 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
21569064caaeSRandy Dunlap 	[OP_LAYOUTCOMMIT]	= (nfsd4_dec)nfsd4_decode_notsupp,
21579064caaeSRandy Dunlap 	[OP_LAYOUTGET]		= (nfsd4_dec)nfsd4_decode_notsupp,
21589064caaeSRandy Dunlap 	[OP_LAYOUTRETURN]	= (nfsd4_dec)nfsd4_decode_notsupp,
21599cf514ccSChristoph Hellwig #endif
216004f4ad16SJ. Bruce Fields 	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_secinfo_no_name,
21619064caaeSRandy Dunlap 	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
21629064caaeSRandy Dunlap 	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
216317456804SBryan Schumaker 	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_test_stateid,
21649064caaeSRandy Dunlap 	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
2165345c2842SMi Jinlong 	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_destroy_clientid,
21664dc6ec00SJ. Bruce Fields 	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
216787a15a80SAnna Schumaker 
216887a15a80SAnna Schumaker 	/* new operations for NFSv4.2 */
216995d871f0SAnna Schumaker 	[OP_ALLOCATE]		= (nfsd4_dec)nfsd4_decode_fallocate,
217029ae7f9dSAnna Schumaker 	[OP_COPY]		= (nfsd4_dec)nfsd4_decode_copy,
217151911868SOlga Kornievskaia 	[OP_COPY_NOTIFY]	= (nfsd4_dec)nfsd4_decode_copy_notify,
2172b0cb9085SAnna Schumaker 	[OP_DEALLOCATE]		= (nfsd4_dec)nfsd4_decode_fallocate,
217387a15a80SAnna Schumaker 	[OP_IO_ADVISE]		= (nfsd4_dec)nfsd4_decode_notsupp,
217487a15a80SAnna Schumaker 	[OP_LAYOUTERROR]	= (nfsd4_dec)nfsd4_decode_notsupp,
217587a15a80SAnna Schumaker 	[OP_LAYOUTSTATS]	= (nfsd4_dec)nfsd4_decode_notsupp,
2176885e2bf3SOlga Kornievskaia 	[OP_OFFLOAD_CANCEL]	= (nfsd4_dec)nfsd4_decode_offload_status,
21776308bc98SOlga Kornievskaia 	[OP_OFFLOAD_STATUS]	= (nfsd4_dec)nfsd4_decode_offload_status,
2178528b8493SAnna Schumaker 	[OP_READ_PLUS]		= (nfsd4_dec)nfsd4_decode_read,
217924bab491SAnna Schumaker 	[OP_SEEK]		= (nfsd4_dec)nfsd4_decode_seek,
218087a15a80SAnna Schumaker 	[OP_WRITE_SAME]		= (nfsd4_dec)nfsd4_decode_notsupp,
2181ffa0160aSChristoph Hellwig 	[OP_CLONE]		= (nfsd4_dec)nfsd4_decode_clone,
218223e50fe3SFrank van der Linden 	/* RFC 8276 extended atributes operations */
218323e50fe3SFrank van der Linden 	[OP_GETXATTR]		= (nfsd4_dec)nfsd4_decode_getxattr,
218423e50fe3SFrank van der Linden 	[OP_SETXATTR]		= (nfsd4_dec)nfsd4_decode_setxattr,
218523e50fe3SFrank van der Linden 	[OP_LISTXATTRS]		= (nfsd4_dec)nfsd4_decode_listxattrs,
218623e50fe3SFrank van der Linden 	[OP_REMOVEXATTR]	= (nfsd4_dec)nfsd4_decode_removexattr,
21872db134ebSAndy Adamson };
21882db134ebSAndy Adamson 
2189e1a90ebdSAnna Schumaker static inline bool
2190e1a90ebdSAnna Schumaker nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
2191e1a90ebdSAnna Schumaker {
21928217d146SAnna Schumaker 	if (op->opnum < FIRST_NFS4_OP)
2193e1a90ebdSAnna Schumaker 		return false;
21948217d146SAnna Schumaker 	else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
2195e1a90ebdSAnna Schumaker 		return false;
21968217d146SAnna Schumaker 	else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
21978217d146SAnna Schumaker 		return false;
21988217d146SAnna Schumaker 	else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
2199e1a90ebdSAnna Schumaker 		return false;
2200e1a90ebdSAnna Schumaker 	return true;
2201e1a90ebdSAnna Schumaker }
2202f2feb96bSBenny Halevy 
2203347e0ad9SBenny Halevy static __be32
22041da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
22051da177e4SLinus Torvalds {
22061da177e4SLinus Torvalds 	DECODE_HEAD;
22071da177e4SLinus Torvalds 	struct nfsd4_op *op;
22081091006cSJ. Bruce Fields 	bool cachethis = false;
2209a5cddc88SJ. Bruce Fields 	int auth_slack= argp->rqstp->rq_auth_slack;
2210a5cddc88SJ. Bruce Fields 	int max_reply = auth_slack + 8; /* opcnt, status */
2211b0e35fdaSJ. Bruce Fields 	int readcount = 0;
2212b0e35fdaSJ. Bruce Fields 	int readbytes = 0;
22131da177e4SLinus Torvalds 	int i;
22141da177e4SLinus Torvalds 
22151da177e4SLinus Torvalds 	READ_BUF(4);
221606553991SJ. Bruce Fields 	argp->taglen = be32_to_cpup(p++);
22174aed9c46SJ. Bruce Fields 	READ_BUF(argp->taglen);
22181da177e4SLinus Torvalds 	SAVEMEM(argp->tag, argp->taglen);
22194aed9c46SJ. Bruce Fields 	READ_BUF(8);
222006553991SJ. Bruce Fields 	argp->minorversion = be32_to_cpup(p++);
222106553991SJ. Bruce Fields 	argp->opcnt = be32_to_cpup(p++);
22224f0cefbfSJ. Bruce Fields 	max_reply += 4 + (XDR_QUADLEN(argp->taglen) << 2);
22231da177e4SLinus Torvalds 
22241da177e4SLinus Torvalds 	if (argp->taglen > NFSD4_MAX_TAGLEN)
22251da177e4SLinus Torvalds 		goto xdr_error;
22260078117cSJ. Bruce Fields 	/*
22270078117cSJ. Bruce Fields 	 * NFS4ERR_RESOURCE is a more helpful error than GARBAGE_ARGS
22280078117cSJ. Bruce Fields 	 * here, so we return success at the xdr level so that
22290078117cSJ. Bruce Fields 	 * nfsd4_proc can handle this is an NFS-level error.
22300078117cSJ. Bruce Fields 	 */
22310078117cSJ. Bruce Fields 	if (argp->opcnt > NFSD_MAX_OPS_PER_COMPOUND)
22320078117cSJ. Bruce Fields 		return 0;
22331da177e4SLinus Torvalds 
2234e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
22355d6031caSJ. Bruce Fields 		argp->ops = kzalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
22361da177e4SLinus Torvalds 		if (!argp->ops) {
22371da177e4SLinus Torvalds 			argp->ops = argp->iops;
2238817cb9d4SChuck Lever 			dprintk("nfsd: couldn't allocate room for COMPOUND\n");
22391da177e4SLinus Torvalds 			goto xdr_error;
22401da177e4SLinus Torvalds 		}
22411da177e4SLinus Torvalds 	}
22421da177e4SLinus Torvalds 
2243e1a90ebdSAnna Schumaker 	if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
224430cff1ffSBenny Halevy 		argp->opcnt = 0;
224530cff1ffSBenny Halevy 
22461da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
22471da177e4SLinus Torvalds 		op = &argp->ops[i];
22481da177e4SLinus Torvalds 		op->replay = NULL;
22491da177e4SLinus Torvalds 
22508a61b18cSJ. Bruce Fields 		READ_BUF(4);
225106553991SJ. Bruce Fields 		op->opnum = be32_to_cpup(p++);
22521da177e4SLinus Torvalds 
2253*08281341SChuck Lever 		if (nfsd4_opnum_in_range(argp, op)) {
2254e1a90ebdSAnna Schumaker 			op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
2255*08281341SChuck Lever 			if (op->status != nfs_ok)
2256*08281341SChuck Lever 				trace_nfsd_compound_decode_err(argp->rqstp,
2257*08281341SChuck Lever 							       argp->opcnt, i,
2258*08281341SChuck Lever 							       op->opnum,
2259*08281341SChuck Lever 							       op->status);
2260*08281341SChuck Lever 		} else {
22611da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
22621da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
22631da177e4SLinus Torvalds 		}
2264f4f9ef4aSJ. Bruce Fields 		op->opdesc = OPDESC(op);
22651091006cSJ. Bruce Fields 		/*
22661091006cSJ. Bruce Fields 		 * We'll try to cache the result in the DRC if any one
22671091006cSJ. Bruce Fields 		 * op in the compound wants to be cached:
22681091006cSJ. Bruce Fields 		 */
22691091006cSJ. Bruce Fields 		cachethis |= nfsd4_cache_this_op(op);
22706ff40decSJ. Bruce Fields 
2271528b8493SAnna Schumaker 		if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) {
2272b0e35fdaSJ. Bruce Fields 			readcount++;
2273b0e35fdaSJ. Bruce Fields 			readbytes += nfsd4_max_reply(argp->rqstp, op);
2274b0e35fdaSJ. Bruce Fields 		} else
22754f0cefbfSJ. Bruce Fields 			max_reply += nfsd4_max_reply(argp->rqstp, op);
2276f7b43d0cSJ. Bruce Fields 		/*
22777323f0d2SKinglong Mee 		 * OP_LOCK and OP_LOCKT may return a conflicting lock.
22787323f0d2SKinglong Mee 		 * (Special case because it will just skip encoding this
22797323f0d2SKinglong Mee 		 * if it runs out of xdr buffer space, and it is the only
22807323f0d2SKinglong Mee 		 * operation that behaves this way.)
2281f7b43d0cSJ. Bruce Fields 		 */
22827323f0d2SKinglong Mee 		if (op->opnum == OP_LOCK || op->opnum == OP_LOCKT)
2283f7b43d0cSJ. Bruce Fields 			max_reply += NFS4_OPAQUE_LIMIT;
2284e372ba60SJ. Bruce Fields 
2285e372ba60SJ. Bruce Fields 		if (op->status) {
2286e372ba60SJ. Bruce Fields 			argp->opcnt = i+1;
2287e372ba60SJ. Bruce Fields 			break;
2288e372ba60SJ. Bruce Fields 		}
22891da177e4SLinus Torvalds 	}
22901091006cSJ. Bruce Fields 	/* Sessions make the DRC unnecessary: */
22911091006cSJ. Bruce Fields 	if (argp->minorversion)
22921091006cSJ. Bruce Fields 		cachethis = false;
2293b0e35fdaSJ. Bruce Fields 	svc_reserve(argp->rqstp, max_reply + readbytes);
22941091006cSJ. Bruce Fields 	argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
22951da177e4SLinus Torvalds 
2296a5cddc88SJ. Bruce Fields 	if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
2297779fb0f3SJeff Layton 		clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags);
2298b0e35fdaSJ. Bruce Fields 
22991da177e4SLinus Torvalds 	DECODE_TAIL;
23001da177e4SLinus Torvalds }
23011da177e4SLinus Torvalds 
2302b8800921SNeilBrown static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode,
2303b8800921SNeilBrown 			     struct svc_export *exp)
2304c654b8a9SJ. Bruce Fields {
2305b8800921SNeilBrown 	if (exp->ex_flags & NFSEXP_V4ROOT) {
2306b8800921SNeilBrown 		*p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time));
2307b8800921SNeilBrown 		*p++ = 0;
2308b8800921SNeilBrown 	} else if (IS_I_VERSION(inode)) {
230939ca1bf6SAmir Goldstein 		p = xdr_encode_hyper(p, nfsd4_change_attribute(stat, inode));
2310c654b8a9SJ. Bruce Fields 	} else {
2311d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(stat->ctime.tv_sec);
2312d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(stat->ctime.tv_nsec);
2313c654b8a9SJ. Bruce Fields 	}
2314d05d5744SJ. Bruce Fields 	return p;
2315c654b8a9SJ. Bruce Fields }
2316c654b8a9SJ. Bruce Fields 
231716945141SJ. Bruce Fields /*
231816945141SJ. Bruce Fields  * ctime (in NFSv4, time_metadata) is not writeable, and the client
231916945141SJ. Bruce Fields  * doesn't really care what resolution could theoretically be stored by
232016945141SJ. Bruce Fields  * the filesystem.
232116945141SJ. Bruce Fields  *
232216945141SJ. Bruce Fields  * The client cares how close together changes can be while still
232316945141SJ. Bruce Fields  * guaranteeing ctime changes.  For most filesystems (which have
232416945141SJ. Bruce Fields  * timestamps with nanosecond fields) that is limited by the resolution
232516945141SJ. Bruce Fields  * of the time returned from current_time() (which I'm assuming to be
232616945141SJ. Bruce Fields  * 1/HZ).
232716945141SJ. Bruce Fields  */
232816945141SJ. Bruce Fields static __be32 *encode_time_delta(__be32 *p, struct inode *inode)
232916945141SJ. Bruce Fields {
2330e4598e38SArnd Bergmann 	struct timespec64 ts;
233116945141SJ. Bruce Fields 	u32 ns;
233216945141SJ. Bruce Fields 
233316945141SJ. Bruce Fields 	ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
2334e4598e38SArnd Bergmann 	ts = ns_to_timespec64(ns);
233516945141SJ. Bruce Fields 
233616945141SJ. Bruce Fields 	p = xdr_encode_hyper(p, ts.tv_sec);
233716945141SJ. Bruce Fields 	*p++ = cpu_to_be32(ts.tv_nsec);
233816945141SJ. Bruce Fields 
233916945141SJ. Bruce Fields 	return p;
234016945141SJ. Bruce Fields }
234116945141SJ. Bruce Fields 
2342d05d5744SJ. Bruce Fields static __be32 *encode_cinfo(__be32 *p, struct nfsd4_change_info *c)
2343c654b8a9SJ. Bruce Fields {
2344d05d5744SJ. Bruce Fields 	*p++ = cpu_to_be32(c->atomic);
2345c654b8a9SJ. Bruce Fields 	if (c->change_supported) {
2346d05d5744SJ. Bruce Fields 		p = xdr_encode_hyper(p, c->before_change);
2347d05d5744SJ. Bruce Fields 		p = xdr_encode_hyper(p, c->after_change);
2348c654b8a9SJ. Bruce Fields 	} else {
2349d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->before_ctime_sec);
2350d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->before_ctime_nsec);
2351d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->after_ctime_sec);
2352d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->after_ctime_nsec);
2353c654b8a9SJ. Bruce Fields 	}
2354d05d5744SJ. Bruce Fields 	return p;
2355c654b8a9SJ. Bruce Fields }
23561da177e4SLinus Torvalds 
235781c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
2358e7a0444aSWeston Andros Adamson  * separated @sep, escaped with esc_enter and esc_exit.
235981c3f413SJ.Bruce Fields  */
2360ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
2361ddd1ea56SJ. Bruce Fields 					  char *components, char esc_enter,
2362ddd1ea56SJ. Bruce Fields 					  char esc_exit)
236381c3f413SJ.Bruce Fields {
2364ddd1ea56SJ. Bruce Fields 	__be32 *p;
2365082d4bd7SJ. Bruce Fields 	__be32 pathlen;
2366082d4bd7SJ. Bruce Fields 	int pathlen_offset;
236781c3f413SJ.Bruce Fields 	int strlen, count=0;
2368e7a0444aSWeston Andros Adamson 	char *str, *end, *next;
236981c3f413SJ.Bruce Fields 
237081c3f413SJ.Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
2371082d4bd7SJ. Bruce Fields 
2372082d4bd7SJ. Bruce Fields 	pathlen_offset = xdr->buf->len;
2373ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2374ddd1ea56SJ. Bruce Fields 	if (!p)
237581c3f413SJ.Bruce Fields 		return nfserr_resource;
2376082d4bd7SJ. Bruce Fields 	p++; /* We will fill this in with @count later */
2377082d4bd7SJ. Bruce Fields 
237881c3f413SJ.Bruce Fields 	end = str = components;
237981c3f413SJ.Bruce Fields 	while (*end) {
2380e7a0444aSWeston Andros Adamson 		bool found_esc = false;
2381e7a0444aSWeston Andros Adamson 
2382e7a0444aSWeston Andros Adamson 		/* try to parse as esc_start, ..., esc_end, sep */
2383e7a0444aSWeston Andros Adamson 		if (*str == esc_enter) {
2384e7a0444aSWeston Andros Adamson 			for (; *end && (*end != esc_exit); end++)
2385e7a0444aSWeston Andros Adamson 				/* find esc_exit or end of string */;
2386e7a0444aSWeston Andros Adamson 			next = end + 1;
2387e7a0444aSWeston Andros Adamson 			if (*end && (!*next || *next == sep)) {
2388e7a0444aSWeston Andros Adamson 				str++;
2389e7a0444aSWeston Andros Adamson 				found_esc = true;
2390e7a0444aSWeston Andros Adamson 			}
2391e7a0444aSWeston Andros Adamson 		}
2392e7a0444aSWeston Andros Adamson 
2393e7a0444aSWeston Andros Adamson 		if (!found_esc)
239481c3f413SJ.Bruce Fields 			for (; *end && (*end != sep); end++)
2395e7a0444aSWeston Andros Adamson 				/* find sep or end of string */;
2396e7a0444aSWeston Andros Adamson 
239781c3f413SJ.Bruce Fields 		strlen = end - str;
239881c3f413SJ.Bruce Fields 		if (strlen) {
2399ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, strlen + 4);
2400ddd1ea56SJ. Bruce Fields 			if (!p)
240181c3f413SJ.Bruce Fields 				return nfserr_resource;
24020c0c267bSJ. Bruce Fields 			p = xdr_encode_opaque(p, str, strlen);
240381c3f413SJ.Bruce Fields 			count++;
240481c3f413SJ.Bruce Fields 		}
240581c3f413SJ.Bruce Fields 		else
240681c3f413SJ.Bruce Fields 			end++;
24075a64e569SBenjamin Coddington 		if (found_esc)
24085a64e569SBenjamin Coddington 			end = next;
24095a64e569SBenjamin Coddington 
241081c3f413SJ.Bruce Fields 		str = end;
241181c3f413SJ.Bruce Fields 	}
2412bf7491f1SBenjamin Coddington 	pathlen = htonl(count);
2413082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
241481c3f413SJ.Bruce Fields 	return 0;
241581c3f413SJ.Bruce Fields }
241681c3f413SJ.Bruce Fields 
2417e7a0444aSWeston Andros Adamson /* Encode as an array of strings the string given with components
2418e7a0444aSWeston Andros Adamson  * separated @sep.
2419e7a0444aSWeston Andros Adamson  */
2420ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
2421ddd1ea56SJ. Bruce Fields 				      char *components)
2422e7a0444aSWeston Andros Adamson {
2423ddd1ea56SJ. Bruce Fields 	return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
2424e7a0444aSWeston Andros Adamson }
2425e7a0444aSWeston Andros Adamson 
242681c3f413SJ.Bruce Fields /*
242781c3f413SJ.Bruce Fields  * encode a location element of a fs_locations structure
242881c3f413SJ.Bruce Fields  */
2429ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
2430ddd1ea56SJ. Bruce Fields 					struct nfsd4_fs_location *location)
243181c3f413SJ.Bruce Fields {
2432b37ad28bSAl Viro 	__be32 status;
243381c3f413SJ.Bruce Fields 
2434ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
2435e7a0444aSWeston Andros Adamson 						'[', ']');
243681c3f413SJ.Bruce Fields 	if (status)
243781c3f413SJ.Bruce Fields 		return status;
2438ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components(xdr, '/', location->path);
243981c3f413SJ.Bruce Fields 	if (status)
244081c3f413SJ.Bruce Fields 		return status;
244181c3f413SJ.Bruce Fields 	return 0;
244281c3f413SJ.Bruce Fields }
244381c3f413SJ.Bruce Fields 
244481c3f413SJ.Bruce Fields /*
2445ed748aacSTrond Myklebust  * Encode a path in RFC3530 'pathname4' format
244681c3f413SJ.Bruce Fields  */
2447ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_path(struct xdr_stream *xdr,
2448ddd1ea56SJ. Bruce Fields 				const struct path *root,
2449ddd1ea56SJ. Bruce Fields 				const struct path *path)
245081c3f413SJ.Bruce Fields {
2451301f0268SAl Viro 	struct path cur = *path;
2452ddd1ea56SJ. Bruce Fields 	__be32 *p;
2453ed748aacSTrond Myklebust 	struct dentry **components = NULL;
2454ed748aacSTrond Myklebust 	unsigned int ncomponents = 0;
2455ed748aacSTrond Myklebust 	__be32 err = nfserr_jukebox;
245681c3f413SJ.Bruce Fields 
2457ed748aacSTrond Myklebust 	dprintk("nfsd4_encode_components(");
245881c3f413SJ.Bruce Fields 
2459ed748aacSTrond Myklebust 	path_get(&cur);
2460ed748aacSTrond Myklebust 	/* First walk the path up to the nfsd root, and store the
2461ed748aacSTrond Myklebust 	 * dentries/path components in an array.
2462ed748aacSTrond Myklebust 	 */
2463ed748aacSTrond Myklebust 	for (;;) {
2464b77a4b2eSKinglong Mee 		if (path_equal(&cur, root))
2465ed748aacSTrond Myklebust 			break;
2466ed748aacSTrond Myklebust 		if (cur.dentry == cur.mnt->mnt_root) {
2467ed748aacSTrond Myklebust 			if (follow_up(&cur))
2468ed748aacSTrond Myklebust 				continue;
2469ed748aacSTrond Myklebust 			goto out_free;
247081c3f413SJ.Bruce Fields 		}
2471ed748aacSTrond Myklebust 		if ((ncomponents & 15) == 0) {
2472ed748aacSTrond Myklebust 			struct dentry **new;
2473ed748aacSTrond Myklebust 			new = krealloc(components,
2474ed748aacSTrond Myklebust 					sizeof(*new) * (ncomponents + 16),
2475ed748aacSTrond Myklebust 					GFP_KERNEL);
2476ed748aacSTrond Myklebust 			if (!new)
2477ed748aacSTrond Myklebust 				goto out_free;
2478ed748aacSTrond Myklebust 			components = new;
2479ed748aacSTrond Myklebust 		}
2480ed748aacSTrond Myklebust 		components[ncomponents++] = cur.dentry;
2481ed748aacSTrond Myklebust 		cur.dentry = dget_parent(cur.dentry);
2482ed748aacSTrond Myklebust 	}
2483ddd1ea56SJ. Bruce Fields 	err = nfserr_resource;
2484ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2485ddd1ea56SJ. Bruce Fields 	if (!p)
2486ed748aacSTrond Myklebust 		goto out_free;
2487c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(ncomponents);
2488ed748aacSTrond Myklebust 
2489ed748aacSTrond Myklebust 	while (ncomponents) {
2490ed748aacSTrond Myklebust 		struct dentry *dentry = components[ncomponents - 1];
2491301f0268SAl Viro 		unsigned int len;
2492ed748aacSTrond Myklebust 
2493301f0268SAl Viro 		spin_lock(&dentry->d_lock);
2494301f0268SAl Viro 		len = dentry->d_name.len;
2495ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, len + 4);
2496ddd1ea56SJ. Bruce Fields 		if (!p) {
2497301f0268SAl Viro 			spin_unlock(&dentry->d_lock);
2498ed748aacSTrond Myklebust 			goto out_free;
2499301f0268SAl Viro 		}
25000c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, dentry->d_name.name, len);
2501a455589fSAl Viro 		dprintk("/%pd", dentry);
2502301f0268SAl Viro 		spin_unlock(&dentry->d_lock);
2503ed748aacSTrond Myklebust 		dput(dentry);
2504ed748aacSTrond Myklebust 		ncomponents--;
2505ed748aacSTrond Myklebust 	}
2506ed748aacSTrond Myklebust 
2507ed748aacSTrond Myklebust 	err = 0;
2508ed748aacSTrond Myklebust out_free:
2509ed748aacSTrond Myklebust 	dprintk(")\n");
2510ed748aacSTrond Myklebust 	while (ncomponents)
2511ed748aacSTrond Myklebust 		dput(components[--ncomponents]);
2512ed748aacSTrond Myklebust 	kfree(components);
2513ed748aacSTrond Myklebust 	path_put(&cur);
2514ed748aacSTrond Myklebust 	return err;
2515ed748aacSTrond Myklebust }
2516ed748aacSTrond Myklebust 
2517ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr,
2518ddd1ea56SJ. Bruce Fields 			struct svc_rqst *rqstp, const struct path *path)
2519ed748aacSTrond Myklebust {
2520ed748aacSTrond Myklebust 	struct svc_export *exp_ps;
2521ed748aacSTrond Myklebust 	__be32 res;
2522ed748aacSTrond Myklebust 
2523ed748aacSTrond Myklebust 	exp_ps = rqst_find_fsidzero_export(rqstp);
2524ed748aacSTrond Myklebust 	if (IS_ERR(exp_ps))
2525ed748aacSTrond Myklebust 		return nfserrno(PTR_ERR(exp_ps));
2526ddd1ea56SJ. Bruce Fields 	res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
2527ed748aacSTrond Myklebust 	exp_put(exp_ps);
2528ed748aacSTrond Myklebust 	return res;
252981c3f413SJ.Bruce Fields }
253081c3f413SJ.Bruce Fields 
253181c3f413SJ.Bruce Fields /*
253281c3f413SJ.Bruce Fields  *  encode a fs_locations structure
253381c3f413SJ.Bruce Fields  */
2534ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr,
2535ddd1ea56SJ. Bruce Fields 			struct svc_rqst *rqstp, struct svc_export *exp)
253681c3f413SJ.Bruce Fields {
2537b37ad28bSAl Viro 	__be32 status;
2538cc45f017SAl Viro 	int i;
2539ddd1ea56SJ. Bruce Fields 	__be32 *p;
254081c3f413SJ.Bruce Fields 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
254181c3f413SJ.Bruce Fields 
2542ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
254381c3f413SJ.Bruce Fields 	if (status)
254481c3f413SJ.Bruce Fields 		return status;
2545ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2546ddd1ea56SJ. Bruce Fields 	if (!p)
254781c3f413SJ.Bruce Fields 		return nfserr_resource;
2548c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(fslocs->locations_count);
254981c3f413SJ.Bruce Fields 	for (i=0; i<fslocs->locations_count; i++) {
2550ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
255181c3f413SJ.Bruce Fields 		if (status)
255281c3f413SJ.Bruce Fields 			return status;
255381c3f413SJ.Bruce Fields 	}
255481c3f413SJ.Bruce Fields 	return 0;
255581c3f413SJ.Bruce Fields }
25561da177e4SLinus Torvalds 
25573d2544b1SJ. Bruce Fields static u32 nfs4_file_type(umode_t mode)
25583d2544b1SJ. Bruce Fields {
25593d2544b1SJ. Bruce Fields 	switch (mode & S_IFMT) {
25603d2544b1SJ. Bruce Fields 	case S_IFIFO:	return NF4FIFO;
25613d2544b1SJ. Bruce Fields 	case S_IFCHR:	return NF4CHR;
25623d2544b1SJ. Bruce Fields 	case S_IFDIR:	return NF4DIR;
25633d2544b1SJ. Bruce Fields 	case S_IFBLK:	return NF4BLK;
25643d2544b1SJ. Bruce Fields 	case S_IFLNK:	return NF4LNK;
25653d2544b1SJ. Bruce Fields 	case S_IFREG:	return NF4REG;
25663d2544b1SJ. Bruce Fields 	case S_IFSOCK:	return NF4SOCK;
25673d2544b1SJ. Bruce Fields 	default:	return NF4BAD;
256825fef48bSTom Rix 	}
25693d2544b1SJ. Bruce Fields }
25701da177e4SLinus Torvalds 
2571b37ad28bSAl Viro static inline __be32
2572ddd1ea56SJ. Bruce Fields nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2573ddd1ea56SJ. Bruce Fields 		     struct nfs4_ace *ace)
25741da177e4SLinus Torvalds {
25753554116dSJ. Bruce Fields 	if (ace->whotype != NFS4_ACL_WHO_NAMED)
2576ddd1ea56SJ. Bruce Fields 		return nfs4_acl_write_who(xdr, ace->whotype);
25773554116dSJ. Bruce Fields 	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
2578ddd1ea56SJ. Bruce Fields 		return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
2579ab8e4aeeSEric W. Biederman 	else
2580ddd1ea56SJ. Bruce Fields 		return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
25811da177e4SLinus Torvalds }
25821da177e4SLinus Torvalds 
25836896f15aSKinglong Mee static inline __be32
25848a4c3926SJeff Layton nfsd4_encode_layout_types(struct xdr_stream *xdr, u32 layout_types)
25856896f15aSKinglong Mee {
25866896f15aSKinglong Mee 	__be32		*p;
25878a4c3926SJeff Layton 	unsigned long	i = hweight_long(layout_types);
25886896f15aSKinglong Mee 
25898a4c3926SJeff Layton 	p = xdr_reserve_space(xdr, 4 + 4 * i);
25906896f15aSKinglong Mee 	if (!p)
25916896f15aSKinglong Mee 		return nfserr_resource;
25928a4c3926SJeff Layton 
25938a4c3926SJeff Layton 	*p++ = cpu_to_be32(i);
25948a4c3926SJeff Layton 
25958a4c3926SJeff Layton 	for (i = LAYOUT_NFSV4_1_FILES; i < LAYOUT_TYPE_MAX; ++i)
25968a4c3926SJeff Layton 		if (layout_types & (1 << i))
25978a4c3926SJeff Layton 			*p++ = cpu_to_be32(i);
25986896f15aSKinglong Mee 
25996896f15aSKinglong Mee 	return 0;
26006896f15aSKinglong Mee }
26016896f15aSKinglong Mee 
260242ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
260342ca0993SJ.Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
260442ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
2605c2227a39SKinglong Mee #define WORD2_ABSENT_FS_ATTRS 0
260642ca0993SJ.Bruce Fields 
260718032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
260818032ca0SDavid Quigley static inline __be32
2609ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2610ddd1ea56SJ. Bruce Fields 			    void *context, int len)
261118032ca0SDavid Quigley {
2612ddd1ea56SJ. Bruce Fields 	__be32 *p;
261318032ca0SDavid Quigley 
2614ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
2615ddd1ea56SJ. Bruce Fields 	if (!p)
261618032ca0SDavid Quigley 		return nfserr_resource;
261718032ca0SDavid Quigley 
261818032ca0SDavid Quigley 	/*
261918032ca0SDavid Quigley 	 * For now we use a 0 here to indicate the null translation; in
262018032ca0SDavid Quigley 	 * the future we may place a call to translation code here.
262118032ca0SDavid Quigley 	 */
2622c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* lfs */
2623c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* pi */
262418032ca0SDavid Quigley 	p = xdr_encode_opaque(p, context, len);
262518032ca0SDavid Quigley 	return 0;
262618032ca0SDavid Quigley }
262718032ca0SDavid Quigley #else
262818032ca0SDavid Quigley static inline __be32
2629ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2630ddd1ea56SJ. Bruce Fields 			    void *context, int len)
263118032ca0SDavid Quigley { return 0; }
263218032ca0SDavid Quigley #endif
263318032ca0SDavid Quigley 
2634c2227a39SKinglong Mee static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
263542ca0993SJ.Bruce Fields {
263642ca0993SJ.Bruce Fields 	/* As per referral draft:  */
263742ca0993SJ.Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
263842ca0993SJ.Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
263942ca0993SJ.Bruce Fields 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
264042ca0993SJ.Bruce Fields 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
264142ca0993SJ.Bruce Fields 			*rdattr_err = NFSERR_MOVED;
264242ca0993SJ.Bruce Fields 		else
264342ca0993SJ.Bruce Fields 			return nfserr_moved;
264442ca0993SJ.Bruce Fields 	}
264542ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
264642ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
2647c2227a39SKinglong Mee 	*bmval2 &= WORD2_ABSENT_FS_ATTRS;
264842ca0993SJ.Bruce Fields 	return 0;
264942ca0993SJ.Bruce Fields }
26501da177e4SLinus Torvalds 
2651ae7095a7SJ. Bruce Fields 
2652ae7095a7SJ. Bruce Fields static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
2653ae7095a7SJ. Bruce Fields {
2654ae7095a7SJ. Bruce Fields 	struct path path = exp->ex_path;
2655ae7095a7SJ. Bruce Fields 	int err;
2656ae7095a7SJ. Bruce Fields 
2657ae7095a7SJ. Bruce Fields 	path_get(&path);
2658ae7095a7SJ. Bruce Fields 	while (follow_up(&path)) {
2659ae7095a7SJ. Bruce Fields 		if (path.dentry != path.mnt->mnt_root)
2660ae7095a7SJ. Bruce Fields 			break;
2661ae7095a7SJ. Bruce Fields 	}
2662a528d35eSDavid Howells 	err = vfs_getattr(&path, stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
2663ae7095a7SJ. Bruce Fields 	path_put(&path);
2664ae7095a7SJ. Bruce Fields 	return err;
2665ae7095a7SJ. Bruce Fields }
2666ae7095a7SJ. Bruce Fields 
266775976de6SKinglong Mee static __be32
266875976de6SKinglong Mee nfsd4_encode_bitmap(struct xdr_stream *xdr, u32 bmval0, u32 bmval1, u32 bmval2)
266975976de6SKinglong Mee {
267075976de6SKinglong Mee 	__be32 *p;
267175976de6SKinglong Mee 
267275976de6SKinglong Mee 	if (bmval2) {
267375976de6SKinglong Mee 		p = xdr_reserve_space(xdr, 16);
267475976de6SKinglong Mee 		if (!p)
267575976de6SKinglong Mee 			goto out_resource;
267675976de6SKinglong Mee 		*p++ = cpu_to_be32(3);
267775976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
267875976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval1);
267975976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval2);
268075976de6SKinglong Mee 	} else if (bmval1) {
268175976de6SKinglong Mee 		p = xdr_reserve_space(xdr, 12);
268275976de6SKinglong Mee 		if (!p)
268375976de6SKinglong Mee 			goto out_resource;
268475976de6SKinglong Mee 		*p++ = cpu_to_be32(2);
268575976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
268675976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval1);
268775976de6SKinglong Mee 	} else {
268875976de6SKinglong Mee 		p = xdr_reserve_space(xdr, 8);
268975976de6SKinglong Mee 		if (!p)
269075976de6SKinglong Mee 			goto out_resource;
269175976de6SKinglong Mee 		*p++ = cpu_to_be32(1);
269275976de6SKinglong Mee 		*p++ = cpu_to_be32(bmval0);
269375976de6SKinglong Mee 	}
269475976de6SKinglong Mee 
269575976de6SKinglong Mee 	return 0;
269675976de6SKinglong Mee out_resource:
269775976de6SKinglong Mee 	return nfserr_resource;
269875976de6SKinglong Mee }
269975976de6SKinglong Mee 
27001da177e4SLinus Torvalds /*
27011da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
27021da177e4SLinus Torvalds  * ourselves.
27031da177e4SLinus Torvalds  */
2704da2ebce6SJeff Layton static __be32
2705d5184658SJ. Bruce Fields nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
2706d5184658SJ. Bruce Fields 		struct svc_export *exp,
2707d5184658SJ. Bruce Fields 		struct dentry *dentry, u32 *bmval,
2708406a7ea9SFrank Filz 		struct svc_rqst *rqstp, int ignore_crossmnt)
27091da177e4SLinus Torvalds {
27101da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
27111da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
27127e705706SAndy Adamson 	u32 bmval2 = bmval[2];
27131da177e4SLinus Torvalds 	struct kstat stat;
2714d50e6136SJ. Bruce Fields 	struct svc_fh *tempfh = NULL;
27151da177e4SLinus Torvalds 	struct kstatfs statfs;
2716ddd1ea56SJ. Bruce Fields 	__be32 *p;
27171fcea5b2SJ. Bruce Fields 	int starting_len = xdr->buf->len;
2718082d4bd7SJ. Bruce Fields 	int attrlen_offset;
2719082d4bd7SJ. Bruce Fields 	__be32 attrlen;
27201da177e4SLinus Torvalds 	u32 dummy;
27211da177e4SLinus Torvalds 	u64 dummy64;
272242ca0993SJ.Bruce Fields 	u32 rdattr_err = 0;
2723b37ad28bSAl Viro 	__be32 status;
2724b8dd7b9aSAl Viro 	int err;
27251da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
27260ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
272718032ca0SDavid Quigley 	void *context = NULL;
272818032ca0SDavid Quigley 	int contextlen;
27290ab88ca4SArnd Bergmann #endif
273018032ca0SDavid Quigley 	bool contextsupport = false;
27317e705706SAndy Adamson 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
27327e705706SAndy Adamson 	u32 minorversion = resp->cstate.minorversion;
2733ebabe9a9SChristoph Hellwig 	struct path path = {
2734ebabe9a9SChristoph Hellwig 		.mnt	= exp->ex_path.mnt,
2735ebabe9a9SChristoph Hellwig 		.dentry	= dentry,
2736ebabe9a9SChristoph Hellwig 	};
27373d733711SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
27381da177e4SLinus Torvalds 
27391da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
2740916d2d84SJ. Bruce Fields 	BUG_ON(!nfsd_attrs_supported(minorversion, bmval));
27411da177e4SLinus Torvalds 
274242ca0993SJ.Bruce Fields 	if (exp->ex_fslocs.migrated) {
2743c2227a39SKinglong Mee 		status = fattr_handle_absent_fs(&bmval0, &bmval1, &bmval2, &rdattr_err);
274442ca0993SJ.Bruce Fields 		if (status)
274542ca0993SJ.Bruce Fields 			goto out;
274642ca0993SJ.Bruce Fields 	}
274742ca0993SJ.Bruce Fields 
2748a528d35eSDavid Howells 	err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
2749b8dd7b9aSAl Viro 	if (err)
27501da177e4SLinus Torvalds 		goto out_nfserr;
275112337901SChristoph Hellwig 	if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
275212337901SChristoph Hellwig 			FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
27531da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
27541da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
2755ebabe9a9SChristoph Hellwig 		err = vfs_statfs(&path, &statfs);
2756b8dd7b9aSAl Viro 		if (err)
27571da177e4SLinus Torvalds 			goto out_nfserr;
27581da177e4SLinus Torvalds 	}
27591da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
2760d50e6136SJ. Bruce Fields 		tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
2761d50e6136SJ. Bruce Fields 		status = nfserr_jukebox;
2762d50e6136SJ. Bruce Fields 		if (!tempfh)
2763d50e6136SJ. Bruce Fields 			goto out;
2764d50e6136SJ. Bruce Fields 		fh_init(tempfh, NFS4_FHSIZE);
2765d50e6136SJ. Bruce Fields 		status = fh_compose(tempfh, exp, dentry, NULL);
27661da177e4SLinus Torvalds 		if (status)
27671da177e4SLinus Torvalds 			goto out;
2768d50e6136SJ. Bruce Fields 		fhp = tempfh;
27691da177e4SLinus Torvalds 	}
27701da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
27710c9d65e7SAndreas Gruenbacher 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
2772b8dd7b9aSAl Viro 		if (err == -EOPNOTSUPP)
27731da177e4SLinus Torvalds 			bmval0 &= ~FATTR4_WORD0_ACL;
2774b8dd7b9aSAl Viro 		else if (err == -EINVAL) {
27751da177e4SLinus Torvalds 			status = nfserr_attrnotsupp;
27761da177e4SLinus Torvalds 			goto out;
2777b8dd7b9aSAl Viro 		} else if (err != 0)
27781da177e4SLinus Torvalds 			goto out_nfserr;
27791da177e4SLinus Torvalds 	}
27802b44f1baSBenny Halevy 
278118032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
2782c2227a39SKinglong Mee 	if ((bmval2 & FATTR4_WORD2_SECURITY_LABEL) ||
2783c2227a39SKinglong Mee 	     bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
278432ddd944SJ. Bruce Fields 		if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
27852b0143b5SDavid Howells 			err = security_inode_getsecctx(d_inode(dentry),
278618032ca0SDavid Quigley 						&context, &contextlen);
278732ddd944SJ. Bruce Fields 		else
278832ddd944SJ. Bruce Fields 			err = -EOPNOTSUPP;
278918032ca0SDavid Quigley 		contextsupport = (err == 0);
279018032ca0SDavid Quigley 		if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
279118032ca0SDavid Quigley 			if (err == -EOPNOTSUPP)
279218032ca0SDavid Quigley 				bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
279318032ca0SDavid Quigley 			else if (err)
279418032ca0SDavid Quigley 				goto out_nfserr;
279518032ca0SDavid Quigley 		}
279618032ca0SDavid Quigley 	}
279718032ca0SDavid Quigley #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
279818032ca0SDavid Quigley 
279975976de6SKinglong Mee 	status = nfsd4_encode_bitmap(xdr, bmval0, bmval1, bmval2);
280075976de6SKinglong Mee 	if (status)
280175976de6SKinglong Mee 		goto out;
2802082d4bd7SJ. Bruce Fields 
2803082d4bd7SJ. Bruce Fields 	attrlen_offset = xdr->buf->len;
2804ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2805ddd1ea56SJ. Bruce Fields 	if (!p)
2806ddd1ea56SJ. Bruce Fields 		goto out_resource;
2807082d4bd7SJ. Bruce Fields 	p++;                /* to be backfilled later */
28081da177e4SLinus Torvalds 
28091da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
2810dcd20869SJ. Bruce Fields 		u32 supp[3];
2811dcd20869SJ. Bruce Fields 
2812dcd20869SJ. Bruce Fields 		memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
28137e705706SAndy Adamson 
28140c9d65e7SAndreas Gruenbacher 		if (!IS_POSIXACL(dentry->d_inode))
2815916d2d84SJ. Bruce Fields 			supp[0] &= ~FATTR4_WORD0_ACL;
281618032ca0SDavid Quigley 		if (!contextsupport)
2817916d2d84SJ. Bruce Fields 			supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
2818916d2d84SJ. Bruce Fields 		if (!supp[2]) {
2819ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 12);
2820ddd1ea56SJ. Bruce Fields 			if (!p)
28212b44f1baSBenny Halevy 				goto out_resource;
2822c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(2);
2823916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[0]);
2824916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[1]);
28257e705706SAndy Adamson 		} else {
2826ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 16);
2827ddd1ea56SJ. Bruce Fields 			if (!p)
28282b44f1baSBenny Halevy 				goto out_resource;
2829c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(3);
2830916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[0]);
2831916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[1]);
2832916d2d84SJ. Bruce Fields 			*p++ = cpu_to_be32(supp[2]);
28337e705706SAndy Adamson 		}
28341da177e4SLinus Torvalds 	}
28351da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
2836ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2837ddd1ea56SJ. Bruce Fields 		if (!p)
28381da177e4SLinus Torvalds 			goto out_resource;
28393d2544b1SJ. Bruce Fields 		dummy = nfs4_file_type(stat.mode);
28406b6d8137SJ. Bruce Fields 		if (dummy == NF4BAD) {
28416b6d8137SJ. Bruce Fields 			status = nfserr_serverfault;
28426b6d8137SJ. Bruce Fields 			goto out;
28436b6d8137SJ. Bruce Fields 		}
2844c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(dummy);
28451da177e4SLinus Torvalds 	}
28461da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
2847ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2848ddd1ea56SJ. Bruce Fields 		if (!p)
28491da177e4SLinus Torvalds 			goto out_resource;
285049640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
2851c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_FH_PERSISTENT);
285249640001SNeilBrown 		else
2853c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_FH_PERSISTENT|
2854c373b0a4SJ. Bruce Fields 						NFS4_FH_VOL_RENAME);
28551da177e4SLinus Torvalds 	}
28561da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
2857ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2858ddd1ea56SJ. Bruce Fields 		if (!p)
28591da177e4SLinus Torvalds 			goto out_resource;
2860b8800921SNeilBrown 		p = encode_change(p, &stat, d_inode(dentry), exp);
28611da177e4SLinus Torvalds 	}
28621da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
2863ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2864ddd1ea56SJ. Bruce Fields 		if (!p)
28651da177e4SLinus Torvalds 			goto out_resource;
2866b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.size);
28671da177e4SLinus Torvalds 	}
28681da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
2869ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2870ddd1ea56SJ. Bruce Fields 		if (!p)
28711da177e4SLinus Torvalds 			goto out_resource;
2872c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
28731da177e4SLinus Torvalds 	}
28741da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
2875ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2876ddd1ea56SJ. Bruce Fields 		if (!p)
28771da177e4SLinus Torvalds 			goto out_resource;
2878c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
28791da177e4SLinus Torvalds 	}
28801da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
2881ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2882ddd1ea56SJ. Bruce Fields 		if (!p)
28831da177e4SLinus Torvalds 			goto out_resource;
2884c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
28851da177e4SLinus Torvalds 	}
28861da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
2887ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
2888ddd1ea56SJ. Bruce Fields 		if (!p)
28891da177e4SLinus Torvalds 			goto out_resource;
289042ca0993SJ.Bruce Fields 		if (exp->ex_fslocs.migrated) {
2891b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
2892b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
2893af6a4e28SNeilBrown 		} else switch(fsid_source(fhp)) {
2894af6a4e28SNeilBrown 		case FSIDSOURCE_FSID:
2895b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
2896b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, (u64)0);
2897af6a4e28SNeilBrown 			break;
2898af6a4e28SNeilBrown 		case FSIDSOURCE_DEV:
2899c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
2900c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(MAJOR(stat.dev));
2901c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
2902c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(MINOR(stat.dev));
2903af6a4e28SNeilBrown 			break;
2904af6a4e28SNeilBrown 		case FSIDSOURCE_UUID:
290594eb3689SKinglong Mee 			p = xdr_encode_opaque_fixed(p, exp->ex_uuid,
290694eb3689SKinglong Mee 								EX_UUID_LEN);
2907af6a4e28SNeilBrown 			break;
29081da177e4SLinus Torvalds 		}
29091da177e4SLinus Torvalds 	}
29101da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
2911ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2912ddd1ea56SJ. Bruce Fields 		if (!p)
29131da177e4SLinus Torvalds 			goto out_resource;
2914c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
29151da177e4SLinus Torvalds 	}
29161da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
2917ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2918ddd1ea56SJ. Bruce Fields 		if (!p)
29191da177e4SLinus Torvalds 			goto out_resource;
2920c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(nn->nfsd4_lease);
29211da177e4SLinus Torvalds 	}
29221da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
2923ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2924ddd1ea56SJ. Bruce Fields 		if (!p)
29251da177e4SLinus Torvalds 			goto out_resource;
2926c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(rdattr_err);
29271da177e4SLinus Torvalds 	}
29281da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
29291da177e4SLinus Torvalds 		struct nfs4_ace *ace;
29301da177e4SLinus Torvalds 
29311da177e4SLinus Torvalds 		if (acl == NULL) {
2932ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
2933ddd1ea56SJ. Bruce Fields 			if (!p)
29341da177e4SLinus Torvalds 				goto out_resource;
29351da177e4SLinus Torvalds 
2936c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
29371da177e4SLinus Torvalds 			goto out_acl;
29381da177e4SLinus Torvalds 		}
2939ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2940ddd1ea56SJ. Bruce Fields 		if (!p)
29411da177e4SLinus Torvalds 			goto out_resource;
2942c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(acl->naces);
29431da177e4SLinus Torvalds 
294428e05dd8SJ. Bruce Fields 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
2945ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4*3);
2946ddd1ea56SJ. Bruce Fields 			if (!p)
29471da177e4SLinus Torvalds 				goto out_resource;
2948c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->type);
2949c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->flag);
2950c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->access_mask &
2951c373b0a4SJ. Bruce Fields 							NFS4_ACE_MASK_ALL);
2952ddd1ea56SJ. Bruce Fields 			status = nfsd4_encode_aclname(xdr, rqstp, ace);
29531da177e4SLinus Torvalds 			if (status)
29541da177e4SLinus Torvalds 				goto out;
29551da177e4SLinus Torvalds 		}
29561da177e4SLinus Torvalds 	}
29571da177e4SLinus Torvalds out_acl:
29581da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
2959ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2960ddd1ea56SJ. Bruce Fields 		if (!p)
29611da177e4SLinus Torvalds 			goto out_resource;
29620c9d65e7SAndreas Gruenbacher 		*p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ?
29631da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
29641da177e4SLinus Torvalds 	}
29651da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
2966ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2967ddd1ea56SJ. Bruce Fields 		if (!p)
29681da177e4SLinus Torvalds 			goto out_resource;
2969c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
29701da177e4SLinus Torvalds 	}
29711da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
2972ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2973ddd1ea56SJ. Bruce Fields 		if (!p)
29741da177e4SLinus Torvalds 			goto out_resource;
2975c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
29761da177e4SLinus Torvalds 	}
29771da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
2978ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2979ddd1ea56SJ. Bruce Fields 		if (!p)
29801da177e4SLinus Torvalds 			goto out_resource;
2981c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
29821da177e4SLinus Torvalds 	}
29831da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
2984ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2985ddd1ea56SJ. Bruce Fields 		if (!p)
29861da177e4SLinus Torvalds 			goto out_resource;
2987c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
29881da177e4SLinus Torvalds 	}
29891da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
2990ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4);
2991ddd1ea56SJ. Bruce Fields 		if (!p)
29921da177e4SLinus Torvalds 			goto out_resource;
29930c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base,
29940c0c267bSJ. Bruce Fields 					fhp->fh_handle.fh_size);
29951da177e4SLinus Torvalds 	}
29961da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
2997ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2998ddd1ea56SJ. Bruce Fields 		if (!p)
29991da177e4SLinus Torvalds 			goto out_resource;
3000b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.ino);
30011da177e4SLinus Torvalds 	}
30021da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
3003ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3004ddd1ea56SJ. Bruce Fields 		if (!p)
30051da177e4SLinus Torvalds 			goto out_resource;
3006b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
30071da177e4SLinus Torvalds 	}
30081da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
3009ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3010ddd1ea56SJ. Bruce Fields 		if (!p)
30111da177e4SLinus Torvalds 			goto out_resource;
3012b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
30131da177e4SLinus Torvalds 	}
30141da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
3015ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3016ddd1ea56SJ. Bruce Fields 		if (!p)
30171da177e4SLinus Torvalds 			goto out_resource;
3018b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_files);
30191da177e4SLinus Torvalds 	}
302081c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
3021ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
302281c3f413SJ.Bruce Fields 		if (status)
302381c3f413SJ.Bruce Fields 			goto out;
302481c3f413SJ.Bruce Fields 	}
30251da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
3026ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3027ddd1ea56SJ. Bruce Fields 		if (!p)
30281da177e4SLinus Torvalds 			goto out_resource;
3029c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
30301da177e4SLinus Torvalds 	}
30311da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
3032ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3033ddd1ea56SJ. Bruce Fields 		if (!p)
30341da177e4SLinus Torvalds 			goto out_resource;
3035b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes);
30361da177e4SLinus Torvalds 	}
30371da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
3038ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3039ddd1ea56SJ. Bruce Fields 		if (!p)
30401da177e4SLinus Torvalds 			goto out_resource;
3041c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(255);
30421da177e4SLinus Torvalds 	}
30431da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
3044ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3045ddd1ea56SJ. Bruce Fields 		if (!p)
30461da177e4SLinus Torvalds 			goto out_resource;
3047c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(statfs.f_namelen);
30481da177e4SLinus Torvalds 	}
30491da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
3050ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3051ddd1ea56SJ. Bruce Fields 		if (!p)
30521da177e4SLinus Torvalds 			goto out_resource;
3053b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
30541da177e4SLinus Torvalds 	}
30551da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
3056ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3057ddd1ea56SJ. Bruce Fields 		if (!p)
30581da177e4SLinus Torvalds 			goto out_resource;
3059b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
30601da177e4SLinus Torvalds 	}
30611da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
3062ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3063ddd1ea56SJ. Bruce Fields 		if (!p)
30641da177e4SLinus Torvalds 			goto out_resource;
3065c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.mode & S_IALLUGO);
30661da177e4SLinus Torvalds 	}
30671da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
3068ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3069ddd1ea56SJ. Bruce Fields 		if (!p)
30701da177e4SLinus Torvalds 			goto out_resource;
3071c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
30721da177e4SLinus Torvalds 	}
30731da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
3074ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3075ddd1ea56SJ. Bruce Fields 		if (!p)
30761da177e4SLinus Torvalds 			goto out_resource;
3077c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.nlink);
30781da177e4SLinus Torvalds 	}
30791da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
3080ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_user(xdr, rqstp, stat.uid);
30811da177e4SLinus Torvalds 		if (status)
30821da177e4SLinus Torvalds 			goto out;
30831da177e4SLinus Torvalds 	}
30841da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
3085ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_group(xdr, rqstp, stat.gid);
30861da177e4SLinus Torvalds 		if (status)
30871da177e4SLinus Torvalds 			goto out;
30881da177e4SLinus Torvalds 	}
30891da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
3090ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3091ddd1ea56SJ. Bruce Fields 		if (!p)
30921da177e4SLinus Torvalds 			goto out_resource;
3093c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32((u32) MAJOR(stat.rdev));
3094c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32((u32) MINOR(stat.rdev));
30951da177e4SLinus Torvalds 	}
30961da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
3097ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3098ddd1ea56SJ. Bruce Fields 		if (!p)
30991da177e4SLinus Torvalds 			goto out_resource;
31001da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
3101b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
31021da177e4SLinus Torvalds 	}
31031da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
3104ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3105ddd1ea56SJ. Bruce Fields 		if (!p)
31061da177e4SLinus Torvalds 			goto out_resource;
31071da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
3108b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
31091da177e4SLinus Torvalds 	}
31101da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
3111ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3112ddd1ea56SJ. Bruce Fields 		if (!p)
31131da177e4SLinus Torvalds 			goto out_resource;
31141da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
3115b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
31161da177e4SLinus Torvalds 	}
31171da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
3118ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3119ddd1ea56SJ. Bruce Fields 		if (!p)
31201da177e4SLinus Torvalds 			goto out_resource;
31211da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
3122b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
31231da177e4SLinus Torvalds 	}
31241da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
3125ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
3126ddd1ea56SJ. Bruce Fields 		if (!p)
31271da177e4SLinus Torvalds 			goto out_resource;
3128b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (s64)stat.atime.tv_sec);
3129c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.atime.tv_nsec);
31301da177e4SLinus Torvalds 	}
31311da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
3132ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
3133ddd1ea56SJ. Bruce Fields 		if (!p)
31341da177e4SLinus Torvalds 			goto out_resource;
313516945141SJ. Bruce Fields 		p = encode_time_delta(p, d_inode(dentry));
31361da177e4SLinus Torvalds 	}
31371da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
3138ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
3139ddd1ea56SJ. Bruce Fields 		if (!p)
31401da177e4SLinus Torvalds 			goto out_resource;
3141b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (s64)stat.ctime.tv_sec);
3142c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.ctime.tv_nsec);
31431da177e4SLinus Torvalds 	}
31441da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
3145ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
3146ddd1ea56SJ. Bruce Fields 		if (!p)
31471da177e4SLinus Torvalds 			goto out_resource;
3148b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
3149c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.mtime.tv_nsec);
31501da177e4SLinus Torvalds 	}
31511da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
31520a2050d7SKinglong Mee 		struct kstat parent_stat;
31530a2050d7SKinglong Mee 		u64 ino = stat.ino;
31540a2050d7SKinglong Mee 
3155ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3156ddd1ea56SJ. Bruce Fields 		if (!p)
31571da177e4SLinus Torvalds                 	goto out_resource;
3158406a7ea9SFrank Filz 		/*
3159406a7ea9SFrank Filz 		 * Get parent's attributes if not ignoring crossmount
3160406a7ea9SFrank Filz 		 * and this is the root of a cross-mounted filesystem.
3161406a7ea9SFrank Filz 		 */
3162406a7ea9SFrank Filz 		if (ignore_crossmnt == 0 &&
31630a2050d7SKinglong Mee 		    dentry == exp->ex_path.mnt->mnt_root) {
31640a2050d7SKinglong Mee 			err = get_parent_attributes(exp, &parent_stat);
31650a2050d7SKinglong Mee 			if (err)
31660a2050d7SKinglong Mee 				goto out_nfserr;
31670a2050d7SKinglong Mee 			ino = parent_stat.ino;
31680a2050d7SKinglong Mee 		}
31690a2050d7SKinglong Mee 		p = xdr_encode_hyper(p, ino);
31701da177e4SLinus Torvalds 	}
31719cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
31726896f15aSKinglong Mee 	if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) {
31738a4c3926SJeff Layton 		status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
31746896f15aSKinglong Mee 		if (status)
31756896f15aSKinglong Mee 			goto out;
31769cf514ccSChristoph Hellwig 	}
31776896f15aSKinglong Mee 
31786896f15aSKinglong Mee 	if (bmval2 & FATTR4_WORD2_LAYOUT_TYPES) {
31798a4c3926SJeff Layton 		status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
31806896f15aSKinglong Mee 		if (status)
31816896f15aSKinglong Mee 			goto out;
31829cf514ccSChristoph Hellwig 	}
31839cf514ccSChristoph Hellwig 
31849cf514ccSChristoph Hellwig 	if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) {
31859cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4);
31869cf514ccSChristoph Hellwig 		if (!p)
31879cf514ccSChristoph Hellwig 			goto out_resource;
31889cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(stat.blksize);
31899cf514ccSChristoph Hellwig 	}
31909cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
31918c18f205SBenny Halevy 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
3192b26b78cbSTrond Myklebust 		u32 supp[3];
3193b26b78cbSTrond Myklebust 
3194b26b78cbSTrond Myklebust 		memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
3195b26b78cbSTrond Myklebust 		supp[0] &= NFSD_SUPPATTR_EXCLCREAT_WORD0;
3196b26b78cbSTrond Myklebust 		supp[1] &= NFSD_SUPPATTR_EXCLCREAT_WORD1;
3197b26b78cbSTrond Myklebust 		supp[2] &= NFSD_SUPPATTR_EXCLCREAT_WORD2;
3198b26b78cbSTrond Myklebust 
3199b26b78cbSTrond Myklebust 		status = nfsd4_encode_bitmap(xdr, supp[0], supp[1], supp[2]);
320075976de6SKinglong Mee 		if (status)
320175976de6SKinglong Mee 			goto out;
32028c18f205SBenny Halevy 	}
32037e705706SAndy Adamson 
3204a8585763SJ. Bruce Fields 	if (bmval2 & FATTR4_WORD2_CHANGE_ATTR_TYPE) {
3205a8585763SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3206a8585763SJ. Bruce Fields 		if (!p)
3207a8585763SJ. Bruce Fields 			goto out_resource;
3208a8585763SJ. Bruce Fields 		if (IS_I_VERSION(d_inode(dentry)))
3209a8585763SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_CHANGE_TYPE_IS_MONOTONIC_INCR);
3210a8585763SJ. Bruce Fields 		else
3211a8585763SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_CHANGE_TYPE_IS_TIME_METADATA);
3212a8585763SJ. Bruce Fields 	}
3213a8585763SJ. Bruce Fields 
32140ab88ca4SArnd Bergmann #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
32157d580722SKinglong Mee 	if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
32167d580722SKinglong Mee 		status = nfsd4_encode_security_label(xdr, rqstp, context,
32177d580722SKinglong Mee 								contextlen);
32187d580722SKinglong Mee 		if (status)
32197d580722SKinglong Mee 			goto out;
32207d580722SKinglong Mee 	}
32210ab88ca4SArnd Bergmann #endif
32227d580722SKinglong Mee 
32230e885e84SFrank van der Linden 	if (bmval2 & FATTR4_WORD2_XATTR_SUPPORT) {
32240e885e84SFrank van der Linden 		p = xdr_reserve_space(xdr, 4);
32250e885e84SFrank van der Linden 		if (!p)
32260e885e84SFrank van der Linden 			goto out_resource;
32270e885e84SFrank van der Linden 		err = xattr_supported_namespace(d_inode(dentry),
32280e885e84SFrank van der Linden 						XATTR_USER_PREFIX);
32290e885e84SFrank van der Linden 		*p++ = cpu_to_be32(err == 0);
32300e885e84SFrank van der Linden 	}
32310e885e84SFrank van der Linden 
3232082d4bd7SJ. Bruce Fields 	attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
3233082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
32341da177e4SLinus Torvalds 	status = nfs_ok;
32351da177e4SLinus Torvalds 
32361da177e4SLinus Torvalds out:
3237ba4e55bbSJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
323818032ca0SDavid Quigley 	if (context)
323918032ca0SDavid Quigley 		security_release_secctx(context, contextlen);
3240ba4e55bbSJ. Bruce Fields #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
324128e05dd8SJ. Bruce Fields 	kfree(acl);
324218df11d0SYan, Zheng 	if (tempfh) {
3243d50e6136SJ. Bruce Fields 		fh_put(tempfh);
324418df11d0SYan, Zheng 		kfree(tempfh);
324518df11d0SYan, Zheng 	}
32461fcea5b2SJ. Bruce Fields 	if (status)
32471fcea5b2SJ. Bruce Fields 		xdr_truncate_encode(xdr, starting_len);
32481da177e4SLinus Torvalds 	return status;
32491da177e4SLinus Torvalds out_nfserr:
3250b8dd7b9aSAl Viro 	status = nfserrno(err);
32511da177e4SLinus Torvalds 	goto out;
32521da177e4SLinus Torvalds out_resource:
32531da177e4SLinus Torvalds 	status = nfserr_resource;
32541da177e4SLinus Torvalds 	goto out;
32551da177e4SLinus Torvalds }
32561da177e4SLinus Torvalds 
32572825a7f9SJ. Bruce Fields static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
32582825a7f9SJ. Bruce Fields 				struct xdr_buf *buf, __be32 *p, int bytes)
32592825a7f9SJ. Bruce Fields {
32602825a7f9SJ. Bruce Fields 	xdr->scratch.iov_len = 0;
32612825a7f9SJ. Bruce Fields 	memset(buf, 0, sizeof(struct xdr_buf));
32622825a7f9SJ. Bruce Fields 	buf->head[0].iov_base = p;
32632825a7f9SJ. Bruce Fields 	buf->head[0].iov_len = 0;
32642825a7f9SJ. Bruce Fields 	buf->len = 0;
32652825a7f9SJ. Bruce Fields 	xdr->buf = buf;
32662825a7f9SJ. Bruce Fields 	xdr->iov = buf->head;
32672825a7f9SJ. Bruce Fields 	xdr->p = p;
32682825a7f9SJ. Bruce Fields 	xdr->end = (void *)p + bytes;
32692825a7f9SJ. Bruce Fields 	buf->buflen = bytes;
32702825a7f9SJ. Bruce Fields }
32712825a7f9SJ. Bruce Fields 
3272d5184658SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
3273d5184658SJ. Bruce Fields 			struct svc_fh *fhp, struct svc_export *exp,
3274d5184658SJ. Bruce Fields 			struct dentry *dentry, u32 *bmval,
3275d5184658SJ. Bruce Fields 			struct svc_rqst *rqstp, int ignore_crossmnt)
3276d5184658SJ. Bruce Fields {
32772825a7f9SJ. Bruce Fields 	struct xdr_buf dummy;
3278d5184658SJ. Bruce Fields 	struct xdr_stream xdr;
3279d5184658SJ. Bruce Fields 	__be32 ret;
3280d5184658SJ. Bruce Fields 
32812825a7f9SJ. Bruce Fields 	svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
3282d5184658SJ. Bruce Fields 	ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
3283d5184658SJ. Bruce Fields 							ignore_crossmnt);
3284d5184658SJ. Bruce Fields 	*p = xdr.p;
3285d5184658SJ. Bruce Fields 	return ret;
3286d5184658SJ. Bruce Fields }
3287d5184658SJ. Bruce Fields 
3288c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
3289c0ce6ec8SJ. Bruce Fields {
3290c0ce6ec8SJ. Bruce Fields 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
3291c0ce6ec8SJ. Bruce Fields 		return 1;
3292c0ce6ec8SJ. Bruce Fields 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
3293c0ce6ec8SJ. Bruce Fields 		return 1;
3294c0ce6ec8SJ. Bruce Fields 	return 0;
3295c0ce6ec8SJ. Bruce Fields }
3296c0ce6ec8SJ. Bruce Fields 
3297b37ad28bSAl Viro static __be32
3298561f0ed4SJ. Bruce Fields nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
3299561f0ed4SJ. Bruce Fields 			const char *name, int namlen)
33001da177e4SLinus Torvalds {
33011da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
33021da177e4SLinus Torvalds 	struct dentry *dentry;
3303b37ad28bSAl Viro 	__be32 nfserr;
3304406a7ea9SFrank Filz 	int ignore_crossmnt = 0;
33051da177e4SLinus Torvalds 
33066c2d4798SAl Viro 	dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
33071da177e4SLinus Torvalds 	if (IS_ERR(dentry))
33081da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
33091da177e4SLinus Torvalds 
33101da177e4SLinus Torvalds 	exp_get(exp);
3311406a7ea9SFrank Filz 	/*
3312406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
3313406a7ea9SFrank Filz 	 * attributes that are only properties of the underlying filesystem
3314406a7ea9SFrank Filz 	 * as opposed to the cross-mounted file system. In such a case,
3315406a7ea9SFrank Filz 	 * we will not follow the cross mount and will fill the attribtutes
3316406a7ea9SFrank Filz 	 * directly from the mountpoint dentry.
3317406a7ea9SFrank Filz 	 */
33183227fa41SJ. Bruce Fields 	if (nfsd_mountpoint(dentry, exp)) {
3319021d3a72SJ.Bruce Fields 		int err;
3320021d3a72SJ.Bruce Fields 
33213227fa41SJ. Bruce Fields 		if (!(exp->ex_flags & NFSEXP_V4ROOT)
33223227fa41SJ. Bruce Fields 				&& !attributes_need_mount(cd->rd_bmval)) {
33233227fa41SJ. Bruce Fields 			ignore_crossmnt = 1;
33243227fa41SJ. Bruce Fields 			goto out_encode;
33253227fa41SJ. Bruce Fields 		}
3326dcb488a3SAndy Adamson 		/*
3327dcb488a3SAndy Adamson 		 * Why the heck aren't we just using nfsd_lookup??
3328dcb488a3SAndy Adamson 		 * Different "."/".." handling?  Something else?
3329dcb488a3SAndy Adamson 		 * At least, add a comment here to explain....
3330dcb488a3SAndy Adamson 		 */
3331021d3a72SJ.Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
3332021d3a72SJ.Bruce Fields 		if (err) {
3333021d3a72SJ.Bruce Fields 			nfserr = nfserrno(err);
33341da177e4SLinus Torvalds 			goto out_put;
33351da177e4SLinus Torvalds 		}
3336dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
3337dcb488a3SAndy Adamson 		if (nfserr)
3338dcb488a3SAndy Adamson 			goto out_put;
33391da177e4SLinus Torvalds 
33401da177e4SLinus Torvalds 	}
33413227fa41SJ. Bruce Fields out_encode:
3342561f0ed4SJ. Bruce Fields 	nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval,
3343406a7ea9SFrank Filz 					cd->rd_rqstp, ignore_crossmnt);
33441da177e4SLinus Torvalds out_put:
33451da177e4SLinus Torvalds 	dput(dentry);
33461da177e4SLinus Torvalds 	exp_put(exp);
33471da177e4SLinus Torvalds 	return nfserr;
33481da177e4SLinus Torvalds }
33491da177e4SLinus Torvalds 
33502ebbc012SAl Viro static __be32 *
3351561f0ed4SJ. Bruce Fields nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
33521da177e4SLinus Torvalds {
3353561f0ed4SJ. Bruce Fields 	__be32 *p;
3354561f0ed4SJ. Bruce Fields 
3355c3a45617SKinglong Mee 	p = xdr_reserve_space(xdr, 20);
3356561f0ed4SJ. Bruce Fields 	if (!p)
33571da177e4SLinus Torvalds 		return NULL;
33581da177e4SLinus Torvalds 	*p++ = htonl(2);
33591da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
33601da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
33611da177e4SLinus Torvalds 
336287915c64SJ. Bruce Fields 	*p++ = htonl(4);     /* attribute length */
33631da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
33641da177e4SLinus Torvalds 	return p;
33651da177e4SLinus Torvalds }
33661da177e4SLinus Torvalds 
33671da177e4SLinus Torvalds static int
3368a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
3369a0ad13efSNeilBrown 		    loff_t offset, u64 ino, unsigned int d_type)
33701da177e4SLinus Torvalds {
3371a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
33721da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
3373561f0ed4SJ. Bruce Fields 	struct xdr_stream *xdr = cd->xdr;
3374561f0ed4SJ. Bruce Fields 	int start_offset = xdr->buf->len;
3375561f0ed4SJ. Bruce Fields 	int cookie_offset;
3376aee37764SJ. Bruce Fields 	u32 name_and_cookie;
3377561f0ed4SJ. Bruce Fields 	int entry_bytes;
3378b37ad28bSAl Viro 	__be32 nfserr = nfserr_toosmall;
3379561f0ed4SJ. Bruce Fields 	__be64 wire_offset;
3380561f0ed4SJ. Bruce Fields 	__be32 *p;
33811da177e4SLinus Torvalds 
33821da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
33831da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
33841da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
33851da177e4SLinus Torvalds 		return 0;
33861da177e4SLinus Torvalds 	}
33871da177e4SLinus Torvalds 
3388561f0ed4SJ. Bruce Fields 	if (cd->cookie_offset) {
3389561f0ed4SJ. Bruce Fields 		wire_offset = cpu_to_be64(offset);
3390561f0ed4SJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset,
3391561f0ed4SJ. Bruce Fields 							&wire_offset, 8);
3392561f0ed4SJ. Bruce Fields 	}
33931da177e4SLinus Torvalds 
3394561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
3395561f0ed4SJ. Bruce Fields 	if (!p)
33961da177e4SLinus Torvalds 		goto fail;
33971da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
3398561f0ed4SJ. Bruce Fields 	cookie_offset = xdr->buf->len;
3399561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 3*4 + namlen);
3400561f0ed4SJ. Bruce Fields 	if (!p)
3401561f0ed4SJ. Bruce Fields 		goto fail;
34021da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
34031da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
34041da177e4SLinus Torvalds 
3405561f0ed4SJ. Bruce Fields 	nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
34061da177e4SLinus Torvalds 	switch (nfserr) {
34071da177e4SLinus Torvalds 	case nfs_ok:
34081da177e4SLinus Torvalds 		break;
34091da177e4SLinus Torvalds 	case nfserr_resource:
34101da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
34111da177e4SLinus Torvalds 		goto fail;
3412b2c0cea6SJ. Bruce Fields 	case nfserr_noent:
3413f41c5ad2SKinglong Mee 		xdr_truncate_encode(xdr, start_offset);
3414b2c0cea6SJ. Bruce Fields 		goto skip_entry;
34151da177e4SLinus Torvalds 	default:
34161da177e4SLinus Torvalds 		/*
34171da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
34181da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
34191da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
34201da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
34211da177e4SLinus Torvalds 		 * entire READDIR operation(!)
34221da177e4SLinus Torvalds 		 */
34231da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
34241da177e4SLinus Torvalds 			goto fail;
3425561f0ed4SJ. Bruce Fields 		p = nfsd4_encode_rdattr_error(xdr, nfserr);
342634081efcSFred Isaman 		if (p == NULL) {
342734081efcSFred Isaman 			nfserr = nfserr_toosmall;
34281da177e4SLinus Torvalds 			goto fail;
34291da177e4SLinus Torvalds 		}
343034081efcSFred Isaman 	}
3431561f0ed4SJ. Bruce Fields 	nfserr = nfserr_toosmall;
3432561f0ed4SJ. Bruce Fields 	entry_bytes = xdr->buf->len - start_offset;
3433561f0ed4SJ. Bruce Fields 	if (entry_bytes > cd->rd_maxcount)
3434561f0ed4SJ. Bruce Fields 		goto fail;
3435561f0ed4SJ. Bruce Fields 	cd->rd_maxcount -= entry_bytes;
3436aee37764SJ. Bruce Fields 	/*
3437aee37764SJ. Bruce Fields 	 * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so
3438aee37764SJ. Bruce Fields 	 * let's always let through the first entry, at least:
3439aee37764SJ. Bruce Fields 	 */
34400ec016e3SJ. Bruce Fields 	if (!cd->rd_dircount)
34410ec016e3SJ. Bruce Fields 		goto fail;
34420ec016e3SJ. Bruce Fields 	name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8;
3443aee37764SJ. Bruce Fields 	if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
3444aee37764SJ. Bruce Fields 		goto fail;
3445aee37764SJ. Bruce Fields 	cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
34460ec016e3SJ. Bruce Fields 
3447561f0ed4SJ. Bruce Fields 	cd->cookie_offset = cookie_offset;
3448b2c0cea6SJ. Bruce Fields skip_entry:
34491da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
34501da177e4SLinus Torvalds 	return 0;
34511da177e4SLinus Torvalds fail:
3452561f0ed4SJ. Bruce Fields 	xdr_truncate_encode(xdr, start_offset);
34531da177e4SLinus Torvalds 	cd->common.err = nfserr;
34541da177e4SLinus Torvalds 	return -EINVAL;
34551da177e4SLinus Torvalds }
34561da177e4SLinus Torvalds 
3457d0a381ddSJ. Bruce Fields static __be32
3458d0a381ddSJ. Bruce Fields nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
3459e2f282b9SBenny Halevy {
3460bc749ca4SJ. Bruce Fields 	__be32 *p;
3461e2f282b9SBenny Halevy 
3462d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, sizeof(stateid_t));
3463d0a381ddSJ. Bruce Fields 	if (!p)
3464d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3465c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sid->si_generation);
34660c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, &sid->si_opaque,
34670c0c267bSJ. Bruce Fields 					sizeof(stateid_opaque_t));
3468d0a381ddSJ. Bruce Fields 	return 0;
3469e2f282b9SBenny Halevy }
3470e2f282b9SBenny Halevy 
3471695e12f8SBenny Halevy static __be32
3472b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
34731da177e4SLinus Torvalds {
3474d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3475bc749ca4SJ. Bruce Fields 	__be32 *p;
34761da177e4SLinus Torvalds 
3477d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
3478d0a381ddSJ. Bruce Fields 	if (!p)
3479d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3480c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(access->ac_supported);
3481c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(access->ac_resp_access);
3482bac966d6SJ. Bruce Fields 	return 0;
34831da177e4SLinus Torvalds }
34841da177e4SLinus Torvalds 
34851d1bc8f2SJ. Bruce Fields static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts)
34861d1bc8f2SJ. Bruce Fields {
3487d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
34881d1bc8f2SJ. Bruce Fields 	__be32 *p;
34891d1bc8f2SJ. Bruce Fields 
3490d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8);
3491d0a381ddSJ. Bruce Fields 	if (!p)
3492d0a381ddSJ. Bruce Fields 		return nfserr_resource;
34930c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, bcts->sessionid.data,
34940c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
3495c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(bcts->dir);
34964ce85c8cSChuck Lever 	/* Upshifting from TCP to RDMA is not supported */
3497c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);
3498bac966d6SJ. Bruce Fields 	return 0;
34991d1bc8f2SJ. Bruce Fields }
35001d1bc8f2SJ. Bruce Fields 
3501695e12f8SBenny Halevy static __be32
3502b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
35031da177e4SLinus Torvalds {
3504d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3505d0a381ddSJ. Bruce Fields 
3506bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &close->cl_stateid);
35071da177e4SLinus Torvalds }
35081da177e4SLinus Torvalds 
35091da177e4SLinus Torvalds 
3510695e12f8SBenny Halevy static __be32
3511b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
35121da177e4SLinus Torvalds {
3513d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3514bc749ca4SJ. Bruce Fields 	__be32 *p;
35151da177e4SLinus Torvalds 
3516d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
3517d0a381ddSJ. Bruce Fields 	if (!p)
3518d0a381ddSJ. Bruce Fields 		return nfserr_resource;
35190c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, commit->co_verf.data,
35200c0c267bSJ. Bruce Fields 						NFS4_VERIFIER_SIZE);
3521bac966d6SJ. Bruce Fields 	return 0;
35221da177e4SLinus Torvalds }
35231da177e4SLinus Torvalds 
3524695e12f8SBenny Halevy static __be32
3525b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
35261da177e4SLinus Torvalds {
3527d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3528bc749ca4SJ. Bruce Fields 	__be32 *p;
35291da177e4SLinus Torvalds 
353075976de6SKinglong Mee 	p = xdr_reserve_space(xdr, 20);
3531d0a381ddSJ. Bruce Fields 	if (!p)
3532d0a381ddSJ. Bruce Fields 		return nfserr_resource;
353375976de6SKinglong Mee 	encode_cinfo(p, &create->cr_cinfo);
3534b96811cdSTrond Myklebust 	return nfsd4_encode_bitmap(xdr, create->cr_bmval[0],
353575976de6SKinglong Mee 			create->cr_bmval[1], create->cr_bmval[2]);
35361da177e4SLinus Torvalds }
35371da177e4SLinus Torvalds 
3538b37ad28bSAl Viro static __be32
3539b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
35401da177e4SLinus Torvalds {
35411da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
3542d5184658SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
35431da177e4SLinus Torvalds 
3544bac966d6SJ. Bruce Fields 	return nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry,
3545bac966d6SJ. Bruce Fields 				    getattr->ga_bmval, resp->rqstp, 0);
35461da177e4SLinus Torvalds }
35471da177e4SLinus Torvalds 
3548695e12f8SBenny Halevy static __be32
3549695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
35501da177e4SLinus Torvalds {
3551d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3552695e12f8SBenny Halevy 	struct svc_fh *fhp = *fhpp;
35531da177e4SLinus Torvalds 	unsigned int len;
3554bc749ca4SJ. Bruce Fields 	__be32 *p;
35551da177e4SLinus Torvalds 
35561da177e4SLinus Torvalds 	len = fhp->fh_handle.fh_size;
3557d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, len + 4);
3558d0a381ddSJ. Bruce Fields 	if (!p)
3559d0a381ddSJ. Bruce Fields 		return nfserr_resource;
35600c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base, len);
3561bac966d6SJ. Bruce Fields 	return 0;
35621da177e4SLinus Torvalds }
35631da177e4SLinus Torvalds 
35641da177e4SLinus Torvalds /*
35651da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
35661da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
35671da177e4SLinus Torvalds */
3568d0a381ddSJ. Bruce Fields static __be32
3569d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
35701da177e4SLinus Torvalds {
35717c13f344SJ. Bruce Fields 	struct xdr_netobj *conf = &ld->ld_owner;
3572bc749ca4SJ. Bruce Fields 	__be32 *p;
35731da177e4SLinus Torvalds 
35748c7424cfSJ. Bruce Fields again:
3575d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
35768c7424cfSJ. Bruce Fields 	if (!p) {
35778c7424cfSJ. Bruce Fields 		/*
35788c7424cfSJ. Bruce Fields 		 * Don't fail to return the result just because we can't
35798c7424cfSJ. Bruce Fields 		 * return the conflicting open:
35808c7424cfSJ. Bruce Fields 		 */
35818c7424cfSJ. Bruce Fields 		if (conf->len) {
3582f98bac5aSKinglong Mee 			kfree(conf->data);
35838c7424cfSJ. Bruce Fields 			conf->len = 0;
35848c7424cfSJ. Bruce Fields 			conf->data = NULL;
35858c7424cfSJ. Bruce Fields 			goto again;
35868c7424cfSJ. Bruce Fields 		}
3587d0a381ddSJ. Bruce Fields 		return nfserr_resource;
35888c7424cfSJ. Bruce Fields 	}
3589b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, ld->ld_start);
3590b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, ld->ld_length);
3591c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(ld->ld_type);
35927c13f344SJ. Bruce Fields 	if (conf->len) {
35930c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8);
35940c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, conf->data, conf->len);
3595f98bac5aSKinglong Mee 		kfree(conf->data);
35961da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
3597b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64)0); /* clientid */
3598c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0); /* length of owner name */
35991da177e4SLinus Torvalds 	}
3600d0a381ddSJ. Bruce Fields 	return nfserr_denied;
36011da177e4SLinus Torvalds }
36021da177e4SLinus Torvalds 
3603695e12f8SBenny Halevy static __be32
3604b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
36051da177e4SLinus Torvalds {
3606d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3607d0a381ddSJ. Bruce Fields 
3608e2f282b9SBenny Halevy 	if (!nfserr)
3609d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
3610e2f282b9SBenny Halevy 	else if (nfserr == nfserr_denied)
3611d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
3612f98bac5aSKinglong Mee 
3613695e12f8SBenny Halevy 	return nfserr;
36141da177e4SLinus Torvalds }
36151da177e4SLinus Torvalds 
3616695e12f8SBenny Halevy static __be32
3617b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
36181da177e4SLinus Torvalds {
3619d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3620d0a381ddSJ. Bruce Fields 
36211da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
3622d0a381ddSJ. Bruce Fields 		nfsd4_encode_lock_denied(xdr, &lockt->lt_denied);
3623695e12f8SBenny Halevy 	return nfserr;
36241da177e4SLinus Torvalds }
36251da177e4SLinus Torvalds 
3626695e12f8SBenny Halevy static __be32
3627b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
36281da177e4SLinus Torvalds {
3629d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3630d0a381ddSJ. Bruce Fields 
3631bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &locku->lu_stateid);
36321da177e4SLinus Torvalds }
36331da177e4SLinus Torvalds 
36341da177e4SLinus Torvalds 
3635695e12f8SBenny Halevy static __be32
3636b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
36371da177e4SLinus Torvalds {
3638d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3639bc749ca4SJ. Bruce Fields 	__be32 *p;
36401da177e4SLinus Torvalds 
3641d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 20);
3642d0a381ddSJ. Bruce Fields 	if (!p)
3643d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3644d05d5744SJ. Bruce Fields 	p = encode_cinfo(p, &link->li_cinfo);
3645bac966d6SJ. Bruce Fields 	return 0;
36461da177e4SLinus Torvalds }
36471da177e4SLinus Torvalds 
36481da177e4SLinus Torvalds 
3649695e12f8SBenny Halevy static __be32
3650b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
36511da177e4SLinus Torvalds {
3652d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3653bc749ca4SJ. Bruce Fields 	__be32 *p;
36541da177e4SLinus Torvalds 
3655d0a381ddSJ. Bruce Fields 	nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid);
3656d0a381ddSJ. Bruce Fields 	if (nfserr)
3657bac966d6SJ. Bruce Fields 		return nfserr;
365875976de6SKinglong Mee 	p = xdr_reserve_space(xdr, 24);
3659d0a381ddSJ. Bruce Fields 	if (!p)
3660d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3661d05d5744SJ. Bruce Fields 	p = encode_cinfo(p, &open->op_cinfo);
3662c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(open->op_rflags);
36631da177e4SLinus Torvalds 
366475976de6SKinglong Mee 	nfserr = nfsd4_encode_bitmap(xdr, open->op_bmval[0], open->op_bmval[1],
366575976de6SKinglong Mee 					open->op_bmval[2]);
366675976de6SKinglong Mee 	if (nfserr)
3667bac966d6SJ. Bruce Fields 		return nfserr;
366875976de6SKinglong Mee 
366975976de6SKinglong Mee 	p = xdr_reserve_space(xdr, 4);
367075976de6SKinglong Mee 	if (!p)
367175976de6SKinglong Mee 		return nfserr_resource;
367275976de6SKinglong Mee 
367375976de6SKinglong Mee 	*p++ = cpu_to_be32(open->op_delegate_type);
36741da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
36751da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
36761da177e4SLinus Torvalds 		break;
36771da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
3678d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3679d0a381ddSJ. Bruce Fields 		if (nfserr)
3680d0a381ddSJ. Bruce Fields 			return nfserr;
3681d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 20);
3682d0a381ddSJ. Bruce Fields 		if (!p)
3683d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3684c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(open->op_recall);
36851da177e4SLinus Torvalds 
36861da177e4SLinus Torvalds 		/*
36871da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
36881da177e4SLinus Torvalds 		 */
3689c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3690c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3691c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3692c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);   /* XXX: is NULL principal ok? */
36931da177e4SLinus Torvalds 		break;
36941da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
3695d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3696d0a381ddSJ. Bruce Fields 		if (nfserr)
3697d0a381ddSJ. Bruce Fields 			return nfserr;
3698d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 32);
3699d0a381ddSJ. Bruce Fields 		if (!p)
3700d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3701c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
37021da177e4SLinus Torvalds 
37031da177e4SLinus Torvalds 		/*
37041da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
37051da177e4SLinus Torvalds 		 */
3706c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_LIMIT_SIZE);
3707c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(~(u32)0);
3708c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(~(u32)0);
37091da177e4SLinus Torvalds 
37101da177e4SLinus Torvalds 		/*
37111da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
37121da177e4SLinus Torvalds 		 */
3713c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3714c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3715c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3716c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);   /* XXX: is NULL principal ok? */
37171da177e4SLinus Torvalds 		break;
3718d24433cdSBenny Halevy 	case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
3719d24433cdSBenny Halevy 		switch (open->op_why_no_deleg) {
3720d24433cdSBenny Halevy 		case WND4_CONTENTION:
3721d24433cdSBenny Halevy 		case WND4_RESOURCE:
3722d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 8);
3723d0a381ddSJ. Bruce Fields 			if (!p)
3724d0a381ddSJ. Bruce Fields 				return nfserr_resource;
3725c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(open->op_why_no_deleg);
3726c373b0a4SJ. Bruce Fields 			/* deleg signaling not supported yet: */
3727c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
3728d24433cdSBenny Halevy 			break;
3729d24433cdSBenny Halevy 		default:
3730d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
3731d0a381ddSJ. Bruce Fields 			if (!p)
3732d0a381ddSJ. Bruce Fields 				return nfserr_resource;
3733c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(open->op_why_no_deleg);
3734d24433cdSBenny Halevy 		}
3735d24433cdSBenny Halevy 		break;
37361da177e4SLinus Torvalds 	default:
37371da177e4SLinus Torvalds 		BUG();
37381da177e4SLinus Torvalds 	}
37391da177e4SLinus Torvalds 	/* XXX save filehandle here */
3740bac966d6SJ. Bruce Fields 	return 0;
37411da177e4SLinus Torvalds }
37421da177e4SLinus Torvalds 
3743695e12f8SBenny Halevy static __be32
3744b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
37451da177e4SLinus Torvalds {
3746d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3747d0a381ddSJ. Bruce Fields 
3748bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid);
37491da177e4SLinus Torvalds }
37501da177e4SLinus Torvalds 
3751695e12f8SBenny Halevy static __be32
3752b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
37531da177e4SLinus Torvalds {
3754d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3755d0a381ddSJ. Bruce Fields 
3756bac966d6SJ. Bruce Fields 	return nfsd4_encode_stateid(xdr, &od->od_stateid);
37571da177e4SLinus Torvalds }
37581da177e4SLinus Torvalds 
3759dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_splice_read(
3760dc97618dSJ. Bruce Fields 				struct nfsd4_compoundres *resp,
3761dc97618dSJ. Bruce Fields 				struct nfsd4_read *read,
3762dc97618dSJ. Bruce Fields 				struct file *file, unsigned long maxcount)
37631da177e4SLinus Torvalds {
3764dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
376534a78b48SJ. Bruce Fields 	struct xdr_buf *buf = xdr->buf;
376676e5492bSChuck Lever 	int status, space_left;
3767dc97618dSJ. Bruce Fields 	u32 eof;
3768dc97618dSJ. Bruce Fields 	__be32 nfserr;
3769fec25fa4SJ. Bruce Fields 	__be32 *p = xdr->p - 2;
3770dc97618dSJ. Bruce Fields 
3771d5d5c304SKinglong Mee 	/* Make sure there will be room for padding if needed */
3772d5d5c304SKinglong Mee 	if (xdr->end - xdr->p < 1)
3773dc97618dSJ. Bruce Fields 		return nfserr_resource;
3774dc97618dSJ. Bruce Fields 
377587c5942eSChuck Lever 	nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
377683a63072STrond Myklebust 				  file, read->rd_offset, &maxcount, &eof);
377787c5942eSChuck Lever 	read->rd_length = maxcount;
377876e5492bSChuck Lever 	if (nfserr)
377976e5492bSChuck Lever 		goto out_err;
378076e5492bSChuck Lever 	status = svc_encode_result_payload(read->rd_rqstp,
378176e5492bSChuck Lever 					   buf->head[0].iov_len, maxcount);
378276e5492bSChuck Lever 	if (status) {
378376e5492bSChuck Lever 		nfserr = nfserrno(status);
378476e5492bSChuck Lever 		goto out_err;
3785dc97618dSJ. Bruce Fields 	}
3786dc97618dSJ. Bruce Fields 
3787fec25fa4SJ. Bruce Fields 	*(p++) = htonl(eof);
3788fec25fa4SJ. Bruce Fields 	*(p++) = htonl(maxcount);
3789dc97618dSJ. Bruce Fields 
379034a78b48SJ. Bruce Fields 	buf->page_len = maxcount;
379134a78b48SJ. Bruce Fields 	buf->len += maxcount;
379215b23ef5SJ. Bruce Fields 	xdr->page_ptr += (buf->page_base + maxcount + PAGE_SIZE - 1)
379315b23ef5SJ. Bruce Fields 							/ PAGE_SIZE;
3794dc97618dSJ. Bruce Fields 
3795dc97618dSJ. Bruce Fields 	/* Use rest of head for padding and remaining ops: */
379634a78b48SJ. Bruce Fields 	buf->tail[0].iov_base = xdr->p;
379734a78b48SJ. Bruce Fields 	buf->tail[0].iov_len = 0;
3798fec25fa4SJ. Bruce Fields 	xdr->iov = buf->tail;
3799dc97618dSJ. Bruce Fields 	if (maxcount&3) {
3800fec25fa4SJ. Bruce Fields 		int pad = 4 - (maxcount&3);
3801fec25fa4SJ. Bruce Fields 
3802fec25fa4SJ. Bruce Fields 		*(xdr->p++) = 0;
3803fec25fa4SJ. Bruce Fields 
380434a78b48SJ. Bruce Fields 		buf->tail[0].iov_base += maxcount&3;
3805fec25fa4SJ. Bruce Fields 		buf->tail[0].iov_len = pad;
3806fec25fa4SJ. Bruce Fields 		buf->len += pad;
3807dc97618dSJ. Bruce Fields 	}
3808dc97618dSJ. Bruce Fields 
3809dc97618dSJ. Bruce Fields 	space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
381034a78b48SJ. Bruce Fields 				buf->buflen - buf->len);
381134a78b48SJ. Bruce Fields 	buf->buflen = buf->len + space_left;
3812dc97618dSJ. Bruce Fields 	xdr->end = (__be32 *)((void *)xdr->end + space_left);
3813dc97618dSJ. Bruce Fields 
3814dc97618dSJ. Bruce Fields 	return 0;
381576e5492bSChuck Lever 
381676e5492bSChuck Lever out_err:
381776e5492bSChuck Lever 	/*
381876e5492bSChuck Lever 	 * nfsd_splice_actor may have already messed with the
381976e5492bSChuck Lever 	 * page length; reset it so as not to confuse
382076e5492bSChuck Lever 	 * xdr_truncate_encode in our caller.
382176e5492bSChuck Lever 	 */
382276e5492bSChuck Lever 	buf->page_len = 0;
382376e5492bSChuck Lever 	return nfserr;
3824dc97618dSJ. Bruce Fields }
3825dc97618dSJ. Bruce Fields 
3826dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
3827dc97618dSJ. Bruce Fields 				 struct nfsd4_read *read,
3828dc97618dSJ. Bruce Fields 				 struct file *file, unsigned long maxcount)
3829dc97618dSJ. Bruce Fields {
3830dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
38311da177e4SLinus Torvalds 	u32 eof;
3832dc97618dSJ. Bruce Fields 	int starting_len = xdr->buf->len - 8;
3833dc97618dSJ. Bruce Fields 	__be32 nfserr;
3834dc97618dSJ. Bruce Fields 	__be32 tmp;
3835b0420980SJ. Bruce Fields 	int pad;
38361da177e4SLinus Torvalds 
3837403217f3SAnna Schumaker 	read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, maxcount);
3838403217f3SAnna Schumaker 	if (read->rd_vlen < 0)
3839403217f3SAnna Schumaker 		return nfserr_resource;
38401da177e4SLinus Torvalds 
384187c5942eSChuck Lever 	nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset,
384283a63072STrond Myklebust 			    resp->rqstp->rq_vec, read->rd_vlen, &maxcount,
384383a63072STrond Myklebust 			    &eof);
384487c5942eSChuck Lever 	read->rd_length = maxcount;
3845dc97618dSJ. Bruce Fields 	if (nfserr)
38461da177e4SLinus Torvalds 		return nfserr;
384703493bcaSChuck Lever 	if (svc_encode_result_payload(resp->rqstp, starting_len + 8, maxcount))
384841205539SChuck Lever 		return nfserr_io;
38497dcf4ab9SChuck Lever 	xdr_truncate_encode(xdr, starting_len + 8 + xdr_align_size(maxcount));
3850dc97618dSJ. Bruce Fields 
3851dc97618dSJ. Bruce Fields 	tmp = htonl(eof);
3852dc97618dSJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, starting_len    , &tmp, 4);
3853dc97618dSJ. Bruce Fields 	tmp = htonl(maxcount);
3854dc97618dSJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
3855dc97618dSJ. Bruce Fields 
38567dcf4ab9SChuck Lever 	tmp = xdr_zero;
3857b0420980SJ. Bruce Fields 	pad = (maxcount&3) ? 4 - (maxcount&3) : 0;
3858b0420980SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, starting_len + 8 + maxcount,
38597dcf4ab9SChuck Lever 								&tmp, pad);
38601da177e4SLinus Torvalds 	return 0;
3861dc97618dSJ. Bruce Fields 
3862dc97618dSJ. Bruce Fields }
3863dc97618dSJ. Bruce Fields 
3864dc97618dSJ. Bruce Fields static __be32
3865dc97618dSJ. Bruce Fields nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
3866dc97618dSJ. Bruce Fields 		  struct nfsd4_read *read)
3867dc97618dSJ. Bruce Fields {
3868dc97618dSJ. Bruce Fields 	unsigned long maxcount;
3869dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
38705c4583b2SJeff Layton 	struct file *file;
3871dc97618dSJ. Bruce Fields 	int starting_len = xdr->buf->len;
3872dc97618dSJ. Bruce Fields 	__be32 *p;
3873dc97618dSJ. Bruce Fields 
38745c4583b2SJeff Layton 	if (nfserr)
38755c4583b2SJeff Layton 		return nfserr;
38765c4583b2SJeff Layton 	file = read->rd_nf->nf_file;
38775c4583b2SJeff Layton 
3878dc97618dSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
3879dc97618dSJ. Bruce Fields 	if (!p) {
3880779fb0f3SJeff Layton 		WARN_ON_ONCE(test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags));
3881bac966d6SJ. Bruce Fields 		return nfserr_resource;
3882dc97618dSJ. Bruce Fields 	}
388368e8bb03SChristoph Hellwig 	if (resp->xdr.buf->page_len &&
388468e8bb03SChristoph Hellwig 	    test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) {
3885b0420980SJ. Bruce Fields 		WARN_ON_ONCE(1);
3886bac966d6SJ. Bruce Fields 		return nfserr_resource;
3887dc97618dSJ. Bruce Fields 	}
3888dc97618dSJ. Bruce Fields 	xdr_commit_encode(xdr);
3889dc97618dSJ. Bruce Fields 
3890dc97618dSJ. Bruce Fields 	maxcount = svc_max_payload(resp->rqstp);
389168e8bb03SChristoph Hellwig 	maxcount = min_t(unsigned long, maxcount,
389268e8bb03SChristoph Hellwig 			 (xdr->buf->buflen - xdr->buf->len));
38933c7aa15dSKinglong Mee 	maxcount = min_t(unsigned long, maxcount, read->rd_length);
3894dc97618dSJ. Bruce Fields 
389568e8bb03SChristoph Hellwig 	if (file->f_op->splice_read &&
389668e8bb03SChristoph Hellwig 	    test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags))
389796bcad50SChristoph Hellwig 		nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
3898dc97618dSJ. Bruce Fields 	else
389996bcad50SChristoph Hellwig 		nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
3900dc97618dSJ. Bruce Fields 
390196bcad50SChristoph Hellwig 	if (nfserr)
3902dc97618dSJ. Bruce Fields 		xdr_truncate_encode(xdr, starting_len);
390396bcad50SChristoph Hellwig 
390496bcad50SChristoph Hellwig 	return nfserr;
39051da177e4SLinus Torvalds }
39061da177e4SLinus Torvalds 
3907b37ad28bSAl Viro static __be32
3908b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
39091da177e4SLinus Torvalds {
39101da177e4SLinus Torvalds 	int maxcount;
3911476a7b1fSJ. Bruce Fields 	__be32 wire_count;
3912476a7b1fSJ. Bruce Fields 	int zero = 0;
3913ddd1ea56SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
39141fcea5b2SJ. Bruce Fields 	int length_offset = xdr->buf->len;
391576e5492bSChuck Lever 	int status;
3916bc749ca4SJ. Bruce Fields 	__be32 *p;
39171da177e4SLinus Torvalds 
39182825a7f9SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
39192825a7f9SJ. Bruce Fields 	if (!p)
39202825a7f9SJ. Bruce Fields 		return nfserr_resource;
39211da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
3922d0a381ddSJ. Bruce Fields 
3923476a7b1fSJ. Bruce Fields 	p = xdr_reserve_space(xdr, maxcount);
3924476a7b1fSJ. Bruce Fields 	if (!p)
39254e21ac4bSJ. Bruce Fields 		return nfserr_resource;
39261da177e4SLinus Torvalds 	/*
3927fd4a0edfSMiklos Szeredi 	 * XXX: By default, vfs_readlink() will truncate symlinks if they
3928fd4a0edfSMiklos Szeredi 	 * would overflow the buffer.  Is this kosher in NFSv4?  If not, one
3929fd4a0edfSMiklos Szeredi 	 * easy fix is: if vfs_readlink() precisely fills the buffer, assume
3930fd4a0edfSMiklos Szeredi 	 * that truncation occurred, and return NFS4ERR_RESOURCE.
39311da177e4SLinus Torvalds 	 */
3932476a7b1fSJ. Bruce Fields 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
3933476a7b1fSJ. Bruce Fields 						(char *)p, &maxcount);
39341da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
3935d3f627c8SJ. Bruce Fields 		nfserr = nfserr_inval;
393676e5492bSChuck Lever 	if (nfserr)
393776e5492bSChuck Lever 		goto out_err;
393876e5492bSChuck Lever 	status = svc_encode_result_payload(readlink->rl_rqstp, length_offset,
393976e5492bSChuck Lever 					   maxcount);
394076e5492bSChuck Lever 	if (status) {
394176e5492bSChuck Lever 		nfserr = nfserrno(status);
394276e5492bSChuck Lever 		goto out_err;
3943d3f627c8SJ. Bruce Fields 	}
39441da177e4SLinus Torvalds 
3945476a7b1fSJ. Bruce Fields 	wire_count = htonl(maxcount);
3946476a7b1fSJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4);
394769bbd9c7SAvi Kivity 	xdr_truncate_encode(xdr, length_offset + 4 + ALIGN(maxcount, 4));
3948476a7b1fSJ. Bruce Fields 	if (maxcount & 3)
3949476a7b1fSJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount,
3950476a7b1fSJ. Bruce Fields 						&zero, 4 - (maxcount&3));
39511da177e4SLinus Torvalds 	return 0;
395276e5492bSChuck Lever 
395376e5492bSChuck Lever out_err:
395476e5492bSChuck Lever 	xdr_truncate_encode(xdr, length_offset);
395576e5492bSChuck Lever 	return nfserr;
39561da177e4SLinus Torvalds }
39571da177e4SLinus Torvalds 
3958b37ad28bSAl Viro static __be32
3959b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
39601da177e4SLinus Torvalds {
39611da177e4SLinus Torvalds 	int maxcount;
3962561f0ed4SJ. Bruce Fields 	int bytes_left;
39631da177e4SLinus Torvalds 	loff_t offset;
3964561f0ed4SJ. Bruce Fields 	__be64 wire_offset;
3965ddd1ea56SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
39661fcea5b2SJ. Bruce Fields 	int starting_len = xdr->buf->len;
3967bc749ca4SJ. Bruce Fields 	__be32 *p;
39681da177e4SLinus Torvalds 
3969d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
3970d0a381ddSJ. Bruce Fields 	if (!p)
3971d0a381ddSJ. Bruce Fields 		return nfserr_resource;
39721da177e4SLinus Torvalds 
39731da177e4SLinus Torvalds 	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
3974c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);
3975c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);
39764aea24b2SJ. Bruce Fields 	resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p)
39774aea24b2SJ. Bruce Fields 				- (char *)resp->xdr.buf->head[0].iov_base;
39781da177e4SLinus Torvalds 
39791da177e4SLinus Torvalds 	/*
3980561f0ed4SJ. Bruce Fields 	 * Number of bytes left for directory entries allowing for the
3981561f0ed4SJ. Bruce Fields 	 * final 8 bytes of the readdir and a following failed op:
39821da177e4SLinus Torvalds 	 */
3983561f0ed4SJ. Bruce Fields 	bytes_left = xdr->buf->buflen - xdr->buf->len
3984561f0ed4SJ. Bruce Fields 			- COMPOUND_ERR_SLACK_SPACE - 8;
3985561f0ed4SJ. Bruce Fields 	if (bytes_left < 0) {
3986561f0ed4SJ. Bruce Fields 		nfserr = nfserr_resource;
3987561f0ed4SJ. Bruce Fields 		goto err_no_verf;
3988561f0ed4SJ. Bruce Fields 	}
39899c2ece6eSScott Mayhew 	maxcount = svc_max_payload(resp->rqstp);
39909c2ece6eSScott Mayhew 	maxcount = min_t(u32, readdir->rd_maxcount, maxcount);
3991561f0ed4SJ. Bruce Fields 	/*
3992561f0ed4SJ. Bruce Fields 	 * Note the rfc defines rd_maxcount as the size of the
3993561f0ed4SJ. Bruce Fields 	 * READDIR4resok structure, which includes the verifier above
3994561f0ed4SJ. Bruce Fields 	 * and the 8 bytes encoded at the end of this function:
3995561f0ed4SJ. Bruce Fields 	 */
3996561f0ed4SJ. Bruce Fields 	if (maxcount < 16) {
39971da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
39981da177e4SLinus Torvalds 		goto err_no_verf;
39991da177e4SLinus Torvalds 	}
4000561f0ed4SJ. Bruce Fields 	maxcount = min_t(int, maxcount-16, bytes_left);
40011da177e4SLinus Torvalds 
4002aee37764SJ. Bruce Fields 	/* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
4003aee37764SJ. Bruce Fields 	if (!readdir->rd_dircount)
40049c2ece6eSScott Mayhew 		readdir->rd_dircount = svc_max_payload(resp->rqstp);
4005aee37764SJ. Bruce Fields 
4006561f0ed4SJ. Bruce Fields 	readdir->xdr = xdr;
4007561f0ed4SJ. Bruce Fields 	readdir->rd_maxcount = maxcount;
40081da177e4SLinus Torvalds 	readdir->common.err = 0;
4009561f0ed4SJ. Bruce Fields 	readdir->cookie_offset = 0;
40101da177e4SLinus Torvalds 
40111da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
40121da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
40131da177e4SLinus Torvalds 			      &offset,
40141da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
40151da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
40161da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
4017561f0ed4SJ. Bruce Fields 	    xdr->buf->len == starting_len + 8) {
4018561f0ed4SJ. Bruce Fields 		/* nothing encoded; which limit did we hit?: */
4019561f0ed4SJ. Bruce Fields 		if (maxcount - 16 < bytes_left)
4020561f0ed4SJ. Bruce Fields 			/* It was the fault of rd_maxcount: */
40211da177e4SLinus Torvalds 			nfserr = nfserr_toosmall;
4022561f0ed4SJ. Bruce Fields 		else
4023561f0ed4SJ. Bruce Fields 			/* We ran out of buffer space: */
4024561f0ed4SJ. Bruce Fields 			nfserr = nfserr_resource;
4025561f0ed4SJ. Bruce Fields 	}
40261da177e4SLinus Torvalds 	if (nfserr)
40271da177e4SLinus Torvalds 		goto err_no_verf;
40281da177e4SLinus Torvalds 
4029561f0ed4SJ. Bruce Fields 	if (readdir->cookie_offset) {
4030561f0ed4SJ. Bruce Fields 		wire_offset = cpu_to_be64(offset);
4031561f0ed4SJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset,
4032561f0ed4SJ. Bruce Fields 							&wire_offset, 8);
4033561f0ed4SJ. Bruce Fields 	}
40341da177e4SLinus Torvalds 
4035561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
4036561f0ed4SJ. Bruce Fields 	if (!p) {
4037561f0ed4SJ. Bruce Fields 		WARN_ON_ONCE(1);
4038561f0ed4SJ. Bruce Fields 		goto err_no_verf;
4039561f0ed4SJ. Bruce Fields 	}
40401da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
40411da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
40421da177e4SLinus Torvalds 
40431da177e4SLinus Torvalds 	return 0;
40441da177e4SLinus Torvalds err_no_verf:
40451fcea5b2SJ. Bruce Fields 	xdr_truncate_encode(xdr, starting_len);
40461da177e4SLinus Torvalds 	return nfserr;
40471da177e4SLinus Torvalds }
40481da177e4SLinus Torvalds 
4049695e12f8SBenny Halevy static __be32
4050b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
40511da177e4SLinus Torvalds {
4052d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4053bc749ca4SJ. Bruce Fields 	__be32 *p;
40541da177e4SLinus Torvalds 
4055d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 20);
4056d0a381ddSJ. Bruce Fields 	if (!p)
4057d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4058d05d5744SJ. Bruce Fields 	p = encode_cinfo(p, &remove->rm_cinfo);
4059bac966d6SJ. Bruce Fields 	return 0;
40601da177e4SLinus Torvalds }
40611da177e4SLinus Torvalds 
4062695e12f8SBenny Halevy static __be32
4063b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
40641da177e4SLinus Torvalds {
4065d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4066bc749ca4SJ. Bruce Fields 	__be32 *p;
40671da177e4SLinus Torvalds 
4068d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 40);
4069d0a381ddSJ. Bruce Fields 	if (!p)
4070d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4071d05d5744SJ. Bruce Fields 	p = encode_cinfo(p, &rename->rn_sinfo);
4072d05d5744SJ. Bruce Fields 	p = encode_cinfo(p, &rename->rn_tinfo);
4073bac966d6SJ. Bruce Fields 	return 0;
40741da177e4SLinus Torvalds }
40751da177e4SLinus Torvalds 
4076695e12f8SBenny Halevy static __be32
4077bac966d6SJ. Bruce Fields nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
4078dcb488a3SAndy Adamson {
4079676e4ebdSChuck Lever 	u32 i, nflavs, supported;
40804796f457SJ. Bruce Fields 	struct exp_flavor_info *flavs;
40814796f457SJ. Bruce Fields 	struct exp_flavor_info def_flavs[2];
4082676e4ebdSChuck Lever 	__be32 *p, *flavorsp;
4083676e4ebdSChuck Lever 	static bool report = true;
4084dcb488a3SAndy Adamson 
40854796f457SJ. Bruce Fields 	if (exp->ex_nflavors) {
40864796f457SJ. Bruce Fields 		flavs = exp->ex_flavors;
40874796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
40884796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
40894796f457SJ. Bruce Fields 		flavs = def_flavs;
40904796f457SJ. Bruce Fields 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
40914796f457SJ. Bruce Fields 			nflavs = 2;
40924796f457SJ. Bruce Fields 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
40934796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
40944796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
40954796f457SJ. Bruce Fields 			nflavs = 1;
40964796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
40974796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
40984796f457SJ. Bruce Fields 		} else {
40994796f457SJ. Bruce Fields 			nflavs = 1;
41004796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
41014796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
41024796f457SJ. Bruce Fields 		}
41034796f457SJ. Bruce Fields 	}
41044796f457SJ. Bruce Fields 
4105676e4ebdSChuck Lever 	supported = 0;
4106d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
4107d0a381ddSJ. Bruce Fields 	if (!p)
4108bac966d6SJ. Bruce Fields 		return nfserr_resource;
4109676e4ebdSChuck Lever 	flavorsp = p++;		/* to be backfilled later */
4110676e4ebdSChuck Lever 
41114796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
4112676e4ebdSChuck Lever 		rpc_authflavor_t pf = flavs[i].pseudoflavor;
4113a77c806fSChuck Lever 		struct rpcsec_gss_info info;
4114dcb488a3SAndy Adamson 
4115676e4ebdSChuck Lever 		if (rpcauth_get_gssinfo(pf, &info) == 0) {
4116676e4ebdSChuck Lever 			supported++;
4117d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4 + 4 +
4118d0a381ddSJ. Bruce Fields 					      XDR_LEN(info.oid.len) + 4 + 4);
4119d0a381ddSJ. Bruce Fields 			if (!p)
4120bac966d6SJ. Bruce Fields 				return nfserr_resource;
4121c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(RPC_AUTH_GSS);
41220c0c267bSJ. Bruce Fields 			p = xdr_encode_opaque(p,  info.oid.data, info.oid.len);
4123c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(info.qop);
4124c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(info.service);
4125676e4ebdSChuck Lever 		} else if (pf < RPC_AUTH_MAXFLAVOR) {
4126676e4ebdSChuck Lever 			supported++;
4127d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
4128d0a381ddSJ. Bruce Fields 			if (!p)
4129bac966d6SJ. Bruce Fields 				return nfserr_resource;
4130c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(pf);
4131676e4ebdSChuck Lever 		} else {
4132676e4ebdSChuck Lever 			if (report)
4133676e4ebdSChuck Lever 				pr_warn("NFS: SECINFO: security flavor %u "
4134676e4ebdSChuck Lever 					"is not supported\n", pf);
4135dcb488a3SAndy Adamson 		}
4136dcb488a3SAndy Adamson 	}
4137a77c806fSChuck Lever 
4138676e4ebdSChuck Lever 	if (nflavs != supported)
4139676e4ebdSChuck Lever 		report = false;
4140676e4ebdSChuck Lever 	*flavorsp = htonl(supported);
4141bac966d6SJ. Bruce Fields 	return 0;
4142dcb488a3SAndy Adamson }
4143dcb488a3SAndy Adamson 
414422b6dee8SMi Jinlong static __be32
414522b6dee8SMi Jinlong nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
414622b6dee8SMi Jinlong 		     struct nfsd4_secinfo *secinfo)
414722b6dee8SMi Jinlong {
4148d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4149d0a381ddSJ. Bruce Fields 
4150bac966d6SJ. Bruce Fields 	return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
415122b6dee8SMi Jinlong }
415222b6dee8SMi Jinlong 
415322b6dee8SMi Jinlong static __be32
415422b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
415522b6dee8SMi Jinlong 		     struct nfsd4_secinfo_no_name *secinfo)
415622b6dee8SMi Jinlong {
4157d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4158d0a381ddSJ. Bruce Fields 
4159bac966d6SJ. Bruce Fields 	return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
416022b6dee8SMi Jinlong }
416122b6dee8SMi Jinlong 
41621da177e4SLinus Torvalds /*
41631da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
41641da177e4SLinus Torvalds  * regardless of the error status.
41651da177e4SLinus Torvalds  */
4166695e12f8SBenny Halevy static __be32
4167b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
41681da177e4SLinus Torvalds {
4169d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4170bc749ca4SJ. Bruce Fields 	__be32 *p;
41711da177e4SLinus Torvalds 
4172d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 16);
4173d0a381ddSJ. Bruce Fields 	if (!p)
4174d0a381ddSJ. Bruce Fields 		return nfserr_resource;
41751da177e4SLinus Torvalds 	if (nfserr) {
4176c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
4177c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
4178c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
4179c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
41801da177e4SLinus Torvalds 	}
41811da177e4SLinus Torvalds 	else {
4182c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
4183c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[0]);
4184c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[1]);
4185c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[2]);
41861da177e4SLinus Torvalds 	}
4187695e12f8SBenny Halevy 	return nfserr;
41881da177e4SLinus Torvalds }
41891da177e4SLinus Torvalds 
4190695e12f8SBenny Halevy static __be32
4191b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
41921da177e4SLinus Torvalds {
4193d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4194bc749ca4SJ. Bruce Fields 	__be32 *p;
41951da177e4SLinus Torvalds 
41961da177e4SLinus Torvalds 	if (!nfserr) {
4197d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8 + NFS4_VERIFIER_SIZE);
4198d0a381ddSJ. Bruce Fields 		if (!p)
4199d0a381ddSJ. Bruce Fields 			return nfserr_resource;
42000c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &scd->se_clientid, 8);
42010c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &scd->se_confirm,
42020c0c267bSJ. Bruce Fields 						NFS4_VERIFIER_SIZE);
42031da177e4SLinus Torvalds 	}
42041da177e4SLinus Torvalds 	else if (nfserr == nfserr_clid_inuse) {
4205d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
4206d0a381ddSJ. Bruce Fields 		if (!p)
4207d0a381ddSJ. Bruce Fields 			return nfserr_resource;
4208c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
4209c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
42101da177e4SLinus Torvalds 	}
4211695e12f8SBenny Halevy 	return nfserr;
42121da177e4SLinus Torvalds }
42131da177e4SLinus Torvalds 
4214695e12f8SBenny Halevy static __be32
4215b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
42161da177e4SLinus Torvalds {
4217d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4218bc749ca4SJ. Bruce Fields 	__be32 *p;
42191da177e4SLinus Torvalds 
4220d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 16);
4221d0a381ddSJ. Bruce Fields 	if (!p)
4222d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4223c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(write->wr_bytes_written);
4224c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(write->wr_how_written);
42250c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
42260c0c267bSJ. Bruce Fields 						NFS4_VERIFIER_SIZE);
4227bac966d6SJ. Bruce Fields 	return 0;
42281da177e4SLinus Torvalds }
42291da177e4SLinus Torvalds 
4230695e12f8SBenny Halevy static __be32
423157b7b43bSJ. Bruce Fields nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
42322db134ebSAndy Adamson 			 struct nfsd4_exchange_id *exid)
42332db134ebSAndy Adamson {
4234d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4235bc749ca4SJ. Bruce Fields 	__be32 *p;
42360733d213SAndy Adamson 	char *major_id;
42370733d213SAndy Adamson 	char *server_scope;
42380733d213SAndy Adamson 	int major_id_sz;
42390733d213SAndy Adamson 	int server_scope_sz;
42400733d213SAndy Adamson 	uint64_t minor_id = 0;
42417627d7dcSScott Mayhew 	struct nfsd_net *nn = net_generic(SVC_NET(resp->rqstp), nfsd_net_id);
42420733d213SAndy Adamson 
42437627d7dcSScott Mayhew 	major_id = nn->nfsd_name;
42447627d7dcSScott Mayhew 	major_id_sz = strlen(nn->nfsd_name);
42457627d7dcSScott Mayhew 	server_scope = nn->nfsd_name;
42467627d7dcSScott Mayhew 	server_scope_sz = strlen(nn->nfsd_name);
42470733d213SAndy Adamson 
4248d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr,
42490733d213SAndy Adamson 		8 /* eir_clientid */ +
42500733d213SAndy Adamson 		4 /* eir_sequenceid */ +
42510733d213SAndy Adamson 		4 /* eir_flags */ +
4252a8bb84bcSKinglong Mee 		4 /* spr_how */);
4253d0a381ddSJ. Bruce Fields 	if (!p)
4254d0a381ddSJ. Bruce Fields 		return nfserr_resource;
42550733d213SAndy Adamson 
42560c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, &exid->clientid, 8);
4257c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(exid->seqid);
4258c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(exid->flags);
42590733d213SAndy Adamson 
4260c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(exid->spa_how);
4261a8bb84bcSKinglong Mee 
426257266a6eSJ. Bruce Fields 	switch (exid->spa_how) {
426357266a6eSJ. Bruce Fields 	case SP4_NONE:
426457266a6eSJ. Bruce Fields 		break;
426557266a6eSJ. Bruce Fields 	case SP4_MACH_CRED:
426657266a6eSJ. Bruce Fields 		/* spo_must_enforce bitmap: */
4267bac966d6SJ. Bruce Fields 		nfserr = nfsd4_encode_bitmap(xdr,
4268ed941643SAndrew Elble 					exid->spo_must_enforce[0],
4269ed941643SAndrew Elble 					exid->spo_must_enforce[1],
4270ed941643SAndrew Elble 					exid->spo_must_enforce[2]);
4271bac966d6SJ. Bruce Fields 		if (nfserr)
4272bac966d6SJ. Bruce Fields 			return nfserr;
4273ed941643SAndrew Elble 		/* spo_must_allow bitmap: */
4274bac966d6SJ. Bruce Fields 		nfserr = nfsd4_encode_bitmap(xdr,
4275ed941643SAndrew Elble 					exid->spo_must_allow[0],
4276ed941643SAndrew Elble 					exid->spo_must_allow[1],
4277ed941643SAndrew Elble 					exid->spo_must_allow[2]);
4278bac966d6SJ. Bruce Fields 		if (nfserr)
4279bac966d6SJ. Bruce Fields 			return nfserr;
428057266a6eSJ. Bruce Fields 		break;
428157266a6eSJ. Bruce Fields 	default:
428257266a6eSJ. Bruce Fields 		WARN_ON_ONCE(1);
428357266a6eSJ. Bruce Fields 	}
42840733d213SAndy Adamson 
4285d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr,
4286a8bb84bcSKinglong Mee 		8 /* so_minor_id */ +
4287a8bb84bcSKinglong Mee 		4 /* so_major_id.len */ +
4288a8bb84bcSKinglong Mee 		(XDR_QUADLEN(major_id_sz) * 4) +
4289a8bb84bcSKinglong Mee 		4 /* eir_server_scope.len */ +
4290a8bb84bcSKinglong Mee 		(XDR_QUADLEN(server_scope_sz) * 4) +
4291a8bb84bcSKinglong Mee 		4 /* eir_server_impl_id.count (0) */);
4292d0a381ddSJ. Bruce Fields 	if (!p)
4293d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4294a8bb84bcSKinglong Mee 
42950733d213SAndy Adamson 	/* The server_owner struct */
4296b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, minor_id);      /* Minor id */
42970733d213SAndy Adamson 	/* major id */
42980c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, major_id, major_id_sz);
42990733d213SAndy Adamson 
43000733d213SAndy Adamson 	/* Server scope */
43010c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, server_scope, server_scope_sz);
43020733d213SAndy Adamson 
43030733d213SAndy Adamson 	/* Implementation id */
4304c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);	/* zero length nfs_impl_id4 array */
43050733d213SAndy Adamson 	return 0;
43062db134ebSAndy Adamson }
43072db134ebSAndy Adamson 
43082db134ebSAndy Adamson static __be32
430957b7b43bSJ. Bruce Fields nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
43102db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
43112db134ebSAndy Adamson {
4312d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4313bc749ca4SJ. Bruce Fields 	__be32 *p;
4314ec6b5d7bSAndy Adamson 
4315d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 24);
4316d0a381ddSJ. Bruce Fields 	if (!p)
4317d0a381ddSJ. Bruce Fields 		return nfserr_resource;
43180c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, sess->sessionid.data,
43190c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
4320c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->seqid);
4321c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->flags);
4322ec6b5d7bSAndy Adamson 
4323d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 28);
4324d0a381ddSJ. Bruce Fields 	if (!p)
4325d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4326c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* headerpadsz */
4327c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxreq_sz);
4328c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxresp_sz);
4329c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxresp_cached);
4330c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxops);
4331c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxreqs);
4332c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs);
4333ec6b5d7bSAndy Adamson 
4334ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs) {
4335d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
4336d0a381ddSJ. Bruce Fields 		if (!p)
4337d0a381ddSJ. Bruce Fields 			return nfserr_resource;
4338c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(sess->fore_channel.rdma_attrs);
4339ec6b5d7bSAndy Adamson 	}
4340ec6b5d7bSAndy Adamson 
4341d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 28);
4342d0a381ddSJ. Bruce Fields 	if (!p)
4343d0a381ddSJ. Bruce Fields 		return nfserr_resource;
4344c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* headerpadsz */
4345c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxreq_sz);
4346c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxresp_sz);
4347c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxresp_cached);
4348c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxops);
4349c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxreqs);
4350c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs);
4351ec6b5d7bSAndy Adamson 
4352ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs) {
4353d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
4354d0a381ddSJ. Bruce Fields 		if (!p)
4355d0a381ddSJ. Bruce Fields 			return nfserr_resource;
4356c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(sess->back_channel.rdma_attrs);
4357ec6b5d7bSAndy Adamson 	}
4358ec6b5d7bSAndy Adamson 	return 0;
43592db134ebSAndy Adamson }
43602db134ebSAndy Adamson 
43612db134ebSAndy Adamson static __be32
436257b7b43bSJ. Bruce Fields nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
43632db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
43642db134ebSAndy Adamson {
4365d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
4366bc749ca4SJ. Bruce Fields 	__be32 *p;
4367b85d4c01SBenny Halevy 
4368d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20);
4369d0a381ddSJ. Bruce Fields 	if (!p)
4370d0a381ddSJ. Bruce Fields 		return nfserr_resource;
43710c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, seq->sessionid.data,
43720c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
4373c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->seqid);
4374c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->slotid);
4375b7d7ca35SJ. Bruce Fields 	/* Note slotid's are numbered from zero: */
4376c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */
4377c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */
4378c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->status_flags);
4379b85d4c01SBenny Halevy 
4380f5236013SJ. Bruce Fields 	resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
4381b85d4c01SBenny Halevy 	return 0;
43822db134ebSAndy Adamson }
43832db134ebSAndy Adamson 
43842355c596SJ. Bruce Fields static __be32
438557b7b43bSJ. Bruce Fields nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
438617456804SBryan Schumaker 			  struct nfsd4_test_stateid *test_stateid)
438717456804SBryan Schumaker {
4388d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
438903cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid, *next;
439017456804SBryan Schumaker 	__be32 *p;
439117456804SBryan Schumaker 
4392d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids));
4393d0a381ddSJ. Bruce Fields 	if (!p)
4394d0a381ddSJ. Bruce Fields 		return nfserr_resource;
439517456804SBryan Schumaker 	*p++ = htonl(test_stateid->ts_num_ids);
439617456804SBryan Schumaker 
439703cfb420SBryan Schumaker 	list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
439802f5fde5SAl Viro 		*p++ = stateid->ts_id_status;
439917456804SBryan Schumaker 	}
440017456804SBryan Schumaker 
4401bac966d6SJ. Bruce Fields 	return 0;
440217456804SBryan Schumaker }
440317456804SBryan Schumaker 
44049cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
44059cf514ccSChristoph Hellwig static __be32
44069cf514ccSChristoph Hellwig nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
44079cf514ccSChristoph Hellwig 		struct nfsd4_getdeviceinfo *gdev)
44089cf514ccSChristoph Hellwig {
44099cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
4410f961e3f2SJ. Bruce Fields 	const struct nfsd4_layout_ops *ops;
44119cf514ccSChristoph Hellwig 	u32 starting_len = xdr->buf->len, needed_len;
44129cf514ccSChristoph Hellwig 	__be32 *p;
44139cf514ccSChristoph Hellwig 
44149cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
44159cf514ccSChristoph Hellwig 	if (!p)
4416bac966d6SJ. Bruce Fields 		return nfserr_resource;
44179cf514ccSChristoph Hellwig 
44189cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(gdev->gd_layout_type);
44199cf514ccSChristoph Hellwig 
44209cf514ccSChristoph Hellwig 	/* If maxcount is 0 then just update notifications */
44219cf514ccSChristoph Hellwig 	if (gdev->gd_maxcount != 0) {
4422f961e3f2SJ. Bruce Fields 		ops = nfsd4_layout_ops[gdev->gd_layout_type];
44239cf514ccSChristoph Hellwig 		nfserr = ops->encode_getdeviceinfo(xdr, gdev);
44249cf514ccSChristoph Hellwig 		if (nfserr) {
44259cf514ccSChristoph Hellwig 			/*
44269cf514ccSChristoph Hellwig 			 * We don't bother to burden the layout drivers with
44279cf514ccSChristoph Hellwig 			 * enforcing gd_maxcount, just tell the client to
44289cf514ccSChristoph Hellwig 			 * come back with a bigger buffer if it's not enough.
44299cf514ccSChristoph Hellwig 			 */
44309cf514ccSChristoph Hellwig 			if (xdr->buf->len + 4 > gdev->gd_maxcount)
44319cf514ccSChristoph Hellwig 				goto toosmall;
4432bac966d6SJ. Bruce Fields 			return nfserr;
44339cf514ccSChristoph Hellwig 		}
44349cf514ccSChristoph Hellwig 	}
44359cf514ccSChristoph Hellwig 
44369cf514ccSChristoph Hellwig 	if (gdev->gd_notify_types) {
44379cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4 + 4);
44389cf514ccSChristoph Hellwig 		if (!p)
4439bac966d6SJ. Bruce Fields 			return nfserr_resource;
44409cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(1);			/* bitmap length */
44419cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(gdev->gd_notify_types);
44429cf514ccSChristoph Hellwig 	} else {
44439cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4);
44449cf514ccSChristoph Hellwig 		if (!p)
4445bac966d6SJ. Bruce Fields 			return nfserr_resource;
44469cf514ccSChristoph Hellwig 		*p++ = 0;
44479cf514ccSChristoph Hellwig 	}
44489cf514ccSChristoph Hellwig 
4449bac966d6SJ. Bruce Fields 	return 0;
44509cf514ccSChristoph Hellwig toosmall:
44519cf514ccSChristoph Hellwig 	dprintk("%s: maxcount too small\n", __func__);
44529cf514ccSChristoph Hellwig 	needed_len = xdr->buf->len + 4 /* notifications */;
44539cf514ccSChristoph Hellwig 	xdr_truncate_encode(xdr, starting_len);
44549cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
4455bac966d6SJ. Bruce Fields 	if (!p)
4456bac966d6SJ. Bruce Fields 		return nfserr_resource;
44579cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(needed_len);
4458bac966d6SJ. Bruce Fields 	return nfserr_toosmall;
44599cf514ccSChristoph Hellwig }
44609cf514ccSChristoph Hellwig 
44619cf514ccSChristoph Hellwig static __be32
44629cf514ccSChristoph Hellwig nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
44639cf514ccSChristoph Hellwig 		struct nfsd4_layoutget *lgp)
44649cf514ccSChristoph Hellwig {
44659cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
4466f961e3f2SJ. Bruce Fields 	const struct nfsd4_layout_ops *ops;
44679cf514ccSChristoph Hellwig 	__be32 *p;
44689cf514ccSChristoph Hellwig 
44699cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 36 + sizeof(stateid_opaque_t));
44709cf514ccSChristoph Hellwig 	if (!p)
4471bac966d6SJ. Bruce Fields 		return nfserr_resource;
44729cf514ccSChristoph Hellwig 
44739cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(1);	/* we always set return-on-close */
44749cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_sid.si_generation);
44759cf514ccSChristoph Hellwig 	p = xdr_encode_opaque_fixed(p, &lgp->lg_sid.si_opaque,
44769cf514ccSChristoph Hellwig 				    sizeof(stateid_opaque_t));
44779cf514ccSChristoph Hellwig 
44789cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(1);	/* we always return a single layout */
44799cf514ccSChristoph Hellwig 	p = xdr_encode_hyper(p, lgp->lg_seg.offset);
44809cf514ccSChristoph Hellwig 	p = xdr_encode_hyper(p, lgp->lg_seg.length);
44819cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_seg.iomode);
44829cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_layout_type);
44839cf514ccSChristoph Hellwig 
4484f961e3f2SJ. Bruce Fields 	ops = nfsd4_layout_ops[lgp->lg_layout_type];
4485bac966d6SJ. Bruce Fields 	return ops->encode_layoutget(xdr, lgp);
44869cf514ccSChristoph Hellwig }
44879cf514ccSChristoph Hellwig 
44889cf514ccSChristoph Hellwig static __be32
44899cf514ccSChristoph Hellwig nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr,
44909cf514ccSChristoph Hellwig 			  struct nfsd4_layoutcommit *lcp)
44919cf514ccSChristoph Hellwig {
44929cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
44939cf514ccSChristoph Hellwig 	__be32 *p;
44949cf514ccSChristoph Hellwig 
44959cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
44969cf514ccSChristoph Hellwig 	if (!p)
44979cf514ccSChristoph Hellwig 		return nfserr_resource;
44989cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lcp->lc_size_chg);
44999cf514ccSChristoph Hellwig 	if (lcp->lc_size_chg) {
45009cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 8);
45019cf514ccSChristoph Hellwig 		if (!p)
45029cf514ccSChristoph Hellwig 			return nfserr_resource;
45039cf514ccSChristoph Hellwig 		p = xdr_encode_hyper(p, lcp->lc_newsize);
45049cf514ccSChristoph Hellwig 	}
45059cf514ccSChristoph Hellwig 
4506bac966d6SJ. Bruce Fields 	return 0;
45079cf514ccSChristoph Hellwig }
45089cf514ccSChristoph Hellwig 
45099cf514ccSChristoph Hellwig static __be32
45109cf514ccSChristoph Hellwig nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
45119cf514ccSChristoph Hellwig 		struct nfsd4_layoutreturn *lrp)
45129cf514ccSChristoph Hellwig {
45139cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
45149cf514ccSChristoph Hellwig 	__be32 *p;
45159cf514ccSChristoph Hellwig 
45169cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
45179cf514ccSChristoph Hellwig 	if (!p)
45189cf514ccSChristoph Hellwig 		return nfserr_resource;
45199cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lrp->lrs_present);
45209cf514ccSChristoph Hellwig 	if (lrp->lrs_present)
4521376675daSKinglong Mee 		return nfsd4_encode_stateid(xdr, &lrp->lr_sid);
4522bac966d6SJ. Bruce Fields 	return 0;
45239cf514ccSChristoph Hellwig }
45249cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
45259cf514ccSChristoph Hellwig 
45262db134ebSAndy Adamson static __be32
4527e0639dc5SOlga Kornievskaia nfsd42_encode_write_res(struct nfsd4_compoundres *resp,
4528e0639dc5SOlga Kornievskaia 		struct nfsd42_write_res *write, bool sync)
452929ae7f9dSAnna Schumaker {
453029ae7f9dSAnna Schumaker 	__be32 *p;
4531e0639dc5SOlga Kornievskaia 	p = xdr_reserve_space(&resp->xdr, 4);
453229ae7f9dSAnna Schumaker 	if (!p)
453329ae7f9dSAnna Schumaker 		return nfserr_resource;
453429ae7f9dSAnna Schumaker 
4535e0639dc5SOlga Kornievskaia 	if (sync)
453629ae7f9dSAnna Schumaker 		*p++ = cpu_to_be32(0);
4537e0639dc5SOlga Kornievskaia 	else {
4538e0639dc5SOlga Kornievskaia 		__be32 nfserr;
4539e0639dc5SOlga Kornievskaia 		*p++ = cpu_to_be32(1);
4540e0639dc5SOlga Kornievskaia 		nfserr = nfsd4_encode_stateid(&resp->xdr, &write->cb_stateid);
4541e0639dc5SOlga Kornievskaia 		if (nfserr)
4542e0639dc5SOlga Kornievskaia 			return nfserr;
4543e0639dc5SOlga Kornievskaia 	}
4544e0639dc5SOlga Kornievskaia 	p = xdr_reserve_space(&resp->xdr, 8 + 4 + NFS4_VERIFIER_SIZE);
4545e0639dc5SOlga Kornievskaia 	if (!p)
4546e0639dc5SOlga Kornievskaia 		return nfserr_resource;
4547e0639dc5SOlga Kornievskaia 
454829ae7f9dSAnna Schumaker 	p = xdr_encode_hyper(p, write->wr_bytes_written);
454929ae7f9dSAnna Schumaker 	*p++ = cpu_to_be32(write->wr_stable_how);
455029ae7f9dSAnna Schumaker 	p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
455129ae7f9dSAnna Schumaker 				    NFS4_VERIFIER_SIZE);
455229ae7f9dSAnna Schumaker 	return nfs_ok;
455329ae7f9dSAnna Schumaker }
455429ae7f9dSAnna Schumaker 
455529ae7f9dSAnna Schumaker static __be32
455651911868SOlga Kornievskaia nfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns)
455751911868SOlga Kornievskaia {
455851911868SOlga Kornievskaia 	struct xdr_stream *xdr = &resp->xdr;
455951911868SOlga Kornievskaia 	struct nfs42_netaddr *addr;
456051911868SOlga Kornievskaia 	__be32 *p;
456151911868SOlga Kornievskaia 
456251911868SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 4);
456351911868SOlga Kornievskaia 	*p++ = cpu_to_be32(ns->nl4_type);
456451911868SOlga Kornievskaia 
456551911868SOlga Kornievskaia 	switch (ns->nl4_type) {
456651911868SOlga Kornievskaia 	case NL4_NETADDR:
456751911868SOlga Kornievskaia 		addr = &ns->u.nl4_addr;
456851911868SOlga Kornievskaia 
456951911868SOlga Kornievskaia 		/* netid_len, netid, uaddr_len, uaddr (port included
457051911868SOlga Kornievskaia 		 * in RPCBIND_MAXUADDRLEN)
457151911868SOlga Kornievskaia 		 */
457251911868SOlga Kornievskaia 		p = xdr_reserve_space(xdr,
457351911868SOlga Kornievskaia 			4 /* netid len */ +
457451911868SOlga Kornievskaia 			(XDR_QUADLEN(addr->netid_len) * 4) +
457551911868SOlga Kornievskaia 			4 /* uaddr len */ +
457651911868SOlga Kornievskaia 			(XDR_QUADLEN(addr->addr_len) * 4));
457751911868SOlga Kornievskaia 		if (!p)
457851911868SOlga Kornievskaia 			return nfserr_resource;
457951911868SOlga Kornievskaia 
458051911868SOlga Kornievskaia 		*p++ = cpu_to_be32(addr->netid_len);
458151911868SOlga Kornievskaia 		p = xdr_encode_opaque_fixed(p, addr->netid,
458251911868SOlga Kornievskaia 					    addr->netid_len);
458351911868SOlga Kornievskaia 		*p++ = cpu_to_be32(addr->addr_len);
458451911868SOlga Kornievskaia 		p = xdr_encode_opaque_fixed(p, addr->addr,
458551911868SOlga Kornievskaia 					addr->addr_len);
458651911868SOlga Kornievskaia 		break;
458751911868SOlga Kornievskaia 	default:
458851911868SOlga Kornievskaia 		WARN_ON_ONCE(ns->nl4_type != NL4_NETADDR);
458951911868SOlga Kornievskaia 		return nfserr_inval;
459051911868SOlga Kornievskaia 	}
459151911868SOlga Kornievskaia 
459251911868SOlga Kornievskaia 	return 0;
459351911868SOlga Kornievskaia }
459451911868SOlga Kornievskaia 
459551911868SOlga Kornievskaia static __be32
459629ae7f9dSAnna Schumaker nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
459729ae7f9dSAnna Schumaker 		  struct nfsd4_copy *copy)
459829ae7f9dSAnna Schumaker {
459929ae7f9dSAnna Schumaker 	__be32 *p;
460029ae7f9dSAnna Schumaker 
4601e0639dc5SOlga Kornievskaia 	nfserr = nfsd42_encode_write_res(resp, &copy->cp_res,
4602e0639dc5SOlga Kornievskaia 			copy->cp_synchronous);
460329ae7f9dSAnna Schumaker 	if (nfserr)
460429ae7f9dSAnna Schumaker 		return nfserr;
460529ae7f9dSAnna Schumaker 
460629ae7f9dSAnna Schumaker 	p = xdr_reserve_space(&resp->xdr, 4 + 4);
4607edcc8452SJ. Bruce Fields 	*p++ = xdr_one; /* cr_consecutive */
460829ae7f9dSAnna Schumaker 	*p++ = cpu_to_be32(copy->cp_synchronous);
4609bac966d6SJ. Bruce Fields 	return 0;
461029ae7f9dSAnna Schumaker }
461129ae7f9dSAnna Schumaker 
461229ae7f9dSAnna Schumaker static __be32
46136308bc98SOlga Kornievskaia nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
46146308bc98SOlga Kornievskaia 			    struct nfsd4_offload_status *os)
46156308bc98SOlga Kornievskaia {
46166308bc98SOlga Kornievskaia 	struct xdr_stream *xdr = &resp->xdr;
46176308bc98SOlga Kornievskaia 	__be32 *p;
46186308bc98SOlga Kornievskaia 
46196308bc98SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 8 + 4);
46206308bc98SOlga Kornievskaia 	if (!p)
46216308bc98SOlga Kornievskaia 		return nfserr_resource;
46226308bc98SOlga Kornievskaia 	p = xdr_encode_hyper(p, os->count);
46236308bc98SOlga Kornievskaia 	*p++ = cpu_to_be32(0);
4624528b8493SAnna Schumaker 	return nfserr;
4625528b8493SAnna Schumaker }
4626528b8493SAnna Schumaker 
4627528b8493SAnna Schumaker static __be32
4628528b8493SAnna Schumaker nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
4629528b8493SAnna Schumaker 			    struct nfsd4_read *read,
46309f0b5792SAnna Schumaker 			    unsigned long *maxcount, u32 *eof,
46319f0b5792SAnna Schumaker 			    loff_t *pos)
4632528b8493SAnna Schumaker {
4633528b8493SAnna Schumaker 	struct xdr_stream *xdr = &resp->xdr;
4634528b8493SAnna Schumaker 	struct file *file = read->rd_nf->nf_file;
4635528b8493SAnna Schumaker 	int starting_len = xdr->buf->len;
46369f0b5792SAnna Schumaker 	loff_t hole_pos;
4637528b8493SAnna Schumaker 	__be32 nfserr;
4638528b8493SAnna Schumaker 	__be32 *p, tmp;
4639528b8493SAnna Schumaker 	__be64 tmp64;
4640528b8493SAnna Schumaker 
46419f0b5792SAnna Schumaker 	hole_pos = pos ? *pos : vfs_llseek(file, read->rd_offset, SEEK_HOLE);
46422db27992SAnna Schumaker 	if (hole_pos > read->rd_offset)
4643278765eaSAnna Schumaker 		*maxcount = min_t(unsigned long, *maxcount, hole_pos - read->rd_offset);
4644278765eaSAnna Schumaker 	*maxcount = min_t(unsigned long, *maxcount, (xdr->buf->buflen - xdr->buf->len));
4645528b8493SAnna Schumaker 
4646528b8493SAnna Schumaker 	/* Content type, offset, byte count */
4647528b8493SAnna Schumaker 	p = xdr_reserve_space(xdr, 4 + 8 + 4);
4648528b8493SAnna Schumaker 	if (!p)
4649528b8493SAnna Schumaker 		return nfserr_resource;
4650528b8493SAnna Schumaker 
4651278765eaSAnna Schumaker 	read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, *maxcount);
4652528b8493SAnna Schumaker 	if (read->rd_vlen < 0)
4653528b8493SAnna Schumaker 		return nfserr_resource;
4654528b8493SAnna Schumaker 
4655528b8493SAnna Schumaker 	nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset,
4656278765eaSAnna Schumaker 			    resp->rqstp->rq_vec, read->rd_vlen, maxcount, eof);
4657528b8493SAnna Schumaker 	if (nfserr)
4658528b8493SAnna Schumaker 		return nfserr;
4659528b8493SAnna Schumaker 
4660528b8493SAnna Schumaker 	tmp = htonl(NFS4_CONTENT_DATA);
4661528b8493SAnna Schumaker 	write_bytes_to_xdr_buf(xdr->buf, starting_len,      &tmp,   4);
4662528b8493SAnna Schumaker 	tmp64 = cpu_to_be64(read->rd_offset);
4663528b8493SAnna Schumaker 	write_bytes_to_xdr_buf(xdr->buf, starting_len + 4,  &tmp64, 8);
4664278765eaSAnna Schumaker 	tmp = htonl(*maxcount);
4665528b8493SAnna Schumaker 	write_bytes_to_xdr_buf(xdr->buf, starting_len + 12, &tmp,   4);
4666528b8493SAnna Schumaker 	return nfs_ok;
4667528b8493SAnna Schumaker }
4668528b8493SAnna Schumaker 
4669528b8493SAnna Schumaker static __be32
46702db27992SAnna Schumaker nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp,
46712db27992SAnna Schumaker 			    struct nfsd4_read *read,
4672278765eaSAnna Schumaker 			    unsigned long *maxcount, u32 *eof)
46732db27992SAnna Schumaker {
46742db27992SAnna Schumaker 	struct file *file = read->rd_nf->nf_file;
4675278765eaSAnna Schumaker 	loff_t data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA);
46769f0b5792SAnna Schumaker 	loff_t f_size = i_size_read(file_inode(file));
4677278765eaSAnna Schumaker 	unsigned long count;
46782db27992SAnna Schumaker 	__be32 *p;
46792db27992SAnna Schumaker 
4680278765eaSAnna Schumaker 	if (data_pos == -ENXIO)
46819f0b5792SAnna Schumaker 		data_pos = f_size;
46829f0b5792SAnna Schumaker 	else if (data_pos <= read->rd_offset || (data_pos < f_size && data_pos % PAGE_SIZE))
46839f0b5792SAnna Schumaker 		return nfsd4_encode_read_plus_data(resp, read, maxcount, eof, &f_size);
4684278765eaSAnna Schumaker 	count = data_pos - read->rd_offset;
4685278765eaSAnna Schumaker 
46862db27992SAnna Schumaker 	/* Content type, offset, byte count */
46872db27992SAnna Schumaker 	p = xdr_reserve_space(&resp->xdr, 4 + 8 + 8);
46882db27992SAnna Schumaker 	if (!p)
46892db27992SAnna Schumaker 		return nfserr_resource;
46902db27992SAnna Schumaker 
46912db27992SAnna Schumaker 	*p++ = htonl(NFS4_CONTENT_HOLE);
46922db27992SAnna Schumaker 	 p   = xdr_encode_hyper(p, read->rd_offset);
4693278765eaSAnna Schumaker 	 p   = xdr_encode_hyper(p, count);
46942db27992SAnna Schumaker 
46959f0b5792SAnna Schumaker 	*eof = (read->rd_offset + count) >= f_size;
4696278765eaSAnna Schumaker 	*maxcount = min_t(unsigned long, count, *maxcount);
46972db27992SAnna Schumaker 	return nfs_ok;
46982db27992SAnna Schumaker }
46992db27992SAnna Schumaker 
47002db27992SAnna Schumaker static __be32
4701528b8493SAnna Schumaker nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
4702528b8493SAnna Schumaker 		       struct nfsd4_read *read)
4703528b8493SAnna Schumaker {
4704278765eaSAnna Schumaker 	unsigned long maxcount, count;
4705528b8493SAnna Schumaker 	struct xdr_stream *xdr = &resp->xdr;
4706528b8493SAnna Schumaker 	struct file *file;
4707528b8493SAnna Schumaker 	int starting_len = xdr->buf->len;
47089f0b5792SAnna Schumaker 	int last_segment = xdr->buf->len;
4709528b8493SAnna Schumaker 	int segments = 0;
4710528b8493SAnna Schumaker 	__be32 *p, tmp;
47119f0b5792SAnna Schumaker 	bool is_data;
47122db27992SAnna Schumaker 	loff_t pos;
4713528b8493SAnna Schumaker 	u32 eof;
4714528b8493SAnna Schumaker 
4715528b8493SAnna Schumaker 	if (nfserr)
4716528b8493SAnna Schumaker 		return nfserr;
4717528b8493SAnna Schumaker 	file = read->rd_nf->nf_file;
4718528b8493SAnna Schumaker 
4719528b8493SAnna Schumaker 	/* eof flag, segment count */
4720528b8493SAnna Schumaker 	p = xdr_reserve_space(xdr, 4 + 4);
4721528b8493SAnna Schumaker 	if (!p)
4722528b8493SAnna Schumaker 		return nfserr_resource;
4723528b8493SAnna Schumaker 	xdr_commit_encode(xdr);
4724528b8493SAnna Schumaker 
4725528b8493SAnna Schumaker 	maxcount = svc_max_payload(resp->rqstp);
4726528b8493SAnna Schumaker 	maxcount = min_t(unsigned long, maxcount,
4727528b8493SAnna Schumaker 			 (xdr->buf->buflen - xdr->buf->len));
4728528b8493SAnna Schumaker 	maxcount = min_t(unsigned long, maxcount, read->rd_length);
4729278765eaSAnna Schumaker 	count    = maxcount;
4730528b8493SAnna Schumaker 
4731528b8493SAnna Schumaker 	eof = read->rd_offset >= i_size_read(file_inode(file));
47322db27992SAnna Schumaker 	if (eof)
47332db27992SAnna Schumaker 		goto out;
47342db27992SAnna Schumaker 
47359f0b5792SAnna Schumaker 	pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE);
47369f0b5792SAnna Schumaker 	is_data = pos > read->rd_offset;
47372db27992SAnna Schumaker 
47389f0b5792SAnna Schumaker 	while (count > 0 && !eof) {
4739278765eaSAnna Schumaker 		maxcount = count;
47409f0b5792SAnna Schumaker 		if (is_data)
47419f0b5792SAnna Schumaker 			nfserr = nfsd4_encode_read_plus_data(resp, read, &maxcount, &eof,
47429f0b5792SAnna Schumaker 						segments == 0 ? &pos : NULL);
47439f0b5792SAnna Schumaker 		else
4744278765eaSAnna Schumaker 			nfserr = nfsd4_encode_read_plus_hole(resp, read, &maxcount, &eof);
4745278765eaSAnna Schumaker 		if (nfserr)
4746278765eaSAnna Schumaker 			goto out;
4747278765eaSAnna Schumaker 		count -= maxcount;
4748278765eaSAnna Schumaker 		read->rd_offset += maxcount;
47499f0b5792SAnna Schumaker 		is_data = !is_data;
47509f0b5792SAnna Schumaker 		last_segment = xdr->buf->len;
4751528b8493SAnna Schumaker 		segments++;
4752528b8493SAnna Schumaker 	}
4753528b8493SAnna Schumaker 
47542db27992SAnna Schumaker out:
4755278765eaSAnna Schumaker 	if (nfserr && segments == 0)
4756528b8493SAnna Schumaker 		xdr_truncate_encode(xdr, starting_len);
4757528b8493SAnna Schumaker 	else {
4758528b8493SAnna Schumaker 		tmp = htonl(eof);
4759528b8493SAnna Schumaker 		write_bytes_to_xdr_buf(xdr->buf, starting_len,     &tmp, 4);
4760528b8493SAnna Schumaker 		tmp = htonl(segments);
4761528b8493SAnna Schumaker 		write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
47629f0b5792SAnna Schumaker 		if (nfserr) {
47639f0b5792SAnna Schumaker 			xdr_truncate_encode(xdr, last_segment);
4764278765eaSAnna Schumaker 			nfserr = nfs_ok;
4765528b8493SAnna Schumaker 		}
47669f0b5792SAnna Schumaker 	}
47676308bc98SOlga Kornievskaia 
47686308bc98SOlga Kornievskaia 	return nfserr;
47696308bc98SOlga Kornievskaia }
47706308bc98SOlga Kornievskaia 
47716308bc98SOlga Kornievskaia static __be32
477251911868SOlga Kornievskaia nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
477351911868SOlga Kornievskaia 			 struct nfsd4_copy_notify *cn)
477451911868SOlga Kornievskaia {
477551911868SOlga Kornievskaia 	struct xdr_stream *xdr = &resp->xdr;
477651911868SOlga Kornievskaia 	__be32 *p;
477751911868SOlga Kornievskaia 
477851911868SOlga Kornievskaia 	if (nfserr)
477951911868SOlga Kornievskaia 		return nfserr;
478051911868SOlga Kornievskaia 
478151911868SOlga Kornievskaia 	/* 8 sec, 4 nsec */
478251911868SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 12);
478351911868SOlga Kornievskaia 	if (!p)
478451911868SOlga Kornievskaia 		return nfserr_resource;
478551911868SOlga Kornievskaia 
478651911868SOlga Kornievskaia 	/* cnr_lease_time */
478751911868SOlga Kornievskaia 	p = xdr_encode_hyper(p, cn->cpn_sec);
478851911868SOlga Kornievskaia 	*p++ = cpu_to_be32(cn->cpn_nsec);
478951911868SOlga Kornievskaia 
479051911868SOlga Kornievskaia 	/* cnr_stateid */
479151911868SOlga Kornievskaia 	nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid);
479251911868SOlga Kornievskaia 	if (nfserr)
479351911868SOlga Kornievskaia 		return nfserr;
479451911868SOlga Kornievskaia 
479551911868SOlga Kornievskaia 	/* cnr_src.nl_nsvr */
479651911868SOlga Kornievskaia 	p = xdr_reserve_space(xdr, 4);
479751911868SOlga Kornievskaia 	if (!p)
479851911868SOlga Kornievskaia 		return nfserr_resource;
479951911868SOlga Kornievskaia 
480051911868SOlga Kornievskaia 	*p++ = cpu_to_be32(1);
480151911868SOlga Kornievskaia 
480251911868SOlga Kornievskaia 	return nfsd42_encode_nl4_server(resp, &cn->cpn_src);
480351911868SOlga Kornievskaia }
480451911868SOlga Kornievskaia 
480551911868SOlga Kornievskaia static __be32
480624bab491SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
480724bab491SAnna Schumaker 		  struct nfsd4_seek *seek)
480824bab491SAnna Schumaker {
480924bab491SAnna Schumaker 	__be32 *p;
481024bab491SAnna Schumaker 
481124bab491SAnna Schumaker 	p = xdr_reserve_space(&resp->xdr, 4 + 8);
481224bab491SAnna Schumaker 	*p++ = cpu_to_be32(seek->seek_eof);
481324bab491SAnna Schumaker 	p = xdr_encode_hyper(p, seek->seek_pos);
481424bab491SAnna Schumaker 
4815bac966d6SJ. Bruce Fields 	return 0;
481624bab491SAnna Schumaker }
481724bab491SAnna Schumaker 
481824bab491SAnna Schumaker static __be32
4819695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
4820695e12f8SBenny Halevy {
4821695e12f8SBenny Halevy 	return nfserr;
4822695e12f8SBenny Halevy }
4823695e12f8SBenny Halevy 
482423e50fe3SFrank van der Linden /*
482523e50fe3SFrank van der Linden  * Encode kmalloc-ed buffer in to XDR stream.
482623e50fe3SFrank van der Linden  */
4827b9a49237SChuck Lever static __be32
482823e50fe3SFrank van der Linden nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
482923e50fe3SFrank van der Linden {
483023e50fe3SFrank van der Linden 	u32 cplen;
483123e50fe3SFrank van der Linden 	__be32 *p;
483223e50fe3SFrank van der Linden 
483323e50fe3SFrank van der Linden 	cplen = min_t(unsigned long, buflen,
483423e50fe3SFrank van der Linden 		      ((void *)xdr->end - (void *)xdr->p));
483523e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, cplen);
483623e50fe3SFrank van der Linden 	if (!p)
483723e50fe3SFrank van der Linden 		return nfserr_resource;
483823e50fe3SFrank van der Linden 
483923e50fe3SFrank van der Linden 	memcpy(p, buf, cplen);
484023e50fe3SFrank van der Linden 	buf += cplen;
484123e50fe3SFrank van der Linden 	buflen -= cplen;
484223e50fe3SFrank van der Linden 
484323e50fe3SFrank van der Linden 	while (buflen) {
484423e50fe3SFrank van der Linden 		cplen = min_t(u32, buflen, PAGE_SIZE);
484523e50fe3SFrank van der Linden 		p = xdr_reserve_space(xdr, cplen);
484623e50fe3SFrank van der Linden 		if (!p)
484723e50fe3SFrank van der Linden 			return nfserr_resource;
484823e50fe3SFrank van der Linden 
484923e50fe3SFrank van der Linden 		memcpy(p, buf, cplen);
485023e50fe3SFrank van der Linden 
485123e50fe3SFrank van der Linden 		if (cplen < PAGE_SIZE) {
485223e50fe3SFrank van der Linden 			/*
485323e50fe3SFrank van der Linden 			 * We're done, with a length that wasn't page
485423e50fe3SFrank van der Linden 			 * aligned, so possibly not word aligned. Pad
485523e50fe3SFrank van der Linden 			 * any trailing bytes with 0.
485623e50fe3SFrank van der Linden 			 */
485723e50fe3SFrank van der Linden 			xdr_encode_opaque_fixed(p, NULL, cplen);
485823e50fe3SFrank van der Linden 			break;
485923e50fe3SFrank van der Linden 		}
486023e50fe3SFrank van der Linden 
486123e50fe3SFrank van der Linden 		buflen -= PAGE_SIZE;
486223e50fe3SFrank van der Linden 		buf += PAGE_SIZE;
486323e50fe3SFrank van der Linden 	}
486423e50fe3SFrank van der Linden 
486523e50fe3SFrank van der Linden 	return 0;
486623e50fe3SFrank van der Linden }
486723e50fe3SFrank van der Linden 
486823e50fe3SFrank van der Linden static __be32
486923e50fe3SFrank van der Linden nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
487023e50fe3SFrank van der Linden 		      struct nfsd4_getxattr *getxattr)
487123e50fe3SFrank van der Linden {
487223e50fe3SFrank van der Linden 	struct xdr_stream *xdr = &resp->xdr;
487323e50fe3SFrank van der Linden 	__be32 *p, err;
487423e50fe3SFrank van der Linden 
487523e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 4);
487623e50fe3SFrank van der Linden 	if (!p)
487723e50fe3SFrank van der Linden 		return nfserr_resource;
487823e50fe3SFrank van der Linden 
487923e50fe3SFrank van der Linden 	*p = cpu_to_be32(getxattr->getxa_len);
488023e50fe3SFrank van der Linden 
488123e50fe3SFrank van der Linden 	if (getxattr->getxa_len == 0)
488223e50fe3SFrank van der Linden 		return 0;
488323e50fe3SFrank van der Linden 
488423e50fe3SFrank van der Linden 	err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
488523e50fe3SFrank van der Linden 				    getxattr->getxa_len);
488623e50fe3SFrank van der Linden 
488723e50fe3SFrank van der Linden 	kvfree(getxattr->getxa_buf);
488823e50fe3SFrank van der Linden 
488923e50fe3SFrank van der Linden 	return err;
489023e50fe3SFrank van der Linden }
489123e50fe3SFrank van der Linden 
489223e50fe3SFrank van der Linden static __be32
489323e50fe3SFrank van der Linden nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
489423e50fe3SFrank van der Linden 		      struct nfsd4_setxattr *setxattr)
489523e50fe3SFrank van der Linden {
489623e50fe3SFrank van der Linden 	struct xdr_stream *xdr = &resp->xdr;
489723e50fe3SFrank van der Linden 	__be32 *p;
489823e50fe3SFrank van der Linden 
489923e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 20);
490023e50fe3SFrank van der Linden 	if (!p)
490123e50fe3SFrank van der Linden 		return nfserr_resource;
490223e50fe3SFrank van der Linden 
490323e50fe3SFrank van der Linden 	encode_cinfo(p, &setxattr->setxa_cinfo);
490423e50fe3SFrank van der Linden 
490523e50fe3SFrank van der Linden 	return 0;
490623e50fe3SFrank van der Linden }
490723e50fe3SFrank van der Linden 
490823e50fe3SFrank van der Linden /*
490923e50fe3SFrank van der Linden  * See if there are cookie values that can be rejected outright.
491023e50fe3SFrank van der Linden  */
491123e50fe3SFrank van der Linden static __be32
491223e50fe3SFrank van der Linden nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
491323e50fe3SFrank van der Linden 				u32 *offsetp)
491423e50fe3SFrank van der Linden {
491523e50fe3SFrank van der Linden 	u64 cookie = listxattrs->lsxa_cookie;
491623e50fe3SFrank van der Linden 
491723e50fe3SFrank van der Linden 	/*
491823e50fe3SFrank van der Linden 	 * If the cookie is larger than the maximum number we can fit
491923e50fe3SFrank van der Linden 	 * in either the buffer we just got back from vfs_listxattr, or,
492023e50fe3SFrank van der Linden 	 * XDR-encoded, in the return buffer, it's invalid.
492123e50fe3SFrank van der Linden 	 */
492223e50fe3SFrank van der Linden 	if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
492323e50fe3SFrank van der Linden 		return nfserr_badcookie;
492423e50fe3SFrank van der Linden 
492523e50fe3SFrank van der Linden 	if (cookie > (listxattrs->lsxa_maxcount /
492623e50fe3SFrank van der Linden 		      (XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4)))
492723e50fe3SFrank van der Linden 		return nfserr_badcookie;
492823e50fe3SFrank van der Linden 
492923e50fe3SFrank van der Linden 	*offsetp = (u32)cookie;
493023e50fe3SFrank van der Linden 	return 0;
493123e50fe3SFrank van der Linden }
493223e50fe3SFrank van der Linden 
493323e50fe3SFrank van der Linden static __be32
493423e50fe3SFrank van der Linden nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
493523e50fe3SFrank van der Linden 			struct nfsd4_listxattrs *listxattrs)
493623e50fe3SFrank van der Linden {
493723e50fe3SFrank van der Linden 	struct xdr_stream *xdr = &resp->xdr;
493823e50fe3SFrank van der Linden 	u32 cookie_offset, count_offset, eof;
493923e50fe3SFrank van der Linden 	u32 left, xdrleft, slen, count;
494023e50fe3SFrank van der Linden 	u32 xdrlen, offset;
494123e50fe3SFrank van der Linden 	u64 cookie;
494223e50fe3SFrank van der Linden 	char *sp;
4943b9a49237SChuck Lever 	__be32 status, tmp;
494423e50fe3SFrank van der Linden 	__be32 *p;
494523e50fe3SFrank van der Linden 	u32 nuser;
494623e50fe3SFrank van der Linden 
494723e50fe3SFrank van der Linden 	eof = 1;
494823e50fe3SFrank van der Linden 
494923e50fe3SFrank van der Linden 	status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
495023e50fe3SFrank van der Linden 	if (status)
495123e50fe3SFrank van der Linden 		goto out;
495223e50fe3SFrank van der Linden 
495323e50fe3SFrank van der Linden 	/*
495423e50fe3SFrank van der Linden 	 * Reserve space for the cookie and the name array count. Record
495523e50fe3SFrank van der Linden 	 * the offsets to save them later.
495623e50fe3SFrank van der Linden 	 */
495723e50fe3SFrank van der Linden 	cookie_offset = xdr->buf->len;
495823e50fe3SFrank van der Linden 	count_offset = cookie_offset + 8;
495923e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 12);
496023e50fe3SFrank van der Linden 	if (!p) {
496123e50fe3SFrank van der Linden 		status = nfserr_resource;
496223e50fe3SFrank van der Linden 		goto out;
496323e50fe3SFrank van der Linden 	}
496423e50fe3SFrank van der Linden 
496523e50fe3SFrank van der Linden 	count = 0;
496623e50fe3SFrank van der Linden 	left = listxattrs->lsxa_len;
496723e50fe3SFrank van der Linden 	sp = listxattrs->lsxa_buf;
496823e50fe3SFrank van der Linden 	nuser = 0;
496923e50fe3SFrank van der Linden 
497023e50fe3SFrank van der Linden 	xdrleft = listxattrs->lsxa_maxcount;
497123e50fe3SFrank van der Linden 
497223e50fe3SFrank van der Linden 	while (left > 0 && xdrleft > 0) {
497323e50fe3SFrank van der Linden 		slen = strlen(sp);
497423e50fe3SFrank van der Linden 
497523e50fe3SFrank van der Linden 		/*
49764cce11faSAlex Dewar 		 * Check if this is a "user." attribute, skip it if not.
497723e50fe3SFrank van der Linden 		 */
497823e50fe3SFrank van der Linden 		if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
497923e50fe3SFrank van der Linden 			goto contloop;
498023e50fe3SFrank van der Linden 
498123e50fe3SFrank van der Linden 		slen -= XATTR_USER_PREFIX_LEN;
498223e50fe3SFrank van der Linden 		xdrlen = 4 + ((slen + 3) & ~3);
498323e50fe3SFrank van der Linden 		if (xdrlen > xdrleft) {
498423e50fe3SFrank van der Linden 			if (count == 0) {
498523e50fe3SFrank van der Linden 				/*
498623e50fe3SFrank van der Linden 				 * Can't even fit the first attribute name.
498723e50fe3SFrank van der Linden 				 */
498823e50fe3SFrank van der Linden 				status = nfserr_toosmall;
498923e50fe3SFrank van der Linden 				goto out;
499023e50fe3SFrank van der Linden 			}
499123e50fe3SFrank van der Linden 			eof = 0;
499223e50fe3SFrank van der Linden 			goto wreof;
499323e50fe3SFrank van der Linden 		}
499423e50fe3SFrank van der Linden 
499523e50fe3SFrank van der Linden 		left -= XATTR_USER_PREFIX_LEN;
499623e50fe3SFrank van der Linden 		sp += XATTR_USER_PREFIX_LEN;
499723e50fe3SFrank van der Linden 		if (nuser++ < offset)
499823e50fe3SFrank van der Linden 			goto contloop;
499923e50fe3SFrank van der Linden 
500023e50fe3SFrank van der Linden 
500123e50fe3SFrank van der Linden 		p = xdr_reserve_space(xdr, xdrlen);
500223e50fe3SFrank van der Linden 		if (!p) {
500323e50fe3SFrank van der Linden 			status = nfserr_resource;
500423e50fe3SFrank van der Linden 			goto out;
500523e50fe3SFrank van der Linden 		}
500623e50fe3SFrank van der Linden 
5007e2a1840eSAlex Dewar 		xdr_encode_opaque(p, sp, slen);
500823e50fe3SFrank van der Linden 
500923e50fe3SFrank van der Linden 		xdrleft -= xdrlen;
501023e50fe3SFrank van der Linden 		count++;
501123e50fe3SFrank van der Linden contloop:
501223e50fe3SFrank van der Linden 		sp += slen + 1;
501323e50fe3SFrank van der Linden 		left -= slen + 1;
501423e50fe3SFrank van der Linden 	}
501523e50fe3SFrank van der Linden 
501623e50fe3SFrank van der Linden 	/*
501723e50fe3SFrank van der Linden 	 * If there were user attributes to copy, but we didn't copy
501823e50fe3SFrank van der Linden 	 * any, the offset was too large (e.g. the cookie was invalid).
501923e50fe3SFrank van der Linden 	 */
502023e50fe3SFrank van der Linden 	if (nuser > 0 && count == 0) {
502123e50fe3SFrank van der Linden 		status = nfserr_badcookie;
502223e50fe3SFrank van der Linden 		goto out;
502323e50fe3SFrank van der Linden 	}
502423e50fe3SFrank van der Linden 
502523e50fe3SFrank van der Linden wreof:
502623e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 4);
502723e50fe3SFrank van der Linden 	if (!p) {
502823e50fe3SFrank van der Linden 		status = nfserr_resource;
502923e50fe3SFrank van der Linden 		goto out;
503023e50fe3SFrank van der Linden 	}
503123e50fe3SFrank van der Linden 	*p = cpu_to_be32(eof);
503223e50fe3SFrank van der Linden 
503323e50fe3SFrank van der Linden 	cookie = offset + count;
503423e50fe3SFrank van der Linden 
503523e50fe3SFrank van der Linden 	write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8);
5036b9a49237SChuck Lever 	tmp = cpu_to_be32(count);
5037b9a49237SChuck Lever 	write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4);
503823e50fe3SFrank van der Linden out:
503923e50fe3SFrank van der Linden 	if (listxattrs->lsxa_len)
504023e50fe3SFrank van der Linden 		kvfree(listxattrs->lsxa_buf);
504123e50fe3SFrank van der Linden 	return status;
504223e50fe3SFrank van der Linden }
504323e50fe3SFrank van der Linden 
504423e50fe3SFrank van der Linden static __be32
504523e50fe3SFrank van der Linden nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
504623e50fe3SFrank van der Linden 			 struct nfsd4_removexattr *removexattr)
504723e50fe3SFrank van der Linden {
504823e50fe3SFrank van der Linden 	struct xdr_stream *xdr = &resp->xdr;
504923e50fe3SFrank van der Linden 	__be32 *p;
505023e50fe3SFrank van der Linden 
505123e50fe3SFrank van der Linden 	p = xdr_reserve_space(xdr, 20);
505223e50fe3SFrank van der Linden 	if (!p)
505323e50fe3SFrank van der Linden 		return nfserr_resource;
505423e50fe3SFrank van der Linden 
505523e50fe3SFrank van der Linden 	p = encode_cinfo(p, &removexattr->rmxa_cinfo);
505623e50fe3SFrank van der Linden 	return 0;
505723e50fe3SFrank van der Linden }
505823e50fe3SFrank van der Linden 
5059695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
5060695e12f8SBenny Halevy 
50612db134ebSAndy Adamson /*
50622db134ebSAndy Adamson  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
50632db134ebSAndy Adamson  * since we don't need to filter out obsolete ops as this is
50642db134ebSAndy Adamson  * done in the decoding phase.
50652db134ebSAndy Adamson  */
5066c1df609dSChuck Lever static const nfsd4_enc nfsd4_enc_ops[] = {
5067ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_enc)nfsd4_encode_access,
5068ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_enc)nfsd4_encode_close,
5069ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_enc)nfsd4_encode_commit,
5070ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_enc)nfsd4_encode_create,
5071ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_enc)nfsd4_encode_noop,
5072ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
5073ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_enc)nfsd4_encode_getattr,
5074ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_enc)nfsd4_encode_getfh,
5075ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_enc)nfsd4_encode_link,
5076ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_enc)nfsd4_encode_lock,
5077ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_enc)nfsd4_encode_lockt,
5078ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_enc)nfsd4_encode_locku,
5079ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_enc)nfsd4_encode_noop,
5080ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_enc)nfsd4_encode_noop,
5081ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
5082ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_enc)nfsd4_encode_open,
508384f09f46SBenny Halevy 	[OP_OPENATTR]		= (nfsd4_enc)nfsd4_encode_noop,
5084ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_enc)nfsd4_encode_open_confirm,
5085ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_enc)nfsd4_encode_open_downgrade,
5086ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_enc)nfsd4_encode_noop,
5087ad1060c8SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_enc)nfsd4_encode_noop,
5088ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_enc)nfsd4_encode_noop,
5089ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_enc)nfsd4_encode_read,
5090ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_enc)nfsd4_encode_readdir,
5091ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_enc)nfsd4_encode_readlink,
5092ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_enc)nfsd4_encode_remove,
5093ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_enc)nfsd4_encode_rename,
5094ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_enc)nfsd4_encode_noop,
5095ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_enc)nfsd4_encode_noop,
5096ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_enc)nfsd4_encode_noop,
5097ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_enc)nfsd4_encode_secinfo,
5098ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_enc)nfsd4_encode_setattr,
5099ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_enc)nfsd4_encode_setclientid,
5100ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop,
5101ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
5102ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_enc)nfsd4_encode_write,
5103ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_enc)nfsd4_encode_noop,
51042db134ebSAndy Adamson 
51052db134ebSAndy Adamson 	/* NFSv4.1 operations */
51062db134ebSAndy Adamson 	[OP_BACKCHANNEL_CTL]	= (nfsd4_enc)nfsd4_encode_noop,
51071d1bc8f2SJ. Bruce Fields 	[OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session,
51082db134ebSAndy Adamson 	[OP_EXCHANGE_ID]	= (nfsd4_enc)nfsd4_encode_exchange_id,
51092db134ebSAndy Adamson 	[OP_CREATE_SESSION]	= (nfsd4_enc)nfsd4_encode_create_session,
511043212cc7SKinglong Mee 	[OP_DESTROY_SESSION]	= (nfsd4_enc)nfsd4_encode_noop,
511143212cc7SKinglong Mee 	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
51122db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
51139cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
51149cf514ccSChristoph Hellwig 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_getdeviceinfo,
51159cf514ccSChristoph Hellwig 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
51169cf514ccSChristoph Hellwig 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_layoutcommit,
51179cf514ccSChristoph Hellwig 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_layoutget,
51189cf514ccSChristoph Hellwig 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_layoutreturn,
51199cf514ccSChristoph Hellwig #else
51202db134ebSAndy Adamson 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_noop,
51212db134ebSAndy Adamson 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
51222db134ebSAndy Adamson 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_noop,
51232db134ebSAndy Adamson 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_noop,
51242db134ebSAndy Adamson 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
51259cf514ccSChristoph Hellwig #endif
512622b6dee8SMi Jinlong 	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_secinfo_no_name,
51272db134ebSAndy Adamson 	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
51282db134ebSAndy Adamson 	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
512917456804SBryan Schumaker 	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_test_stateid,
51302db134ebSAndy Adamson 	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
51312db134ebSAndy Adamson 	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
51322db134ebSAndy Adamson 	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
513387a15a80SAnna Schumaker 
513487a15a80SAnna Schumaker 	/* NFSv4.2 operations */
513587a15a80SAnna Schumaker 	[OP_ALLOCATE]		= (nfsd4_enc)nfsd4_encode_noop,
513629ae7f9dSAnna Schumaker 	[OP_COPY]		= (nfsd4_enc)nfsd4_encode_copy,
513751911868SOlga Kornievskaia 	[OP_COPY_NOTIFY]	= (nfsd4_enc)nfsd4_encode_copy_notify,
513887a15a80SAnna Schumaker 	[OP_DEALLOCATE]		= (nfsd4_enc)nfsd4_encode_noop,
513987a15a80SAnna Schumaker 	[OP_IO_ADVISE]		= (nfsd4_enc)nfsd4_encode_noop,
514087a15a80SAnna Schumaker 	[OP_LAYOUTERROR]	= (nfsd4_enc)nfsd4_encode_noop,
514187a15a80SAnna Schumaker 	[OP_LAYOUTSTATS]	= (nfsd4_enc)nfsd4_encode_noop,
514287a15a80SAnna Schumaker 	[OP_OFFLOAD_CANCEL]	= (nfsd4_enc)nfsd4_encode_noop,
51436308bc98SOlga Kornievskaia 	[OP_OFFLOAD_STATUS]	= (nfsd4_enc)nfsd4_encode_offload_status,
5144528b8493SAnna Schumaker 	[OP_READ_PLUS]		= (nfsd4_enc)nfsd4_encode_read_plus,
514524bab491SAnna Schumaker 	[OP_SEEK]		= (nfsd4_enc)nfsd4_encode_seek,
514687a15a80SAnna Schumaker 	[OP_WRITE_SAME]		= (nfsd4_enc)nfsd4_encode_noop,
5147ffa0160aSChristoph Hellwig 	[OP_CLONE]		= (nfsd4_enc)nfsd4_encode_noop,
514823e50fe3SFrank van der Linden 
514923e50fe3SFrank van der Linden 	/* RFC 8276 extended atributes operations */
515023e50fe3SFrank van der Linden 	[OP_GETXATTR]		= (nfsd4_enc)nfsd4_encode_getxattr,
515123e50fe3SFrank van der Linden 	[OP_SETXATTR]		= (nfsd4_enc)nfsd4_encode_setxattr,
515223e50fe3SFrank van der Linden 	[OP_LISTXATTRS]		= (nfsd4_enc)nfsd4_encode_listxattrs,
515323e50fe3SFrank van der Linden 	[OP_REMOVEXATTR]	= (nfsd4_enc)nfsd4_encode_removexattr,
5154695e12f8SBenny Halevy };
5155695e12f8SBenny Halevy 
5156496c262cSAndy Adamson /*
5157a8095f7eSJ. Bruce Fields  * Calculate whether we still have space to encode repsize bytes.
5158a8095f7eSJ. Bruce Fields  * There are two considerations:
5159a8095f7eSJ. Bruce Fields  *     - For NFS versions >=4.1, the size of the reply must stay within
5160a8095f7eSJ. Bruce Fields  *       session limits
5161a8095f7eSJ. Bruce Fields  *     - For all NFS versions, we must stay within limited preallocated
5162a8095f7eSJ. Bruce Fields  *       buffer space.
5163496c262cSAndy Adamson  *
5164a8095f7eSJ. Bruce Fields  * This is called before the operation is processed, so can only provide
5165a8095f7eSJ. Bruce Fields  * an upper estimate.  For some nonidempotent operations (such as
5166a8095f7eSJ. Bruce Fields  * getattr), it's not necessarily a problem if that estimate is wrong,
5167a8095f7eSJ. Bruce Fields  * as we can fail it after processing without significant side effects.
5168496c262cSAndy Adamson  */
5169a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
5170496c262cSAndy Adamson {
517167492c99SJ. Bruce Fields 	struct xdr_buf *buf = &resp->rqstp->rq_res;
5172a8095f7eSJ. Bruce Fields 	struct nfsd4_slot *slot = resp->cstate.slot;
5173496c262cSAndy Adamson 
517447ee5298SJ. Bruce Fields 	if (buf->len + respsize <= buf->buflen)
517547ee5298SJ. Bruce Fields 		return nfs_ok;
517647ee5298SJ. Bruce Fields 	if (!nfsd4_has_session(&resp->cstate))
517747ee5298SJ. Bruce Fields 		return nfserr_resource;
517847ee5298SJ. Bruce Fields 	if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
517947ee5298SJ. Bruce Fields 		WARN_ON_ONCE(1);
5180496c262cSAndy Adamson 		return nfserr_rep_too_big_to_cache;
5181ea8d7720SJ. Bruce Fields 	}
518247ee5298SJ. Bruce Fields 	return nfserr_rep_too_big;
5183496c262cSAndy Adamson }
5184496c262cSAndy Adamson 
51851da177e4SLinus Torvalds void
51861da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
51871da177e4SLinus Torvalds {
5188082d4bd7SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
51899411b1d4SJ. Bruce Fields 	struct nfs4_stateowner *so = resp->cstate.replay_owner;
51905f4ab945SJ. Bruce Fields 	struct svc_rqst *rqstp = resp->rqstp;
519134b1744cSJ. Bruce Fields 	const struct nfsd4_operation *opdesc = op->opdesc;
5192082d4bd7SJ. Bruce Fields 	int post_err_offset;
519307d1f802SJ. Bruce Fields 	nfsd4_enc encoder;
5194bc749ca4SJ. Bruce Fields 	__be32 *p;
51951da177e4SLinus Torvalds 
5196d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
5197d0a381ddSJ. Bruce Fields 	if (!p) {
5198d0a381ddSJ. Bruce Fields 		WARN_ON_ONCE(1);
5199d0a381ddSJ. Bruce Fields 		return;
5200d0a381ddSJ. Bruce Fields 	}
5201c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(op->opnum);
5202082d4bd7SJ. Bruce Fields 	post_err_offset = xdr->buf->len;
52031da177e4SLinus Torvalds 
5204695e12f8SBenny Halevy 	if (op->opnum == OP_ILLEGAL)
5205695e12f8SBenny Halevy 		goto status;
5206b7571e4cSJ. Bruce Fields 	if (op->status && opdesc &&
5207b7571e4cSJ. Bruce Fields 			!(opdesc->op_flags & OP_NONTRIVIAL_ERROR_ENCODE))
5208b7571e4cSJ. Bruce Fields 		goto status;
5209695e12f8SBenny Halevy 	BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
5210695e12f8SBenny Halevy 	       !nfsd4_enc_ops[op->opnum]);
521107d1f802SJ. Bruce Fields 	encoder = nfsd4_enc_ops[op->opnum];
521207d1f802SJ. Bruce Fields 	op->status = encoder(resp, op->status, &op->u);
5213*08281341SChuck Lever 	if (op->status)
5214*08281341SChuck Lever 		trace_nfsd_compound_encode_err(rqstp, op->opnum, op->status);
521534b1744cSJ. Bruce Fields 	if (opdesc && opdesc->op_release)
521634b1744cSJ. Bruce Fields 		opdesc->op_release(&op->u);
52172825a7f9SJ. Bruce Fields 	xdr_commit_encode(xdr);
52182825a7f9SJ. Bruce Fields 
5219067e1aceSJ. Bruce Fields 	/* nfsd4_check_resp_size guarantees enough room for error status */
52205f4ab945SJ. Bruce Fields 	if (!op->status) {
52215f4ab945SJ. Bruce Fields 		int space_needed = 0;
52225f4ab945SJ. Bruce Fields 		if (!nfsd4_last_compound_op(rqstp))
52235f4ab945SJ. Bruce Fields 			space_needed = COMPOUND_ERR_SLACK_SPACE;
52245f4ab945SJ. Bruce Fields 		op->status = nfsd4_check_resp_size(resp, space_needed);
52255f4ab945SJ. Bruce Fields 	}
5226c8f13d97SJ. Bruce Fields 	if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
5227c8f13d97SJ. Bruce Fields 		struct nfsd4_slot *slot = resp->cstate.slot;
5228c8f13d97SJ. Bruce Fields 
5229c8f13d97SJ. Bruce Fields 		if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
5230c8f13d97SJ. Bruce Fields 			op->status = nfserr_rep_too_big_to_cache;
5231c8f13d97SJ. Bruce Fields 		else
5232c8f13d97SJ. Bruce Fields 			op->status = nfserr_rep_too_big;
5233c8f13d97SJ. Bruce Fields 	}
523407d1f802SJ. Bruce Fields 	if (op->status == nfserr_resource ||
523507d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big ||
523607d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big_to_cache) {
523707d1f802SJ. Bruce Fields 		/*
523807d1f802SJ. Bruce Fields 		 * The operation may have already been encoded or
523907d1f802SJ. Bruce Fields 		 * partially encoded.  No op returns anything additional
524007d1f802SJ. Bruce Fields 		 * in the case of one of these three errors, so we can
524107d1f802SJ. Bruce Fields 		 * just truncate back to after the status.  But it's a
524207d1f802SJ. Bruce Fields 		 * bug if we had to do this on a non-idempotent op:
524307d1f802SJ. Bruce Fields 		 */
524407d1f802SJ. Bruce Fields 		warn_on_nonidempotent_op(op);
5245082d4bd7SJ. Bruce Fields 		xdr_truncate_encode(xdr, post_err_offset);
524607d1f802SJ. Bruce Fields 	}
52479411b1d4SJ. Bruce Fields 	if (so) {
5248082d4bd7SJ. Bruce Fields 		int len = xdr->buf->len - post_err_offset;
5249082d4bd7SJ. Bruce Fields 
52509411b1d4SJ. Bruce Fields 		so->so_replay.rp_status = op->status;
5251082d4bd7SJ. Bruce Fields 		so->so_replay.rp_buflen = len;
5252082d4bd7SJ. Bruce Fields 		read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
5253082d4bd7SJ. Bruce Fields 						so->so_replay.rp_buf, len);
52549411b1d4SJ. Bruce Fields 	}
5255695e12f8SBenny Halevy status:
5256082d4bd7SJ. Bruce Fields 	/* Note that op->status is already in network byte order: */
5257082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, post_err_offset - 4, &op->status, 4);
52581da177e4SLinus Torvalds }
52591da177e4SLinus Torvalds 
52601da177e4SLinus Torvalds /*
52611da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
52621da177e4SLinus Torvalds  *
52631da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
52641da177e4SLinus Torvalds  * previously sent already encoded operation.
52651da177e4SLinus Torvalds  */
52661da177e4SLinus Torvalds void
5267d0a381ddSJ. Bruce Fields nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
52681da177e4SLinus Torvalds {
5269bc749ca4SJ. Bruce Fields 	__be32 *p;
52701da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
52711da177e4SLinus Torvalds 
5272d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
5273d0a381ddSJ. Bruce Fields 	if (!p) {
5274d0a381ddSJ. Bruce Fields 		WARN_ON_ONCE(1);
5275d0a381ddSJ. Bruce Fields 		return;
5276d0a381ddSJ. Bruce Fields 	}
5277c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(op->opnum);
52781da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
52791da177e4SLinus Torvalds 
52800c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen);
52811da177e4SLinus Torvalds }
52821da177e4SLinus Torvalds 
52838537488bSChristoph Hellwig void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
52841da177e4SLinus Torvalds {
52853e98abffSJ. Bruce Fields 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
52863e98abffSJ. Bruce Fields 
52871da177e4SLinus Torvalds 	if (args->ops != args->iops) {
52881da177e4SLinus Torvalds 		kfree(args->ops);
52891da177e4SLinus Torvalds 		args->ops = args->iops;
52901da177e4SLinus Torvalds 	}
52911da177e4SLinus Torvalds 	kfree(args->tmpp);
52921da177e4SLinus Torvalds 	args->tmpp = NULL;
52931da177e4SLinus Torvalds 	while (args->to_free) {
5294d5e23383SJ. Bruce Fields 		struct svcxdr_tmpbuf *tb = args->to_free;
52951da177e4SLinus Torvalds 		args->to_free = tb->next;
52961da177e4SLinus Torvalds 		kfree(tb);
52971da177e4SLinus Torvalds 	}
52981da177e4SLinus Torvalds }
52991da177e4SLinus Torvalds 
53001da177e4SLinus Torvalds int
5301026fec7eSChristoph Hellwig nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p)
53021da177e4SLinus Torvalds {
5303026fec7eSChristoph Hellwig 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
5304026fec7eSChristoph Hellwig 
5305e874f9f8SJeff Layton 	if (rqstp->rq_arg.head[0].iov_len % 4) {
5306e874f9f8SJeff Layton 		/* client is nuts */
5307e874f9f8SJeff Layton 		dprintk("%s: compound not properly padded! (peeraddr=%pISc xid=0x%x)",
5308e874f9f8SJeff Layton 			__func__, svc_addr(rqstp), be32_to_cpu(rqstp->rq_xid));
5309e874f9f8SJeff Layton 		return 0;
5310e874f9f8SJeff Layton 	}
53111da177e4SLinus Torvalds 	args->p = p;
53121da177e4SLinus Torvalds 	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
53131da177e4SLinus Torvalds 	args->pagelist = rqstp->rq_arg.pages;
53141da177e4SLinus Torvalds 	args->pagelen = rqstp->rq_arg.page_len;
5315eae03e2aSChuck Lever 	args->tail = false;
53161da177e4SLinus Torvalds 	args->tmpp = NULL;
53171da177e4SLinus Torvalds 	args->to_free = NULL;
53181da177e4SLinus Torvalds 	args->ops = args->iops;
53191da177e4SLinus Torvalds 	args->rqstp = rqstp;
53201da177e4SLinus Torvalds 
53213e98abffSJ. Bruce Fields 	return !nfsd4_decode_compound(args);
53221da177e4SLinus Torvalds }
53231da177e4SLinus Torvalds 
53241da177e4SLinus Torvalds int
532563f8de37SChristoph Hellwig nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p)
53261da177e4SLinus Torvalds {
532763f8de37SChristoph Hellwig 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
53286ac90391SJ. Bruce Fields 	struct xdr_buf *buf = resp->xdr.buf;
53296ac90391SJ. Bruce Fields 
53306ac90391SJ. Bruce Fields 	WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
53316ac90391SJ. Bruce Fields 				 buf->tail[0].iov_len);
5332dd97fddeSJ. Bruce Fields 
5333cc028a10SChuck Lever 	*p = resp->cstate.status;
5334cc028a10SChuck Lever 
53352825a7f9SJ. Bruce Fields 	rqstp->rq_next_page = resp->xdr.page_ptr + 1;
53362825a7f9SJ. Bruce Fields 
53371da177e4SLinus Torvalds 	p = resp->tagp;
53381da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
53391da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
53401da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
53411da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
53421da177e4SLinus Torvalds 
5343b607664eSTrond Myklebust 	nfsd4_sequence_done(resp);
53441da177e4SLinus Torvalds 	return 1;
53451da177e4SLinus Torvalds }
53461da177e4SLinus Torvalds 
53471da177e4SLinus Torvalds /*
53481da177e4SLinus Torvalds  * Local variables:
53491da177e4SLinus Torvalds  *  c-basic-offset: 8
53501da177e4SLinus Torvalds  * End:
53511da177e4SLinus Torvalds  */
5352