xref: /linux/fs/nfsd/nfs4xdr.c (revision af90f707fa6d54dbb725c4b919c976cd23cd07f2)
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 
365a0e3ad6STejun Heo #include <linux/slab.h>
371da177e4SLinus Torvalds #include <linux/namei.h>
38341eb184SBoaz Harrosh #include <linux/statfs.h>
390733d213SAndy Adamson #include <linux/utsname.h>
4017456804SBryan Schumaker #include <linux/pagemap.h>
414796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h>
429a74af21SBoaz Harrosh 
432ca72e17SJ. Bruce Fields #include "idmap.h"
442ca72e17SJ. Bruce Fields #include "acl.h"
459a74af21SBoaz Harrosh #include "xdr4.h"
460a3adadeSJ. Bruce Fields #include "vfs.h"
4717456804SBryan Schumaker #include "state.h"
481091006cSJ. Bruce Fields #include "cache.h"
493d733711SStanislav Kinsbursky #include "netns.h"
509cf514ccSChristoph Hellwig #include "pnfs.h"
512ca72e17SJ. Bruce Fields 
5218032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
5318032ca0SDavid Quigley #include <linux/security.h>
5418032ca0SDavid Quigley #endif
5518032ca0SDavid Quigley 
5618032ca0SDavid Quigley 
571da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
581da177e4SLinus Torvalds 
5942ca0993SJ.Bruce Fields /*
6042ca0993SJ.Bruce Fields  * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
6142ca0993SJ.Bruce Fields  * directory in order to indicate to the client that a filesystem boundary is present
6242ca0993SJ.Bruce Fields  * We use a fixed fsid for a referral
6342ca0993SJ.Bruce Fields  */
6442ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
6542ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL
6642ca0993SJ.Bruce Fields 
67b37ad28bSAl Viro static __be32
68a36b1725SJ. Bruce Fields check_filename(char *str, int len)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	int i;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	if (len == 0)
731da177e4SLinus Torvalds 		return nfserr_inval;
741da177e4SLinus Torvalds 	if (isdotent(str, len))
75a36b1725SJ. Bruce Fields 		return nfserr_badname;
761da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
771da177e4SLinus Torvalds 		if (str[i] == '/')
78a36b1725SJ. Bruce Fields 			return nfserr_badname;
791da177e4SLinus Torvalds 	return 0;
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds #define DECODE_HEAD				\
832ebbc012SAl Viro 	__be32 *p;				\
84b37ad28bSAl Viro 	__be32 status
851da177e4SLinus Torvalds #define DECODE_TAIL				\
861da177e4SLinus Torvalds 	status = 0;				\
871da177e4SLinus Torvalds out:						\
881da177e4SLinus Torvalds 	return status;				\
891da177e4SLinus Torvalds xdr_error:					\
90817cb9d4SChuck Lever 	dprintk("NFSD: xdr error (%s:%d)\n",	\
91817cb9d4SChuck Lever 			__FILE__, __LINE__);	\
921da177e4SLinus Torvalds 	status = nfserr_bad_xdr;		\
931da177e4SLinus Torvalds 	goto out
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds #define READMEM(x,nbytes) do {			\
961da177e4SLinus Torvalds 	x = (char *)p;				\
971da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
981da177e4SLinus Torvalds } while (0)
991da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do {			\
1001da177e4SLinus Torvalds 	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
1011da177e4SLinus Torvalds  		savemem(argp, p, nbytes) :	\
1021da177e4SLinus Torvalds  		(char *)p)) {			\
103817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
104817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1051da177e4SLinus Torvalds 		goto xdr_error;			\
1061da177e4SLinus Torvalds 		}				\
1071da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1081da177e4SLinus Torvalds } while (0)
1091da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do {			\
1101da177e4SLinus Torvalds 	memcpy((x), p, nbytes);			\
1111da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1121da177e4SLinus Torvalds } while (0)
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
1151da177e4SLinus Torvalds #define READ_BUF(nbytes)  do {			\
1161da177e4SLinus Torvalds 	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
1171da177e4SLinus Torvalds 		p = argp->p;			\
1181da177e4SLinus Torvalds 		argp->p += XDR_QUADLEN(nbytes);	\
1191da177e4SLinus Torvalds 	} else if (!(p = read_buf(argp, nbytes))) { \
120817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
121817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1221da177e4SLinus Torvalds 		goto xdr_error;			\
1231da177e4SLinus Torvalds 	}					\
1241da177e4SLinus Torvalds } while (0)
1251da177e4SLinus Torvalds 
126590b7431SJ. Bruce Fields static void next_decode_page(struct nfsd4_compoundargs *argp)
127590b7431SJ. Bruce Fields {
128590b7431SJ. Bruce Fields 	argp->p = page_address(argp->pagelist[0]);
129365da4adSJ. Bruce Fields 	argp->pagelist++;
130590b7431SJ. Bruce Fields 	if (argp->pagelen < PAGE_SIZE) {
131590b7431SJ. Bruce Fields 		argp->end = argp->p + (argp->pagelen>>2);
132590b7431SJ. Bruce Fields 		argp->pagelen = 0;
133590b7431SJ. Bruce Fields 	} else {
134590b7431SJ. Bruce Fields 		argp->end = argp->p + (PAGE_SIZE>>2);
135590b7431SJ. Bruce Fields 		argp->pagelen -= PAGE_SIZE;
136590b7431SJ. Bruce Fields 	}
137590b7431SJ. Bruce Fields }
138590b7431SJ. Bruce Fields 
139ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	/* We want more bytes than seem to be available.
1421da177e4SLinus Torvalds 	 * Maybe we need a new page, maybe we have just run out
1431da177e4SLinus Torvalds 	 */
144ca2a05aaSJ. Bruce Fields 	unsigned int avail = (char *)argp->end - (char *)argp->p;
1452ebbc012SAl Viro 	__be32 *p;
1461da177e4SLinus Torvalds 	if (avail + argp->pagelen < nbytes)
1471da177e4SLinus Torvalds 		return NULL;
1481da177e4SLinus Torvalds 	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
1491da177e4SLinus Torvalds 		return NULL;
1501da177e4SLinus Torvalds 	/* ok, we can do it with the current plus the next page */
1511da177e4SLinus Torvalds 	if (nbytes <= sizeof(argp->tmp))
1521da177e4SLinus Torvalds 		p = argp->tmp;
1531da177e4SLinus Torvalds 	else {
1541da177e4SLinus Torvalds 		kfree(argp->tmpp);
1551da177e4SLinus Torvalds 		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
1561da177e4SLinus Torvalds 		if (!p)
1571da177e4SLinus Torvalds 			return NULL;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	}
160ca2a05aaSJ. Bruce Fields 	/*
161ca2a05aaSJ. Bruce Fields 	 * The following memcpy is safe because read_buf is always
162ca2a05aaSJ. Bruce Fields 	 * called with nbytes > avail, and the two cases above both
163ca2a05aaSJ. Bruce Fields 	 * guarantee p points to at least nbytes bytes.
164ca2a05aaSJ. Bruce Fields 	 */
1651da177e4SLinus Torvalds 	memcpy(p, argp->p, avail);
166590b7431SJ. Bruce Fields 	next_decode_page(argp);
1671da177e4SLinus Torvalds 	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
1681da177e4SLinus Torvalds 	argp->p += XDR_QUADLEN(nbytes - avail);
1691da177e4SLinus Torvalds 	return p;
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds 
17260adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
17360adfc50SAndy Adamson {
17460adfc50SAndy Adamson 	return (clid->cl_boot == 0) && (clid->cl_id == 0);
17560adfc50SAndy Adamson }
17660adfc50SAndy Adamson 
1772d8498dbSChristoph Hellwig /**
178d5e23383SJ. Bruce Fields  * svcxdr_tmpalloc - allocate memory to be freed after compound processing
179ce043ac8SJ. Bruce Fields  * @argp: NFSv4 compound argument structure
180ce043ac8SJ. Bruce Fields  * @p: pointer to be freed (with kfree())
1812d8498dbSChristoph Hellwig  *
1822d8498dbSChristoph Hellwig  * Marks @p to be freed when processing the compound operation
1832d8498dbSChristoph Hellwig  * described in @argp finishes.
1842d8498dbSChristoph Hellwig  */
185d5e23383SJ. Bruce Fields static void *
186d5e23383SJ. Bruce Fields svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)
1871da177e4SLinus Torvalds {
188d5e23383SJ. Bruce Fields 	struct svcxdr_tmpbuf *tb;
1891da177e4SLinus Torvalds 
190d5e23383SJ. Bruce Fields 	tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL);
1911da177e4SLinus Torvalds 	if (!tb)
192d5e23383SJ. Bruce Fields 		return NULL;
1931da177e4SLinus Torvalds 	tb->next = argp->to_free;
1941da177e4SLinus Torvalds 	argp->to_free = tb;
195d5e23383SJ. Bruce Fields 	return tb->buf;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
19829c353b3SJ. Bruce Fields /*
19929c353b3SJ. Bruce Fields  * For xdr strings that need to be passed to other kernel api's
20029c353b3SJ. Bruce Fields  * as null-terminated strings.
20129c353b3SJ. Bruce Fields  *
20229c353b3SJ. Bruce Fields  * Note null-terminating in place usually isn't safe since the
20329c353b3SJ. Bruce Fields  * buffer might end on a page boundary.
20429c353b3SJ. Bruce Fields  */
20529c353b3SJ. Bruce Fields static char *
20629c353b3SJ. Bruce Fields svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
20729c353b3SJ. Bruce Fields {
208d5e23383SJ. Bruce Fields 	char *p = svcxdr_tmpalloc(argp, len + 1);
20929c353b3SJ. Bruce Fields 
21029c353b3SJ. Bruce Fields 	if (!p)
21129c353b3SJ. Bruce Fields 		return NULL;
21229c353b3SJ. Bruce Fields 	memcpy(p, buf, len);
21329c353b3SJ. Bruce Fields 	p[len] = '\0';
21429c353b3SJ. Bruce Fields 	return p;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2172d8498dbSChristoph Hellwig /**
2182d8498dbSChristoph Hellwig  * savemem - duplicate a chunk of memory for later processing
2192d8498dbSChristoph Hellwig  * @argp: NFSv4 compound argument structure to be freed with
2202d8498dbSChristoph Hellwig  * @p: pointer to be duplicated
2212d8498dbSChristoph Hellwig  * @nbytes: length to be duplicated
2222d8498dbSChristoph Hellwig  *
2232d8498dbSChristoph Hellwig  * Returns a pointer to a copy of @nbytes bytes of memory at @p
2242d8498dbSChristoph Hellwig  * that are preserved until processing of the NFSv4 compound
2252d8498dbSChristoph Hellwig  * operation described by @argp finishes.
2262d8498dbSChristoph Hellwig  */
2272ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
2281da177e4SLinus Torvalds {
229d5e23383SJ. Bruce Fields 	void *ret;
230d5e23383SJ. Bruce Fields 
231d5e23383SJ. Bruce Fields 	ret = svcxdr_tmpalloc(argp, nbytes);
232d5e23383SJ. Bruce Fields 	if (!ret)
233a4db5fe5SJ. Bruce Fields 		return NULL;
234d5e23383SJ. Bruce Fields 	memcpy(ret, p, nbytes);
235d5e23383SJ. Bruce Fields 	return ret;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds 
2384c94e13eSChristoph Hellwig /*
2394c94e13eSChristoph Hellwig  * We require the high 32 bits of 'seconds' to be 0, and
2404c94e13eSChristoph Hellwig  * we ignore all 32 bits of 'nseconds'.
2414c94e13eSChristoph Hellwig  */
2424c94e13eSChristoph Hellwig static __be32
2434c94e13eSChristoph Hellwig nfsd4_decode_time(struct nfsd4_compoundargs *argp, struct timespec *tv)
2444c94e13eSChristoph Hellwig {
2454c94e13eSChristoph Hellwig 	DECODE_HEAD;
2464c94e13eSChristoph Hellwig 	u64 sec;
2474c94e13eSChristoph Hellwig 
2484c94e13eSChristoph Hellwig 	READ_BUF(12);
2494c94e13eSChristoph Hellwig 	p = xdr_decode_hyper(p, &sec);
2504c94e13eSChristoph Hellwig 	tv->tv_sec = sec;
2514c94e13eSChristoph Hellwig 	tv->tv_nsec = be32_to_cpup(p++);
2524c94e13eSChristoph Hellwig 	if (tv->tv_nsec >= (u32)1000000000)
2534c94e13eSChristoph Hellwig 		return nfserr_inval;
2544c94e13eSChristoph Hellwig 
2554c94e13eSChristoph Hellwig 	DECODE_TAIL;
2564c94e13eSChristoph Hellwig }
2574c94e13eSChristoph Hellwig 
258b37ad28bSAl Viro static __be32
2591da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
2601da177e4SLinus Torvalds {
2611da177e4SLinus Torvalds 	u32 bmlen;
2621da177e4SLinus Torvalds 	DECODE_HEAD;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	bmval[0] = 0;
2651da177e4SLinus Torvalds 	bmval[1] = 0;
2667e705706SAndy Adamson 	bmval[2] = 0;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	READ_BUF(4);
26906553991SJ. Bruce Fields 	bmlen = be32_to_cpup(p++);
2701da177e4SLinus Torvalds 	if (bmlen > 1000)
2711da177e4SLinus Torvalds 		goto xdr_error;
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	READ_BUF(bmlen << 2);
2741da177e4SLinus Torvalds 	if (bmlen > 0)
27506553991SJ. Bruce Fields 		bmval[0] = be32_to_cpup(p++);
2761da177e4SLinus Torvalds 	if (bmlen > 1)
27706553991SJ. Bruce Fields 		bmval[1] = be32_to_cpup(p++);
2787e705706SAndy Adamson 	if (bmlen > 2)
27906553991SJ. Bruce Fields 		bmval[2] = be32_to_cpup(p++);
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	DECODE_TAIL;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
284b37ad28bSAl Viro static __be32
2853c8e0316SYu Zhiguo nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
28618032ca0SDavid Quigley 		   struct iattr *iattr, struct nfs4_acl **acl,
28718032ca0SDavid Quigley 		   struct xdr_netobj *label)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	int expected_len, len = 0;
2901da177e4SLinus Torvalds 	u32 dummy32;
2911da177e4SLinus Torvalds 	char *buf;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	DECODE_HEAD;
2941da177e4SLinus Torvalds 	iattr->ia_valid = 0;
2951da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
2961da177e4SLinus Torvalds 		return status;
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	READ_BUF(4);
29906553991SJ. Bruce Fields 	expected_len = be32_to_cpup(p++);
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
3021da177e4SLinus Torvalds 		READ_BUF(8);
3031da177e4SLinus Torvalds 		len += 8;
304542d1ab3SJ. Bruce Fields 		p = xdr_decode_hyper(p, &iattr->ia_size);
3051da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
3061da177e4SLinus Torvalds 	}
3071da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
30864a817cfSJ. Bruce Fields 		u32 nace;
30928e05dd8SJ. Bruce Fields 		struct nfs4_ace *ace;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 		READ_BUF(4); len += 4;
31206553991SJ. Bruce Fields 		nace = be32_to_cpup(p++);
3131da177e4SLinus Torvalds 
31428e05dd8SJ. Bruce Fields 		if (nace > NFS4_ACL_MAX)
315798df338SJ. Bruce Fields 			return nfserr_fbig;
31628e05dd8SJ. Bruce Fields 
317d5e23383SJ. Bruce Fields 		*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
318eba1c99cSKinglong Mee 		if (*acl == NULL)
319eba1c99cSKinglong Mee 			return nfserr_jukebox;
320eba1c99cSKinglong Mee 
32128e05dd8SJ. Bruce Fields 		(*acl)->naces = nace;
32228e05dd8SJ. Bruce Fields 		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
3231da177e4SLinus Torvalds 			READ_BUF(16); len += 16;
32406553991SJ. Bruce Fields 			ace->type = be32_to_cpup(p++);
32506553991SJ. Bruce Fields 			ace->flag = be32_to_cpup(p++);
32606553991SJ. Bruce Fields 			ace->access_mask = be32_to_cpup(p++);
32706553991SJ. Bruce Fields 			dummy32 = be32_to_cpup(p++);
3281da177e4SLinus Torvalds 			READ_BUF(dummy32);
3291da177e4SLinus Torvalds 			len += XDR_QUADLEN(dummy32) << 2;
3301da177e4SLinus Torvalds 			READMEM(buf, dummy32);
33128e05dd8SJ. Bruce Fields 			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
3323c726023SJ. Bruce Fields 			status = nfs_ok;
33328e05dd8SJ. Bruce Fields 			if (ace->whotype != NFS4_ACL_WHO_NAMED)
334ab8e4aeeSEric W. Biederman 				;
33528e05dd8SJ. Bruce Fields 			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
3363c726023SJ. Bruce Fields 				status = nfsd_map_name_to_gid(argp->rqstp,
337ab8e4aeeSEric W. Biederman 						buf, dummy32, &ace->who_gid);
3381da177e4SLinus Torvalds 			else
3393c726023SJ. Bruce Fields 				status = nfsd_map_name_to_uid(argp->rqstp,
340ab8e4aeeSEric W. Biederman 						buf, dummy32, &ace->who_uid);
3413c726023SJ. Bruce Fields 			if (status)
3423c726023SJ. Bruce Fields 				return status;
3431da177e4SLinus Torvalds 		}
3441da177e4SLinus Torvalds 	} else
3451da177e4SLinus Torvalds 		*acl = NULL;
3461da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
3471da177e4SLinus Torvalds 		READ_BUF(4);
3481da177e4SLinus Torvalds 		len += 4;
34906553991SJ. Bruce Fields 		iattr->ia_mode = be32_to_cpup(p++);
3501da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
3511da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
3541da177e4SLinus Torvalds 		READ_BUF(4);
3551da177e4SLinus Torvalds 		len += 4;
35606553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
3571da177e4SLinus Torvalds 		READ_BUF(dummy32);
3581da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3591da177e4SLinus Torvalds 		READMEM(buf, dummy32);
36047c85291SNeilBrown 		if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
36147c85291SNeilBrown 			return status;
3621da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
3631da177e4SLinus Torvalds 	}
3641da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
3651da177e4SLinus Torvalds 		READ_BUF(4);
3661da177e4SLinus Torvalds 		len += 4;
36706553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
3681da177e4SLinus Torvalds 		READ_BUF(dummy32);
3691da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3701da177e4SLinus Torvalds 		READMEM(buf, dummy32);
37147c85291SNeilBrown 		if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
37247c85291SNeilBrown 			return status;
3731da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
3741da177e4SLinus Torvalds 	}
3751da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
3761da177e4SLinus Torvalds 		READ_BUF(4);
3771da177e4SLinus Torvalds 		len += 4;
37806553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
3791da177e4SLinus Torvalds 		switch (dummy32) {
3801da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3811da177e4SLinus Torvalds 			len += 12;
3824c94e13eSChristoph Hellwig 			status = nfsd4_decode_time(argp, &iattr->ia_atime);
3834c94e13eSChristoph Hellwig 			if (status)
3844c94e13eSChristoph Hellwig 				return status;
3851da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
3861da177e4SLinus Torvalds 			break;
3871da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3881da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
3891da177e4SLinus Torvalds 			break;
3901da177e4SLinus Torvalds 		default:
3911da177e4SLinus Torvalds 			goto xdr_error;
3921da177e4SLinus Torvalds 		}
3931da177e4SLinus Torvalds 	}
3941da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
3951da177e4SLinus Torvalds 		READ_BUF(4);
3961da177e4SLinus Torvalds 		len += 4;
39706553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
3981da177e4SLinus Torvalds 		switch (dummy32) {
3991da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
4001da177e4SLinus Torvalds 			len += 12;
4014c94e13eSChristoph Hellwig 			status = nfsd4_decode_time(argp, &iattr->ia_mtime);
4024c94e13eSChristoph Hellwig 			if (status)
4034c94e13eSChristoph Hellwig 				return status;
4041da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
4051da177e4SLinus Torvalds 			break;
4061da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
4071da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
4081da177e4SLinus Torvalds 			break;
4091da177e4SLinus Torvalds 		default:
4101da177e4SLinus Torvalds 			goto xdr_error;
4111da177e4SLinus Torvalds 		}
4121da177e4SLinus Torvalds 	}
41318032ca0SDavid Quigley 
41418032ca0SDavid Quigley 	label->len = 0;
41518032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
41618032ca0SDavid Quigley 	if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
41718032ca0SDavid Quigley 		READ_BUF(4);
41818032ca0SDavid Quigley 		len += 4;
41906553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++); /* lfs: we don't use it */
42018032ca0SDavid Quigley 		READ_BUF(4);
42118032ca0SDavid Quigley 		len += 4;
42206553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++); /* pi: we don't use it either */
42318032ca0SDavid Quigley 		READ_BUF(4);
42418032ca0SDavid Quigley 		len += 4;
42506553991SJ. Bruce Fields 		dummy32 = be32_to_cpup(p++);
42618032ca0SDavid Quigley 		READ_BUF(dummy32);
4271ec8c0c4SKinglong Mee 		if (dummy32 > NFS4_MAXLABELLEN)
42818032ca0SDavid Quigley 			return nfserr_badlabel;
42918032ca0SDavid Quigley 		len += (XDR_QUADLEN(dummy32) << 2);
43018032ca0SDavid Quigley 		READMEM(buf, dummy32);
43129c353b3SJ. Bruce Fields 		label->len = dummy32;
43229c353b3SJ. Bruce Fields 		label->data = svcxdr_dupstr(argp, buf, dummy32);
43318032ca0SDavid Quigley 		if (!label->data)
43418032ca0SDavid Quigley 			return nfserr_jukebox;
43518032ca0SDavid Quigley 	}
43618032ca0SDavid Quigley #endif
43718032ca0SDavid Quigley 
4383c8e0316SYu Zhiguo 	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
4393c8e0316SYu Zhiguo 	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
4403c8e0316SYu Zhiguo 	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
4413c8e0316SYu Zhiguo 		READ_BUF(expected_len - len);
4423c8e0316SYu Zhiguo 	else if (len != expected_len)
4431da177e4SLinus Torvalds 		goto xdr_error;
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	DECODE_TAIL;
4461da177e4SLinus Torvalds }
4471da177e4SLinus Torvalds 
448b37ad28bSAl Viro static __be32
449e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
450e31a1b66SBenny Halevy {
451e31a1b66SBenny Halevy 	DECODE_HEAD;
452e31a1b66SBenny Halevy 
453e31a1b66SBenny Halevy 	READ_BUF(sizeof(stateid_t));
45406553991SJ. Bruce Fields 	sid->si_generation = be32_to_cpup(p++);
455e31a1b66SBenny Halevy 	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
456e31a1b66SBenny Halevy 
457e31a1b66SBenny Halevy 	DECODE_TAIL;
458e31a1b66SBenny Halevy }
459e31a1b66SBenny Halevy 
460e31a1b66SBenny Halevy static __be32
4611da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
4621da177e4SLinus Torvalds {
4631da177e4SLinus Torvalds 	DECODE_HEAD;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	READ_BUF(4);
46606553991SJ. Bruce Fields 	access->ac_req_access = be32_to_cpup(p++);
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	DECODE_TAIL;
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
471acb2887eSJ. Bruce Fields static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
472acb2887eSJ. Bruce Fields {
473acb2887eSJ. Bruce Fields 	DECODE_HEAD;
47412fc3e92SJ. Bruce Fields 	u32 dummy, uid, gid;
475acb2887eSJ. Bruce Fields 	char *machine_name;
476acb2887eSJ. Bruce Fields 	int i;
477acb2887eSJ. Bruce Fields 	int nr_secflavs;
478acb2887eSJ. Bruce Fields 
479acb2887eSJ. Bruce Fields 	/* callback_sec_params4 */
480acb2887eSJ. Bruce Fields 	READ_BUF(4);
48106553991SJ. Bruce Fields 	nr_secflavs = be32_to_cpup(p++);
48257569a70SJ. Bruce Fields 	if (nr_secflavs)
48312fc3e92SJ. Bruce Fields 		cbs->flavor = (u32)(-1);
48457569a70SJ. Bruce Fields 	else
48557569a70SJ. Bruce Fields 		/* Is this legal? Be generous, take it to mean AUTH_NONE: */
48657569a70SJ. Bruce Fields 		cbs->flavor = 0;
487acb2887eSJ. Bruce Fields 	for (i = 0; i < nr_secflavs; ++i) {
488acb2887eSJ. Bruce Fields 		READ_BUF(4);
48906553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
490acb2887eSJ. Bruce Fields 		switch (dummy) {
491acb2887eSJ. Bruce Fields 		case RPC_AUTH_NULL:
492acb2887eSJ. Bruce Fields 			/* Nothing to read */
49312fc3e92SJ. Bruce Fields 			if (cbs->flavor == (u32)(-1))
49412fc3e92SJ. Bruce Fields 				cbs->flavor = RPC_AUTH_NULL;
495acb2887eSJ. Bruce Fields 			break;
496acb2887eSJ. Bruce Fields 		case RPC_AUTH_UNIX:
497acb2887eSJ. Bruce Fields 			READ_BUF(8);
498acb2887eSJ. Bruce Fields 			/* stamp */
49906553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
500acb2887eSJ. Bruce Fields 
501acb2887eSJ. Bruce Fields 			/* machine name */
50206553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
503acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
504acb2887eSJ. Bruce Fields 			SAVEMEM(machine_name, dummy);
505acb2887eSJ. Bruce Fields 
506acb2887eSJ. Bruce Fields 			/* uid, gid */
507acb2887eSJ. Bruce Fields 			READ_BUF(8);
50806553991SJ. Bruce Fields 			uid = be32_to_cpup(p++);
50906553991SJ. Bruce Fields 			gid = be32_to_cpup(p++);
510acb2887eSJ. Bruce Fields 
511acb2887eSJ. Bruce Fields 			/* more gids */
512acb2887eSJ. Bruce Fields 			READ_BUF(4);
51306553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
514acb2887eSJ. Bruce Fields 			READ_BUF(dummy * 4);
51512fc3e92SJ. Bruce Fields 			if (cbs->flavor == (u32)(-1)) {
51603bc6d1cSEric W. Biederman 				kuid_t kuid = make_kuid(&init_user_ns, uid);
51703bc6d1cSEric W. Biederman 				kgid_t kgid = make_kgid(&init_user_ns, gid);
51803bc6d1cSEric W. Biederman 				if (uid_valid(kuid) && gid_valid(kgid)) {
51903bc6d1cSEric W. Biederman 					cbs->uid = kuid;
52003bc6d1cSEric W. Biederman 					cbs->gid = kgid;
52112fc3e92SJ. Bruce Fields 					cbs->flavor = RPC_AUTH_UNIX;
52203bc6d1cSEric W. Biederman 				} else {
52303bc6d1cSEric W. Biederman 					dprintk("RPC_AUTH_UNIX with invalid"
52403bc6d1cSEric W. Biederman 						"uid or gid ignoring!\n");
52503bc6d1cSEric W. Biederman 				}
52612fc3e92SJ. Bruce Fields 			}
527acb2887eSJ. Bruce Fields 			break;
528acb2887eSJ. Bruce Fields 		case RPC_AUTH_GSS:
529acb2887eSJ. Bruce Fields 			dprintk("RPC_AUTH_GSS callback secflavor "
530acb2887eSJ. Bruce Fields 				"not supported!\n");
531acb2887eSJ. Bruce Fields 			READ_BUF(8);
532acb2887eSJ. Bruce Fields 			/* gcbp_service */
53306553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
534acb2887eSJ. Bruce Fields 			/* gcbp_handle_from_server */
53506553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
536acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
537acb2887eSJ. Bruce Fields 			p += XDR_QUADLEN(dummy);
538acb2887eSJ. Bruce Fields 			/* gcbp_handle_from_client */
539acb2887eSJ. Bruce Fields 			READ_BUF(4);
54006553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
541acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
542acb2887eSJ. Bruce Fields 			break;
543acb2887eSJ. Bruce Fields 		default:
544acb2887eSJ. Bruce Fields 			dprintk("Illegal callback secflavor\n");
545acb2887eSJ. Bruce Fields 			return nfserr_inval;
546acb2887eSJ. Bruce Fields 		}
547acb2887eSJ. Bruce Fields 	}
548acb2887eSJ. Bruce Fields 	DECODE_TAIL;
549acb2887eSJ. Bruce Fields }
550acb2887eSJ. Bruce Fields 
551cb73a9f4SJ. Bruce Fields static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc)
552cb73a9f4SJ. Bruce Fields {
553cb73a9f4SJ. Bruce Fields 	DECODE_HEAD;
554cb73a9f4SJ. Bruce Fields 
555cb73a9f4SJ. Bruce Fields 	READ_BUF(4);
55606553991SJ. Bruce Fields 	bc->bc_cb_program = be32_to_cpup(p++);
557cb73a9f4SJ. Bruce Fields 	nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
558cb73a9f4SJ. Bruce Fields 
559cb73a9f4SJ. Bruce Fields 	DECODE_TAIL;
560cb73a9f4SJ. Bruce Fields }
561cb73a9f4SJ. Bruce Fields 
5621d1bc8f2SJ. Bruce Fields static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts)
5631d1bc8f2SJ. Bruce Fields {
5641d1bc8f2SJ. Bruce Fields 	DECODE_HEAD;
5651d1bc8f2SJ. Bruce Fields 
5661d1bc8f2SJ. Bruce Fields 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 8);
5671d1bc8f2SJ. Bruce Fields 	COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
56806553991SJ. Bruce Fields 	bcts->dir = be32_to_cpup(p++);
5696ce2357fSBryan Schumaker 	/* XXX: skipping ctsa_use_conn_in_rdma_mode.  Perhaps Tom Tucker
5706ce2357fSBryan Schumaker 	 * could help us figure out we should be using it. */
5711d1bc8f2SJ. Bruce Fields 	DECODE_TAIL;
5721d1bc8f2SJ. Bruce Fields }
5731d1bc8f2SJ. Bruce Fields 
574b37ad28bSAl Viro static __be32
5751da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds 	DECODE_HEAD;
5781da177e4SLinus Torvalds 
579e31a1b66SBenny Halevy 	READ_BUF(4);
58006553991SJ. Bruce Fields 	close->cl_seqid = be32_to_cpup(p++);
581e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &close->cl_stateid);
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 	DECODE_TAIL;
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 
587b37ad28bSAl Viro static __be32
5881da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
5891da177e4SLinus Torvalds {
5901da177e4SLinus Torvalds 	DECODE_HEAD;
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 	READ_BUF(12);
593542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &commit->co_offset);
59406553991SJ. Bruce Fields 	commit->co_count = be32_to_cpup(p++);
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	DECODE_TAIL;
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
599b37ad28bSAl Viro static __be32
6001da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
6011da177e4SLinus Torvalds {
6021da177e4SLinus Torvalds 	DECODE_HEAD;
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 	READ_BUF(4);
60506553991SJ. Bruce Fields 	create->cr_type = be32_to_cpup(p++);
6061da177e4SLinus Torvalds 	switch (create->cr_type) {
6071da177e4SLinus Torvalds 	case NF4LNK:
6081da177e4SLinus Torvalds 		READ_BUF(4);
6097fb84306SJ. Bruce Fields 		create->cr_datalen = be32_to_cpup(p++);
6107fb84306SJ. Bruce Fields 		READ_BUF(create->cr_datalen);
61129c353b3SJ. Bruce Fields 		create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen);
6127fb84306SJ. Bruce Fields 		if (!create->cr_data)
61376f47128SJ. Bruce Fields 			return nfserr_jukebox;
6141da177e4SLinus Torvalds 		break;
6151da177e4SLinus Torvalds 	case NF4BLK:
6161da177e4SLinus Torvalds 	case NF4CHR:
6171da177e4SLinus Torvalds 		READ_BUF(8);
61806553991SJ. Bruce Fields 		create->cr_specdata1 = be32_to_cpup(p++);
61906553991SJ. Bruce Fields 		create->cr_specdata2 = be32_to_cpup(p++);
6201da177e4SLinus Torvalds 		break;
6211da177e4SLinus Torvalds 	case NF4SOCK:
6221da177e4SLinus Torvalds 	case NF4FIFO:
6231da177e4SLinus Torvalds 	case NF4DIR:
6241da177e4SLinus Torvalds 	default:
6251da177e4SLinus Torvalds 		break;
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	READ_BUF(4);
62906553991SJ. Bruce Fields 	create->cr_namelen = be32_to_cpup(p++);
6301da177e4SLinus Torvalds 	READ_BUF(create->cr_namelen);
6311da177e4SLinus Torvalds 	SAVEMEM(create->cr_name, create->cr_namelen);
632a36b1725SJ. Bruce Fields 	if ((status = check_filename(create->cr_name, create->cr_namelen)))
6331da177e4SLinus Torvalds 		return status;
6341da177e4SLinus Torvalds 
6353c8e0316SYu Zhiguo 	status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
63618032ca0SDavid Quigley 				    &create->cr_acl, &create->cr_label);
637c0d6fc8aSBenny Halevy 	if (status)
6381da177e4SLinus Torvalds 		goto out;
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 	DECODE_TAIL;
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds 
643b37ad28bSAl Viro static inline __be32
6441da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
6451da177e4SLinus Torvalds {
646e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
6471da177e4SLinus Torvalds }
6481da177e4SLinus Torvalds 
649b37ad28bSAl Viro static inline __be32
6501da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
6511da177e4SLinus Torvalds {
6521da177e4SLinus Torvalds 	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds 
655b37ad28bSAl Viro static __be32
6561da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
6571da177e4SLinus Torvalds {
6581da177e4SLinus Torvalds 	DECODE_HEAD;
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds 	READ_BUF(4);
66106553991SJ. Bruce Fields 	link->li_namelen = be32_to_cpup(p++);
6621da177e4SLinus Torvalds 	READ_BUF(link->li_namelen);
6631da177e4SLinus Torvalds 	SAVEMEM(link->li_name, link->li_namelen);
664a36b1725SJ. Bruce Fields 	if ((status = check_filename(link->li_name, link->li_namelen)))
6651da177e4SLinus Torvalds 		return status;
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds 	DECODE_TAIL;
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds 
670b37ad28bSAl Viro static __be32
6711da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
6721da177e4SLinus Torvalds {
6731da177e4SLinus Torvalds 	DECODE_HEAD;
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds 	/*
6761da177e4SLinus Torvalds 	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
6771da177e4SLinus Torvalds 	*/
6781da177e4SLinus Torvalds 	READ_BUF(28);
67906553991SJ. Bruce Fields 	lock->lk_type = be32_to_cpup(p++);
6801da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
6811da177e4SLinus Torvalds 		goto xdr_error;
68206553991SJ. Bruce Fields 	lock->lk_reclaim = be32_to_cpup(p++);
683542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lock->lk_offset);
684542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lock->lk_length);
68506553991SJ. Bruce Fields 	lock->lk_is_new = be32_to_cpup(p++);
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds 	if (lock->lk_is_new) {
688e31a1b66SBenny Halevy 		READ_BUF(4);
68906553991SJ. Bruce Fields 		lock->lk_new_open_seqid = be32_to_cpup(p++);
690e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
691e31a1b66SBenny Halevy 		if (status)
692e31a1b66SBenny Halevy 			return status;
693e31a1b66SBenny Halevy 		READ_BUF(8 + sizeof(clientid_t));
69406553991SJ. Bruce Fields 		lock->lk_new_lock_seqid = be32_to_cpup(p++);
6951da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
69606553991SJ. Bruce Fields 		lock->lk_new_owner.len = be32_to_cpup(p++);
6971da177e4SLinus Torvalds 		READ_BUF(lock->lk_new_owner.len);
6981da177e4SLinus Torvalds 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
6991da177e4SLinus Torvalds 	} else {
700e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
701e31a1b66SBenny Halevy 		if (status)
702e31a1b66SBenny Halevy 			return status;
703e31a1b66SBenny Halevy 		READ_BUF(4);
70406553991SJ. Bruce Fields 		lock->lk_old_lock_seqid = be32_to_cpup(p++);
7051da177e4SLinus Torvalds 	}
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	DECODE_TAIL;
7081da177e4SLinus Torvalds }
7091da177e4SLinus Torvalds 
710b37ad28bSAl Viro static __be32
7111da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
7121da177e4SLinus Torvalds {
7131da177e4SLinus Torvalds 	DECODE_HEAD;
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 	READ_BUF(32);
71606553991SJ. Bruce Fields 	lockt->lt_type = be32_to_cpup(p++);
7171da177e4SLinus Torvalds 	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
7181da177e4SLinus Torvalds 		goto xdr_error;
719542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lockt->lt_offset);
720542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &lockt->lt_length);
7211da177e4SLinus Torvalds 	COPYMEM(&lockt->lt_clientid, 8);
72206553991SJ. Bruce Fields 	lockt->lt_owner.len = be32_to_cpup(p++);
7231da177e4SLinus Torvalds 	READ_BUF(lockt->lt_owner.len);
7241da177e4SLinus Torvalds 	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	DECODE_TAIL;
7271da177e4SLinus Torvalds }
7281da177e4SLinus Torvalds 
729b37ad28bSAl Viro static __be32
7301da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
7311da177e4SLinus Torvalds {
7321da177e4SLinus Torvalds 	DECODE_HEAD;
7331da177e4SLinus Torvalds 
734e31a1b66SBenny Halevy 	READ_BUF(8);
73506553991SJ. Bruce Fields 	locku->lu_type = be32_to_cpup(p++);
7361da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
7371da177e4SLinus Torvalds 		goto xdr_error;
73806553991SJ. Bruce Fields 	locku->lu_seqid = be32_to_cpup(p++);
739e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
740e31a1b66SBenny Halevy 	if (status)
741e31a1b66SBenny Halevy 		return status;
742e31a1b66SBenny Halevy 	READ_BUF(16);
743542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &locku->lu_offset);
744542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &locku->lu_length);
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 	DECODE_TAIL;
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds 
749b37ad28bSAl Viro static __be32
7501da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
7511da177e4SLinus Torvalds {
7521da177e4SLinus Torvalds 	DECODE_HEAD;
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds 	READ_BUF(4);
75506553991SJ. Bruce Fields 	lookup->lo_len = be32_to_cpup(p++);
7561da177e4SLinus Torvalds 	READ_BUF(lookup->lo_len);
7571da177e4SLinus Torvalds 	SAVEMEM(lookup->lo_name, lookup->lo_len);
758a36b1725SJ. Bruce Fields 	if ((status = check_filename(lookup->lo_name, lookup->lo_len)))
7591da177e4SLinus Torvalds 		return status;
7601da177e4SLinus Torvalds 
7611da177e4SLinus Torvalds 	DECODE_TAIL;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
7642c8bd7e0SBenny Halevy static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when)
76504f9e664SJ. Bruce Fields {
76604f9e664SJ. Bruce Fields 	__be32 *p;
76704f9e664SJ. Bruce Fields 	u32 w;
76804f9e664SJ. Bruce Fields 
76904f9e664SJ. Bruce Fields 	READ_BUF(4);
77006553991SJ. Bruce Fields 	w = be32_to_cpup(p++);
7712c8bd7e0SBenny Halevy 	*share_access = w & NFS4_SHARE_ACCESS_MASK;
7722c8bd7e0SBenny Halevy 	*deleg_want = w & NFS4_SHARE_WANT_MASK;
7732c8bd7e0SBenny Halevy 	if (deleg_when)
7742c8bd7e0SBenny Halevy 		*deleg_when = w & NFS4_SHARE_WHEN_MASK;
7752c8bd7e0SBenny Halevy 
77604f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_ACCESS_MASK) {
77704f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_READ:
77804f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_WRITE:
77904f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_BOTH:
78004f9e664SJ. Bruce Fields 		break;
78104f9e664SJ. Bruce Fields 	default:
78204f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
78304f9e664SJ. Bruce Fields 	}
784fc0d14feSBenny Halevy 	w &= ~NFS4_SHARE_ACCESS_MASK;
78504f9e664SJ. Bruce Fields 	if (!w)
78604f9e664SJ. Bruce Fields 		return nfs_ok;
78704f9e664SJ. Bruce Fields 	if (!argp->minorversion)
78804f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
78904f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_WANT_MASK) {
79004f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_PREFERENCE:
79104f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_READ_DELEG:
79204f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_WRITE_DELEG:
79304f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_ANY_DELEG:
79404f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_DELEG:
79504f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_CANCEL:
79604f9e664SJ. Bruce Fields 		break;
79704f9e664SJ. Bruce Fields 	default:
79804f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
79904f9e664SJ. Bruce Fields 	}
80092bac8c5SBenny Halevy 	w &= ~NFS4_SHARE_WANT_MASK;
80104f9e664SJ. Bruce Fields 	if (!w)
80204f9e664SJ. Bruce Fields 		return nfs_ok;
8032c8bd7e0SBenny Halevy 
8042c8bd7e0SBenny Halevy 	if (!deleg_when)	/* open_downgrade */
8052c8bd7e0SBenny Halevy 		return nfserr_inval;
80604f9e664SJ. Bruce Fields 	switch (w) {
80704f9e664SJ. Bruce Fields 	case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
80804f9e664SJ. Bruce Fields 	case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
809c668fc6dSBenny Halevy 	case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
810c668fc6dSBenny Halevy 	      NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
81104f9e664SJ. Bruce Fields 		return nfs_ok;
81204f9e664SJ. Bruce Fields 	}
81304f9e664SJ. Bruce Fields xdr_error:
81404f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
81504f9e664SJ. Bruce Fields }
81604f9e664SJ. Bruce Fields 
81704f9e664SJ. Bruce Fields static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
81804f9e664SJ. Bruce Fields {
81904f9e664SJ. Bruce Fields 	__be32 *p;
82004f9e664SJ. Bruce Fields 
82104f9e664SJ. Bruce Fields 	READ_BUF(4);
82206553991SJ. Bruce Fields 	*x = be32_to_cpup(p++);
82304f9e664SJ. Bruce Fields 	/* Note: unlinke access bits, deny bits may be zero. */
82401cd4afaSDan Carpenter 	if (*x & ~NFS4_SHARE_DENY_BOTH)
82504f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
82604f9e664SJ. Bruce Fields 	return nfs_ok;
82704f9e664SJ. Bruce Fields xdr_error:
82804f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
82904f9e664SJ. Bruce Fields }
83004f9e664SJ. Bruce Fields 
831a084daf5SJ. Bruce Fields static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
832a084daf5SJ. Bruce Fields {
833a084daf5SJ. Bruce Fields 	__be32 *p;
834a084daf5SJ. Bruce Fields 
835a084daf5SJ. Bruce Fields 	READ_BUF(4);
83606553991SJ. Bruce Fields 	o->len = be32_to_cpup(p++);
837a084daf5SJ. Bruce Fields 
838a084daf5SJ. Bruce Fields 	if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT)
839a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
840a084daf5SJ. Bruce Fields 
841a084daf5SJ. Bruce Fields 	READ_BUF(o->len);
842a084daf5SJ. Bruce Fields 	SAVEMEM(o->data, o->len);
843a084daf5SJ. Bruce Fields 	return nfs_ok;
844a084daf5SJ. Bruce Fields xdr_error:
845a084daf5SJ. Bruce Fields 	return nfserr_bad_xdr;
846a084daf5SJ. Bruce Fields }
847a084daf5SJ. Bruce Fields 
848b37ad28bSAl Viro static __be32
8491da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	DECODE_HEAD;
8522c8bd7e0SBenny Halevy 	u32 dummy;
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds 	memset(open->op_bmval, 0, sizeof(open->op_bmval));
8551da177e4SLinus Torvalds 	open->op_iattr.ia_valid = 0;
856fe0750e5SJ. Bruce Fields 	open->op_openowner = NULL;
8571da177e4SLinus Torvalds 
8589d313b17SJ. Bruce Fields 	open->op_xdr_error = 0;
8591da177e4SLinus Torvalds 	/* seqid, share_access, share_deny, clientid, ownerlen */
86004f9e664SJ. Bruce Fields 	READ_BUF(4);
86106553991SJ. Bruce Fields 	open->op_seqid = be32_to_cpup(p++);
8622c8bd7e0SBenny Halevy 	/* decode, yet ignore deleg_when until supported */
8632c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open->op_share_access,
8642c8bd7e0SBenny Halevy 					   &open->op_deleg_want, &dummy);
86504f9e664SJ. Bruce Fields 	if (status)
86604f9e664SJ. Bruce Fields 		goto xdr_error;
86704f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
86804f9e664SJ. Bruce Fields 	if (status)
86904f9e664SJ. Bruce Fields 		goto xdr_error;
870a084daf5SJ. Bruce Fields 	READ_BUF(sizeof(clientid_t));
8711da177e4SLinus Torvalds 	COPYMEM(&open->op_clientid, sizeof(clientid_t));
872a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &open->op_owner);
873a084daf5SJ. Bruce Fields 	if (status)
874a084daf5SJ. Bruce Fields 		goto xdr_error;
875a084daf5SJ. Bruce Fields 	READ_BUF(4);
87606553991SJ. Bruce Fields 	open->op_create = be32_to_cpup(p++);
8771da177e4SLinus Torvalds 	switch (open->op_create) {
8781da177e4SLinus Torvalds 	case NFS4_OPEN_NOCREATE:
8791da177e4SLinus Torvalds 		break;
8801da177e4SLinus Torvalds 	case NFS4_OPEN_CREATE:
8811da177e4SLinus Torvalds 		READ_BUF(4);
88206553991SJ. Bruce Fields 		open->op_createmode = be32_to_cpup(p++);
8831da177e4SLinus Torvalds 		switch (open->op_createmode) {
8841da177e4SLinus Torvalds 		case NFS4_CREATE_UNCHECKED:
8851da177e4SLinus Torvalds 		case NFS4_CREATE_GUARDED:
886c0d6fc8aSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
88718032ca0SDavid Quigley 				&open->op_iattr, &open->op_acl, &open->op_label);
888c0d6fc8aSBenny Halevy 			if (status)
8891da177e4SLinus Torvalds 				goto out;
8901da177e4SLinus Torvalds 			break;
8911da177e4SLinus Torvalds 		case NFS4_CREATE_EXCLUSIVE:
892ab4684d1SChuck Lever 			READ_BUF(NFS4_VERIFIER_SIZE);
893ab4684d1SChuck Lever 			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
8941da177e4SLinus Torvalds 			break;
89579fb54abSBenny Halevy 		case NFS4_CREATE_EXCLUSIVE4_1:
89679fb54abSBenny Halevy 			if (argp->minorversion < 1)
89779fb54abSBenny Halevy 				goto xdr_error;
898ab4684d1SChuck Lever 			READ_BUF(NFS4_VERIFIER_SIZE);
899ab4684d1SChuck Lever 			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
90079fb54abSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
90118032ca0SDavid Quigley 				&open->op_iattr, &open->op_acl, &open->op_label);
90279fb54abSBenny Halevy 			if (status)
90379fb54abSBenny Halevy 				goto out;
90479fb54abSBenny Halevy 			break;
9051da177e4SLinus Torvalds 		default:
9061da177e4SLinus Torvalds 			goto xdr_error;
9071da177e4SLinus Torvalds 		}
9081da177e4SLinus Torvalds 		break;
9091da177e4SLinus Torvalds 	default:
9101da177e4SLinus Torvalds 		goto xdr_error;
9111da177e4SLinus Torvalds 	}
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	/* open_claim */
9141da177e4SLinus Torvalds 	READ_BUF(4);
91506553991SJ. Bruce Fields 	open->op_claim_type = be32_to_cpup(p++);
9161da177e4SLinus Torvalds 	switch (open->op_claim_type) {
9171da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_NULL:
9181da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
9191da177e4SLinus Torvalds 		READ_BUF(4);
92006553991SJ. Bruce Fields 		open->op_fname.len = be32_to_cpup(p++);
9211da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
9221da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
923a36b1725SJ. Bruce Fields 		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
9241da177e4SLinus Torvalds 			return status;
9251da177e4SLinus Torvalds 		break;
9261da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_PREVIOUS:
9271da177e4SLinus Torvalds 		READ_BUF(4);
92806553991SJ. Bruce Fields 		open->op_delegate_type = be32_to_cpup(p++);
9291da177e4SLinus Torvalds 		break;
9301da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
931e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
932e31a1b66SBenny Halevy 		if (status)
933e31a1b66SBenny Halevy 			return status;
934e31a1b66SBenny Halevy 		READ_BUF(4);
93506553991SJ. Bruce Fields 		open->op_fname.len = be32_to_cpup(p++);
9361da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
9371da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
938a36b1725SJ. Bruce Fields 		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
9391da177e4SLinus Torvalds 			return status;
9401da177e4SLinus Torvalds 		break;
9418b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_FH:
9428b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
9438b289b2cSJ. Bruce Fields 		if (argp->minorversion < 1)
9448b289b2cSJ. Bruce Fields 			goto xdr_error;
9458b289b2cSJ. Bruce Fields 		/* void */
9468b289b2cSJ. Bruce Fields 		break;
9478b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
9488b289b2cSJ. Bruce Fields 		if (argp->minorversion < 1)
9498b289b2cSJ. Bruce Fields 			goto xdr_error;
9508b289b2cSJ. Bruce Fields 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
9518b289b2cSJ. Bruce Fields 		if (status)
9528b289b2cSJ. Bruce Fields 			return status;
9538b289b2cSJ. Bruce Fields 		break;
9541da177e4SLinus Torvalds 	default:
9551da177e4SLinus Torvalds 		goto xdr_error;
9561da177e4SLinus Torvalds 	}
9571da177e4SLinus Torvalds 
9581da177e4SLinus Torvalds 	DECODE_TAIL;
9591da177e4SLinus Torvalds }
9601da177e4SLinus Torvalds 
961b37ad28bSAl Viro static __be32
9621da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
9631da177e4SLinus Torvalds {
9641da177e4SLinus Torvalds 	DECODE_HEAD;
9651da177e4SLinus Torvalds 
966e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
967e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
968e1a90ebdSAnna Schumaker 
969e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
970e31a1b66SBenny Halevy 	if (status)
971e31a1b66SBenny Halevy 		return status;
972e31a1b66SBenny Halevy 	READ_BUF(4);
97306553991SJ. Bruce Fields 	open_conf->oc_seqid = be32_to_cpup(p++);
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 	DECODE_TAIL;
9761da177e4SLinus Torvalds }
9771da177e4SLinus Torvalds 
978b37ad28bSAl Viro static __be32
9791da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
9801da177e4SLinus Torvalds {
9811da177e4SLinus Torvalds 	DECODE_HEAD;
9821da177e4SLinus Torvalds 
983e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
984e31a1b66SBenny Halevy 	if (status)
985e31a1b66SBenny Halevy 		return status;
98604f9e664SJ. Bruce Fields 	READ_BUF(4);
98706553991SJ. Bruce Fields 	open_down->od_seqid = be32_to_cpup(p++);
9882c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
9892c8bd7e0SBenny Halevy 					   &open_down->od_deleg_want, NULL);
99004f9e664SJ. Bruce Fields 	if (status)
99104f9e664SJ. Bruce Fields 		return status;
99204f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
99304f9e664SJ. Bruce Fields 	if (status)
99404f9e664SJ. Bruce Fields 		return status;
9951da177e4SLinus Torvalds 	DECODE_TAIL;
9961da177e4SLinus Torvalds }
9971da177e4SLinus Torvalds 
998b37ad28bSAl Viro static __be32
9991da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
10001da177e4SLinus Torvalds {
10011da177e4SLinus Torvalds 	DECODE_HEAD;
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	READ_BUF(4);
100406553991SJ. Bruce Fields 	putfh->pf_fhlen = be32_to_cpup(p++);
10051da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
10061da177e4SLinus Torvalds 		goto xdr_error;
10071da177e4SLinus Torvalds 	READ_BUF(putfh->pf_fhlen);
10081da177e4SLinus Torvalds 	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds 	DECODE_TAIL;
10111da177e4SLinus Torvalds }
10121da177e4SLinus Torvalds 
1013b37ad28bSAl Viro static __be32
1014e1a90ebdSAnna Schumaker nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, void *p)
1015e1a90ebdSAnna Schumaker {
1016e1a90ebdSAnna Schumaker 	if (argp->minorversion == 0)
1017e1a90ebdSAnna Schumaker 		return nfs_ok;
1018e1a90ebdSAnna Schumaker 	return nfserr_notsupp;
1019e1a90ebdSAnna Schumaker }
1020e1a90ebdSAnna Schumaker 
1021e1a90ebdSAnna Schumaker static __be32
10221da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
10231da177e4SLinus Torvalds {
10241da177e4SLinus Torvalds 	DECODE_HEAD;
10251da177e4SLinus Torvalds 
1026e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
1027e31a1b66SBenny Halevy 	if (status)
1028e31a1b66SBenny Halevy 		return status;
1029e31a1b66SBenny Halevy 	READ_BUF(12);
1030542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &read->rd_offset);
103106553991SJ. Bruce Fields 	read->rd_length = be32_to_cpup(p++);
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	DECODE_TAIL;
10341da177e4SLinus Torvalds }
10351da177e4SLinus Torvalds 
1036b37ad28bSAl Viro static __be32
10371da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
10381da177e4SLinus Torvalds {
10391da177e4SLinus Torvalds 	DECODE_HEAD;
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 	READ_BUF(24);
1042542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &readdir->rd_cookie);
10431da177e4SLinus Torvalds 	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
104406553991SJ. Bruce Fields 	readdir->rd_dircount = be32_to_cpup(p++);
104506553991SJ. Bruce Fields 	readdir->rd_maxcount = be32_to_cpup(p++);
10461da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
10471da177e4SLinus Torvalds 		goto out;
10481da177e4SLinus Torvalds 
10491da177e4SLinus Torvalds 	DECODE_TAIL;
10501da177e4SLinus Torvalds }
10511da177e4SLinus Torvalds 
1052b37ad28bSAl Viro static __be32
10531da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
10541da177e4SLinus Torvalds {
10551da177e4SLinus Torvalds 	DECODE_HEAD;
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	READ_BUF(4);
105806553991SJ. Bruce Fields 	remove->rm_namelen = be32_to_cpup(p++);
10591da177e4SLinus Torvalds 	READ_BUF(remove->rm_namelen);
10601da177e4SLinus Torvalds 	SAVEMEM(remove->rm_name, remove->rm_namelen);
1061a36b1725SJ. Bruce Fields 	if ((status = check_filename(remove->rm_name, remove->rm_namelen)))
10621da177e4SLinus Torvalds 		return status;
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	DECODE_TAIL;
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds 
1067b37ad28bSAl Viro static __be32
10681da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
10691da177e4SLinus Torvalds {
10701da177e4SLinus Torvalds 	DECODE_HEAD;
10711da177e4SLinus Torvalds 
10721da177e4SLinus Torvalds 	READ_BUF(4);
107306553991SJ. Bruce Fields 	rename->rn_snamelen = be32_to_cpup(p++);
10741da177e4SLinus Torvalds 	READ_BUF(rename->rn_snamelen + 4);
10751da177e4SLinus Torvalds 	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
107606553991SJ. Bruce Fields 	rename->rn_tnamelen = be32_to_cpup(p++);
10771da177e4SLinus Torvalds 	READ_BUF(rename->rn_tnamelen);
10781da177e4SLinus Torvalds 	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
1079a36b1725SJ. Bruce Fields 	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen)))
10801da177e4SLinus Torvalds 		return status;
1081a36b1725SJ. Bruce Fields 	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen)))
10821da177e4SLinus Torvalds 		return status;
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 	DECODE_TAIL;
10851da177e4SLinus Torvalds }
10861da177e4SLinus Torvalds 
1087b37ad28bSAl Viro static __be32
10881da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
10891da177e4SLinus Torvalds {
10901da177e4SLinus Torvalds 	DECODE_HEAD;
10911da177e4SLinus Torvalds 
1092e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1093e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1094e1a90ebdSAnna Schumaker 
10951da177e4SLinus Torvalds 	READ_BUF(sizeof(clientid_t));
10961da177e4SLinus Torvalds 	COPYMEM(clientid, sizeof(clientid_t));
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	DECODE_TAIL;
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds 
1101b37ad28bSAl Viro static __be32
1102dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1103dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
1104dcb488a3SAndy Adamson {
1105dcb488a3SAndy Adamson 	DECODE_HEAD;
1106dcb488a3SAndy Adamson 
1107dcb488a3SAndy Adamson 	READ_BUF(4);
110806553991SJ. Bruce Fields 	secinfo->si_namelen = be32_to_cpup(p++);
1109dcb488a3SAndy Adamson 	READ_BUF(secinfo->si_namelen);
1110dcb488a3SAndy Adamson 	SAVEMEM(secinfo->si_name, secinfo->si_namelen);
1111a36b1725SJ. Bruce Fields 	status = check_filename(secinfo->si_name, secinfo->si_namelen);
1112dcb488a3SAndy Adamson 	if (status)
1113dcb488a3SAndy Adamson 		return status;
1114dcb488a3SAndy Adamson 	DECODE_TAIL;
1115dcb488a3SAndy Adamson }
1116dcb488a3SAndy Adamson 
1117dcb488a3SAndy Adamson static __be32
111804f4ad16SJ. Bruce Fields nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
111904f4ad16SJ. Bruce Fields 		     struct nfsd4_secinfo_no_name *sin)
112004f4ad16SJ. Bruce Fields {
112104f4ad16SJ. Bruce Fields 	DECODE_HEAD;
112204f4ad16SJ. Bruce Fields 
112304f4ad16SJ. Bruce Fields 	READ_BUF(4);
112406553991SJ. Bruce Fields 	sin->sin_style = be32_to_cpup(p++);
112504f4ad16SJ. Bruce Fields 	DECODE_TAIL;
112604f4ad16SJ. Bruce Fields }
112704f4ad16SJ. Bruce Fields 
112804f4ad16SJ. Bruce Fields static __be32
11291da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
11301da177e4SLinus Torvalds {
1131e31a1b66SBenny Halevy 	__be32 status;
11321da177e4SLinus Torvalds 
1133e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
1134e31a1b66SBenny Halevy 	if (status)
1135e31a1b66SBenny Halevy 		return status;
11363c8e0316SYu Zhiguo 	return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
113718032ca0SDavid Quigley 				  &setattr->sa_acl, &setattr->sa_label);
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds 
1140b37ad28bSAl Viro static __be32
11411da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
11421da177e4SLinus Torvalds {
11431da177e4SLinus Torvalds 	DECODE_HEAD;
11441da177e4SLinus Torvalds 
1145e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1146e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1147e1a90ebdSAnna Schumaker 
1148ab4684d1SChuck Lever 	READ_BUF(NFS4_VERIFIER_SIZE);
1149ab4684d1SChuck Lever 	COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE);
11501da177e4SLinus Torvalds 
1151a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &setclientid->se_name);
1152a084daf5SJ. Bruce Fields 	if (status)
1153a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
1154a084daf5SJ. Bruce Fields 	READ_BUF(8);
115506553991SJ. Bruce Fields 	setclientid->se_callback_prog = be32_to_cpup(p++);
115606553991SJ. Bruce Fields 	setclientid->se_callback_netid_len = be32_to_cpup(p++);
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_netid_len + 4);
11591da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
116006553991SJ. Bruce Fields 	setclientid->se_callback_addr_len = be32_to_cpup(p++);
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_addr_len + 4);
11631da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
116406553991SJ. Bruce Fields 	setclientid->se_callback_ident = be32_to_cpup(p++);
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 	DECODE_TAIL;
11671da177e4SLinus Torvalds }
11681da177e4SLinus Torvalds 
1169b37ad28bSAl Viro static __be32
11701da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
11711da177e4SLinus Torvalds {
11721da177e4SLinus Torvalds 	DECODE_HEAD;
11731da177e4SLinus Torvalds 
1174e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1175e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1176e1a90ebdSAnna Schumaker 
1177ab4684d1SChuck Lever 	READ_BUF(8 + NFS4_VERIFIER_SIZE);
11781da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_clientid, 8);
1179ab4684d1SChuck Lever 	COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE);
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 	DECODE_TAIL;
11821da177e4SLinus Torvalds }
11831da177e4SLinus Torvalds 
11841da177e4SLinus Torvalds /* Also used for NVERIFY */
1185b37ad28bSAl Viro static __be32
11861da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
11871da177e4SLinus Torvalds {
11881da177e4SLinus Torvalds 	DECODE_HEAD;
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
11911da177e4SLinus Torvalds 		goto out;
11921da177e4SLinus Torvalds 
11931da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
1194e5f95703SJ. Bruce Fields 	 * nfsd4_proc_verify */
1195e5f95703SJ. Bruce Fields 
11961da177e4SLinus Torvalds 	READ_BUF(4);
119706553991SJ. Bruce Fields 	verify->ve_attrlen = be32_to_cpup(p++);
11981da177e4SLinus Torvalds 	READ_BUF(verify->ve_attrlen);
11991da177e4SLinus Torvalds 	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
12001da177e4SLinus Torvalds 
12011da177e4SLinus Torvalds 	DECODE_TAIL;
12021da177e4SLinus Torvalds }
12031da177e4SLinus Torvalds 
1204b37ad28bSAl Viro static __be32
12051da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
12061da177e4SLinus Torvalds {
12071da177e4SLinus Torvalds 	int avail;
12081da177e4SLinus Torvalds 	int len;
12091da177e4SLinus Torvalds 	DECODE_HEAD;
12101da177e4SLinus Torvalds 
1211e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
1212e31a1b66SBenny Halevy 	if (status)
1213e31a1b66SBenny Halevy 		return status;
1214e31a1b66SBenny Halevy 	READ_BUF(16);
1215542d1ab3SJ. Bruce Fields 	p = xdr_decode_hyper(p, &write->wr_offset);
121606553991SJ. Bruce Fields 	write->wr_stable_how = be32_to_cpup(p++);
12171da177e4SLinus Torvalds 	if (write->wr_stable_how > 2)
12181da177e4SLinus Torvalds 		goto xdr_error;
121906553991SJ. Bruce Fields 	write->wr_buflen = be32_to_cpup(p++);
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	/* Sorry .. no magic macros for this.. *
12221da177e4SLinus Torvalds 	 * READ_BUF(write->wr_buflen);
12231da177e4SLinus Torvalds 	 * SAVEMEM(write->wr_buf, write->wr_buflen);
12241da177e4SLinus Torvalds 	 */
12251da177e4SLinus Torvalds 	avail = (char*)argp->end - (char*)argp->p;
12261da177e4SLinus Torvalds 	if (avail + argp->pagelen < write->wr_buflen) {
1227817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n",
1228817cb9d4SChuck Lever 				__FILE__, __LINE__);
12291da177e4SLinus Torvalds 		goto xdr_error;
12301da177e4SLinus Torvalds 	}
123170cc7f75SJ. Bruce Fields 	write->wr_head.iov_base = p;
123270cc7f75SJ. Bruce Fields 	write->wr_head.iov_len = avail;
123370cc7f75SJ. Bruce Fields 	write->wr_pagelist = argp->pagelist;
12345a80a54dSJ. Bruce Fields 
12355a80a54dSJ. Bruce Fields 	len = XDR_QUADLEN(write->wr_buflen) << 2;
12365a80a54dSJ. Bruce Fields 	if (len >= avail) {
12375a80a54dSJ. Bruce Fields 		int pages;
12385a80a54dSJ. Bruce Fields 
12395a80a54dSJ. Bruce Fields 		len -= avail;
12405a80a54dSJ. Bruce Fields 
12415a80a54dSJ. Bruce Fields 		pages = len >> PAGE_SHIFT;
12425a80a54dSJ. Bruce Fields 		argp->pagelist += pages;
12435a80a54dSJ. Bruce Fields 		argp->pagelen -= pages * PAGE_SIZE;
12445a80a54dSJ. Bruce Fields 		len -= pages * PAGE_SIZE;
12455a80a54dSJ. Bruce Fields 
12465a80a54dSJ. Bruce Fields 		argp->p = (__be32 *)page_address(argp->pagelist[0]);
1247365da4adSJ. Bruce Fields 		argp->pagelist++;
12485a80a54dSJ. Bruce Fields 		argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
12491da177e4SLinus Torvalds 	}
12505a80a54dSJ. Bruce Fields 	argp->p += XDR_QUADLEN(len);
12511da177e4SLinus Torvalds 
12521da177e4SLinus Torvalds 	DECODE_TAIL;
12531da177e4SLinus Torvalds }
12541da177e4SLinus Torvalds 
1255b37ad28bSAl Viro static __be32
12561da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
12571da177e4SLinus Torvalds {
12581da177e4SLinus Torvalds 	DECODE_HEAD;
12591da177e4SLinus Torvalds 
1260e1a90ebdSAnna Schumaker 	if (argp->minorversion >= 1)
1261e1a90ebdSAnna Schumaker 		return nfserr_notsupp;
1262e1a90ebdSAnna Schumaker 
12631da177e4SLinus Torvalds 	READ_BUF(12);
12641da177e4SLinus Torvalds 	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
126506553991SJ. Bruce Fields 	rlockowner->rl_owner.len = be32_to_cpup(p++);
12661da177e4SLinus Torvalds 	READ_BUF(rlockowner->rl_owner.len);
12671da177e4SLinus Torvalds 	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
12681da177e4SLinus Torvalds 
126960adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
127060adfc50SAndy Adamson 		return nfserr_inval;
12711da177e4SLinus Torvalds 	DECODE_TAIL;
12721da177e4SLinus Torvalds }
12731da177e4SLinus Torvalds 
1274b37ad28bSAl Viro static __be32
12752db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
12760733d213SAndy Adamson 			 struct nfsd4_exchange_id *exid)
12772db134ebSAndy Adamson {
12785afa040bSMi Jinlong 	int dummy, tmp;
12790733d213SAndy Adamson 	DECODE_HEAD;
12800733d213SAndy Adamson 
12810733d213SAndy Adamson 	READ_BUF(NFS4_VERIFIER_SIZE);
12820733d213SAndy Adamson 	COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE);
12830733d213SAndy Adamson 
1284a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &exid->clname);
1285a084daf5SJ. Bruce Fields 	if (status)
1286a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
12870733d213SAndy Adamson 
12880733d213SAndy Adamson 	READ_BUF(4);
128906553991SJ. Bruce Fields 	exid->flags = be32_to_cpup(p++);
12900733d213SAndy Adamson 
12910733d213SAndy Adamson 	/* Ignore state_protect4_a */
12920733d213SAndy Adamson 	READ_BUF(4);
129306553991SJ. Bruce Fields 	exid->spa_how = be32_to_cpup(p++);
12940733d213SAndy Adamson 	switch (exid->spa_how) {
12950733d213SAndy Adamson 	case SP4_NONE:
12960733d213SAndy Adamson 		break;
12970733d213SAndy Adamson 	case SP4_MACH_CRED:
12980733d213SAndy Adamson 		/* spo_must_enforce */
12990733d213SAndy Adamson 		READ_BUF(4);
130006553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13010733d213SAndy Adamson 		READ_BUF(dummy * 4);
13020733d213SAndy Adamson 		p += dummy;
13030733d213SAndy Adamson 
13040733d213SAndy Adamson 		/* spo_must_allow */
13050733d213SAndy Adamson 		READ_BUF(4);
130606553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13070733d213SAndy Adamson 		READ_BUF(dummy * 4);
13080733d213SAndy Adamson 		p += dummy;
13090733d213SAndy Adamson 		break;
13100733d213SAndy Adamson 	case SP4_SSV:
13110733d213SAndy Adamson 		/* ssp_ops */
13120733d213SAndy Adamson 		READ_BUF(4);
131306553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13140733d213SAndy Adamson 		READ_BUF(dummy * 4);
13150733d213SAndy Adamson 		p += dummy;
13160733d213SAndy Adamson 
13170733d213SAndy Adamson 		READ_BUF(4);
131806553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13190733d213SAndy Adamson 		READ_BUF(dummy * 4);
13200733d213SAndy Adamson 		p += dummy;
13210733d213SAndy Adamson 
13220733d213SAndy Adamson 		/* ssp_hash_algs<> */
13230733d213SAndy Adamson 		READ_BUF(4);
132406553991SJ. Bruce Fields 		tmp = be32_to_cpup(p++);
13255afa040bSMi Jinlong 		while (tmp--) {
13260733d213SAndy Adamson 			READ_BUF(4);
132706553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
13280733d213SAndy Adamson 			READ_BUF(dummy);
13290733d213SAndy Adamson 			p += XDR_QUADLEN(dummy);
13305afa040bSMi Jinlong 		}
13315afa040bSMi Jinlong 
13325afa040bSMi Jinlong 		/* ssp_encr_algs<> */
13335afa040bSMi Jinlong 		READ_BUF(4);
133406553991SJ. Bruce Fields 		tmp = be32_to_cpup(p++);
13355afa040bSMi Jinlong 		while (tmp--) {
13365afa040bSMi Jinlong 			READ_BUF(4);
133706553991SJ. Bruce Fields 			dummy = be32_to_cpup(p++);
13385afa040bSMi Jinlong 			READ_BUF(dummy);
13395afa040bSMi Jinlong 			p += XDR_QUADLEN(dummy);
13405afa040bSMi Jinlong 		}
13410733d213SAndy Adamson 
13420733d213SAndy Adamson 		/* ssp_window and ssp_num_gss_handles */
13430733d213SAndy Adamson 		READ_BUF(8);
134406553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
134506553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13460733d213SAndy Adamson 		break;
13470733d213SAndy Adamson 	default:
13480733d213SAndy Adamson 		goto xdr_error;
13490733d213SAndy Adamson 	}
13500733d213SAndy Adamson 
13510733d213SAndy Adamson 	/* Ignore Implementation ID */
13520733d213SAndy Adamson 	READ_BUF(4);    /* nfs_impl_id4 array length */
135306553991SJ. Bruce Fields 	dummy = be32_to_cpup(p++);
13540733d213SAndy Adamson 
13550733d213SAndy Adamson 	if (dummy > 1)
13560733d213SAndy Adamson 		goto xdr_error;
13570733d213SAndy Adamson 
13580733d213SAndy Adamson 	if (dummy == 1) {
13590733d213SAndy Adamson 		/* nii_domain */
13600733d213SAndy Adamson 		READ_BUF(4);
136106553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13620733d213SAndy Adamson 		READ_BUF(dummy);
13630733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
13640733d213SAndy Adamson 
13650733d213SAndy Adamson 		/* nii_name */
13660733d213SAndy Adamson 		READ_BUF(4);
136706553991SJ. Bruce Fields 		dummy = be32_to_cpup(p++);
13680733d213SAndy Adamson 		READ_BUF(dummy);
13690733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
13700733d213SAndy Adamson 
13710733d213SAndy Adamson 		/* nii_date */
13720733d213SAndy Adamson 		READ_BUF(12);
13730733d213SAndy Adamson 		p += 3;
13740733d213SAndy Adamson 	}
13750733d213SAndy Adamson 	DECODE_TAIL;
13762db134ebSAndy Adamson }
13772db134ebSAndy Adamson 
13782db134ebSAndy Adamson static __be32
13792db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
13802db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
13812db134ebSAndy Adamson {
1382ec6b5d7bSAndy Adamson 	DECODE_HEAD;
1383ec6b5d7bSAndy Adamson 	u32 dummy;
1384ec6b5d7bSAndy Adamson 
1385ec6b5d7bSAndy Adamson 	READ_BUF(16);
1386ec6b5d7bSAndy Adamson 	COPYMEM(&sess->clientid, 8);
138706553991SJ. Bruce Fields 	sess->seqid = be32_to_cpup(p++);
138806553991SJ. Bruce Fields 	sess->flags = be32_to_cpup(p++);
1389ec6b5d7bSAndy Adamson 
1390ec6b5d7bSAndy Adamson 	/* Fore channel attrs */
1391ec6b5d7bSAndy Adamson 	READ_BUF(28);
139206553991SJ. Bruce Fields 	dummy = be32_to_cpup(p++); /* headerpadsz is always 0 */
139306553991SJ. Bruce Fields 	sess->fore_channel.maxreq_sz = be32_to_cpup(p++);
139406553991SJ. Bruce Fields 	sess->fore_channel.maxresp_sz = be32_to_cpup(p++);
139506553991SJ. Bruce Fields 	sess->fore_channel.maxresp_cached = be32_to_cpup(p++);
139606553991SJ. Bruce Fields 	sess->fore_channel.maxops = be32_to_cpup(p++);
139706553991SJ. Bruce Fields 	sess->fore_channel.maxreqs = be32_to_cpup(p++);
139806553991SJ. Bruce Fields 	sess->fore_channel.nr_rdma_attrs = be32_to_cpup(p++);
1399ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs == 1) {
1400ec6b5d7bSAndy Adamson 		READ_BUF(4);
140106553991SJ. Bruce Fields 		sess->fore_channel.rdma_attrs = be32_to_cpup(p++);
1402ec6b5d7bSAndy Adamson 	} else if (sess->fore_channel.nr_rdma_attrs > 1) {
1403ec6b5d7bSAndy Adamson 		dprintk("Too many fore channel attr bitmaps!\n");
1404ec6b5d7bSAndy Adamson 		goto xdr_error;
1405ec6b5d7bSAndy Adamson 	}
1406ec6b5d7bSAndy Adamson 
1407ec6b5d7bSAndy Adamson 	/* Back channel attrs */
1408ec6b5d7bSAndy Adamson 	READ_BUF(28);
140906553991SJ. Bruce Fields 	dummy = be32_to_cpup(p++); /* headerpadsz is always 0 */
141006553991SJ. Bruce Fields 	sess->back_channel.maxreq_sz = be32_to_cpup(p++);
141106553991SJ. Bruce Fields 	sess->back_channel.maxresp_sz = be32_to_cpup(p++);
141206553991SJ. Bruce Fields 	sess->back_channel.maxresp_cached = be32_to_cpup(p++);
141306553991SJ. Bruce Fields 	sess->back_channel.maxops = be32_to_cpup(p++);
141406553991SJ. Bruce Fields 	sess->back_channel.maxreqs = be32_to_cpup(p++);
141506553991SJ. Bruce Fields 	sess->back_channel.nr_rdma_attrs = be32_to_cpup(p++);
1416ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs == 1) {
1417ec6b5d7bSAndy Adamson 		READ_BUF(4);
141806553991SJ. Bruce Fields 		sess->back_channel.rdma_attrs = be32_to_cpup(p++);
1419ec6b5d7bSAndy Adamson 	} else if (sess->back_channel.nr_rdma_attrs > 1) {
1420ec6b5d7bSAndy Adamson 		dprintk("Too many back channel attr bitmaps!\n");
1421ec6b5d7bSAndy Adamson 		goto xdr_error;
1422ec6b5d7bSAndy Adamson 	}
1423ec6b5d7bSAndy Adamson 
1424acb2887eSJ. Bruce Fields 	READ_BUF(4);
142506553991SJ. Bruce Fields 	sess->callback_prog = be32_to_cpup(p++);
1426acb2887eSJ. Bruce Fields 	nfsd4_decode_cb_sec(argp, &sess->cb_sec);
1427ec6b5d7bSAndy Adamson 	DECODE_TAIL;
14282db134ebSAndy Adamson }
14292db134ebSAndy Adamson 
14302db134ebSAndy Adamson static __be32
14312db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
14322db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
14332db134ebSAndy Adamson {
1434e10e0cfcSBenny Halevy 	DECODE_HEAD;
1435e10e0cfcSBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN);
1436e10e0cfcSBenny Halevy 	COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1437e10e0cfcSBenny Halevy 
1438e10e0cfcSBenny Halevy 	DECODE_TAIL;
14392db134ebSAndy Adamson }
14402db134ebSAndy Adamson 
14412db134ebSAndy Adamson static __be32
1442e1ca12dfSBryan Schumaker nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1443e1ca12dfSBryan Schumaker 			  struct nfsd4_free_stateid *free_stateid)
1444e1ca12dfSBryan Schumaker {
1445e1ca12dfSBryan Schumaker 	DECODE_HEAD;
1446e1ca12dfSBryan Schumaker 
1447e1ca12dfSBryan Schumaker 	READ_BUF(sizeof(stateid_t));
144806553991SJ. Bruce Fields 	free_stateid->fr_stateid.si_generation = be32_to_cpup(p++);
1449e1ca12dfSBryan Schumaker 	COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
1450e1ca12dfSBryan Schumaker 
1451e1ca12dfSBryan Schumaker 	DECODE_TAIL;
1452e1ca12dfSBryan Schumaker }
1453e1ca12dfSBryan Schumaker 
1454e1ca12dfSBryan Schumaker static __be32
14552db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
14562db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
14572db134ebSAndy Adamson {
1458b85d4c01SBenny Halevy 	DECODE_HEAD;
1459b85d4c01SBenny Halevy 
1460b85d4c01SBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
1461b85d4c01SBenny Halevy 	COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
146206553991SJ. Bruce Fields 	seq->seqid = be32_to_cpup(p++);
146306553991SJ. Bruce Fields 	seq->slotid = be32_to_cpup(p++);
146406553991SJ. Bruce Fields 	seq->maxslots = be32_to_cpup(p++);
146506553991SJ. Bruce Fields 	seq->cachethis = be32_to_cpup(p++);
1466b85d4c01SBenny Halevy 
1467b85d4c01SBenny Halevy 	DECODE_TAIL;
14682db134ebSAndy Adamson }
14692db134ebSAndy Adamson 
147017456804SBryan Schumaker static __be32
147117456804SBryan Schumaker nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
147217456804SBryan Schumaker {
147317456804SBryan Schumaker 	int i;
147403cfb420SBryan Schumaker 	__be32 *p, status;
147503cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid;
147617456804SBryan Schumaker 
147717456804SBryan Schumaker 	READ_BUF(4);
147817456804SBryan Schumaker 	test_stateid->ts_num_ids = ntohl(*p++);
147917456804SBryan Schumaker 
148003cfb420SBryan Schumaker 	INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
148117456804SBryan Schumaker 
148217456804SBryan Schumaker 	for (i = 0; i < test_stateid->ts_num_ids; i++) {
1483d5e23383SJ. Bruce Fields 		stateid = svcxdr_tmpalloc(argp, sizeof(*stateid));
148403cfb420SBryan Schumaker 		if (!stateid) {
1485afcf6792SAl Viro 			status = nfserrno(-ENOMEM);
148603cfb420SBryan Schumaker 			goto out;
148703cfb420SBryan Schumaker 		}
148803cfb420SBryan Schumaker 
148903cfb420SBryan Schumaker 		INIT_LIST_HEAD(&stateid->ts_id_list);
149003cfb420SBryan Schumaker 		list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
149103cfb420SBryan Schumaker 
149203cfb420SBryan Schumaker 		status = nfsd4_decode_stateid(argp, &stateid->ts_id_stateid);
149317456804SBryan Schumaker 		if (status)
149403cfb420SBryan Schumaker 			goto out;
149517456804SBryan Schumaker 	}
149617456804SBryan Schumaker 
149717456804SBryan Schumaker 	status = 0;
149817456804SBryan Schumaker out:
149917456804SBryan Schumaker 	return status;
150017456804SBryan Schumaker xdr_error:
150117456804SBryan Schumaker 	dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
150217456804SBryan Schumaker 	status = nfserr_bad_xdr;
150317456804SBryan Schumaker 	goto out;
150417456804SBryan Schumaker }
150517456804SBryan Schumaker 
1506345c2842SMi Jinlong static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, struct nfsd4_destroy_clientid *dc)
1507345c2842SMi Jinlong {
1508345c2842SMi Jinlong 	DECODE_HEAD;
1509345c2842SMi Jinlong 
1510345c2842SMi Jinlong 	READ_BUF(8);
1511345c2842SMi Jinlong 	COPYMEM(&dc->clientid, 8);
1512345c2842SMi Jinlong 
1513345c2842SMi Jinlong 	DECODE_TAIL;
1514345c2842SMi Jinlong }
1515345c2842SMi Jinlong 
15164dc6ec00SJ. Bruce Fields static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
15174dc6ec00SJ. Bruce Fields {
15184dc6ec00SJ. Bruce Fields 	DECODE_HEAD;
15194dc6ec00SJ. Bruce Fields 
15204dc6ec00SJ. Bruce Fields 	READ_BUF(4);
152106553991SJ. Bruce Fields 	rc->rca_one_fs = be32_to_cpup(p++);
15224dc6ec00SJ. Bruce Fields 
15234dc6ec00SJ. Bruce Fields 	DECODE_TAIL;
15244dc6ec00SJ. Bruce Fields }
15254dc6ec00SJ. Bruce Fields 
15269cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
15279cf514ccSChristoph Hellwig static __be32
15289cf514ccSChristoph Hellwig nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
15299cf514ccSChristoph Hellwig 		struct nfsd4_getdeviceinfo *gdev)
15309cf514ccSChristoph Hellwig {
15319cf514ccSChristoph Hellwig 	DECODE_HEAD;
15329cf514ccSChristoph Hellwig 	u32 num, i;
15339cf514ccSChristoph Hellwig 
15349cf514ccSChristoph Hellwig 	READ_BUF(sizeof(struct nfsd4_deviceid) + 3 * 4);
15359cf514ccSChristoph Hellwig 	COPYMEM(&gdev->gd_devid, sizeof(struct nfsd4_deviceid));
15369cf514ccSChristoph Hellwig 	gdev->gd_layout_type = be32_to_cpup(p++);
15379cf514ccSChristoph Hellwig 	gdev->gd_maxcount = be32_to_cpup(p++);
15389cf514ccSChristoph Hellwig 	num = be32_to_cpup(p++);
15399cf514ccSChristoph Hellwig 	if (num) {
15409cf514ccSChristoph Hellwig 		READ_BUF(4 * num);
15419cf514ccSChristoph Hellwig 		gdev->gd_notify_types = be32_to_cpup(p++);
15429cf514ccSChristoph Hellwig 		for (i = 1; i < num; i++) {
15439cf514ccSChristoph Hellwig 			if (be32_to_cpup(p++)) {
15449cf514ccSChristoph Hellwig 				status = nfserr_inval;
15459cf514ccSChristoph Hellwig 				goto out;
15469cf514ccSChristoph Hellwig 			}
15479cf514ccSChristoph Hellwig 		}
15489cf514ccSChristoph Hellwig 	}
15499cf514ccSChristoph Hellwig 	DECODE_TAIL;
15509cf514ccSChristoph Hellwig }
15519cf514ccSChristoph Hellwig 
15529cf514ccSChristoph Hellwig static __be32
15539cf514ccSChristoph Hellwig nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
15549cf514ccSChristoph Hellwig 		struct nfsd4_layoutget *lgp)
15559cf514ccSChristoph Hellwig {
15569cf514ccSChristoph Hellwig 	DECODE_HEAD;
15579cf514ccSChristoph Hellwig 
15589cf514ccSChristoph Hellwig 	READ_BUF(36);
15599cf514ccSChristoph Hellwig 	lgp->lg_signal = be32_to_cpup(p++);
15609cf514ccSChristoph Hellwig 	lgp->lg_layout_type = be32_to_cpup(p++);
15619cf514ccSChristoph Hellwig 	lgp->lg_seg.iomode = be32_to_cpup(p++);
15629cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lgp->lg_seg.offset);
15639cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lgp->lg_seg.length);
15649cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lgp->lg_minlength);
1565db59c0efSKinglong Mee 
1566db59c0efSKinglong Mee 	status = nfsd4_decode_stateid(argp, &lgp->lg_sid);
1567db59c0efSKinglong Mee 	if (status)
1568db59c0efSKinglong Mee 		return status;
1569db59c0efSKinglong Mee 
15709cf514ccSChristoph Hellwig 	READ_BUF(4);
15719cf514ccSChristoph Hellwig 	lgp->lg_maxcount = be32_to_cpup(p++);
15729cf514ccSChristoph Hellwig 
15739cf514ccSChristoph Hellwig 	DECODE_TAIL;
15749cf514ccSChristoph Hellwig }
15759cf514ccSChristoph Hellwig 
15769cf514ccSChristoph Hellwig static __be32
15779cf514ccSChristoph Hellwig nfsd4_decode_layoutcommit(struct nfsd4_compoundargs *argp,
15789cf514ccSChristoph Hellwig 		struct nfsd4_layoutcommit *lcp)
15799cf514ccSChristoph Hellwig {
15809cf514ccSChristoph Hellwig 	DECODE_HEAD;
15819cf514ccSChristoph Hellwig 	u32 timechange;
15829cf514ccSChristoph Hellwig 
15839cf514ccSChristoph Hellwig 	READ_BUF(20);
15849cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lcp->lc_seg.offset);
15859cf514ccSChristoph Hellwig 	p = xdr_decode_hyper(p, &lcp->lc_seg.length);
15869cf514ccSChristoph Hellwig 	lcp->lc_reclaim = be32_to_cpup(p++);
1587db59c0efSKinglong Mee 
1588db59c0efSKinglong Mee 	status = nfsd4_decode_stateid(argp, &lcp->lc_sid);
1589db59c0efSKinglong Mee 	if (status)
1590db59c0efSKinglong Mee 		return status;
1591db59c0efSKinglong Mee 
15929cf514ccSChristoph Hellwig 	READ_BUF(4);
15939cf514ccSChristoph Hellwig 	lcp->lc_newoffset = be32_to_cpup(p++);
15949cf514ccSChristoph Hellwig 	if (lcp->lc_newoffset) {
15959cf514ccSChristoph Hellwig 		READ_BUF(8);
15969cf514ccSChristoph Hellwig 		p = xdr_decode_hyper(p, &lcp->lc_last_wr);
15979cf514ccSChristoph Hellwig 	} else
15989cf514ccSChristoph Hellwig 		lcp->lc_last_wr = 0;
15999cf514ccSChristoph Hellwig 	READ_BUF(4);
16009cf514ccSChristoph Hellwig 	timechange = be32_to_cpup(p++);
16019cf514ccSChristoph Hellwig 	if (timechange) {
16029cf514ccSChristoph Hellwig 		status = nfsd4_decode_time(argp, &lcp->lc_mtime);
16039cf514ccSChristoph Hellwig 		if (status)
16049cf514ccSChristoph Hellwig 			return status;
16059cf514ccSChristoph Hellwig 	} else {
16069cf514ccSChristoph Hellwig 		lcp->lc_mtime.tv_nsec = UTIME_NOW;
16079cf514ccSChristoph Hellwig 	}
16089cf514ccSChristoph Hellwig 	READ_BUF(8);
16099cf514ccSChristoph Hellwig 	lcp->lc_layout_type = be32_to_cpup(p++);
16109cf514ccSChristoph Hellwig 
16119cf514ccSChristoph Hellwig 	/*
16129cf514ccSChristoph Hellwig 	 * Save the layout update in XDR format and let the layout driver deal
16139cf514ccSChristoph Hellwig 	 * with it later.
16149cf514ccSChristoph Hellwig 	 */
16159cf514ccSChristoph Hellwig 	lcp->lc_up_len = be32_to_cpup(p++);
16169cf514ccSChristoph Hellwig 	if (lcp->lc_up_len > 0) {
16179cf514ccSChristoph Hellwig 		READ_BUF(lcp->lc_up_len);
16189cf514ccSChristoph Hellwig 		READMEM(lcp->lc_up_layout, lcp->lc_up_len);
16199cf514ccSChristoph Hellwig 	}
16209cf514ccSChristoph Hellwig 
16219cf514ccSChristoph Hellwig 	DECODE_TAIL;
16229cf514ccSChristoph Hellwig }
16239cf514ccSChristoph Hellwig 
16249cf514ccSChristoph Hellwig static __be32
16259cf514ccSChristoph Hellwig nfsd4_decode_layoutreturn(struct nfsd4_compoundargs *argp,
16269cf514ccSChristoph Hellwig 		struct nfsd4_layoutreturn *lrp)
16279cf514ccSChristoph Hellwig {
16289cf514ccSChristoph Hellwig 	DECODE_HEAD;
16299cf514ccSChristoph Hellwig 
16309cf514ccSChristoph Hellwig 	READ_BUF(16);
16319cf514ccSChristoph Hellwig 	lrp->lr_reclaim = be32_to_cpup(p++);
16329cf514ccSChristoph Hellwig 	lrp->lr_layout_type = be32_to_cpup(p++);
16339cf514ccSChristoph Hellwig 	lrp->lr_seg.iomode = be32_to_cpup(p++);
16349cf514ccSChristoph Hellwig 	lrp->lr_return_type = be32_to_cpup(p++);
16359cf514ccSChristoph Hellwig 	if (lrp->lr_return_type == RETURN_FILE) {
16369cf514ccSChristoph Hellwig 		READ_BUF(16);
16379cf514ccSChristoph Hellwig 		p = xdr_decode_hyper(p, &lrp->lr_seg.offset);
16389cf514ccSChristoph Hellwig 		p = xdr_decode_hyper(p, &lrp->lr_seg.length);
1639db59c0efSKinglong Mee 
1640db59c0efSKinglong Mee 		status = nfsd4_decode_stateid(argp, &lrp->lr_sid);
1641db59c0efSKinglong Mee 		if (status)
1642db59c0efSKinglong Mee 			return status;
1643db59c0efSKinglong Mee 
16449cf514ccSChristoph Hellwig 		READ_BUF(4);
16459cf514ccSChristoph Hellwig 		lrp->lrf_body_len = be32_to_cpup(p++);
16469cf514ccSChristoph Hellwig 		if (lrp->lrf_body_len > 0) {
16479cf514ccSChristoph Hellwig 			READ_BUF(lrp->lrf_body_len);
16489cf514ccSChristoph Hellwig 			READMEM(lrp->lrf_body, lrp->lrf_body_len);
16499cf514ccSChristoph Hellwig 		}
16509cf514ccSChristoph Hellwig 	} else {
16519cf514ccSChristoph Hellwig 		lrp->lr_seg.offset = 0;
16529cf514ccSChristoph Hellwig 		lrp->lr_seg.length = NFS4_MAX_UINT64;
16539cf514ccSChristoph Hellwig 	}
16549cf514ccSChristoph Hellwig 
16559cf514ccSChristoph Hellwig 	DECODE_TAIL;
16569cf514ccSChristoph Hellwig }
16579cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
16589cf514ccSChristoph Hellwig 
16592db134ebSAndy Adamson static __be32
166095d871f0SAnna Schumaker nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
166195d871f0SAnna Schumaker 		       struct nfsd4_fallocate *fallocate)
166295d871f0SAnna Schumaker {
166395d871f0SAnna Schumaker 	DECODE_HEAD;
166495d871f0SAnna Schumaker 
166595d871f0SAnna Schumaker 	status = nfsd4_decode_stateid(argp, &fallocate->falloc_stateid);
166695d871f0SAnna Schumaker 	if (status)
166795d871f0SAnna Schumaker 		return status;
166895d871f0SAnna Schumaker 
166995d871f0SAnna Schumaker 	READ_BUF(16);
167095d871f0SAnna Schumaker 	p = xdr_decode_hyper(p, &fallocate->falloc_offset);
167195d871f0SAnna Schumaker 	xdr_decode_hyper(p, &fallocate->falloc_length);
167295d871f0SAnna Schumaker 
167395d871f0SAnna Schumaker 	DECODE_TAIL;
167495d871f0SAnna Schumaker }
167595d871f0SAnna Schumaker 
167695d871f0SAnna Schumaker static __be32
167724bab491SAnna Schumaker nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
167824bab491SAnna Schumaker {
167924bab491SAnna Schumaker 	DECODE_HEAD;
168024bab491SAnna Schumaker 
168124bab491SAnna Schumaker 	status = nfsd4_decode_stateid(argp, &seek->seek_stateid);
168224bab491SAnna Schumaker 	if (status)
168324bab491SAnna Schumaker 		return status;
168424bab491SAnna Schumaker 
168524bab491SAnna Schumaker 	READ_BUF(8 + 4);
168624bab491SAnna Schumaker 	p = xdr_decode_hyper(p, &seek->seek_offset);
168724bab491SAnna Schumaker 	seek->seek_whence = be32_to_cpup(p);
168824bab491SAnna Schumaker 
168924bab491SAnna Schumaker 	DECODE_TAIL;
169024bab491SAnna Schumaker }
169124bab491SAnna Schumaker 
169224bab491SAnna Schumaker static __be32
1693347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
1694347e0ad9SBenny Halevy {
1695347e0ad9SBenny Halevy 	return nfs_ok;
1696347e0ad9SBenny Halevy }
1697347e0ad9SBenny Halevy 
16983c375c6fSBenny Halevy static __be32
16993c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p)
17003c375c6fSBenny Halevy {
17011e685ec2SBenny Halevy 	return nfserr_notsupp;
17023c375c6fSBenny Halevy }
17033c375c6fSBenny Halevy 
1704347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *);
1705347e0ad9SBenny Halevy 
1706347e0ad9SBenny Halevy static nfsd4_dec nfsd4_dec_ops[] = {
1707ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
1708ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
1709ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
1710ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
1711ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
1712ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
1713ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
1714ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
1715ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
1716ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
1717ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
1718ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
1719ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
1720ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
1721ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1722ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
1723ad1060c8SJ. Bruce Fields 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
1724ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_open_confirm,
1725ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
1726ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
1727e1a90ebdSAnna Schumaker 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_putpubfh,
1728ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
1729ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
1730ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
1731ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
1732ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
1733ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
1734ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_renew,
1735ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
1736ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
1737ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
1738ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
1739ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_setclientid,
1740ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm,
1741ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1742ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
1743ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_release_lockowner,
17442db134ebSAndy Adamson 
17452db134ebSAndy Adamson 	/* new operations for NFSv4.1 */
1746cb73a9f4SJ. Bruce Fields 	[OP_BACKCHANNEL_CTL]	= (nfsd4_dec)nfsd4_decode_backchannel_ctl,
17471d1bc8f2SJ. Bruce Fields 	[OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session,
17489064caaeSRandy Dunlap 	[OP_EXCHANGE_ID]	= (nfsd4_dec)nfsd4_decode_exchange_id,
17499064caaeSRandy Dunlap 	[OP_CREATE_SESSION]	= (nfsd4_dec)nfsd4_decode_create_session,
17509064caaeSRandy Dunlap 	[OP_DESTROY_SESSION]	= (nfsd4_dec)nfsd4_decode_destroy_session,
1751e1ca12dfSBryan Schumaker 	[OP_FREE_STATEID]	= (nfsd4_dec)nfsd4_decode_free_stateid,
17529064caaeSRandy Dunlap 	[OP_GET_DIR_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
17539cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
17549cf514ccSChristoph Hellwig 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_getdeviceinfo,
17559cf514ccSChristoph Hellwig 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
17569cf514ccSChristoph Hellwig 	[OP_LAYOUTCOMMIT]	= (nfsd4_dec)nfsd4_decode_layoutcommit,
17579cf514ccSChristoph Hellwig 	[OP_LAYOUTGET]		= (nfsd4_dec)nfsd4_decode_layoutget,
17589cf514ccSChristoph Hellwig 	[OP_LAYOUTRETURN]	= (nfsd4_dec)nfsd4_decode_layoutreturn,
17599cf514ccSChristoph Hellwig #else
17609064caaeSRandy Dunlap 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_notsupp,
17619064caaeSRandy Dunlap 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
17629064caaeSRandy Dunlap 	[OP_LAYOUTCOMMIT]	= (nfsd4_dec)nfsd4_decode_notsupp,
17639064caaeSRandy Dunlap 	[OP_LAYOUTGET]		= (nfsd4_dec)nfsd4_decode_notsupp,
17649064caaeSRandy Dunlap 	[OP_LAYOUTRETURN]	= (nfsd4_dec)nfsd4_decode_notsupp,
17659cf514ccSChristoph Hellwig #endif
176604f4ad16SJ. Bruce Fields 	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_secinfo_no_name,
17679064caaeSRandy Dunlap 	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
17689064caaeSRandy Dunlap 	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
176917456804SBryan Schumaker 	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_test_stateid,
17709064caaeSRandy Dunlap 	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
1771345c2842SMi Jinlong 	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_destroy_clientid,
17724dc6ec00SJ. Bruce Fields 	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
177387a15a80SAnna Schumaker 
177487a15a80SAnna Schumaker 	/* new operations for NFSv4.2 */
177595d871f0SAnna Schumaker 	[OP_ALLOCATE]		= (nfsd4_dec)nfsd4_decode_fallocate,
177687a15a80SAnna Schumaker 	[OP_COPY]		= (nfsd4_dec)nfsd4_decode_notsupp,
177787a15a80SAnna Schumaker 	[OP_COPY_NOTIFY]	= (nfsd4_dec)nfsd4_decode_notsupp,
1778b0cb9085SAnna Schumaker 	[OP_DEALLOCATE]		= (nfsd4_dec)nfsd4_decode_fallocate,
177987a15a80SAnna Schumaker 	[OP_IO_ADVISE]		= (nfsd4_dec)nfsd4_decode_notsupp,
178087a15a80SAnna Schumaker 	[OP_LAYOUTERROR]	= (nfsd4_dec)nfsd4_decode_notsupp,
178187a15a80SAnna Schumaker 	[OP_LAYOUTSTATS]	= (nfsd4_dec)nfsd4_decode_notsupp,
178287a15a80SAnna Schumaker 	[OP_OFFLOAD_CANCEL]	= (nfsd4_dec)nfsd4_decode_notsupp,
178387a15a80SAnna Schumaker 	[OP_OFFLOAD_STATUS]	= (nfsd4_dec)nfsd4_decode_notsupp,
178487a15a80SAnna Schumaker 	[OP_READ_PLUS]		= (nfsd4_dec)nfsd4_decode_notsupp,
178524bab491SAnna Schumaker 	[OP_SEEK]		= (nfsd4_dec)nfsd4_decode_seek,
178687a15a80SAnna Schumaker 	[OP_WRITE_SAME]		= (nfsd4_dec)nfsd4_decode_notsupp,
17872db134ebSAndy Adamson };
17882db134ebSAndy Adamson 
1789e1a90ebdSAnna Schumaker static inline bool
1790e1a90ebdSAnna Schumaker nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
1791e1a90ebdSAnna Schumaker {
17928217d146SAnna Schumaker 	if (op->opnum < FIRST_NFS4_OP)
1793e1a90ebdSAnna Schumaker 		return false;
17948217d146SAnna Schumaker 	else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
1795e1a90ebdSAnna Schumaker 		return false;
17968217d146SAnna Schumaker 	else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
17978217d146SAnna Schumaker 		return false;
17988217d146SAnna Schumaker 	else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
1799e1a90ebdSAnna Schumaker 		return false;
1800e1a90ebdSAnna Schumaker 	return true;
1801e1a90ebdSAnna Schumaker }
1802f2feb96bSBenny Halevy 
1803347e0ad9SBenny Halevy static __be32
18041da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
18051da177e4SLinus Torvalds {
18061da177e4SLinus Torvalds 	DECODE_HEAD;
18071da177e4SLinus Torvalds 	struct nfsd4_op *op;
18081091006cSJ. Bruce Fields 	bool cachethis = false;
1809a5cddc88SJ. Bruce Fields 	int auth_slack= argp->rqstp->rq_auth_slack;
1810a5cddc88SJ. Bruce Fields 	int max_reply = auth_slack + 8; /* opcnt, status */
1811b0e35fdaSJ. Bruce Fields 	int readcount = 0;
1812b0e35fdaSJ. Bruce Fields 	int readbytes = 0;
18131da177e4SLinus Torvalds 	int i;
18141da177e4SLinus Torvalds 
18151da177e4SLinus Torvalds 	READ_BUF(4);
181606553991SJ. Bruce Fields 	argp->taglen = be32_to_cpup(p++);
18171da177e4SLinus Torvalds 	READ_BUF(argp->taglen + 8);
18181da177e4SLinus Torvalds 	SAVEMEM(argp->tag, argp->taglen);
181906553991SJ. Bruce Fields 	argp->minorversion = be32_to_cpup(p++);
182006553991SJ. Bruce Fields 	argp->opcnt = be32_to_cpup(p++);
18214f0cefbfSJ. Bruce Fields 	max_reply += 4 + (XDR_QUADLEN(argp->taglen) << 2);
18221da177e4SLinus Torvalds 
18231da177e4SLinus Torvalds 	if (argp->taglen > NFSD4_MAX_TAGLEN)
18241da177e4SLinus Torvalds 		goto xdr_error;
18251da177e4SLinus Torvalds 	if (argp->opcnt > 100)
18261da177e4SLinus Torvalds 		goto xdr_error;
18271da177e4SLinus Torvalds 
1828e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
18295d6031caSJ. Bruce Fields 		argp->ops = kzalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
18301da177e4SLinus Torvalds 		if (!argp->ops) {
18311da177e4SLinus Torvalds 			argp->ops = argp->iops;
1832817cb9d4SChuck Lever 			dprintk("nfsd: couldn't allocate room for COMPOUND\n");
18331da177e4SLinus Torvalds 			goto xdr_error;
18341da177e4SLinus Torvalds 		}
18351da177e4SLinus Torvalds 	}
18361da177e4SLinus Torvalds 
1837e1a90ebdSAnna Schumaker 	if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
183830cff1ffSBenny Halevy 		argp->opcnt = 0;
183930cff1ffSBenny Halevy 
18401da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
18411da177e4SLinus Torvalds 		op = &argp->ops[i];
18421da177e4SLinus Torvalds 		op->replay = NULL;
18431da177e4SLinus Torvalds 
18448a61b18cSJ. Bruce Fields 		READ_BUF(4);
184506553991SJ. Bruce Fields 		op->opnum = be32_to_cpup(p++);
18461da177e4SLinus Torvalds 
1847e1a90ebdSAnna Schumaker 		if (nfsd4_opnum_in_range(argp, op))
1848e1a90ebdSAnna Schumaker 			op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
1849347e0ad9SBenny Halevy 		else {
18501da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
18511da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
18521da177e4SLinus Torvalds 		}
18531091006cSJ. Bruce Fields 		/*
18541091006cSJ. Bruce Fields 		 * We'll try to cache the result in the DRC if any one
18551091006cSJ. Bruce Fields 		 * op in the compound wants to be cached:
18561091006cSJ. Bruce Fields 		 */
18571091006cSJ. Bruce Fields 		cachethis |= nfsd4_cache_this_op(op);
18586ff40decSJ. Bruce Fields 
1859b0e35fdaSJ. Bruce Fields 		if (op->opnum == OP_READ) {
1860b0e35fdaSJ. Bruce Fields 			readcount++;
1861b0e35fdaSJ. Bruce Fields 			readbytes += nfsd4_max_reply(argp->rqstp, op);
1862b0e35fdaSJ. Bruce Fields 		} else
18634f0cefbfSJ. Bruce Fields 			max_reply += nfsd4_max_reply(argp->rqstp, op);
1864f7b43d0cSJ. Bruce Fields 		/*
1865f7b43d0cSJ. Bruce Fields 		 * OP_LOCK may return a conflicting lock.  (Special case
1866f7b43d0cSJ. Bruce Fields 		 * because it will just skip encoding this if it runs
1867f7b43d0cSJ. Bruce Fields 		 * out of xdr buffer space, and it is the only operation
1868f7b43d0cSJ. Bruce Fields 		 * that behaves this way.)
1869f7b43d0cSJ. Bruce Fields 		 */
1870f7b43d0cSJ. Bruce Fields 		if (op->opnum == OP_LOCK)
1871f7b43d0cSJ. Bruce Fields 			max_reply += NFS4_OPAQUE_LIMIT;
1872e372ba60SJ. Bruce Fields 
1873e372ba60SJ. Bruce Fields 		if (op->status) {
1874e372ba60SJ. Bruce Fields 			argp->opcnt = i+1;
1875e372ba60SJ. Bruce Fields 			break;
1876e372ba60SJ. Bruce Fields 		}
18771da177e4SLinus Torvalds 	}
18781091006cSJ. Bruce Fields 	/* Sessions make the DRC unnecessary: */
18791091006cSJ. Bruce Fields 	if (argp->minorversion)
18801091006cSJ. Bruce Fields 		cachethis = false;
1881b0e35fdaSJ. Bruce Fields 	svc_reserve(argp->rqstp, max_reply + readbytes);
18821091006cSJ. Bruce Fields 	argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
18831da177e4SLinus Torvalds 
1884a5cddc88SJ. Bruce Fields 	if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
1885779fb0f3SJeff Layton 		clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags);
1886b0e35fdaSJ. Bruce Fields 
18871da177e4SLinus Torvalds 	DECODE_TAIL;
18881da177e4SLinus Torvalds }
18891da177e4SLinus Torvalds 
1890d05d5744SJ. Bruce Fields static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode)
1891c654b8a9SJ. Bruce Fields {
1892c654b8a9SJ. Bruce Fields 	if (IS_I_VERSION(inode)) {
1893d05d5744SJ. Bruce Fields 		p = xdr_encode_hyper(p, inode->i_version);
1894c654b8a9SJ. Bruce Fields 	} else {
1895d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(stat->ctime.tv_sec);
1896d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(stat->ctime.tv_nsec);
1897c654b8a9SJ. Bruce Fields 	}
1898d05d5744SJ. Bruce Fields 	return p;
1899c654b8a9SJ. Bruce Fields }
1900c654b8a9SJ. Bruce Fields 
1901d05d5744SJ. Bruce Fields static __be32 *encode_cinfo(__be32 *p, struct nfsd4_change_info *c)
1902c654b8a9SJ. Bruce Fields {
1903d05d5744SJ. Bruce Fields 	*p++ = cpu_to_be32(c->atomic);
1904c654b8a9SJ. Bruce Fields 	if (c->change_supported) {
1905d05d5744SJ. Bruce Fields 		p = xdr_encode_hyper(p, c->before_change);
1906d05d5744SJ. Bruce Fields 		p = xdr_encode_hyper(p, c->after_change);
1907c654b8a9SJ. Bruce Fields 	} else {
1908d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->before_ctime_sec);
1909d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->before_ctime_nsec);
1910d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->after_ctime_sec);
1911d05d5744SJ. Bruce Fields 		*p++ = cpu_to_be32(c->after_ctime_nsec);
1912c654b8a9SJ. Bruce Fields 	}
1913d05d5744SJ. Bruce Fields 	return p;
1914c654b8a9SJ. Bruce Fields }
19151da177e4SLinus Torvalds 
191681c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
1917e7a0444aSWeston Andros Adamson  * separated @sep, escaped with esc_enter and esc_exit.
191881c3f413SJ.Bruce Fields  */
1919ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
1920ddd1ea56SJ. Bruce Fields 					  char *components, char esc_enter,
1921ddd1ea56SJ. Bruce Fields 					  char esc_exit)
192281c3f413SJ.Bruce Fields {
1923ddd1ea56SJ. Bruce Fields 	__be32 *p;
1924082d4bd7SJ. Bruce Fields 	__be32 pathlen;
1925082d4bd7SJ. Bruce Fields 	int pathlen_offset;
192681c3f413SJ.Bruce Fields 	int strlen, count=0;
1927e7a0444aSWeston Andros Adamson 	char *str, *end, *next;
192881c3f413SJ.Bruce Fields 
192981c3f413SJ.Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
1930082d4bd7SJ. Bruce Fields 
1931082d4bd7SJ. Bruce Fields 	pathlen_offset = xdr->buf->len;
1932ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
1933ddd1ea56SJ. Bruce Fields 	if (!p)
193481c3f413SJ.Bruce Fields 		return nfserr_resource;
1935082d4bd7SJ. Bruce Fields 	p++; /* We will fill this in with @count later */
1936082d4bd7SJ. Bruce Fields 
193781c3f413SJ.Bruce Fields 	end = str = components;
193881c3f413SJ.Bruce Fields 	while (*end) {
1939e7a0444aSWeston Andros Adamson 		bool found_esc = false;
1940e7a0444aSWeston Andros Adamson 
1941e7a0444aSWeston Andros Adamson 		/* try to parse as esc_start, ..., esc_end, sep */
1942e7a0444aSWeston Andros Adamson 		if (*str == esc_enter) {
1943e7a0444aSWeston Andros Adamson 			for (; *end && (*end != esc_exit); end++)
1944e7a0444aSWeston Andros Adamson 				/* find esc_exit or end of string */;
1945e7a0444aSWeston Andros Adamson 			next = end + 1;
1946e7a0444aSWeston Andros Adamson 			if (*end && (!*next || *next == sep)) {
1947e7a0444aSWeston Andros Adamson 				str++;
1948e7a0444aSWeston Andros Adamson 				found_esc = true;
1949e7a0444aSWeston Andros Adamson 			}
1950e7a0444aSWeston Andros Adamson 		}
1951e7a0444aSWeston Andros Adamson 
1952e7a0444aSWeston Andros Adamson 		if (!found_esc)
195381c3f413SJ.Bruce Fields 			for (; *end && (*end != sep); end++)
1954e7a0444aSWeston Andros Adamson 				/* find sep or end of string */;
1955e7a0444aSWeston Andros Adamson 
195681c3f413SJ.Bruce Fields 		strlen = end - str;
195781c3f413SJ.Bruce Fields 		if (strlen) {
1958ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, strlen + 4);
1959ddd1ea56SJ. Bruce Fields 			if (!p)
196081c3f413SJ.Bruce Fields 				return nfserr_resource;
19610c0c267bSJ. Bruce Fields 			p = xdr_encode_opaque(p, str, strlen);
196281c3f413SJ.Bruce Fields 			count++;
196381c3f413SJ.Bruce Fields 		}
196481c3f413SJ.Bruce Fields 		else
196581c3f413SJ.Bruce Fields 			end++;
19665a64e569SBenjamin Coddington 		if (found_esc)
19675a64e569SBenjamin Coddington 			end = next;
19685a64e569SBenjamin Coddington 
196981c3f413SJ.Bruce Fields 		str = end;
197081c3f413SJ.Bruce Fields 	}
1971bf7491f1SBenjamin Coddington 	pathlen = htonl(count);
1972082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
197381c3f413SJ.Bruce Fields 	return 0;
197481c3f413SJ.Bruce Fields }
197581c3f413SJ.Bruce Fields 
1976e7a0444aSWeston Andros Adamson /* Encode as an array of strings the string given with components
1977e7a0444aSWeston Andros Adamson  * separated @sep.
1978e7a0444aSWeston Andros Adamson  */
1979ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
1980ddd1ea56SJ. Bruce Fields 				      char *components)
1981e7a0444aSWeston Andros Adamson {
1982ddd1ea56SJ. Bruce Fields 	return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
1983e7a0444aSWeston Andros Adamson }
1984e7a0444aSWeston Andros Adamson 
198581c3f413SJ.Bruce Fields /*
198681c3f413SJ.Bruce Fields  * encode a location element of a fs_locations structure
198781c3f413SJ.Bruce Fields  */
1988ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
1989ddd1ea56SJ. Bruce Fields 					struct nfsd4_fs_location *location)
199081c3f413SJ.Bruce Fields {
1991b37ad28bSAl Viro 	__be32 status;
199281c3f413SJ.Bruce Fields 
1993ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
1994e7a0444aSWeston Andros Adamson 						'[', ']');
199581c3f413SJ.Bruce Fields 	if (status)
199681c3f413SJ.Bruce Fields 		return status;
1997ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_components(xdr, '/', location->path);
199881c3f413SJ.Bruce Fields 	if (status)
199981c3f413SJ.Bruce Fields 		return status;
200081c3f413SJ.Bruce Fields 	return 0;
200181c3f413SJ.Bruce Fields }
200281c3f413SJ.Bruce Fields 
200381c3f413SJ.Bruce Fields /*
2004ed748aacSTrond Myklebust  * Encode a path in RFC3530 'pathname4' format
200581c3f413SJ.Bruce Fields  */
2006ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_path(struct xdr_stream *xdr,
2007ddd1ea56SJ. Bruce Fields 				const struct path *root,
2008ddd1ea56SJ. Bruce Fields 				const struct path *path)
200981c3f413SJ.Bruce Fields {
2010301f0268SAl Viro 	struct path cur = *path;
2011ddd1ea56SJ. Bruce Fields 	__be32 *p;
2012ed748aacSTrond Myklebust 	struct dentry **components = NULL;
2013ed748aacSTrond Myklebust 	unsigned int ncomponents = 0;
2014ed748aacSTrond Myklebust 	__be32 err = nfserr_jukebox;
201581c3f413SJ.Bruce Fields 
2016ed748aacSTrond Myklebust 	dprintk("nfsd4_encode_components(");
201781c3f413SJ.Bruce Fields 
2018ed748aacSTrond Myklebust 	path_get(&cur);
2019ed748aacSTrond Myklebust 	/* First walk the path up to the nfsd root, and store the
2020ed748aacSTrond Myklebust 	 * dentries/path components in an array.
2021ed748aacSTrond Myklebust 	 */
2022ed748aacSTrond Myklebust 	for (;;) {
2023b77a4b2eSKinglong Mee 		if (path_equal(&cur, root))
2024ed748aacSTrond Myklebust 			break;
2025ed748aacSTrond Myklebust 		if (cur.dentry == cur.mnt->mnt_root) {
2026ed748aacSTrond Myklebust 			if (follow_up(&cur))
2027ed748aacSTrond Myklebust 				continue;
2028ed748aacSTrond Myklebust 			goto out_free;
202981c3f413SJ.Bruce Fields 		}
2030ed748aacSTrond Myklebust 		if ((ncomponents & 15) == 0) {
2031ed748aacSTrond Myklebust 			struct dentry **new;
2032ed748aacSTrond Myklebust 			new = krealloc(components,
2033ed748aacSTrond Myklebust 					sizeof(*new) * (ncomponents + 16),
2034ed748aacSTrond Myklebust 					GFP_KERNEL);
2035ed748aacSTrond Myklebust 			if (!new)
2036ed748aacSTrond Myklebust 				goto out_free;
2037ed748aacSTrond Myklebust 			components = new;
2038ed748aacSTrond Myklebust 		}
2039ed748aacSTrond Myklebust 		components[ncomponents++] = cur.dentry;
2040ed748aacSTrond Myklebust 		cur.dentry = dget_parent(cur.dentry);
2041ed748aacSTrond Myklebust 	}
2042ddd1ea56SJ. Bruce Fields 	err = nfserr_resource;
2043ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2044ddd1ea56SJ. Bruce Fields 	if (!p)
2045ed748aacSTrond Myklebust 		goto out_free;
2046c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(ncomponents);
2047ed748aacSTrond Myklebust 
2048ed748aacSTrond Myklebust 	while (ncomponents) {
2049ed748aacSTrond Myklebust 		struct dentry *dentry = components[ncomponents - 1];
2050301f0268SAl Viro 		unsigned int len;
2051ed748aacSTrond Myklebust 
2052301f0268SAl Viro 		spin_lock(&dentry->d_lock);
2053301f0268SAl Viro 		len = dentry->d_name.len;
2054ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, len + 4);
2055ddd1ea56SJ. Bruce Fields 		if (!p) {
2056301f0268SAl Viro 			spin_unlock(&dentry->d_lock);
2057ed748aacSTrond Myklebust 			goto out_free;
2058301f0268SAl Viro 		}
20590c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, dentry->d_name.name, len);
2060a455589fSAl Viro 		dprintk("/%pd", dentry);
2061301f0268SAl Viro 		spin_unlock(&dentry->d_lock);
2062ed748aacSTrond Myklebust 		dput(dentry);
2063ed748aacSTrond Myklebust 		ncomponents--;
2064ed748aacSTrond Myklebust 	}
2065ed748aacSTrond Myklebust 
2066ed748aacSTrond Myklebust 	err = 0;
2067ed748aacSTrond Myklebust out_free:
2068ed748aacSTrond Myklebust 	dprintk(")\n");
2069ed748aacSTrond Myklebust 	while (ncomponents)
2070ed748aacSTrond Myklebust 		dput(components[--ncomponents]);
2071ed748aacSTrond Myklebust 	kfree(components);
2072ed748aacSTrond Myklebust 	path_put(&cur);
2073ed748aacSTrond Myklebust 	return err;
2074ed748aacSTrond Myklebust }
2075ed748aacSTrond Myklebust 
2076ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr,
2077ddd1ea56SJ. Bruce Fields 			struct svc_rqst *rqstp, const struct path *path)
2078ed748aacSTrond Myklebust {
2079ed748aacSTrond Myklebust 	struct svc_export *exp_ps;
2080ed748aacSTrond Myklebust 	__be32 res;
2081ed748aacSTrond Myklebust 
2082ed748aacSTrond Myklebust 	exp_ps = rqst_find_fsidzero_export(rqstp);
2083ed748aacSTrond Myklebust 	if (IS_ERR(exp_ps))
2084ed748aacSTrond Myklebust 		return nfserrno(PTR_ERR(exp_ps));
2085ddd1ea56SJ. Bruce Fields 	res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
2086ed748aacSTrond Myklebust 	exp_put(exp_ps);
2087ed748aacSTrond Myklebust 	return res;
208881c3f413SJ.Bruce Fields }
208981c3f413SJ.Bruce Fields 
209081c3f413SJ.Bruce Fields /*
209181c3f413SJ.Bruce Fields  *  encode a fs_locations structure
209281c3f413SJ.Bruce Fields  */
2093ddd1ea56SJ. Bruce Fields static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr,
2094ddd1ea56SJ. Bruce Fields 			struct svc_rqst *rqstp, struct svc_export *exp)
209581c3f413SJ.Bruce Fields {
2096b37ad28bSAl Viro 	__be32 status;
2097cc45f017SAl Viro 	int i;
2098ddd1ea56SJ. Bruce Fields 	__be32 *p;
209981c3f413SJ.Bruce Fields 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
210081c3f413SJ.Bruce Fields 
2101ddd1ea56SJ. Bruce Fields 	status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
210281c3f413SJ.Bruce Fields 	if (status)
210381c3f413SJ.Bruce Fields 		return status;
2104ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2105ddd1ea56SJ. Bruce Fields 	if (!p)
210681c3f413SJ.Bruce Fields 		return nfserr_resource;
2107c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(fslocs->locations_count);
210881c3f413SJ.Bruce Fields 	for (i=0; i<fslocs->locations_count; i++) {
2109ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
211081c3f413SJ.Bruce Fields 		if (status)
211181c3f413SJ.Bruce Fields 			return status;
211281c3f413SJ.Bruce Fields 	}
211381c3f413SJ.Bruce Fields 	return 0;
211481c3f413SJ.Bruce Fields }
21151da177e4SLinus Torvalds 
21163d2544b1SJ. Bruce Fields static u32 nfs4_file_type(umode_t mode)
21173d2544b1SJ. Bruce Fields {
21183d2544b1SJ. Bruce Fields 	switch (mode & S_IFMT) {
21193d2544b1SJ. Bruce Fields 	case S_IFIFO:	return NF4FIFO;
21203d2544b1SJ. Bruce Fields 	case S_IFCHR:	return NF4CHR;
21213d2544b1SJ. Bruce Fields 	case S_IFDIR:	return NF4DIR;
21223d2544b1SJ. Bruce Fields 	case S_IFBLK:	return NF4BLK;
21233d2544b1SJ. Bruce Fields 	case S_IFLNK:	return NF4LNK;
21243d2544b1SJ. Bruce Fields 	case S_IFREG:	return NF4REG;
21253d2544b1SJ. Bruce Fields 	case S_IFSOCK:	return NF4SOCK;
21263d2544b1SJ. Bruce Fields 	default:	return NF4BAD;
21271da177e4SLinus Torvalds 	};
21283d2544b1SJ. Bruce Fields }
21291da177e4SLinus Torvalds 
2130b37ad28bSAl Viro static inline __be32
2131ddd1ea56SJ. Bruce Fields nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2132ddd1ea56SJ. Bruce Fields 		     struct nfs4_ace *ace)
21331da177e4SLinus Torvalds {
21343554116dSJ. Bruce Fields 	if (ace->whotype != NFS4_ACL_WHO_NAMED)
2135ddd1ea56SJ. Bruce Fields 		return nfs4_acl_write_who(xdr, ace->whotype);
21363554116dSJ. Bruce Fields 	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
2137ddd1ea56SJ. Bruce Fields 		return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
2138ab8e4aeeSEric W. Biederman 	else
2139ddd1ea56SJ. Bruce Fields 		return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
21401da177e4SLinus Torvalds }
21411da177e4SLinus Torvalds 
214242ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
214342ca0993SJ.Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
214442ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
214542ca0993SJ.Bruce Fields 
214618032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
214718032ca0SDavid Quigley static inline __be32
2148ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2149ddd1ea56SJ. Bruce Fields 			    void *context, int len)
215018032ca0SDavid Quigley {
2151ddd1ea56SJ. Bruce Fields 	__be32 *p;
215218032ca0SDavid Quigley 
2153ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
2154ddd1ea56SJ. Bruce Fields 	if (!p)
215518032ca0SDavid Quigley 		return nfserr_resource;
215618032ca0SDavid Quigley 
215718032ca0SDavid Quigley 	/*
215818032ca0SDavid Quigley 	 * For now we use a 0 here to indicate the null translation; in
215918032ca0SDavid Quigley 	 * the future we may place a call to translation code here.
216018032ca0SDavid Quigley 	 */
2161c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* lfs */
2162c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* pi */
216318032ca0SDavid Quigley 	p = xdr_encode_opaque(p, context, len);
216418032ca0SDavid Quigley 	return 0;
216518032ca0SDavid Quigley }
216618032ca0SDavid Quigley #else
216718032ca0SDavid Quigley static inline __be32
2168ddd1ea56SJ. Bruce Fields nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
2169ddd1ea56SJ. Bruce Fields 			    void *context, int len)
217018032ca0SDavid Quigley { return 0; }
217118032ca0SDavid Quigley #endif
217218032ca0SDavid Quigley 
2173b37ad28bSAl Viro static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
217442ca0993SJ.Bruce Fields {
217542ca0993SJ.Bruce Fields 	/* As per referral draft:  */
217642ca0993SJ.Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
217742ca0993SJ.Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
217842ca0993SJ.Bruce Fields 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
217942ca0993SJ.Bruce Fields 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
218042ca0993SJ.Bruce Fields 			*rdattr_err = NFSERR_MOVED;
218142ca0993SJ.Bruce Fields 		else
218242ca0993SJ.Bruce Fields 			return nfserr_moved;
218342ca0993SJ.Bruce Fields 	}
218442ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
218542ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
218642ca0993SJ.Bruce Fields 	return 0;
218742ca0993SJ.Bruce Fields }
21881da177e4SLinus Torvalds 
2189ae7095a7SJ. Bruce Fields 
2190ae7095a7SJ. Bruce Fields static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
2191ae7095a7SJ. Bruce Fields {
2192ae7095a7SJ. Bruce Fields 	struct path path = exp->ex_path;
2193ae7095a7SJ. Bruce Fields 	int err;
2194ae7095a7SJ. Bruce Fields 
2195ae7095a7SJ. Bruce Fields 	path_get(&path);
2196ae7095a7SJ. Bruce Fields 	while (follow_up(&path)) {
2197ae7095a7SJ. Bruce Fields 		if (path.dentry != path.mnt->mnt_root)
2198ae7095a7SJ. Bruce Fields 			break;
2199ae7095a7SJ. Bruce Fields 	}
22003dadecceSAl Viro 	err = vfs_getattr(&path, stat);
2201ae7095a7SJ. Bruce Fields 	path_put(&path);
2202ae7095a7SJ. Bruce Fields 	return err;
2203ae7095a7SJ. Bruce Fields }
2204ae7095a7SJ. Bruce Fields 
22051da177e4SLinus Torvalds /*
22061da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
22071da177e4SLinus Torvalds  * ourselves.
22081da177e4SLinus Torvalds  */
2209da2ebce6SJeff Layton static __be32
2210d5184658SJ. Bruce Fields nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
2211d5184658SJ. Bruce Fields 		struct svc_export *exp,
2212d5184658SJ. Bruce Fields 		struct dentry *dentry, u32 *bmval,
2213406a7ea9SFrank Filz 		struct svc_rqst *rqstp, int ignore_crossmnt)
22141da177e4SLinus Torvalds {
22151da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
22161da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
22177e705706SAndy Adamson 	u32 bmval2 = bmval[2];
22181da177e4SLinus Torvalds 	struct kstat stat;
2219d50e6136SJ. Bruce Fields 	struct svc_fh *tempfh = NULL;
22201da177e4SLinus Torvalds 	struct kstatfs statfs;
2221ddd1ea56SJ. Bruce Fields 	__be32 *p;
22221fcea5b2SJ. Bruce Fields 	int starting_len = xdr->buf->len;
2223082d4bd7SJ. Bruce Fields 	int attrlen_offset;
2224082d4bd7SJ. Bruce Fields 	__be32 attrlen;
22251da177e4SLinus Torvalds 	u32 dummy;
22261da177e4SLinus Torvalds 	u64 dummy64;
222742ca0993SJ.Bruce Fields 	u32 rdattr_err = 0;
2228b37ad28bSAl Viro 	__be32 status;
2229b8dd7b9aSAl Viro 	int err;
22301da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
223118032ca0SDavid Quigley 	void *context = NULL;
223218032ca0SDavid Quigley 	int contextlen;
223318032ca0SDavid Quigley 	bool contextsupport = false;
22347e705706SAndy Adamson 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
22357e705706SAndy Adamson 	u32 minorversion = resp->cstate.minorversion;
2236ebabe9a9SChristoph Hellwig 	struct path path = {
2237ebabe9a9SChristoph Hellwig 		.mnt	= exp->ex_path.mnt,
2238ebabe9a9SChristoph Hellwig 		.dentry	= dentry,
2239ebabe9a9SChristoph Hellwig 	};
22403d733711SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
22411da177e4SLinus Torvalds 
22421da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
22437e705706SAndy Adamson 	BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion));
22447e705706SAndy Adamson 	BUG_ON(bmval1 & ~nfsd_suppattrs1(minorversion));
22457e705706SAndy Adamson 	BUG_ON(bmval2 & ~nfsd_suppattrs2(minorversion));
22461da177e4SLinus Torvalds 
224742ca0993SJ.Bruce Fields 	if (exp->ex_fslocs.migrated) {
22487e705706SAndy Adamson 		BUG_ON(bmval[2]);
224942ca0993SJ.Bruce Fields 		status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err);
225042ca0993SJ.Bruce Fields 		if (status)
225142ca0993SJ.Bruce Fields 			goto out;
225242ca0993SJ.Bruce Fields 	}
225342ca0993SJ.Bruce Fields 
22543dadecceSAl Viro 	err = vfs_getattr(&path, &stat);
2255b8dd7b9aSAl Viro 	if (err)
22561da177e4SLinus Torvalds 		goto out_nfserr;
225712337901SChristoph Hellwig 	if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
225812337901SChristoph Hellwig 			FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
22591da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
22601da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
2261ebabe9a9SChristoph Hellwig 		err = vfs_statfs(&path, &statfs);
2262b8dd7b9aSAl Viro 		if (err)
22631da177e4SLinus Torvalds 			goto out_nfserr;
22641da177e4SLinus Torvalds 	}
22651da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
2266d50e6136SJ. Bruce Fields 		tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
2267d50e6136SJ. Bruce Fields 		status = nfserr_jukebox;
2268d50e6136SJ. Bruce Fields 		if (!tempfh)
2269d50e6136SJ. Bruce Fields 			goto out;
2270d50e6136SJ. Bruce Fields 		fh_init(tempfh, NFS4_FHSIZE);
2271d50e6136SJ. Bruce Fields 		status = fh_compose(tempfh, exp, dentry, NULL);
22721da177e4SLinus Torvalds 		if (status)
22731da177e4SLinus Torvalds 			goto out;
2274d50e6136SJ. Bruce Fields 		fhp = tempfh;
22751da177e4SLinus Torvalds 	}
22761da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
22770c9d65e7SAndreas Gruenbacher 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
2278b8dd7b9aSAl Viro 		if (err == -EOPNOTSUPP)
22791da177e4SLinus Torvalds 			bmval0 &= ~FATTR4_WORD0_ACL;
2280b8dd7b9aSAl Viro 		else if (err == -EINVAL) {
22811da177e4SLinus Torvalds 			status = nfserr_attrnotsupp;
22821da177e4SLinus Torvalds 			goto out;
2283b8dd7b9aSAl Viro 		} else if (err != 0)
22841da177e4SLinus Torvalds 			goto out_nfserr;
22851da177e4SLinus Torvalds 	}
22862b44f1baSBenny Halevy 
228718032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
228818032ca0SDavid Quigley 	if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) ||
228918032ca0SDavid Quigley 			bmval[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
22902b0143b5SDavid Howells 		err = security_inode_getsecctx(d_inode(dentry),
229118032ca0SDavid Quigley 						&context, &contextlen);
229218032ca0SDavid Quigley 		contextsupport = (err == 0);
229318032ca0SDavid Quigley 		if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
229418032ca0SDavid Quigley 			if (err == -EOPNOTSUPP)
229518032ca0SDavid Quigley 				bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
229618032ca0SDavid Quigley 			else if (err)
229718032ca0SDavid Quigley 				goto out_nfserr;
229818032ca0SDavid Quigley 		}
229918032ca0SDavid Quigley 	}
230018032ca0SDavid Quigley #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
230118032ca0SDavid Quigley 
23022b44f1baSBenny Halevy 	if (bmval2) {
2303ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
2304ddd1ea56SJ. Bruce Fields 		if (!p)
23051da177e4SLinus Torvalds 			goto out_resource;
2306c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
2307c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bmval0);
2308c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bmval1);
2309c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bmval2);
23102b44f1baSBenny Halevy 	} else if (bmval1) {
2311ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
2312ddd1ea56SJ. Bruce Fields 		if (!p)
23132b44f1baSBenny Halevy 			goto out_resource;
2314c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(2);
2315c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bmval0);
2316c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bmval1);
23177e705706SAndy Adamson 	} else {
2318ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2319ddd1ea56SJ. Bruce Fields 		if (!p)
23202b44f1baSBenny Halevy 			goto out_resource;
2321c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
2322c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bmval0);
23237e705706SAndy Adamson 	}
2324082d4bd7SJ. Bruce Fields 
2325082d4bd7SJ. Bruce Fields 	attrlen_offset = xdr->buf->len;
2326ddd1ea56SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2327ddd1ea56SJ. Bruce Fields 	if (!p)
2328ddd1ea56SJ. Bruce Fields 		goto out_resource;
2329082d4bd7SJ. Bruce Fields 	p++;                /* to be backfilled later */
23301da177e4SLinus Torvalds 
23311da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
23327e705706SAndy Adamson 		u32 word0 = nfsd_suppattrs0(minorversion);
23337e705706SAndy Adamson 		u32 word1 = nfsd_suppattrs1(minorversion);
23347e705706SAndy Adamson 		u32 word2 = nfsd_suppattrs2(minorversion);
23357e705706SAndy Adamson 
23360c9d65e7SAndreas Gruenbacher 		if (!IS_POSIXACL(dentry->d_inode))
233742ca0993SJ.Bruce Fields 			word0 &= ~FATTR4_WORD0_ACL;
233818032ca0SDavid Quigley 		if (!contextsupport)
233918032ca0SDavid Quigley 			word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
23407e705706SAndy Adamson 		if (!word2) {
2341ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 12);
2342ddd1ea56SJ. Bruce Fields 			if (!p)
23432b44f1baSBenny Halevy 				goto out_resource;
2344c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(2);
2345c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(word0);
2346c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(word1);
23477e705706SAndy Adamson 		} else {
2348ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 16);
2349ddd1ea56SJ. Bruce Fields 			if (!p)
23502b44f1baSBenny Halevy 				goto out_resource;
2351c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(3);
2352c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(word0);
2353c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(word1);
2354c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(word2);
23557e705706SAndy Adamson 		}
23561da177e4SLinus Torvalds 	}
23571da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
2358ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2359ddd1ea56SJ. Bruce Fields 		if (!p)
23601da177e4SLinus Torvalds 			goto out_resource;
23613d2544b1SJ. Bruce Fields 		dummy = nfs4_file_type(stat.mode);
23626b6d8137SJ. Bruce Fields 		if (dummy == NF4BAD) {
23636b6d8137SJ. Bruce Fields 			status = nfserr_serverfault;
23646b6d8137SJ. Bruce Fields 			goto out;
23656b6d8137SJ. Bruce Fields 		}
2366c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(dummy);
23671da177e4SLinus Torvalds 	}
23681da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
2369ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2370ddd1ea56SJ. Bruce Fields 		if (!p)
23711da177e4SLinus Torvalds 			goto out_resource;
237249640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
2373c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_FH_PERSISTENT);
237449640001SNeilBrown 		else
2375c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(NFS4_FH_PERSISTENT|
2376c373b0a4SJ. Bruce Fields 						NFS4_FH_VOL_RENAME);
23771da177e4SLinus Torvalds 	}
23781da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
2379ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2380ddd1ea56SJ. Bruce Fields 		if (!p)
23811da177e4SLinus Torvalds 			goto out_resource;
23822b0143b5SDavid Howells 		p = encode_change(p, &stat, d_inode(dentry));
23831da177e4SLinus Torvalds 	}
23841da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
2385ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2386ddd1ea56SJ. Bruce Fields 		if (!p)
23871da177e4SLinus Torvalds 			goto out_resource;
2388b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.size);
23891da177e4SLinus Torvalds 	}
23901da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
2391ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2392ddd1ea56SJ. Bruce Fields 		if (!p)
23931da177e4SLinus Torvalds 			goto out_resource;
2394c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
23951da177e4SLinus Torvalds 	}
23961da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
2397ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2398ddd1ea56SJ. Bruce Fields 		if (!p)
23991da177e4SLinus Torvalds 			goto out_resource;
2400c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
24011da177e4SLinus Torvalds 	}
24021da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
2403ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2404ddd1ea56SJ. Bruce Fields 		if (!p)
24051da177e4SLinus Torvalds 			goto out_resource;
2406c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
24071da177e4SLinus Torvalds 	}
24081da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
2409ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
2410ddd1ea56SJ. Bruce Fields 		if (!p)
24111da177e4SLinus Torvalds 			goto out_resource;
241242ca0993SJ.Bruce Fields 		if (exp->ex_fslocs.migrated) {
2413b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
2414b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
2415af6a4e28SNeilBrown 		} else switch(fsid_source(fhp)) {
2416af6a4e28SNeilBrown 		case FSIDSOURCE_FSID:
2417b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
2418b64c7f3bSJ. Bruce Fields 			p = xdr_encode_hyper(p, (u64)0);
2419af6a4e28SNeilBrown 			break;
2420af6a4e28SNeilBrown 		case FSIDSOURCE_DEV:
2421c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
2422c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(MAJOR(stat.dev));
2423c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
2424c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(MINOR(stat.dev));
2425af6a4e28SNeilBrown 			break;
2426af6a4e28SNeilBrown 		case FSIDSOURCE_UUID:
242794eb3689SKinglong Mee 			p = xdr_encode_opaque_fixed(p, exp->ex_uuid,
242894eb3689SKinglong Mee 								EX_UUID_LEN);
2429af6a4e28SNeilBrown 			break;
24301da177e4SLinus Torvalds 		}
24311da177e4SLinus Torvalds 	}
24321da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
2433ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2434ddd1ea56SJ. Bruce Fields 		if (!p)
24351da177e4SLinus Torvalds 			goto out_resource;
2436c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
24371da177e4SLinus Torvalds 	}
24381da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
2439ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2440ddd1ea56SJ. Bruce Fields 		if (!p)
24411da177e4SLinus Torvalds 			goto out_resource;
2442c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(nn->nfsd4_lease);
24431da177e4SLinus Torvalds 	}
24441da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
2445ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2446ddd1ea56SJ. Bruce Fields 		if (!p)
24471da177e4SLinus Torvalds 			goto out_resource;
2448c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(rdattr_err);
24491da177e4SLinus Torvalds 	}
24501da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
24511da177e4SLinus Torvalds 		struct nfs4_ace *ace;
24521da177e4SLinus Torvalds 
24531da177e4SLinus Torvalds 		if (acl == NULL) {
2454ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
2455ddd1ea56SJ. Bruce Fields 			if (!p)
24561da177e4SLinus Torvalds 				goto out_resource;
24571da177e4SLinus Torvalds 
2458c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
24591da177e4SLinus Torvalds 			goto out_acl;
24601da177e4SLinus Torvalds 		}
2461ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2462ddd1ea56SJ. Bruce Fields 		if (!p)
24631da177e4SLinus Torvalds 			goto out_resource;
2464c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(acl->naces);
24651da177e4SLinus Torvalds 
246628e05dd8SJ. Bruce Fields 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
2467ddd1ea56SJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4*3);
2468ddd1ea56SJ. Bruce Fields 			if (!p)
24691da177e4SLinus Torvalds 				goto out_resource;
2470c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->type);
2471c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->flag);
2472c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(ace->access_mask &
2473c373b0a4SJ. Bruce Fields 							NFS4_ACE_MASK_ALL);
2474ddd1ea56SJ. Bruce Fields 			status = nfsd4_encode_aclname(xdr, rqstp, ace);
24751da177e4SLinus Torvalds 			if (status)
24761da177e4SLinus Torvalds 				goto out;
24771da177e4SLinus Torvalds 		}
24781da177e4SLinus Torvalds 	}
24791da177e4SLinus Torvalds out_acl:
24801da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
2481ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2482ddd1ea56SJ. Bruce Fields 		if (!p)
24831da177e4SLinus Torvalds 			goto out_resource;
24840c9d65e7SAndreas Gruenbacher 		*p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ?
24851da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
24861da177e4SLinus Torvalds 	}
24871da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
2488ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2489ddd1ea56SJ. Bruce Fields 		if (!p)
24901da177e4SLinus Torvalds 			goto out_resource;
2491c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
24921da177e4SLinus Torvalds 	}
24931da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
2494ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2495ddd1ea56SJ. Bruce Fields 		if (!p)
24961da177e4SLinus Torvalds 			goto out_resource;
2497c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
24981da177e4SLinus Torvalds 	}
24991da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
2500ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2501ddd1ea56SJ. Bruce Fields 		if (!p)
25021da177e4SLinus Torvalds 			goto out_resource;
2503c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
25041da177e4SLinus Torvalds 	}
25051da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
2506ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2507ddd1ea56SJ. Bruce Fields 		if (!p)
25081da177e4SLinus Torvalds 			goto out_resource;
2509c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
25101da177e4SLinus Torvalds 	}
25111da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
2512ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4);
2513ddd1ea56SJ. Bruce Fields 		if (!p)
25141da177e4SLinus Torvalds 			goto out_resource;
25150c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base,
25160c0c267bSJ. Bruce Fields 					fhp->fh_handle.fh_size);
25171da177e4SLinus Torvalds 	}
25181da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
2519ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2520ddd1ea56SJ. Bruce Fields 		if (!p)
25211da177e4SLinus Torvalds 			goto out_resource;
2522b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.ino);
25231da177e4SLinus Torvalds 	}
25241da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
2525ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2526ddd1ea56SJ. Bruce Fields 		if (!p)
25271da177e4SLinus Torvalds 			goto out_resource;
2528b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
25291da177e4SLinus Torvalds 	}
25301da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
2531ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2532ddd1ea56SJ. Bruce Fields 		if (!p)
25331da177e4SLinus Torvalds 			goto out_resource;
2534b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
25351da177e4SLinus Torvalds 	}
25361da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
2537ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2538ddd1ea56SJ. Bruce Fields 		if (!p)
25391da177e4SLinus Torvalds 			goto out_resource;
2540b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) statfs.f_files);
25411da177e4SLinus Torvalds 	}
254281c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
2543ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
254481c3f413SJ.Bruce Fields 		if (status)
254581c3f413SJ.Bruce Fields 			goto out;
254681c3f413SJ.Bruce Fields 	}
25471da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
2548ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2549ddd1ea56SJ. Bruce Fields 		if (!p)
25501da177e4SLinus Torvalds 			goto out_resource;
2551c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
25521da177e4SLinus Torvalds 	}
25531da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
2554ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2555ddd1ea56SJ. Bruce Fields 		if (!p)
25561da177e4SLinus Torvalds 			goto out_resource;
2557b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes);
25581da177e4SLinus Torvalds 	}
25591da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
2560ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2561ddd1ea56SJ. Bruce Fields 		if (!p)
25621da177e4SLinus Torvalds 			goto out_resource;
2563c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(255);
25641da177e4SLinus Torvalds 	}
25651da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
2566ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2567ddd1ea56SJ. Bruce Fields 		if (!p)
25681da177e4SLinus Torvalds 			goto out_resource;
2569c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(statfs.f_namelen);
25701da177e4SLinus Torvalds 	}
25711da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
2572ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2573ddd1ea56SJ. Bruce Fields 		if (!p)
25741da177e4SLinus Torvalds 			goto out_resource;
2575b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
25761da177e4SLinus Torvalds 	}
25771da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
2578ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2579ddd1ea56SJ. Bruce Fields 		if (!p)
25801da177e4SLinus Torvalds 			goto out_resource;
2581b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
25821da177e4SLinus Torvalds 	}
25831da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
2584ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2585ddd1ea56SJ. Bruce Fields 		if (!p)
25861da177e4SLinus Torvalds 			goto out_resource;
2587c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.mode & S_IALLUGO);
25881da177e4SLinus Torvalds 	}
25891da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
2590ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2591ddd1ea56SJ. Bruce Fields 		if (!p)
25921da177e4SLinus Torvalds 			goto out_resource;
2593c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
25941da177e4SLinus Torvalds 	}
25951da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
2596ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
2597ddd1ea56SJ. Bruce Fields 		if (!p)
25981da177e4SLinus Torvalds 			goto out_resource;
2599c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.nlink);
26001da177e4SLinus Torvalds 	}
26011da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
2602ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_user(xdr, rqstp, stat.uid);
26031da177e4SLinus Torvalds 		if (status)
26041da177e4SLinus Torvalds 			goto out;
26051da177e4SLinus Torvalds 	}
26061da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
2607ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_group(xdr, rqstp, stat.gid);
26081da177e4SLinus Torvalds 		if (status)
26091da177e4SLinus Torvalds 			goto out;
26101da177e4SLinus Torvalds 	}
26111da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
2612ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2613ddd1ea56SJ. Bruce Fields 		if (!p)
26141da177e4SLinus Torvalds 			goto out_resource;
2615c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32((u32) MAJOR(stat.rdev));
2616c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32((u32) MINOR(stat.rdev));
26171da177e4SLinus Torvalds 	}
26181da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
2619ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2620ddd1ea56SJ. Bruce Fields 		if (!p)
26211da177e4SLinus Torvalds 			goto out_resource;
26221da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
2623b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
26241da177e4SLinus Torvalds 	}
26251da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
2626ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2627ddd1ea56SJ. Bruce Fields 		if (!p)
26281da177e4SLinus Torvalds 			goto out_resource;
26291da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
2630b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
26311da177e4SLinus Torvalds 	}
26321da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
2633ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2634ddd1ea56SJ. Bruce Fields 		if (!p)
26351da177e4SLinus Torvalds 			goto out_resource;
26361da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
2637b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
26381da177e4SLinus Torvalds 	}
26391da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
2640ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2641ddd1ea56SJ. Bruce Fields 		if (!p)
26421da177e4SLinus Torvalds 			goto out_resource;
26431da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
2644b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, dummy64);
26451da177e4SLinus Torvalds 	}
26461da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
2647ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
2648ddd1ea56SJ. Bruce Fields 		if (!p)
26491da177e4SLinus Torvalds 			goto out_resource;
2650b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (s64)stat.atime.tv_sec);
2651c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.atime.tv_nsec);
26521da177e4SLinus Torvalds 	}
26531da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
2654ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
2655ddd1ea56SJ. Bruce Fields 		if (!p)
26561da177e4SLinus Torvalds 			goto out_resource;
2657c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
2658c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(1);
2659c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
26601da177e4SLinus Torvalds 	}
26611da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
2662ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
2663ddd1ea56SJ. Bruce Fields 		if (!p)
26641da177e4SLinus Torvalds 			goto out_resource;
2665b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (s64)stat.ctime.tv_sec);
2666c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.ctime.tv_nsec);
26671da177e4SLinus Torvalds 	}
26681da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
2669ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 12);
2670ddd1ea56SJ. Bruce Fields 		if (!p)
26711da177e4SLinus Torvalds 			goto out_resource;
2672b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
2673c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(stat.mtime.tv_nsec);
26741da177e4SLinus Torvalds 	}
26751da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
2676ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2677ddd1ea56SJ. Bruce Fields 		if (!p)
26781da177e4SLinus Torvalds                 	goto out_resource;
2679406a7ea9SFrank Filz 		/*
2680406a7ea9SFrank Filz 		 * Get parent's attributes if not ignoring crossmount
2681406a7ea9SFrank Filz 		 * and this is the root of a cross-mounted filesystem.
2682406a7ea9SFrank Filz 		 */
2683406a7ea9SFrank Filz 		if (ignore_crossmnt == 0 &&
2684ae7095a7SJ. Bruce Fields 		    dentry == exp->ex_path.mnt->mnt_root)
2685ae7095a7SJ. Bruce Fields 			get_parent_attributes(exp, &stat);
2686b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, stat.ino);
26871da177e4SLinus Torvalds 	}
26889cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
26899cf514ccSChristoph Hellwig 	if ((bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) ||
26909cf514ccSChristoph Hellwig 	    (bmval2 & FATTR4_WORD2_LAYOUT_TYPES)) {
26919cf514ccSChristoph Hellwig 		if (exp->ex_layout_type) {
26929cf514ccSChristoph Hellwig 			p = xdr_reserve_space(xdr, 8);
26939cf514ccSChristoph Hellwig 			if (!p)
26949cf514ccSChristoph Hellwig 				goto out_resource;
26959cf514ccSChristoph Hellwig 			*p++ = cpu_to_be32(1);
26969cf514ccSChristoph Hellwig 			*p++ = cpu_to_be32(exp->ex_layout_type);
26979cf514ccSChristoph Hellwig 		} else {
26989cf514ccSChristoph Hellwig 			p = xdr_reserve_space(xdr, 4);
26999cf514ccSChristoph Hellwig 			if (!p)
27009cf514ccSChristoph Hellwig 				goto out_resource;
27019cf514ccSChristoph Hellwig 			*p++ = cpu_to_be32(0);
27029cf514ccSChristoph Hellwig 		}
27039cf514ccSChristoph Hellwig 	}
27049cf514ccSChristoph Hellwig 
27059cf514ccSChristoph Hellwig 	if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) {
27069cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4);
27079cf514ccSChristoph Hellwig 		if (!p)
27089cf514ccSChristoph Hellwig 			goto out_resource;
27099cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(stat.blksize);
27109cf514ccSChristoph Hellwig 	}
27119cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
271218032ca0SDavid Quigley 	if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
2713ddd1ea56SJ. Bruce Fields 		status = nfsd4_encode_security_label(xdr, rqstp, context,
2714ddd1ea56SJ. Bruce Fields 								contextlen);
271518032ca0SDavid Quigley 		if (status)
271618032ca0SDavid Quigley 			goto out;
271718032ca0SDavid Quigley 	}
27188c18f205SBenny Halevy 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
2719ddd1ea56SJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
2720ddd1ea56SJ. Bruce Fields 		if (!p)
2721de3997a7SJ. Bruce Fields 			goto out_resource;
2722c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
2723c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
2724c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
2725c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFSD_SUPPATTR_EXCLCREAT_WORD2);
27268c18f205SBenny Halevy 	}
27277e705706SAndy Adamson 
2728082d4bd7SJ. Bruce Fields 	attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
2729082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
27301da177e4SLinus Torvalds 	status = nfs_ok;
27311da177e4SLinus Torvalds 
27321da177e4SLinus Torvalds out:
2733ba4e55bbSJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
273418032ca0SDavid Quigley 	if (context)
273518032ca0SDavid Quigley 		security_release_secctx(context, contextlen);
2736ba4e55bbSJ. Bruce Fields #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
273728e05dd8SJ. Bruce Fields 	kfree(acl);
273818df11d0SYan, Zheng 	if (tempfh) {
2739d50e6136SJ. Bruce Fields 		fh_put(tempfh);
274018df11d0SYan, Zheng 		kfree(tempfh);
274118df11d0SYan, Zheng 	}
27421fcea5b2SJ. Bruce Fields 	if (status)
27431fcea5b2SJ. Bruce Fields 		xdr_truncate_encode(xdr, starting_len);
27441da177e4SLinus Torvalds 	return status;
27451da177e4SLinus Torvalds out_nfserr:
2746b8dd7b9aSAl Viro 	status = nfserrno(err);
27471da177e4SLinus Torvalds 	goto out;
27481da177e4SLinus Torvalds out_resource:
27491da177e4SLinus Torvalds 	status = nfserr_resource;
27501da177e4SLinus Torvalds 	goto out;
27511da177e4SLinus Torvalds }
27521da177e4SLinus Torvalds 
27532825a7f9SJ. Bruce Fields static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
27542825a7f9SJ. Bruce Fields 				struct xdr_buf *buf, __be32 *p, int bytes)
27552825a7f9SJ. Bruce Fields {
27562825a7f9SJ. Bruce Fields 	xdr->scratch.iov_len = 0;
27572825a7f9SJ. Bruce Fields 	memset(buf, 0, sizeof(struct xdr_buf));
27582825a7f9SJ. Bruce Fields 	buf->head[0].iov_base = p;
27592825a7f9SJ. Bruce Fields 	buf->head[0].iov_len = 0;
27602825a7f9SJ. Bruce Fields 	buf->len = 0;
27612825a7f9SJ. Bruce Fields 	xdr->buf = buf;
27622825a7f9SJ. Bruce Fields 	xdr->iov = buf->head;
27632825a7f9SJ. Bruce Fields 	xdr->p = p;
27642825a7f9SJ. Bruce Fields 	xdr->end = (void *)p + bytes;
27652825a7f9SJ. Bruce Fields 	buf->buflen = bytes;
27662825a7f9SJ. Bruce Fields }
27672825a7f9SJ. Bruce Fields 
2768d5184658SJ. Bruce Fields __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
2769d5184658SJ. Bruce Fields 			struct svc_fh *fhp, struct svc_export *exp,
2770d5184658SJ. Bruce Fields 			struct dentry *dentry, u32 *bmval,
2771d5184658SJ. Bruce Fields 			struct svc_rqst *rqstp, int ignore_crossmnt)
2772d5184658SJ. Bruce Fields {
27732825a7f9SJ. Bruce Fields 	struct xdr_buf dummy;
2774d5184658SJ. Bruce Fields 	struct xdr_stream xdr;
2775d5184658SJ. Bruce Fields 	__be32 ret;
2776d5184658SJ. Bruce Fields 
27772825a7f9SJ. Bruce Fields 	svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
2778d5184658SJ. Bruce Fields 	ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
2779d5184658SJ. Bruce Fields 							ignore_crossmnt);
2780d5184658SJ. Bruce Fields 	*p = xdr.p;
2781d5184658SJ. Bruce Fields 	return ret;
2782d5184658SJ. Bruce Fields }
2783d5184658SJ. Bruce Fields 
2784c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
2785c0ce6ec8SJ. Bruce Fields {
2786c0ce6ec8SJ. Bruce Fields 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
2787c0ce6ec8SJ. Bruce Fields 		return 1;
2788c0ce6ec8SJ. Bruce Fields 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
2789c0ce6ec8SJ. Bruce Fields 		return 1;
2790c0ce6ec8SJ. Bruce Fields 	return 0;
2791c0ce6ec8SJ. Bruce Fields }
2792c0ce6ec8SJ. Bruce Fields 
2793b37ad28bSAl Viro static __be32
2794561f0ed4SJ. Bruce Fields nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
2795561f0ed4SJ. Bruce Fields 			const char *name, int namlen)
27961da177e4SLinus Torvalds {
27971da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
27981da177e4SLinus Torvalds 	struct dentry *dentry;
2799b37ad28bSAl Viro 	__be32 nfserr;
2800406a7ea9SFrank Filz 	int ignore_crossmnt = 0;
28011da177e4SLinus Torvalds 
28021da177e4SLinus Torvalds 	dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
28031da177e4SLinus Torvalds 	if (IS_ERR(dentry))
28041da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
28052b0143b5SDavid Howells 	if (d_really_is_negative(dentry)) {
2806b2c0cea6SJ. Bruce Fields 		/*
2807b2c0cea6SJ. Bruce Fields 		 * nfsd_buffered_readdir drops the i_mutex between
2808b2c0cea6SJ. Bruce Fields 		 * readdir and calling this callback, leaving a window
2809b2c0cea6SJ. Bruce Fields 		 * where this directory entry could have gone away.
2810b2c0cea6SJ. Bruce Fields 		 */
2811b2c0cea6SJ. Bruce Fields 		dput(dentry);
2812b2c0cea6SJ. Bruce Fields 		return nfserr_noent;
2813b2c0cea6SJ. Bruce Fields 	}
28141da177e4SLinus Torvalds 
28151da177e4SLinus Torvalds 	exp_get(exp);
2816406a7ea9SFrank Filz 	/*
2817406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
2818406a7ea9SFrank Filz 	 * attributes that are only properties of the underlying filesystem
2819406a7ea9SFrank Filz 	 * as opposed to the cross-mounted file system. In such a case,
2820406a7ea9SFrank Filz 	 * we will not follow the cross mount and will fill the attribtutes
2821406a7ea9SFrank Filz 	 * directly from the mountpoint dentry.
2822406a7ea9SFrank Filz 	 */
28233227fa41SJ. Bruce Fields 	if (nfsd_mountpoint(dentry, exp)) {
2824021d3a72SJ.Bruce Fields 		int err;
2825021d3a72SJ.Bruce Fields 
28263227fa41SJ. Bruce Fields 		if (!(exp->ex_flags & NFSEXP_V4ROOT)
28273227fa41SJ. Bruce Fields 				&& !attributes_need_mount(cd->rd_bmval)) {
28283227fa41SJ. Bruce Fields 			ignore_crossmnt = 1;
28293227fa41SJ. Bruce Fields 			goto out_encode;
28303227fa41SJ. Bruce Fields 		}
2831dcb488a3SAndy Adamson 		/*
2832dcb488a3SAndy Adamson 		 * Why the heck aren't we just using nfsd_lookup??
2833dcb488a3SAndy Adamson 		 * Different "."/".." handling?  Something else?
2834dcb488a3SAndy Adamson 		 * At least, add a comment here to explain....
2835dcb488a3SAndy Adamson 		 */
2836021d3a72SJ.Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
2837021d3a72SJ.Bruce Fields 		if (err) {
2838021d3a72SJ.Bruce Fields 			nfserr = nfserrno(err);
28391da177e4SLinus Torvalds 			goto out_put;
28401da177e4SLinus Torvalds 		}
2841dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
2842dcb488a3SAndy Adamson 		if (nfserr)
2843dcb488a3SAndy Adamson 			goto out_put;
28441da177e4SLinus Torvalds 
28451da177e4SLinus Torvalds 	}
28463227fa41SJ. Bruce Fields out_encode:
2847561f0ed4SJ. Bruce Fields 	nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval,
2848406a7ea9SFrank Filz 					cd->rd_rqstp, ignore_crossmnt);
28491da177e4SLinus Torvalds out_put:
28501da177e4SLinus Torvalds 	dput(dentry);
28511da177e4SLinus Torvalds 	exp_put(exp);
28521da177e4SLinus Torvalds 	return nfserr;
28531da177e4SLinus Torvalds }
28541da177e4SLinus Torvalds 
28552ebbc012SAl Viro static __be32 *
2856561f0ed4SJ. Bruce Fields nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
28571da177e4SLinus Torvalds {
2858561f0ed4SJ. Bruce Fields 	__be32 *p;
2859561f0ed4SJ. Bruce Fields 
2860c3a45617SKinglong Mee 	p = xdr_reserve_space(xdr, 20);
2861561f0ed4SJ. Bruce Fields 	if (!p)
28621da177e4SLinus Torvalds 		return NULL;
28631da177e4SLinus Torvalds 	*p++ = htonl(2);
28641da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
28651da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
28661da177e4SLinus Torvalds 
286787915c64SJ. Bruce Fields 	*p++ = htonl(4);     /* attribute length */
28681da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
28691da177e4SLinus Torvalds 	return p;
28701da177e4SLinus Torvalds }
28711da177e4SLinus Torvalds 
28721da177e4SLinus Torvalds static int
2873a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2874a0ad13efSNeilBrown 		    loff_t offset, u64 ino, unsigned int d_type)
28751da177e4SLinus Torvalds {
2876a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
28771da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
2878561f0ed4SJ. Bruce Fields 	struct xdr_stream *xdr = cd->xdr;
2879561f0ed4SJ. Bruce Fields 	int start_offset = xdr->buf->len;
2880561f0ed4SJ. Bruce Fields 	int cookie_offset;
2881aee37764SJ. Bruce Fields 	u32 name_and_cookie;
2882561f0ed4SJ. Bruce Fields 	int entry_bytes;
2883b37ad28bSAl Viro 	__be32 nfserr = nfserr_toosmall;
2884561f0ed4SJ. Bruce Fields 	__be64 wire_offset;
2885561f0ed4SJ. Bruce Fields 	__be32 *p;
28861da177e4SLinus Torvalds 
28871da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
28881da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
28891da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
28901da177e4SLinus Torvalds 		return 0;
28911da177e4SLinus Torvalds 	}
28921da177e4SLinus Torvalds 
2893561f0ed4SJ. Bruce Fields 	if (cd->cookie_offset) {
2894561f0ed4SJ. Bruce Fields 		wire_offset = cpu_to_be64(offset);
2895561f0ed4SJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset,
2896561f0ed4SJ. Bruce Fields 							&wire_offset, 8);
2897561f0ed4SJ. Bruce Fields 	}
28981da177e4SLinus Torvalds 
2899561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
2900561f0ed4SJ. Bruce Fields 	if (!p)
29011da177e4SLinus Torvalds 		goto fail;
29021da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
2903561f0ed4SJ. Bruce Fields 	cookie_offset = xdr->buf->len;
2904561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 3*4 + namlen);
2905561f0ed4SJ. Bruce Fields 	if (!p)
2906561f0ed4SJ. Bruce Fields 		goto fail;
29071da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
29081da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
29091da177e4SLinus Torvalds 
2910561f0ed4SJ. Bruce Fields 	nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
29111da177e4SLinus Torvalds 	switch (nfserr) {
29121da177e4SLinus Torvalds 	case nfs_ok:
29131da177e4SLinus Torvalds 		break;
29141da177e4SLinus Torvalds 	case nfserr_resource:
29151da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
29161da177e4SLinus Torvalds 		goto fail;
2917b2c0cea6SJ. Bruce Fields 	case nfserr_noent:
2918f41c5ad2SKinglong Mee 		xdr_truncate_encode(xdr, start_offset);
2919b2c0cea6SJ. Bruce Fields 		goto skip_entry;
29201da177e4SLinus Torvalds 	default:
29211da177e4SLinus Torvalds 		/*
29221da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
29231da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
29241da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
29251da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
29261da177e4SLinus Torvalds 		 * entire READDIR operation(!)
29271da177e4SLinus Torvalds 		 */
29281da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
29291da177e4SLinus Torvalds 			goto fail;
2930561f0ed4SJ. Bruce Fields 		p = nfsd4_encode_rdattr_error(xdr, nfserr);
293134081efcSFred Isaman 		if (p == NULL) {
293234081efcSFred Isaman 			nfserr = nfserr_toosmall;
29331da177e4SLinus Torvalds 			goto fail;
29341da177e4SLinus Torvalds 		}
293534081efcSFred Isaman 	}
2936561f0ed4SJ. Bruce Fields 	nfserr = nfserr_toosmall;
2937561f0ed4SJ. Bruce Fields 	entry_bytes = xdr->buf->len - start_offset;
2938561f0ed4SJ. Bruce Fields 	if (entry_bytes > cd->rd_maxcount)
2939561f0ed4SJ. Bruce Fields 		goto fail;
2940561f0ed4SJ. Bruce Fields 	cd->rd_maxcount -= entry_bytes;
2941aee37764SJ. Bruce Fields 	/*
2942aee37764SJ. Bruce Fields 	 * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so
2943aee37764SJ. Bruce Fields 	 * let's always let through the first entry, at least:
2944aee37764SJ. Bruce Fields 	 */
29450ec016e3SJ. Bruce Fields 	if (!cd->rd_dircount)
29460ec016e3SJ. Bruce Fields 		goto fail;
29470ec016e3SJ. Bruce Fields 	name_and_cookie = 4 + 4 * XDR_QUADLEN(namlen) + 8;
2948aee37764SJ. Bruce Fields 	if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
2949aee37764SJ. Bruce Fields 		goto fail;
2950aee37764SJ. Bruce Fields 	cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
29510ec016e3SJ. Bruce Fields 
2952561f0ed4SJ. Bruce Fields 	cd->cookie_offset = cookie_offset;
2953b2c0cea6SJ. Bruce Fields skip_entry:
29541da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
29551da177e4SLinus Torvalds 	return 0;
29561da177e4SLinus Torvalds fail:
2957561f0ed4SJ. Bruce Fields 	xdr_truncate_encode(xdr, start_offset);
29581da177e4SLinus Torvalds 	cd->common.err = nfserr;
29591da177e4SLinus Torvalds 	return -EINVAL;
29601da177e4SLinus Torvalds }
29611da177e4SLinus Torvalds 
2962d0a381ddSJ. Bruce Fields static __be32
2963d0a381ddSJ. Bruce Fields nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
2964e2f282b9SBenny Halevy {
2965bc749ca4SJ. Bruce Fields 	__be32 *p;
2966e2f282b9SBenny Halevy 
2967d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, sizeof(stateid_t));
2968d0a381ddSJ. Bruce Fields 	if (!p)
2969d0a381ddSJ. Bruce Fields 		return nfserr_resource;
2970c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sid->si_generation);
29710c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, &sid->si_opaque,
29720c0c267bSJ. Bruce Fields 					sizeof(stateid_opaque_t));
2973d0a381ddSJ. Bruce Fields 	return 0;
2974e2f282b9SBenny Halevy }
2975e2f282b9SBenny Halevy 
2976695e12f8SBenny Halevy static __be32
2977b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
29781da177e4SLinus Torvalds {
2979d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
2980bc749ca4SJ. Bruce Fields 	__be32 *p;
29811da177e4SLinus Torvalds 
29821da177e4SLinus Torvalds 	if (!nfserr) {
2983d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
2984d0a381ddSJ. Bruce Fields 		if (!p)
2985d0a381ddSJ. Bruce Fields 			return nfserr_resource;
2986c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(access->ac_supported);
2987c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(access->ac_resp_access);
29881da177e4SLinus Torvalds 	}
2989695e12f8SBenny Halevy 	return nfserr;
29901da177e4SLinus Torvalds }
29911da177e4SLinus Torvalds 
29921d1bc8f2SJ. Bruce Fields static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts)
29931d1bc8f2SJ. Bruce Fields {
2994d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
29951d1bc8f2SJ. Bruce Fields 	__be32 *p;
29961d1bc8f2SJ. Bruce Fields 
29971d1bc8f2SJ. Bruce Fields 	if (!nfserr) {
2998d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8);
2999d0a381ddSJ. Bruce Fields 		if (!p)
3000d0a381ddSJ. Bruce Fields 			return nfserr_resource;
30010c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, bcts->sessionid.data,
30020c0c267bSJ. Bruce Fields 						NFS4_MAX_SESSIONID_LEN);
3003c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(bcts->dir);
30046e67b5d1SJ. Bruce Fields 		/* Sorry, we do not yet support RDMA over 4.1: */
3005c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
30061d1bc8f2SJ. Bruce Fields 	}
30071d1bc8f2SJ. Bruce Fields 	return nfserr;
30081d1bc8f2SJ. Bruce Fields }
30091d1bc8f2SJ. Bruce Fields 
3010695e12f8SBenny Halevy static __be32
3011b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
30121da177e4SLinus Torvalds {
3013d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3014d0a381ddSJ. Bruce Fields 
3015e2f282b9SBenny Halevy 	if (!nfserr)
3016d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &close->cl_stateid);
3017e2f282b9SBenny Halevy 
3018695e12f8SBenny Halevy 	return nfserr;
30191da177e4SLinus Torvalds }
30201da177e4SLinus Torvalds 
30211da177e4SLinus Torvalds 
3022695e12f8SBenny Halevy static __be32
3023b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
30241da177e4SLinus Torvalds {
3025d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3026bc749ca4SJ. Bruce Fields 	__be32 *p;
30271da177e4SLinus Torvalds 
30281da177e4SLinus Torvalds 	if (!nfserr) {
3029d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
3030d0a381ddSJ. Bruce Fields 		if (!p)
3031d0a381ddSJ. Bruce Fields 			return nfserr_resource;
30320c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, commit->co_verf.data,
30330c0c267bSJ. Bruce Fields 						NFS4_VERIFIER_SIZE);
30341da177e4SLinus Torvalds 	}
3035695e12f8SBenny Halevy 	return nfserr;
30361da177e4SLinus Torvalds }
30371da177e4SLinus Torvalds 
3038695e12f8SBenny Halevy static __be32
3039b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
30401da177e4SLinus Torvalds {
3041d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3042bc749ca4SJ. Bruce Fields 	__be32 *p;
30431da177e4SLinus Torvalds 
30441da177e4SLinus Torvalds 	if (!nfserr) {
3045d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 32);
3046d0a381ddSJ. Bruce Fields 		if (!p)
3047d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3048d05d5744SJ. Bruce Fields 		p = encode_cinfo(p, &create->cr_cinfo);
3049c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(2);
3050c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(create->cr_bmval[0]);
3051c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(create->cr_bmval[1]);
30521da177e4SLinus Torvalds 	}
3053695e12f8SBenny Halevy 	return nfserr;
30541da177e4SLinus Torvalds }
30551da177e4SLinus Torvalds 
3056b37ad28bSAl Viro static __be32
3057b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
30581da177e4SLinus Torvalds {
30591da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
3060d5184658SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
30611da177e4SLinus Torvalds 
30621da177e4SLinus Torvalds 	if (nfserr)
30631da177e4SLinus Torvalds 		return nfserr;
30641da177e4SLinus Torvalds 
3065d5184658SJ. Bruce Fields 	nfserr = nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry,
3066d5184658SJ. Bruce Fields 				    getattr->ga_bmval,
3067406a7ea9SFrank Filz 				    resp->rqstp, 0);
30681da177e4SLinus Torvalds 	return nfserr;
30691da177e4SLinus Torvalds }
30701da177e4SLinus Torvalds 
3071695e12f8SBenny Halevy static __be32
3072695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
30731da177e4SLinus Torvalds {
3074d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3075695e12f8SBenny Halevy 	struct svc_fh *fhp = *fhpp;
30761da177e4SLinus Torvalds 	unsigned int len;
3077bc749ca4SJ. Bruce Fields 	__be32 *p;
30781da177e4SLinus Torvalds 
30791da177e4SLinus Torvalds 	if (!nfserr) {
30801da177e4SLinus Torvalds 		len = fhp->fh_handle.fh_size;
3081d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, len + 4);
3082d0a381ddSJ. Bruce Fields 		if (!p)
3083d0a381ddSJ. Bruce Fields 			return nfserr_resource;
30840c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base, len);
30851da177e4SLinus Torvalds 	}
3086695e12f8SBenny Halevy 	return nfserr;
30871da177e4SLinus Torvalds }
30881da177e4SLinus Torvalds 
30891da177e4SLinus Torvalds /*
30901da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
30911da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
30921da177e4SLinus Torvalds */
3093d0a381ddSJ. Bruce Fields static __be32
3094d0a381ddSJ. Bruce Fields nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
30951da177e4SLinus Torvalds {
30967c13f344SJ. Bruce Fields 	struct xdr_netobj *conf = &ld->ld_owner;
3097bc749ca4SJ. Bruce Fields 	__be32 *p;
30981da177e4SLinus Torvalds 
30998c7424cfSJ. Bruce Fields again:
3100d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
31018c7424cfSJ. Bruce Fields 	if (!p) {
31028c7424cfSJ. Bruce Fields 		/*
31038c7424cfSJ. Bruce Fields 		 * Don't fail to return the result just because we can't
31048c7424cfSJ. Bruce Fields 		 * return the conflicting open:
31058c7424cfSJ. Bruce Fields 		 */
31068c7424cfSJ. Bruce Fields 		if (conf->len) {
3107f98bac5aSKinglong Mee 			kfree(conf->data);
31088c7424cfSJ. Bruce Fields 			conf->len = 0;
31098c7424cfSJ. Bruce Fields 			conf->data = NULL;
31108c7424cfSJ. Bruce Fields 			goto again;
31118c7424cfSJ. Bruce Fields 		}
3112d0a381ddSJ. Bruce Fields 		return nfserr_resource;
31138c7424cfSJ. Bruce Fields 	}
3114b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, ld->ld_start);
3115b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, ld->ld_length);
3116c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(ld->ld_type);
31177c13f344SJ. Bruce Fields 	if (conf->len) {
31180c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8);
31190c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque(p, conf->data, conf->len);
3120f98bac5aSKinglong Mee 		kfree(conf->data);
31211da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
3122b64c7f3bSJ. Bruce Fields 		p = xdr_encode_hyper(p, (u64)0); /* clientid */
3123c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0); /* length of owner name */
31241da177e4SLinus Torvalds 	}
3125d0a381ddSJ. Bruce Fields 	return nfserr_denied;
31261da177e4SLinus Torvalds }
31271da177e4SLinus Torvalds 
3128695e12f8SBenny Halevy static __be32
3129b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
31301da177e4SLinus Torvalds {
3131d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3132d0a381ddSJ. Bruce Fields 
3133e2f282b9SBenny Halevy 	if (!nfserr)
3134d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
3135e2f282b9SBenny Halevy 	else if (nfserr == nfserr_denied)
3136d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
3137f98bac5aSKinglong Mee 
3138695e12f8SBenny Halevy 	return nfserr;
31391da177e4SLinus Torvalds }
31401da177e4SLinus Torvalds 
3141695e12f8SBenny Halevy static __be32
3142b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
31431da177e4SLinus Torvalds {
3144d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3145d0a381ddSJ. Bruce Fields 
31461da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
3147d0a381ddSJ. Bruce Fields 		nfsd4_encode_lock_denied(xdr, &lockt->lt_denied);
3148695e12f8SBenny Halevy 	return nfserr;
31491da177e4SLinus Torvalds }
31501da177e4SLinus Torvalds 
3151695e12f8SBenny Halevy static __be32
3152b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
31531da177e4SLinus Torvalds {
3154d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3155d0a381ddSJ. Bruce Fields 
3156e2f282b9SBenny Halevy 	if (!nfserr)
3157d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &locku->lu_stateid);
31581da177e4SLinus Torvalds 
3159695e12f8SBenny Halevy 	return nfserr;
31601da177e4SLinus Torvalds }
31611da177e4SLinus Torvalds 
31621da177e4SLinus Torvalds 
3163695e12f8SBenny Halevy static __be32
3164b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
31651da177e4SLinus Torvalds {
3166d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3167bc749ca4SJ. Bruce Fields 	__be32 *p;
31681da177e4SLinus Torvalds 
31691da177e4SLinus Torvalds 	if (!nfserr) {
3170d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 20);
3171d0a381ddSJ. Bruce Fields 		if (!p)
3172d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3173d05d5744SJ. Bruce Fields 		p = encode_cinfo(p, &link->li_cinfo);
31741da177e4SLinus Torvalds 	}
3175695e12f8SBenny Halevy 	return nfserr;
31761da177e4SLinus Torvalds }
31771da177e4SLinus Torvalds 
31781da177e4SLinus Torvalds 
3179695e12f8SBenny Halevy static __be32
3180b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
31811da177e4SLinus Torvalds {
3182d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3183bc749ca4SJ. Bruce Fields 	__be32 *p;
31841da177e4SLinus Torvalds 
31851da177e4SLinus Torvalds 	if (nfserr)
31861da177e4SLinus Torvalds 		goto out;
31871da177e4SLinus Torvalds 
3188d0a381ddSJ. Bruce Fields 	nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid);
3189d0a381ddSJ. Bruce Fields 	if (nfserr)
3190d0a381ddSJ. Bruce Fields 		goto out;
3191d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 40);
3192d0a381ddSJ. Bruce Fields 	if (!p)
3193d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3194d05d5744SJ. Bruce Fields 	p = encode_cinfo(p, &open->op_cinfo);
3195c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(open->op_rflags);
3196c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(2);
3197c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(open->op_bmval[0]);
3198c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(open->op_bmval[1]);
3199c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(open->op_delegate_type);
32001da177e4SLinus Torvalds 
32011da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
32021da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
32031da177e4SLinus Torvalds 		break;
32041da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
3205d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3206d0a381ddSJ. Bruce Fields 		if (nfserr)
3207d0a381ddSJ. Bruce Fields 			return nfserr;
3208d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 20);
3209d0a381ddSJ. Bruce Fields 		if (!p)
3210d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3211c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(open->op_recall);
32121da177e4SLinus Torvalds 
32131da177e4SLinus Torvalds 		/*
32141da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
32151da177e4SLinus Torvalds 		 */
3216c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3217c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3218c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3219c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);   /* XXX: is NULL principal ok? */
32201da177e4SLinus Torvalds 		break;
32211da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
3222d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
3223d0a381ddSJ. Bruce Fields 		if (nfserr)
3224d0a381ddSJ. Bruce Fields 			return nfserr;
3225d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 32);
3226d0a381ddSJ. Bruce Fields 		if (!p)
3227d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3228c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
32291da177e4SLinus Torvalds 
32301da177e4SLinus Torvalds 		/*
32311da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
32321da177e4SLinus Torvalds 		 */
3233c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_LIMIT_SIZE);
3234c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(~(u32)0);
3235c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(~(u32)0);
32361da177e4SLinus Torvalds 
32371da177e4SLinus Torvalds 		/*
32381da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
32391da177e4SLinus Torvalds 		 */
3240c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
3241c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3242c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3243c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);   /* XXX: is NULL principal ok? */
32441da177e4SLinus Torvalds 		break;
3245d24433cdSBenny Halevy 	case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
3246d24433cdSBenny Halevy 		switch (open->op_why_no_deleg) {
3247d24433cdSBenny Halevy 		case WND4_CONTENTION:
3248d24433cdSBenny Halevy 		case WND4_RESOURCE:
3249d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 8);
3250d0a381ddSJ. Bruce Fields 			if (!p)
3251d0a381ddSJ. Bruce Fields 				return nfserr_resource;
3252c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(open->op_why_no_deleg);
3253c373b0a4SJ. Bruce Fields 			/* deleg signaling not supported yet: */
3254c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(0);
3255d24433cdSBenny Halevy 			break;
3256d24433cdSBenny Halevy 		default:
3257d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
3258d0a381ddSJ. Bruce Fields 			if (!p)
3259d0a381ddSJ. Bruce Fields 				return nfserr_resource;
3260c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(open->op_why_no_deleg);
3261d24433cdSBenny Halevy 		}
3262d24433cdSBenny Halevy 		break;
32631da177e4SLinus Torvalds 	default:
32641da177e4SLinus Torvalds 		BUG();
32651da177e4SLinus Torvalds 	}
32661da177e4SLinus Torvalds 	/* XXX save filehandle here */
32671da177e4SLinus Torvalds out:
3268695e12f8SBenny Halevy 	return nfserr;
32691da177e4SLinus Torvalds }
32701da177e4SLinus Torvalds 
3271695e12f8SBenny Halevy static __be32
3272b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
32731da177e4SLinus Torvalds {
3274d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3275d0a381ddSJ. Bruce Fields 
3276e2f282b9SBenny Halevy 	if (!nfserr)
3277d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid);
32781da177e4SLinus Torvalds 
3279695e12f8SBenny Halevy 	return nfserr;
32801da177e4SLinus Torvalds }
32811da177e4SLinus Torvalds 
3282695e12f8SBenny Halevy static __be32
3283b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
32841da177e4SLinus Torvalds {
3285d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3286d0a381ddSJ. Bruce Fields 
3287e2f282b9SBenny Halevy 	if (!nfserr)
3288d0a381ddSJ. Bruce Fields 		nfserr = nfsd4_encode_stateid(xdr, &od->od_stateid);
32891da177e4SLinus Torvalds 
3290695e12f8SBenny Halevy 	return nfserr;
32911da177e4SLinus Torvalds }
32921da177e4SLinus Torvalds 
3293dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_splice_read(
3294dc97618dSJ. Bruce Fields 				struct nfsd4_compoundres *resp,
3295dc97618dSJ. Bruce Fields 				struct nfsd4_read *read,
3296dc97618dSJ. Bruce Fields 				struct file *file, unsigned long maxcount)
32971da177e4SLinus Torvalds {
3298dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
329934a78b48SJ. Bruce Fields 	struct xdr_buf *buf = xdr->buf;
3300dc97618dSJ. Bruce Fields 	u32 eof;
3301dc97618dSJ. Bruce Fields 	int space_left;
3302dc97618dSJ. Bruce Fields 	__be32 nfserr;
3303fec25fa4SJ. Bruce Fields 	__be32 *p = xdr->p - 2;
3304dc97618dSJ. Bruce Fields 
3305d5d5c304SKinglong Mee 	/* Make sure there will be room for padding if needed */
3306d5d5c304SKinglong Mee 	if (xdr->end - xdr->p < 1)
3307dc97618dSJ. Bruce Fields 		return nfserr_resource;
3308dc97618dSJ. Bruce Fields 
3309dc97618dSJ. Bruce Fields 	nfserr = nfsd_splice_read(read->rd_rqstp, file,
3310dc97618dSJ. Bruce Fields 				  read->rd_offset, &maxcount);
3311dc97618dSJ. Bruce Fields 	if (nfserr) {
3312dc97618dSJ. Bruce Fields 		/*
3313dc97618dSJ. Bruce Fields 		 * nfsd_splice_actor may have already messed with the
3314dc97618dSJ. Bruce Fields 		 * page length; reset it so as not to confuse
3315dc97618dSJ. Bruce Fields 		 * xdr_truncate_encode:
3316dc97618dSJ. Bruce Fields 		 */
331734a78b48SJ. Bruce Fields 		buf->page_len = 0;
3318dc97618dSJ. Bruce Fields 		return nfserr;
3319dc97618dSJ. Bruce Fields 	}
3320dc97618dSJ. Bruce Fields 
3321dc97618dSJ. Bruce Fields 	eof = (read->rd_offset + maxcount >=
33222b0143b5SDavid Howells 	       d_inode(read->rd_fhp->fh_dentry)->i_size);
3323dc97618dSJ. Bruce Fields 
3324fec25fa4SJ. Bruce Fields 	*(p++) = htonl(eof);
3325fec25fa4SJ. Bruce Fields 	*(p++) = htonl(maxcount);
3326dc97618dSJ. Bruce Fields 
332734a78b48SJ. Bruce Fields 	buf->page_len = maxcount;
332834a78b48SJ. Bruce Fields 	buf->len += maxcount;
332915b23ef5SJ. Bruce Fields 	xdr->page_ptr += (buf->page_base + maxcount + PAGE_SIZE - 1)
333015b23ef5SJ. Bruce Fields 							/ PAGE_SIZE;
3331dc97618dSJ. Bruce Fields 
3332dc97618dSJ. Bruce Fields 	/* Use rest of head for padding and remaining ops: */
333334a78b48SJ. Bruce Fields 	buf->tail[0].iov_base = xdr->p;
333434a78b48SJ. Bruce Fields 	buf->tail[0].iov_len = 0;
3335fec25fa4SJ. Bruce Fields 	xdr->iov = buf->tail;
3336dc97618dSJ. Bruce Fields 	if (maxcount&3) {
3337fec25fa4SJ. Bruce Fields 		int pad = 4 - (maxcount&3);
3338fec25fa4SJ. Bruce Fields 
3339fec25fa4SJ. Bruce Fields 		*(xdr->p++) = 0;
3340fec25fa4SJ. Bruce Fields 
334134a78b48SJ. Bruce Fields 		buf->tail[0].iov_base += maxcount&3;
3342fec25fa4SJ. Bruce Fields 		buf->tail[0].iov_len = pad;
3343fec25fa4SJ. Bruce Fields 		buf->len += pad;
3344dc97618dSJ. Bruce Fields 	}
3345dc97618dSJ. Bruce Fields 
3346dc97618dSJ. Bruce Fields 	space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
334734a78b48SJ. Bruce Fields 				buf->buflen - buf->len);
334834a78b48SJ. Bruce Fields 	buf->buflen = buf->len + space_left;
3349dc97618dSJ. Bruce Fields 	xdr->end = (__be32 *)((void *)xdr->end + space_left);
3350dc97618dSJ. Bruce Fields 
3351dc97618dSJ. Bruce Fields 	return 0;
3352dc97618dSJ. Bruce Fields }
3353dc97618dSJ. Bruce Fields 
3354dc97618dSJ. Bruce Fields static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
3355dc97618dSJ. Bruce Fields 				 struct nfsd4_read *read,
3356dc97618dSJ. Bruce Fields 				 struct file *file, unsigned long maxcount)
3357dc97618dSJ. Bruce Fields {
3358dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
33591da177e4SLinus Torvalds 	u32 eof;
3360afc59400SJ. Bruce Fields 	int v;
3361dc97618dSJ. Bruce Fields 	int starting_len = xdr->buf->len - 8;
33621da177e4SLinus Torvalds 	long len;
3363b0420980SJ. Bruce Fields 	int thislen;
3364dc97618dSJ. Bruce Fields 	__be32 nfserr;
3365dc97618dSJ. Bruce Fields 	__be32 tmp;
3366bc749ca4SJ. Bruce Fields 	__be32 *p;
3367b0420980SJ. Bruce Fields 	u32 zzz = 0;
3368b0420980SJ. Bruce Fields 	int pad;
33691da177e4SLinus Torvalds 
33701da177e4SLinus Torvalds 	len = maxcount;
33711da177e4SLinus Torvalds 	v = 0;
33726ff9897dSJ. Bruce Fields 
33731055414fSKinglong Mee 	thislen = min_t(long, len, ((void *)xdr->end - (void *)xdr->p));
3374b0420980SJ. Bruce Fields 	p = xdr_reserve_space(xdr, (thislen+3)&~3);
3375b0420980SJ. Bruce Fields 	WARN_ON_ONCE(!p);
3376b0420980SJ. Bruce Fields 	resp->rqstp->rq_vec[v].iov_base = p;
33776ff9897dSJ. Bruce Fields 	resp->rqstp->rq_vec[v].iov_len = thislen;
3378b0420980SJ. Bruce Fields 	v++;
3379b0420980SJ. Bruce Fields 	len -= thislen;
3380b0420980SJ. Bruce Fields 
3381b0420980SJ. Bruce Fields 	while (len) {
3382b0420980SJ. Bruce Fields 		thislen = min_t(long, len, PAGE_SIZE);
3383b0420980SJ. Bruce Fields 		p = xdr_reserve_space(xdr, (thislen+3)&~3);
3384b0420980SJ. Bruce Fields 		WARN_ON_ONCE(!p);
3385b0420980SJ. Bruce Fields 		resp->rqstp->rq_vec[v].iov_base = p;
3386b0420980SJ. Bruce Fields 		resp->rqstp->rq_vec[v].iov_len = thislen;
33871da177e4SLinus Torvalds 		v++;
33886ff9897dSJ. Bruce Fields 		len -= thislen;
33891da177e4SLinus Torvalds 	}
33901da177e4SLinus Torvalds 	read->rd_vlen = v;
33911da177e4SLinus Torvalds 
3392dc97618dSJ. Bruce Fields 	nfserr = nfsd_readv(file, read->rd_offset, resp->rqstp->rq_vec,
3393dc97618dSJ. Bruce Fields 			read->rd_vlen, &maxcount);
3394dc97618dSJ. Bruce Fields 	if (nfserr)
33951da177e4SLinus Torvalds 		return nfserr;
3396b0420980SJ. Bruce Fields 	xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3));
3397dc97618dSJ. Bruce Fields 
339844524359SNeilBrown 	eof = (read->rd_offset + maxcount >=
33992b0143b5SDavid Howells 	       d_inode(read->rd_fhp->fh_dentry)->i_size);
34001da177e4SLinus Torvalds 
3401dc97618dSJ. Bruce Fields 	tmp = htonl(eof);
3402dc97618dSJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, starting_len    , &tmp, 4);
3403dc97618dSJ. Bruce Fields 	tmp = htonl(maxcount);
3404dc97618dSJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
3405dc97618dSJ. Bruce Fields 
3406b0420980SJ. Bruce Fields 	pad = (maxcount&3) ? 4 - (maxcount&3) : 0;
3407b0420980SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, starting_len + 8 + maxcount,
3408b0420980SJ. Bruce Fields 								&zzz, pad);
34091da177e4SLinus Torvalds 	return 0;
3410dc97618dSJ. Bruce Fields 
3411dc97618dSJ. Bruce Fields }
3412dc97618dSJ. Bruce Fields 
3413dc97618dSJ. Bruce Fields static __be32
3414dc97618dSJ. Bruce Fields nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
3415dc97618dSJ. Bruce Fields 		  struct nfsd4_read *read)
3416dc97618dSJ. Bruce Fields {
3417dc97618dSJ. Bruce Fields 	unsigned long maxcount;
3418dc97618dSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3419dc97618dSJ. Bruce Fields 	struct file *file = read->rd_filp;
3420dc97618dSJ. Bruce Fields 	int starting_len = xdr->buf->len;
3421e749a462SChristoph Hellwig 	struct raparms *ra = NULL;
3422dc97618dSJ. Bruce Fields 	__be32 *p;
3423dc97618dSJ. Bruce Fields 	__be32 err;
3424dc97618dSJ. Bruce Fields 
3425dc97618dSJ. Bruce Fields 	if (nfserr)
3426dc97618dSJ. Bruce Fields 		return nfserr;
3427dc97618dSJ. Bruce Fields 
3428dc97618dSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
3429dc97618dSJ. Bruce Fields 	if (!p) {
3430779fb0f3SJeff Layton 		WARN_ON_ONCE(test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags));
3431dc97618dSJ. Bruce Fields 		return nfserr_resource;
3432dc97618dSJ. Bruce Fields 	}
3433779fb0f3SJeff Layton 	if (resp->xdr.buf->page_len && test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) {
3434b0420980SJ. Bruce Fields 		WARN_ON_ONCE(1);
3435dc97618dSJ. Bruce Fields 		return nfserr_resource;
3436dc97618dSJ. Bruce Fields 	}
3437dc97618dSJ. Bruce Fields 	xdr_commit_encode(xdr);
3438dc97618dSJ. Bruce Fields 
3439dc97618dSJ. Bruce Fields 	maxcount = svc_max_payload(resp->rqstp);
34403c7aa15dSKinglong Mee 	maxcount = min_t(unsigned long, maxcount, (xdr->buf->buflen - xdr->buf->len));
34413c7aa15dSKinglong Mee 	maxcount = min_t(unsigned long, maxcount, read->rd_length);
3442dc97618dSJ. Bruce Fields 
3443*af90f707SChristoph Hellwig 	if (read->rd_tmp_file)
3444e749a462SChristoph Hellwig 		ra = nfsd_init_raparms(file);
3445dc97618dSJ. Bruce Fields 
3446779fb0f3SJeff Layton 	if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags))
3447dc97618dSJ. Bruce Fields 		err = nfsd4_encode_splice_read(resp, read, file, maxcount);
3448dc97618dSJ. Bruce Fields 	else
3449dc97618dSJ. Bruce Fields 		err = nfsd4_encode_readv(resp, read, file, maxcount);
3450dc97618dSJ. Bruce Fields 
3451e749a462SChristoph Hellwig 	if (ra)
3452e749a462SChristoph Hellwig 		nfsd_put_raparams(file, ra);
3453dc97618dSJ. Bruce Fields 
3454dc97618dSJ. Bruce Fields 	if (err)
3455dc97618dSJ. Bruce Fields 		xdr_truncate_encode(xdr, starting_len);
3456dc97618dSJ. Bruce Fields 	return err;
34571da177e4SLinus Torvalds }
34581da177e4SLinus Torvalds 
3459b37ad28bSAl Viro static __be32
3460b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
34611da177e4SLinus Torvalds {
34621da177e4SLinus Torvalds 	int maxcount;
3463476a7b1fSJ. Bruce Fields 	__be32 wire_count;
3464476a7b1fSJ. Bruce Fields 	int zero = 0;
3465ddd1ea56SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
34661fcea5b2SJ. Bruce Fields 	int length_offset = xdr->buf->len;
3467bc749ca4SJ. Bruce Fields 	__be32 *p;
34681da177e4SLinus Torvalds 
34691da177e4SLinus Torvalds 	if (nfserr)
34701da177e4SLinus Torvalds 		return nfserr;
34712825a7f9SJ. Bruce Fields 
34722825a7f9SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
34732825a7f9SJ. Bruce Fields 	if (!p)
34742825a7f9SJ. Bruce Fields 		return nfserr_resource;
34751da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
3476d0a381ddSJ. Bruce Fields 
3477476a7b1fSJ. Bruce Fields 	p = xdr_reserve_space(xdr, maxcount);
3478476a7b1fSJ. Bruce Fields 	if (!p)
34794e21ac4bSJ. Bruce Fields 		return nfserr_resource;
34801da177e4SLinus Torvalds 	/*
34811da177e4SLinus Torvalds 	 * XXX: By default, the ->readlink() VFS op will truncate symlinks
34821da177e4SLinus Torvalds 	 * if they would overflow the buffer.  Is this kosher in NFSv4?  If
34831da177e4SLinus Torvalds 	 * not, one easy fix is: if ->readlink() precisely fills the buffer,
34841da177e4SLinus Torvalds 	 * assume that truncation occurred, and return NFS4ERR_RESOURCE.
34851da177e4SLinus Torvalds 	 */
3486476a7b1fSJ. Bruce Fields 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
3487476a7b1fSJ. Bruce Fields 						(char *)p, &maxcount);
34881da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
3489d3f627c8SJ. Bruce Fields 		nfserr = nfserr_inval;
3490d3f627c8SJ. Bruce Fields 	if (nfserr) {
34911fcea5b2SJ. Bruce Fields 		xdr_truncate_encode(xdr, length_offset);
34921da177e4SLinus Torvalds 		return nfserr;
3493d3f627c8SJ. Bruce Fields 	}
34941da177e4SLinus Torvalds 
3495476a7b1fSJ. Bruce Fields 	wire_count = htonl(maxcount);
3496476a7b1fSJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4);
349769bbd9c7SAvi Kivity 	xdr_truncate_encode(xdr, length_offset + 4 + ALIGN(maxcount, 4));
3498476a7b1fSJ. Bruce Fields 	if (maxcount & 3)
3499476a7b1fSJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount,
3500476a7b1fSJ. Bruce Fields 						&zero, 4 - (maxcount&3));
35011da177e4SLinus Torvalds 	return 0;
35021da177e4SLinus Torvalds }
35031da177e4SLinus Torvalds 
3504b37ad28bSAl Viro static __be32
3505b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
35061da177e4SLinus Torvalds {
35071da177e4SLinus Torvalds 	int maxcount;
3508561f0ed4SJ. Bruce Fields 	int bytes_left;
35091da177e4SLinus Torvalds 	loff_t offset;
3510561f0ed4SJ. Bruce Fields 	__be64 wire_offset;
3511ddd1ea56SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
35121fcea5b2SJ. Bruce Fields 	int starting_len = xdr->buf->len;
3513bc749ca4SJ. Bruce Fields 	__be32 *p;
35141da177e4SLinus Torvalds 
35151da177e4SLinus Torvalds 	if (nfserr)
35161da177e4SLinus Torvalds 		return nfserr;
35171da177e4SLinus Torvalds 
3518d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
3519d0a381ddSJ. Bruce Fields 	if (!p)
3520d0a381ddSJ. Bruce Fields 		return nfserr_resource;
35211da177e4SLinus Torvalds 
35221da177e4SLinus Torvalds 	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
3523c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);
3524c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);
35254aea24b2SJ. Bruce Fields 	resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p)
35264aea24b2SJ. Bruce Fields 				- (char *)resp->xdr.buf->head[0].iov_base;
35271da177e4SLinus Torvalds 
35281da177e4SLinus Torvalds 	/*
3529561f0ed4SJ. Bruce Fields 	 * Number of bytes left for directory entries allowing for the
3530561f0ed4SJ. Bruce Fields 	 * final 8 bytes of the readdir and a following failed op:
35311da177e4SLinus Torvalds 	 */
3532561f0ed4SJ. Bruce Fields 	bytes_left = xdr->buf->buflen - xdr->buf->len
3533561f0ed4SJ. Bruce Fields 			- COMPOUND_ERR_SLACK_SPACE - 8;
3534561f0ed4SJ. Bruce Fields 	if (bytes_left < 0) {
3535561f0ed4SJ. Bruce Fields 		nfserr = nfserr_resource;
3536561f0ed4SJ. Bruce Fields 		goto err_no_verf;
3537561f0ed4SJ. Bruce Fields 	}
3538561f0ed4SJ. Bruce Fields 	maxcount = min_t(u32, readdir->rd_maxcount, INT_MAX);
3539561f0ed4SJ. Bruce Fields 	/*
3540561f0ed4SJ. Bruce Fields 	 * Note the rfc defines rd_maxcount as the size of the
3541561f0ed4SJ. Bruce Fields 	 * READDIR4resok structure, which includes the verifier above
3542561f0ed4SJ. Bruce Fields 	 * and the 8 bytes encoded at the end of this function:
3543561f0ed4SJ. Bruce Fields 	 */
3544561f0ed4SJ. Bruce Fields 	if (maxcount < 16) {
35451da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
35461da177e4SLinus Torvalds 		goto err_no_verf;
35471da177e4SLinus Torvalds 	}
3548561f0ed4SJ. Bruce Fields 	maxcount = min_t(int, maxcount-16, bytes_left);
35491da177e4SLinus Torvalds 
3550aee37764SJ. Bruce Fields 	/* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
3551aee37764SJ. Bruce Fields 	if (!readdir->rd_dircount)
3552aee37764SJ. Bruce Fields 		readdir->rd_dircount = INT_MAX;
3553aee37764SJ. Bruce Fields 
3554561f0ed4SJ. Bruce Fields 	readdir->xdr = xdr;
3555561f0ed4SJ. Bruce Fields 	readdir->rd_maxcount = maxcount;
35561da177e4SLinus Torvalds 	readdir->common.err = 0;
3557561f0ed4SJ. Bruce Fields 	readdir->cookie_offset = 0;
35581da177e4SLinus Torvalds 
35591da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
35601da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
35611da177e4SLinus Torvalds 			      &offset,
35621da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
35631da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
35641da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
3565561f0ed4SJ. Bruce Fields 	    xdr->buf->len == starting_len + 8) {
3566561f0ed4SJ. Bruce Fields 		/* nothing encoded; which limit did we hit?: */
3567561f0ed4SJ. Bruce Fields 		if (maxcount - 16 < bytes_left)
3568561f0ed4SJ. Bruce Fields 			/* It was the fault of rd_maxcount: */
35691da177e4SLinus Torvalds 			nfserr = nfserr_toosmall;
3570561f0ed4SJ. Bruce Fields 		else
3571561f0ed4SJ. Bruce Fields 			/* We ran out of buffer space: */
3572561f0ed4SJ. Bruce Fields 			nfserr = nfserr_resource;
3573561f0ed4SJ. Bruce Fields 	}
35741da177e4SLinus Torvalds 	if (nfserr)
35751da177e4SLinus Torvalds 		goto err_no_verf;
35761da177e4SLinus Torvalds 
3577561f0ed4SJ. Bruce Fields 	if (readdir->cookie_offset) {
3578561f0ed4SJ. Bruce Fields 		wire_offset = cpu_to_be64(offset);
3579561f0ed4SJ. Bruce Fields 		write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset,
3580561f0ed4SJ. Bruce Fields 							&wire_offset, 8);
3581561f0ed4SJ. Bruce Fields 	}
35821da177e4SLinus Torvalds 
3583561f0ed4SJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
3584561f0ed4SJ. Bruce Fields 	if (!p) {
3585561f0ed4SJ. Bruce Fields 		WARN_ON_ONCE(1);
3586561f0ed4SJ. Bruce Fields 		goto err_no_verf;
3587561f0ed4SJ. Bruce Fields 	}
35881da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
35891da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
35901da177e4SLinus Torvalds 
35911da177e4SLinus Torvalds 	return 0;
35921da177e4SLinus Torvalds err_no_verf:
35931fcea5b2SJ. Bruce Fields 	xdr_truncate_encode(xdr, starting_len);
35941da177e4SLinus Torvalds 	return nfserr;
35951da177e4SLinus Torvalds }
35961da177e4SLinus Torvalds 
3597695e12f8SBenny Halevy static __be32
3598b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
35991da177e4SLinus Torvalds {
3600d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3601bc749ca4SJ. Bruce Fields 	__be32 *p;
36021da177e4SLinus Torvalds 
36031da177e4SLinus Torvalds 	if (!nfserr) {
3604d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 20);
3605d0a381ddSJ. Bruce Fields 		if (!p)
3606d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3607d05d5744SJ. Bruce Fields 		p = encode_cinfo(p, &remove->rm_cinfo);
36081da177e4SLinus Torvalds 	}
3609695e12f8SBenny Halevy 	return nfserr;
36101da177e4SLinus Torvalds }
36111da177e4SLinus Torvalds 
3612695e12f8SBenny Halevy static __be32
3613b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
36141da177e4SLinus Torvalds {
3615d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3616bc749ca4SJ. Bruce Fields 	__be32 *p;
36171da177e4SLinus Torvalds 
36181da177e4SLinus Torvalds 	if (!nfserr) {
3619d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 40);
3620d0a381ddSJ. Bruce Fields 		if (!p)
3621d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3622d05d5744SJ. Bruce Fields 		p = encode_cinfo(p, &rename->rn_sinfo);
3623d05d5744SJ. Bruce Fields 		p = encode_cinfo(p, &rename->rn_tinfo);
36241da177e4SLinus Torvalds 	}
3625695e12f8SBenny Halevy 	return nfserr;
36261da177e4SLinus Torvalds }
36271da177e4SLinus Torvalds 
3628695e12f8SBenny Halevy static __be32
3629d0a381ddSJ. Bruce Fields nfsd4_do_encode_secinfo(struct xdr_stream *xdr,
363022b6dee8SMi Jinlong 			 __be32 nfserr, struct svc_export *exp)
3631dcb488a3SAndy Adamson {
3632676e4ebdSChuck Lever 	u32 i, nflavs, supported;
36334796f457SJ. Bruce Fields 	struct exp_flavor_info *flavs;
36344796f457SJ. Bruce Fields 	struct exp_flavor_info def_flavs[2];
3635676e4ebdSChuck Lever 	__be32 *p, *flavorsp;
3636676e4ebdSChuck Lever 	static bool report = true;
3637dcb488a3SAndy Adamson 
3638dcb488a3SAndy Adamson 	if (nfserr)
3639dcb488a3SAndy Adamson 		goto out;
3640d0a381ddSJ. Bruce Fields 	nfserr = nfserr_resource;
36414796f457SJ. Bruce Fields 	if (exp->ex_nflavors) {
36424796f457SJ. Bruce Fields 		flavs = exp->ex_flavors;
36434796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
36444796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
36454796f457SJ. Bruce Fields 		flavs = def_flavs;
36464796f457SJ. Bruce Fields 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
36474796f457SJ. Bruce Fields 			nflavs = 2;
36484796f457SJ. Bruce Fields 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
36494796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
36504796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
36514796f457SJ. Bruce Fields 			nflavs = 1;
36524796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
36534796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
36544796f457SJ. Bruce Fields 		} else {
36554796f457SJ. Bruce Fields 			nflavs = 1;
36564796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
36574796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
36584796f457SJ. Bruce Fields 		}
36594796f457SJ. Bruce Fields 	}
36604796f457SJ. Bruce Fields 
3661676e4ebdSChuck Lever 	supported = 0;
3662d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4);
3663d0a381ddSJ. Bruce Fields 	if (!p)
3664d0a381ddSJ. Bruce Fields 		goto out;
3665676e4ebdSChuck Lever 	flavorsp = p++;		/* to be backfilled later */
3666676e4ebdSChuck Lever 
36674796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
3668676e4ebdSChuck Lever 		rpc_authflavor_t pf = flavs[i].pseudoflavor;
3669a77c806fSChuck Lever 		struct rpcsec_gss_info info;
3670dcb488a3SAndy Adamson 
3671676e4ebdSChuck Lever 		if (rpcauth_get_gssinfo(pf, &info) == 0) {
3672676e4ebdSChuck Lever 			supported++;
3673d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4 + 4 +
3674d0a381ddSJ. Bruce Fields 					      XDR_LEN(info.oid.len) + 4 + 4);
3675d0a381ddSJ. Bruce Fields 			if (!p)
3676d0a381ddSJ. Bruce Fields 				goto out;
3677c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(RPC_AUTH_GSS);
36780c0c267bSJ. Bruce Fields 			p = xdr_encode_opaque(p,  info.oid.data, info.oid.len);
3679c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(info.qop);
3680c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(info.service);
3681676e4ebdSChuck Lever 		} else if (pf < RPC_AUTH_MAXFLAVOR) {
3682676e4ebdSChuck Lever 			supported++;
3683d0a381ddSJ. Bruce Fields 			p = xdr_reserve_space(xdr, 4);
3684d0a381ddSJ. Bruce Fields 			if (!p)
3685d0a381ddSJ. Bruce Fields 				goto out;
3686c373b0a4SJ. Bruce Fields 			*p++ = cpu_to_be32(pf);
3687676e4ebdSChuck Lever 		} else {
3688676e4ebdSChuck Lever 			if (report)
3689676e4ebdSChuck Lever 				pr_warn("NFS: SECINFO: security flavor %u "
3690676e4ebdSChuck Lever 					"is not supported\n", pf);
3691dcb488a3SAndy Adamson 		}
3692dcb488a3SAndy Adamson 	}
3693a77c806fSChuck Lever 
3694676e4ebdSChuck Lever 	if (nflavs != supported)
3695676e4ebdSChuck Lever 		report = false;
3696676e4ebdSChuck Lever 	*flavorsp = htonl(supported);
3697d0a381ddSJ. Bruce Fields 	nfserr = 0;
3698dcb488a3SAndy Adamson out:
3699dcb488a3SAndy Adamson 	if (exp)
3700dcb488a3SAndy Adamson 		exp_put(exp);
3701695e12f8SBenny Halevy 	return nfserr;
3702dcb488a3SAndy Adamson }
3703dcb488a3SAndy Adamson 
370422b6dee8SMi Jinlong static __be32
370522b6dee8SMi Jinlong nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
370622b6dee8SMi Jinlong 		     struct nfsd4_secinfo *secinfo)
370722b6dee8SMi Jinlong {
3708d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3709d0a381ddSJ. Bruce Fields 
3710d0a381ddSJ. Bruce Fields 	return nfsd4_do_encode_secinfo(xdr, nfserr, secinfo->si_exp);
371122b6dee8SMi Jinlong }
371222b6dee8SMi Jinlong 
371322b6dee8SMi Jinlong static __be32
371422b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
371522b6dee8SMi Jinlong 		     struct nfsd4_secinfo_no_name *secinfo)
371622b6dee8SMi Jinlong {
3717d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3718d0a381ddSJ. Bruce Fields 
3719d0a381ddSJ. Bruce Fields 	return nfsd4_do_encode_secinfo(xdr, nfserr, secinfo->sin_exp);
372022b6dee8SMi Jinlong }
372122b6dee8SMi Jinlong 
37221da177e4SLinus Torvalds /*
37231da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
37241da177e4SLinus Torvalds  * regardless of the error status.
37251da177e4SLinus Torvalds  */
3726695e12f8SBenny Halevy static __be32
3727b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
37281da177e4SLinus Torvalds {
3729d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3730bc749ca4SJ. Bruce Fields 	__be32 *p;
37311da177e4SLinus Torvalds 
3732d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 16);
3733d0a381ddSJ. Bruce Fields 	if (!p)
3734d0a381ddSJ. Bruce Fields 		return nfserr_resource;
37351da177e4SLinus Torvalds 	if (nfserr) {
3736c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
3737c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3738c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3739c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
37401da177e4SLinus Torvalds 	}
37411da177e4SLinus Torvalds 	else {
3742c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(3);
3743c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[0]);
3744c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[1]);
3745c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(setattr->sa_bmval[2]);
37461da177e4SLinus Torvalds 	}
3747695e12f8SBenny Halevy 	return nfserr;
37481da177e4SLinus Torvalds }
37491da177e4SLinus Torvalds 
3750695e12f8SBenny Halevy static __be32
3751b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
37521da177e4SLinus Torvalds {
3753d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3754bc749ca4SJ. Bruce Fields 	__be32 *p;
37551da177e4SLinus Torvalds 
37561da177e4SLinus Torvalds 	if (!nfserr) {
3757d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8 + NFS4_VERIFIER_SIZE);
3758d0a381ddSJ. Bruce Fields 		if (!p)
3759d0a381ddSJ. Bruce Fields 			return nfserr_resource;
37600c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &scd->se_clientid, 8);
37610c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, &scd->se_confirm,
37620c0c267bSJ. Bruce Fields 						NFS4_VERIFIER_SIZE);
37631da177e4SLinus Torvalds 	}
37641da177e4SLinus Torvalds 	else if (nfserr == nfserr_clid_inuse) {
3765d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 8);
3766d0a381ddSJ. Bruce Fields 		if (!p)
3767d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3768c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3769c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
37701da177e4SLinus Torvalds 	}
3771695e12f8SBenny Halevy 	return nfserr;
37721da177e4SLinus Torvalds }
37731da177e4SLinus Torvalds 
3774695e12f8SBenny Halevy static __be32
3775b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
37761da177e4SLinus Torvalds {
3777d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3778bc749ca4SJ. Bruce Fields 	__be32 *p;
37791da177e4SLinus Torvalds 
37801da177e4SLinus Torvalds 	if (!nfserr) {
3781d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
3782d0a381ddSJ. Bruce Fields 		if (!p)
3783d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3784c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(write->wr_bytes_written);
3785c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(write->wr_how_written);
37860c0c267bSJ. Bruce Fields 		p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
37870c0c267bSJ. Bruce Fields 							NFS4_VERIFIER_SIZE);
37881da177e4SLinus Torvalds 	}
3789695e12f8SBenny Halevy 	return nfserr;
37901da177e4SLinus Torvalds }
37911da177e4SLinus Torvalds 
379257266a6eSJ. Bruce Fields static const u32 nfs4_minimal_spo_must_enforce[2] = {
379357266a6eSJ. Bruce Fields 	[1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
379457266a6eSJ. Bruce Fields 	      1 << (OP_EXCHANGE_ID - 32) |
379557266a6eSJ. Bruce Fields 	      1 << (OP_CREATE_SESSION - 32) |
379657266a6eSJ. Bruce Fields 	      1 << (OP_DESTROY_SESSION - 32) |
379757266a6eSJ. Bruce Fields 	      1 << (OP_DESTROY_CLIENTID - 32)
379857266a6eSJ. Bruce Fields };
379957266a6eSJ. Bruce Fields 
3800695e12f8SBenny Halevy static __be32
380157b7b43bSJ. Bruce Fields nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
38022db134ebSAndy Adamson 			 struct nfsd4_exchange_id *exid)
38032db134ebSAndy Adamson {
3804d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3805bc749ca4SJ. Bruce Fields 	__be32 *p;
38060733d213SAndy Adamson 	char *major_id;
38070733d213SAndy Adamson 	char *server_scope;
38080733d213SAndy Adamson 	int major_id_sz;
38090733d213SAndy Adamson 	int server_scope_sz;
38100733d213SAndy Adamson 	uint64_t minor_id = 0;
38110733d213SAndy Adamson 
38120733d213SAndy Adamson 	if (nfserr)
38132db134ebSAndy Adamson 		return nfserr;
38140733d213SAndy Adamson 
38150733d213SAndy Adamson 	major_id = utsname()->nodename;
38160733d213SAndy Adamson 	major_id_sz = strlen(major_id);
38170733d213SAndy Adamson 	server_scope = utsname()->nodename;
38180733d213SAndy Adamson 	server_scope_sz = strlen(server_scope);
38190733d213SAndy Adamson 
3820d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr,
38210733d213SAndy Adamson 		8 /* eir_clientid */ +
38220733d213SAndy Adamson 		4 /* eir_sequenceid */ +
38230733d213SAndy Adamson 		4 /* eir_flags */ +
3824a8bb84bcSKinglong Mee 		4 /* spr_how */);
3825d0a381ddSJ. Bruce Fields 	if (!p)
3826d0a381ddSJ. Bruce Fields 		return nfserr_resource;
38270733d213SAndy Adamson 
38280c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, &exid->clientid, 8);
3829c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(exid->seqid);
3830c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(exid->flags);
38310733d213SAndy Adamson 
3832c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(exid->spa_how);
3833a8bb84bcSKinglong Mee 
383457266a6eSJ. Bruce Fields 	switch (exid->spa_how) {
383557266a6eSJ. Bruce Fields 	case SP4_NONE:
383657266a6eSJ. Bruce Fields 		break;
383757266a6eSJ. Bruce Fields 	case SP4_MACH_CRED:
3838a8bb84bcSKinglong Mee 		/* spo_must_enforce, spo_must_allow */
3839d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 16);
3840d0a381ddSJ. Bruce Fields 		if (!p)
3841d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3842a8bb84bcSKinglong Mee 
384357266a6eSJ. Bruce Fields 		/* spo_must_enforce bitmap: */
3844c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(2);
3845c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[0]);
3846c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[1]);
384757266a6eSJ. Bruce Fields 		/* empty spo_must_allow bitmap: */
3848c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(0);
3849a8bb84bcSKinglong Mee 
385057266a6eSJ. Bruce Fields 		break;
385157266a6eSJ. Bruce Fields 	default:
385257266a6eSJ. Bruce Fields 		WARN_ON_ONCE(1);
385357266a6eSJ. Bruce Fields 	}
38540733d213SAndy Adamson 
3855d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr,
3856a8bb84bcSKinglong Mee 		8 /* so_minor_id */ +
3857a8bb84bcSKinglong Mee 		4 /* so_major_id.len */ +
3858a8bb84bcSKinglong Mee 		(XDR_QUADLEN(major_id_sz) * 4) +
3859a8bb84bcSKinglong Mee 		4 /* eir_server_scope.len */ +
3860a8bb84bcSKinglong Mee 		(XDR_QUADLEN(server_scope_sz) * 4) +
3861a8bb84bcSKinglong Mee 		4 /* eir_server_impl_id.count (0) */);
3862d0a381ddSJ. Bruce Fields 	if (!p)
3863d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3864a8bb84bcSKinglong Mee 
38650733d213SAndy Adamson 	/* The server_owner struct */
3866b64c7f3bSJ. Bruce Fields 	p = xdr_encode_hyper(p, minor_id);      /* Minor id */
38670733d213SAndy Adamson 	/* major id */
38680c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, major_id, major_id_sz);
38690733d213SAndy Adamson 
38700733d213SAndy Adamson 	/* Server scope */
38710c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque(p, server_scope, server_scope_sz);
38720733d213SAndy Adamson 
38730733d213SAndy Adamson 	/* Implementation id */
3874c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0);	/* zero length nfs_impl_id4 array */
38750733d213SAndy Adamson 	return 0;
38762db134ebSAndy Adamson }
38772db134ebSAndy Adamson 
38782db134ebSAndy Adamson static __be32
387957b7b43bSJ. Bruce Fields nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
38802db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
38812db134ebSAndy Adamson {
3882d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3883bc749ca4SJ. Bruce Fields 	__be32 *p;
3884ec6b5d7bSAndy Adamson 
3885ec6b5d7bSAndy Adamson 	if (nfserr)
38862db134ebSAndy Adamson 		return nfserr;
3887ec6b5d7bSAndy Adamson 
3888d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 24);
3889d0a381ddSJ. Bruce Fields 	if (!p)
3890d0a381ddSJ. Bruce Fields 		return nfserr_resource;
38910c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, sess->sessionid.data,
38920c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
3893c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->seqid);
3894c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->flags);
3895ec6b5d7bSAndy Adamson 
3896d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 28);
3897d0a381ddSJ. Bruce Fields 	if (!p)
3898d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3899c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* headerpadsz */
3900c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxreq_sz);
3901c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxresp_sz);
3902c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxresp_cached);
3903c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxops);
3904c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.maxreqs);
3905c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs);
3906ec6b5d7bSAndy Adamson 
3907ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs) {
3908d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3909d0a381ddSJ. Bruce Fields 		if (!p)
3910d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3911c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(sess->fore_channel.rdma_attrs);
3912ec6b5d7bSAndy Adamson 	}
3913ec6b5d7bSAndy Adamson 
3914d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 28);
3915d0a381ddSJ. Bruce Fields 	if (!p)
3916d0a381ddSJ. Bruce Fields 		return nfserr_resource;
3917c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(0); /* headerpadsz */
3918c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxreq_sz);
3919c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxresp_sz);
3920c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxresp_cached);
3921c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxops);
3922c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.maxreqs);
3923c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs);
3924ec6b5d7bSAndy Adamson 
3925ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs) {
3926d0a381ddSJ. Bruce Fields 		p = xdr_reserve_space(xdr, 4);
3927d0a381ddSJ. Bruce Fields 		if (!p)
3928d0a381ddSJ. Bruce Fields 			return nfserr_resource;
3929c373b0a4SJ. Bruce Fields 		*p++ = cpu_to_be32(sess->back_channel.rdma_attrs);
3930ec6b5d7bSAndy Adamson 	}
3931ec6b5d7bSAndy Adamson 	return 0;
39322db134ebSAndy Adamson }
39332db134ebSAndy Adamson 
39342db134ebSAndy Adamson static __be32
393557b7b43bSJ. Bruce Fields nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
39362db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
39372db134ebSAndy Adamson {
3938d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
3939bc749ca4SJ. Bruce Fields 	__be32 *p;
3940b85d4c01SBenny Halevy 
3941b85d4c01SBenny Halevy 	if (nfserr)
39422db134ebSAndy Adamson 		return nfserr;
3943b85d4c01SBenny Halevy 
3944d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20);
3945d0a381ddSJ. Bruce Fields 	if (!p)
3946d0a381ddSJ. Bruce Fields 		return nfserr_resource;
39470c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, seq->sessionid.data,
39480c0c267bSJ. Bruce Fields 					NFS4_MAX_SESSIONID_LEN);
3949c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->seqid);
3950c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->slotid);
3951b7d7ca35SJ. Bruce Fields 	/* Note slotid's are numbered from zero: */
3952c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */
3953c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */
3954c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(seq->status_flags);
3955b85d4c01SBenny Halevy 
3956f5236013SJ. Bruce Fields 	resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
3957b85d4c01SBenny Halevy 	return 0;
39582db134ebSAndy Adamson }
39592db134ebSAndy Adamson 
39602355c596SJ. Bruce Fields static __be32
396157b7b43bSJ. Bruce Fields nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
396217456804SBryan Schumaker 			  struct nfsd4_test_stateid *test_stateid)
396317456804SBryan Schumaker {
3964d0a381ddSJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
396503cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid, *next;
396617456804SBryan Schumaker 	__be32 *p;
396717456804SBryan Schumaker 
3968a11fcce1SJ. Bruce Fields 	if (nfserr)
3969a11fcce1SJ. Bruce Fields 		return nfserr;
3970a11fcce1SJ. Bruce Fields 
3971d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids));
3972d0a381ddSJ. Bruce Fields 	if (!p)
3973d0a381ddSJ. Bruce Fields 		return nfserr_resource;
397417456804SBryan Schumaker 	*p++ = htonl(test_stateid->ts_num_ids);
397517456804SBryan Schumaker 
397603cfb420SBryan Schumaker 	list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
397702f5fde5SAl Viro 		*p++ = stateid->ts_id_status;
397817456804SBryan Schumaker 	}
397917456804SBryan Schumaker 
398017456804SBryan Schumaker 	return nfserr;
398117456804SBryan Schumaker }
398217456804SBryan Schumaker 
39839cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
39849cf514ccSChristoph Hellwig static __be32
39859cf514ccSChristoph Hellwig nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
39869cf514ccSChristoph Hellwig 		struct nfsd4_getdeviceinfo *gdev)
39879cf514ccSChristoph Hellwig {
39889cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
39899cf514ccSChristoph Hellwig 	const struct nfsd4_layout_ops *ops =
39909cf514ccSChristoph Hellwig 		nfsd4_layout_ops[gdev->gd_layout_type];
39919cf514ccSChristoph Hellwig 	u32 starting_len = xdr->buf->len, needed_len;
39929cf514ccSChristoph Hellwig 	__be32 *p;
39939cf514ccSChristoph Hellwig 
39949cf514ccSChristoph Hellwig 	dprintk("%s: err %d\n", __func__, nfserr);
39959cf514ccSChristoph Hellwig 	if (nfserr)
39969cf514ccSChristoph Hellwig 		goto out;
39979cf514ccSChristoph Hellwig 
39989cf514ccSChristoph Hellwig 	nfserr = nfserr_resource;
39999cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
40009cf514ccSChristoph Hellwig 	if (!p)
40019cf514ccSChristoph Hellwig 		goto out;
40029cf514ccSChristoph Hellwig 
40039cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(gdev->gd_layout_type);
40049cf514ccSChristoph Hellwig 
40059cf514ccSChristoph Hellwig 	/* If maxcount is 0 then just update notifications */
40069cf514ccSChristoph Hellwig 	if (gdev->gd_maxcount != 0) {
40079cf514ccSChristoph Hellwig 		nfserr = ops->encode_getdeviceinfo(xdr, gdev);
40089cf514ccSChristoph Hellwig 		if (nfserr) {
40099cf514ccSChristoph Hellwig 			/*
40109cf514ccSChristoph Hellwig 			 * We don't bother to burden the layout drivers with
40119cf514ccSChristoph Hellwig 			 * enforcing gd_maxcount, just tell the client to
40129cf514ccSChristoph Hellwig 			 * come back with a bigger buffer if it's not enough.
40139cf514ccSChristoph Hellwig 			 */
40149cf514ccSChristoph Hellwig 			if (xdr->buf->len + 4 > gdev->gd_maxcount)
40159cf514ccSChristoph Hellwig 				goto toosmall;
40169cf514ccSChristoph Hellwig 			goto out;
40179cf514ccSChristoph Hellwig 		}
40189cf514ccSChristoph Hellwig 	}
40199cf514ccSChristoph Hellwig 
40209cf514ccSChristoph Hellwig 	nfserr = nfserr_resource;
40219cf514ccSChristoph Hellwig 	if (gdev->gd_notify_types) {
40229cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4 + 4);
40239cf514ccSChristoph Hellwig 		if (!p)
40249cf514ccSChristoph Hellwig 			goto out;
40259cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(1);			/* bitmap length */
40269cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(gdev->gd_notify_types);
40279cf514ccSChristoph Hellwig 	} else {
40289cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 4);
40299cf514ccSChristoph Hellwig 		if (!p)
40309cf514ccSChristoph Hellwig 			goto out;
40319cf514ccSChristoph Hellwig 		*p++ = 0;
40329cf514ccSChristoph Hellwig 	}
40339cf514ccSChristoph Hellwig 
40349cf514ccSChristoph Hellwig 	nfserr = 0;
40359cf514ccSChristoph Hellwig out:
40369cf514ccSChristoph Hellwig 	kfree(gdev->gd_device);
40379cf514ccSChristoph Hellwig 	dprintk("%s: done: %d\n", __func__, be32_to_cpu(nfserr));
40389cf514ccSChristoph Hellwig 	return nfserr;
40399cf514ccSChristoph Hellwig 
40409cf514ccSChristoph Hellwig toosmall:
40419cf514ccSChristoph Hellwig 	dprintk("%s: maxcount too small\n", __func__);
40429cf514ccSChristoph Hellwig 	needed_len = xdr->buf->len + 4 /* notifications */;
40439cf514ccSChristoph Hellwig 	xdr_truncate_encode(xdr, starting_len);
40449cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
40459cf514ccSChristoph Hellwig 	if (!p) {
40469cf514ccSChristoph Hellwig 		nfserr = nfserr_resource;
40479cf514ccSChristoph Hellwig 	} else {
40489cf514ccSChristoph Hellwig 		*p++ = cpu_to_be32(needed_len);
40499cf514ccSChristoph Hellwig 		nfserr = nfserr_toosmall;
40509cf514ccSChristoph Hellwig 	}
40519cf514ccSChristoph Hellwig 	goto out;
40529cf514ccSChristoph Hellwig }
40539cf514ccSChristoph Hellwig 
40549cf514ccSChristoph Hellwig static __be32
40559cf514ccSChristoph Hellwig nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
40569cf514ccSChristoph Hellwig 		struct nfsd4_layoutget *lgp)
40579cf514ccSChristoph Hellwig {
40589cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
40599cf514ccSChristoph Hellwig 	const struct nfsd4_layout_ops *ops =
40609cf514ccSChristoph Hellwig 		nfsd4_layout_ops[lgp->lg_layout_type];
40619cf514ccSChristoph Hellwig 	__be32 *p;
40629cf514ccSChristoph Hellwig 
40639cf514ccSChristoph Hellwig 	dprintk("%s: err %d\n", __func__, nfserr);
40649cf514ccSChristoph Hellwig 	if (nfserr)
40659cf514ccSChristoph Hellwig 		goto out;
40669cf514ccSChristoph Hellwig 
40679cf514ccSChristoph Hellwig 	nfserr = nfserr_resource;
40689cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 36 + sizeof(stateid_opaque_t));
40699cf514ccSChristoph Hellwig 	if (!p)
40709cf514ccSChristoph Hellwig 		goto out;
40719cf514ccSChristoph Hellwig 
40729cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(1);	/* we always set return-on-close */
40739cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_sid.si_generation);
40749cf514ccSChristoph Hellwig 	p = xdr_encode_opaque_fixed(p, &lgp->lg_sid.si_opaque,
40759cf514ccSChristoph Hellwig 				    sizeof(stateid_opaque_t));
40769cf514ccSChristoph Hellwig 
40779cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(1);	/* we always return a single layout */
40789cf514ccSChristoph Hellwig 	p = xdr_encode_hyper(p, lgp->lg_seg.offset);
40799cf514ccSChristoph Hellwig 	p = xdr_encode_hyper(p, lgp->lg_seg.length);
40809cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_seg.iomode);
40819cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lgp->lg_layout_type);
40829cf514ccSChristoph Hellwig 
40839cf514ccSChristoph Hellwig 	nfserr = ops->encode_layoutget(xdr, lgp);
40849cf514ccSChristoph Hellwig out:
40859cf514ccSChristoph Hellwig 	kfree(lgp->lg_content);
40869cf514ccSChristoph Hellwig 	return nfserr;
40879cf514ccSChristoph Hellwig }
40889cf514ccSChristoph Hellwig 
40899cf514ccSChristoph Hellwig static __be32
40909cf514ccSChristoph Hellwig nfsd4_encode_layoutcommit(struct nfsd4_compoundres *resp, __be32 nfserr,
40919cf514ccSChristoph Hellwig 			  struct nfsd4_layoutcommit *lcp)
40929cf514ccSChristoph Hellwig {
40939cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
40949cf514ccSChristoph Hellwig 	__be32 *p;
40959cf514ccSChristoph Hellwig 
40969cf514ccSChristoph Hellwig 	if (nfserr)
40979cf514ccSChristoph Hellwig 		return nfserr;
40989cf514ccSChristoph Hellwig 
40999cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
41009cf514ccSChristoph Hellwig 	if (!p)
41019cf514ccSChristoph Hellwig 		return nfserr_resource;
41029cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lcp->lc_size_chg);
41039cf514ccSChristoph Hellwig 	if (lcp->lc_size_chg) {
41049cf514ccSChristoph Hellwig 		p = xdr_reserve_space(xdr, 8);
41059cf514ccSChristoph Hellwig 		if (!p)
41069cf514ccSChristoph Hellwig 			return nfserr_resource;
41079cf514ccSChristoph Hellwig 		p = xdr_encode_hyper(p, lcp->lc_newsize);
41089cf514ccSChristoph Hellwig 	}
41099cf514ccSChristoph Hellwig 
41109cf514ccSChristoph Hellwig 	return nfs_ok;
41119cf514ccSChristoph Hellwig }
41129cf514ccSChristoph Hellwig 
41139cf514ccSChristoph Hellwig static __be32
41149cf514ccSChristoph Hellwig nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
41159cf514ccSChristoph Hellwig 		struct nfsd4_layoutreturn *lrp)
41169cf514ccSChristoph Hellwig {
41179cf514ccSChristoph Hellwig 	struct xdr_stream *xdr = &resp->xdr;
41189cf514ccSChristoph Hellwig 	__be32 *p;
41199cf514ccSChristoph Hellwig 
41209cf514ccSChristoph Hellwig 	if (nfserr)
41219cf514ccSChristoph Hellwig 		return nfserr;
41229cf514ccSChristoph Hellwig 
41239cf514ccSChristoph Hellwig 	p = xdr_reserve_space(xdr, 4);
41249cf514ccSChristoph Hellwig 	if (!p)
41259cf514ccSChristoph Hellwig 		return nfserr_resource;
41269cf514ccSChristoph Hellwig 	*p++ = cpu_to_be32(lrp->lrs_present);
41279cf514ccSChristoph Hellwig 	if (lrp->lrs_present)
4128376675daSKinglong Mee 		return nfsd4_encode_stateid(xdr, &lrp->lr_sid);
41299cf514ccSChristoph Hellwig 	return nfs_ok;
41309cf514ccSChristoph Hellwig }
41319cf514ccSChristoph Hellwig #endif /* CONFIG_NFSD_PNFS */
41329cf514ccSChristoph Hellwig 
41332db134ebSAndy Adamson static __be32
413424bab491SAnna Schumaker nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
413524bab491SAnna Schumaker 		  struct nfsd4_seek *seek)
413624bab491SAnna Schumaker {
413724bab491SAnna Schumaker 	__be32 *p;
413824bab491SAnna Schumaker 
413924bab491SAnna Schumaker 	if (nfserr)
414024bab491SAnna Schumaker 		return nfserr;
414124bab491SAnna Schumaker 
414224bab491SAnna Schumaker 	p = xdr_reserve_space(&resp->xdr, 4 + 8);
414324bab491SAnna Schumaker 	*p++ = cpu_to_be32(seek->seek_eof);
414424bab491SAnna Schumaker 	p = xdr_encode_hyper(p, seek->seek_pos);
414524bab491SAnna Schumaker 
414624bab491SAnna Schumaker 	return nfserr;
414724bab491SAnna Schumaker }
414824bab491SAnna Schumaker 
414924bab491SAnna Schumaker static __be32
4150695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
4151695e12f8SBenny Halevy {
4152695e12f8SBenny Halevy 	return nfserr;
4153695e12f8SBenny Halevy }
4154695e12f8SBenny Halevy 
4155695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
4156695e12f8SBenny Halevy 
41572db134ebSAndy Adamson /*
41582db134ebSAndy Adamson  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
41592db134ebSAndy Adamson  * since we don't need to filter out obsolete ops as this is
41602db134ebSAndy Adamson  * done in the decoding phase.
41612db134ebSAndy Adamson  */
4162695e12f8SBenny Halevy static nfsd4_enc nfsd4_enc_ops[] = {
4163ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_enc)nfsd4_encode_access,
4164ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_enc)nfsd4_encode_close,
4165ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_enc)nfsd4_encode_commit,
4166ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_enc)nfsd4_encode_create,
4167ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_enc)nfsd4_encode_noop,
4168ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
4169ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_enc)nfsd4_encode_getattr,
4170ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_enc)nfsd4_encode_getfh,
4171ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_enc)nfsd4_encode_link,
4172ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_enc)nfsd4_encode_lock,
4173ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_enc)nfsd4_encode_lockt,
4174ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_enc)nfsd4_encode_locku,
4175ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_enc)nfsd4_encode_noop,
4176ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_enc)nfsd4_encode_noop,
4177ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
4178ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_enc)nfsd4_encode_open,
417984f09f46SBenny Halevy 	[OP_OPENATTR]		= (nfsd4_enc)nfsd4_encode_noop,
4180ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_enc)nfsd4_encode_open_confirm,
4181ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_enc)nfsd4_encode_open_downgrade,
4182ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_enc)nfsd4_encode_noop,
4183ad1060c8SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_enc)nfsd4_encode_noop,
4184ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_enc)nfsd4_encode_noop,
4185ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_enc)nfsd4_encode_read,
4186ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_enc)nfsd4_encode_readdir,
4187ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_enc)nfsd4_encode_readlink,
4188ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_enc)nfsd4_encode_remove,
4189ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_enc)nfsd4_encode_rename,
4190ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_enc)nfsd4_encode_noop,
4191ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_enc)nfsd4_encode_noop,
4192ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_enc)nfsd4_encode_noop,
4193ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_enc)nfsd4_encode_secinfo,
4194ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_enc)nfsd4_encode_setattr,
4195ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_enc)nfsd4_encode_setclientid,
4196ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop,
4197ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
4198ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_enc)nfsd4_encode_write,
4199ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_enc)nfsd4_encode_noop,
42002db134ebSAndy Adamson 
42012db134ebSAndy Adamson 	/* NFSv4.1 operations */
42022db134ebSAndy Adamson 	[OP_BACKCHANNEL_CTL]	= (nfsd4_enc)nfsd4_encode_noop,
42031d1bc8f2SJ. Bruce Fields 	[OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session,
42042db134ebSAndy Adamson 	[OP_EXCHANGE_ID]	= (nfsd4_enc)nfsd4_encode_exchange_id,
42052db134ebSAndy Adamson 	[OP_CREATE_SESSION]	= (nfsd4_enc)nfsd4_encode_create_session,
420643212cc7SKinglong Mee 	[OP_DESTROY_SESSION]	= (nfsd4_enc)nfsd4_encode_noop,
420743212cc7SKinglong Mee 	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
42082db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
42099cf514ccSChristoph Hellwig #ifdef CONFIG_NFSD_PNFS
42109cf514ccSChristoph Hellwig 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_getdeviceinfo,
42119cf514ccSChristoph Hellwig 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
42129cf514ccSChristoph Hellwig 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_layoutcommit,
42139cf514ccSChristoph Hellwig 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_layoutget,
42149cf514ccSChristoph Hellwig 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_layoutreturn,
42159cf514ccSChristoph Hellwig #else
42162db134ebSAndy Adamson 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_noop,
42172db134ebSAndy Adamson 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
42182db134ebSAndy Adamson 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_noop,
42192db134ebSAndy Adamson 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_noop,
42202db134ebSAndy Adamson 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
42219cf514ccSChristoph Hellwig #endif
422222b6dee8SMi Jinlong 	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_secinfo_no_name,
42232db134ebSAndy Adamson 	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
42242db134ebSAndy Adamson 	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
422517456804SBryan Schumaker 	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_test_stateid,
42262db134ebSAndy Adamson 	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
42272db134ebSAndy Adamson 	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
42282db134ebSAndy Adamson 	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
422987a15a80SAnna Schumaker 
423087a15a80SAnna Schumaker 	/* NFSv4.2 operations */
423187a15a80SAnna Schumaker 	[OP_ALLOCATE]		= (nfsd4_enc)nfsd4_encode_noop,
423287a15a80SAnna Schumaker 	[OP_COPY]		= (nfsd4_enc)nfsd4_encode_noop,
423387a15a80SAnna Schumaker 	[OP_COPY_NOTIFY]	= (nfsd4_enc)nfsd4_encode_noop,
423487a15a80SAnna Schumaker 	[OP_DEALLOCATE]		= (nfsd4_enc)nfsd4_encode_noop,
423587a15a80SAnna Schumaker 	[OP_IO_ADVISE]		= (nfsd4_enc)nfsd4_encode_noop,
423687a15a80SAnna Schumaker 	[OP_LAYOUTERROR]	= (nfsd4_enc)nfsd4_encode_noop,
423787a15a80SAnna Schumaker 	[OP_LAYOUTSTATS]	= (nfsd4_enc)nfsd4_encode_noop,
423887a15a80SAnna Schumaker 	[OP_OFFLOAD_CANCEL]	= (nfsd4_enc)nfsd4_encode_noop,
423987a15a80SAnna Schumaker 	[OP_OFFLOAD_STATUS]	= (nfsd4_enc)nfsd4_encode_noop,
424087a15a80SAnna Schumaker 	[OP_READ_PLUS]		= (nfsd4_enc)nfsd4_encode_noop,
424124bab491SAnna Schumaker 	[OP_SEEK]		= (nfsd4_enc)nfsd4_encode_seek,
424287a15a80SAnna Schumaker 	[OP_WRITE_SAME]		= (nfsd4_enc)nfsd4_encode_noop,
4243695e12f8SBenny Halevy };
4244695e12f8SBenny Halevy 
4245496c262cSAndy Adamson /*
4246a8095f7eSJ. Bruce Fields  * Calculate whether we still have space to encode repsize bytes.
4247a8095f7eSJ. Bruce Fields  * There are two considerations:
4248a8095f7eSJ. Bruce Fields  *     - For NFS versions >=4.1, the size of the reply must stay within
4249a8095f7eSJ. Bruce Fields  *       session limits
4250a8095f7eSJ. Bruce Fields  *     - For all NFS versions, we must stay within limited preallocated
4251a8095f7eSJ. Bruce Fields  *       buffer space.
4252496c262cSAndy Adamson  *
4253a8095f7eSJ. Bruce Fields  * This is called before the operation is processed, so can only provide
4254a8095f7eSJ. Bruce Fields  * an upper estimate.  For some nonidempotent operations (such as
4255a8095f7eSJ. Bruce Fields  * getattr), it's not necessarily a problem if that estimate is wrong,
4256a8095f7eSJ. Bruce Fields  * as we can fail it after processing without significant side effects.
4257496c262cSAndy Adamson  */
4258a8095f7eSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
4259496c262cSAndy Adamson {
426067492c99SJ. Bruce Fields 	struct xdr_buf *buf = &resp->rqstp->rq_res;
4261a8095f7eSJ. Bruce Fields 	struct nfsd4_slot *slot = resp->cstate.slot;
4262496c262cSAndy Adamson 
426347ee5298SJ. Bruce Fields 	if (buf->len + respsize <= buf->buflen)
426447ee5298SJ. Bruce Fields 		return nfs_ok;
426547ee5298SJ. Bruce Fields 	if (!nfsd4_has_session(&resp->cstate))
426647ee5298SJ. Bruce Fields 		return nfserr_resource;
426747ee5298SJ. Bruce Fields 	if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
426847ee5298SJ. Bruce Fields 		WARN_ON_ONCE(1);
4269496c262cSAndy Adamson 		return nfserr_rep_too_big_to_cache;
4270ea8d7720SJ. Bruce Fields 	}
427147ee5298SJ. Bruce Fields 	return nfserr_rep_too_big;
4272496c262cSAndy Adamson }
4273496c262cSAndy Adamson 
42741da177e4SLinus Torvalds void
42751da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
42761da177e4SLinus Torvalds {
4277082d4bd7SJ. Bruce Fields 	struct xdr_stream *xdr = &resp->xdr;
42789411b1d4SJ. Bruce Fields 	struct nfs4_stateowner *so = resp->cstate.replay_owner;
42795f4ab945SJ. Bruce Fields 	struct svc_rqst *rqstp = resp->rqstp;
4280082d4bd7SJ. Bruce Fields 	int post_err_offset;
428107d1f802SJ. Bruce Fields 	nfsd4_enc encoder;
4282bc749ca4SJ. Bruce Fields 	__be32 *p;
42831da177e4SLinus Torvalds 
4284d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8);
4285d0a381ddSJ. Bruce Fields 	if (!p) {
4286d0a381ddSJ. Bruce Fields 		WARN_ON_ONCE(1);
4287d0a381ddSJ. Bruce Fields 		return;
4288d0a381ddSJ. Bruce Fields 	}
4289c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(op->opnum);
4290082d4bd7SJ. Bruce Fields 	post_err_offset = xdr->buf->len;
42911da177e4SLinus Torvalds 
4292695e12f8SBenny Halevy 	if (op->opnum == OP_ILLEGAL)
4293695e12f8SBenny Halevy 		goto status;
4294695e12f8SBenny Halevy 	BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
4295695e12f8SBenny Halevy 	       !nfsd4_enc_ops[op->opnum]);
429607d1f802SJ. Bruce Fields 	encoder = nfsd4_enc_ops[op->opnum];
429707d1f802SJ. Bruce Fields 	op->status = encoder(resp, op->status, &op->u);
42982825a7f9SJ. Bruce Fields 	xdr_commit_encode(xdr);
42992825a7f9SJ. Bruce Fields 
4300067e1aceSJ. Bruce Fields 	/* nfsd4_check_resp_size guarantees enough room for error status */
43015f4ab945SJ. Bruce Fields 	if (!op->status) {
43025f4ab945SJ. Bruce Fields 		int space_needed = 0;
43035f4ab945SJ. Bruce Fields 		if (!nfsd4_last_compound_op(rqstp))
43045f4ab945SJ. Bruce Fields 			space_needed = COMPOUND_ERR_SLACK_SPACE;
43055f4ab945SJ. Bruce Fields 		op->status = nfsd4_check_resp_size(resp, space_needed);
43065f4ab945SJ. Bruce Fields 	}
4307c8f13d97SJ. Bruce Fields 	if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
4308c8f13d97SJ. Bruce Fields 		struct nfsd4_slot *slot = resp->cstate.slot;
4309c8f13d97SJ. Bruce Fields 
4310c8f13d97SJ. Bruce Fields 		if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
4311c8f13d97SJ. Bruce Fields 			op->status = nfserr_rep_too_big_to_cache;
4312c8f13d97SJ. Bruce Fields 		else
4313c8f13d97SJ. Bruce Fields 			op->status = nfserr_rep_too_big;
4314c8f13d97SJ. Bruce Fields 	}
431507d1f802SJ. Bruce Fields 	if (op->status == nfserr_resource ||
431607d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big ||
431707d1f802SJ. Bruce Fields 	    op->status == nfserr_rep_too_big_to_cache) {
431807d1f802SJ. Bruce Fields 		/*
431907d1f802SJ. Bruce Fields 		 * The operation may have already been encoded or
432007d1f802SJ. Bruce Fields 		 * partially encoded.  No op returns anything additional
432107d1f802SJ. Bruce Fields 		 * in the case of one of these three errors, so we can
432207d1f802SJ. Bruce Fields 		 * just truncate back to after the status.  But it's a
432307d1f802SJ. Bruce Fields 		 * bug if we had to do this on a non-idempotent op:
432407d1f802SJ. Bruce Fields 		 */
432507d1f802SJ. Bruce Fields 		warn_on_nonidempotent_op(op);
4326082d4bd7SJ. Bruce Fields 		xdr_truncate_encode(xdr, post_err_offset);
432707d1f802SJ. Bruce Fields 	}
43289411b1d4SJ. Bruce Fields 	if (so) {
4329082d4bd7SJ. Bruce Fields 		int len = xdr->buf->len - post_err_offset;
4330082d4bd7SJ. Bruce Fields 
43319411b1d4SJ. Bruce Fields 		so->so_replay.rp_status = op->status;
4332082d4bd7SJ. Bruce Fields 		so->so_replay.rp_buflen = len;
4333082d4bd7SJ. Bruce Fields 		read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
4334082d4bd7SJ. Bruce Fields 						so->so_replay.rp_buf, len);
43359411b1d4SJ. Bruce Fields 	}
4336695e12f8SBenny Halevy status:
4337082d4bd7SJ. Bruce Fields 	/* Note that op->status is already in network byte order: */
4338082d4bd7SJ. Bruce Fields 	write_bytes_to_xdr_buf(xdr->buf, post_err_offset - 4, &op->status, 4);
43391da177e4SLinus Torvalds }
43401da177e4SLinus Torvalds 
43411da177e4SLinus Torvalds /*
43421da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
43431da177e4SLinus Torvalds  *
43441da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
43451da177e4SLinus Torvalds  * previously sent already encoded operation.
43461da177e4SLinus Torvalds  */
43471da177e4SLinus Torvalds void
4348d0a381ddSJ. Bruce Fields nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
43491da177e4SLinus Torvalds {
4350bc749ca4SJ. Bruce Fields 	__be32 *p;
43511da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
43521da177e4SLinus Torvalds 
43531da177e4SLinus Torvalds 	BUG_ON(!rp);
43541da177e4SLinus Torvalds 
4355d0a381ddSJ. Bruce Fields 	p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
4356d0a381ddSJ. Bruce Fields 	if (!p) {
4357d0a381ddSJ. Bruce Fields 		WARN_ON_ONCE(1);
4358d0a381ddSJ. Bruce Fields 		return;
4359d0a381ddSJ. Bruce Fields 	}
4360c373b0a4SJ. Bruce Fields 	*p++ = cpu_to_be32(op->opnum);
43611da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
43621da177e4SLinus Torvalds 
43630c0c267bSJ. Bruce Fields 	p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen);
43641da177e4SLinus Torvalds }
43651da177e4SLinus Torvalds 
43661da177e4SLinus Torvalds int
43672ebbc012SAl Viro nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
43681da177e4SLinus Torvalds {
43691da177e4SLinus Torvalds         return xdr_ressize_check(rqstp, p);
43701da177e4SLinus Torvalds }
43711da177e4SLinus Torvalds 
43723e98abffSJ. Bruce Fields int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp)
43731da177e4SLinus Torvalds {
43743e98abffSJ. Bruce Fields 	struct svc_rqst *rqstp = rq;
43753e98abffSJ. Bruce Fields 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
43763e98abffSJ. Bruce Fields 
43771da177e4SLinus Torvalds 	if (args->ops != args->iops) {
43781da177e4SLinus Torvalds 		kfree(args->ops);
43791da177e4SLinus Torvalds 		args->ops = args->iops;
43801da177e4SLinus Torvalds 	}
43811da177e4SLinus Torvalds 	kfree(args->tmpp);
43821da177e4SLinus Torvalds 	args->tmpp = NULL;
43831da177e4SLinus Torvalds 	while (args->to_free) {
4384d5e23383SJ. Bruce Fields 		struct svcxdr_tmpbuf *tb = args->to_free;
43851da177e4SLinus Torvalds 		args->to_free = tb->next;
43861da177e4SLinus Torvalds 		kfree(tb);
43871da177e4SLinus Torvalds 	}
43883e98abffSJ. Bruce Fields 	return 1;
43891da177e4SLinus Torvalds }
43901da177e4SLinus Torvalds 
43911da177e4SLinus Torvalds int
43922ebbc012SAl Viro nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
43931da177e4SLinus Torvalds {
4394e874f9f8SJeff Layton 	if (rqstp->rq_arg.head[0].iov_len % 4) {
4395e874f9f8SJeff Layton 		/* client is nuts */
4396e874f9f8SJeff Layton 		dprintk("%s: compound not properly padded! (peeraddr=%pISc xid=0x%x)",
4397e874f9f8SJeff Layton 			__func__, svc_addr(rqstp), be32_to_cpu(rqstp->rq_xid));
4398e874f9f8SJeff Layton 		return 0;
4399e874f9f8SJeff Layton 	}
44001da177e4SLinus Torvalds 	args->p = p;
44011da177e4SLinus Torvalds 	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
44021da177e4SLinus Torvalds 	args->pagelist = rqstp->rq_arg.pages;
44031da177e4SLinus Torvalds 	args->pagelen = rqstp->rq_arg.page_len;
44041da177e4SLinus Torvalds 	args->tmpp = NULL;
44051da177e4SLinus Torvalds 	args->to_free = NULL;
44061da177e4SLinus Torvalds 	args->ops = args->iops;
44071da177e4SLinus Torvalds 	args->rqstp = rqstp;
44081da177e4SLinus Torvalds 
44093e98abffSJ. Bruce Fields 	return !nfsd4_decode_compound(args);
44101da177e4SLinus Torvalds }
44111da177e4SLinus Torvalds 
44121da177e4SLinus Torvalds int
44132ebbc012SAl Viro nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp)
44141da177e4SLinus Torvalds {
44151da177e4SLinus Torvalds 	/*
44161da177e4SLinus Torvalds 	 * All that remains is to write the tag and operation count...
44171da177e4SLinus Torvalds 	 */
44186ac90391SJ. Bruce Fields 	struct xdr_buf *buf = resp->xdr.buf;
44196ac90391SJ. Bruce Fields 
44206ac90391SJ. Bruce Fields 	WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
44216ac90391SJ. Bruce Fields 				 buf->tail[0].iov_len);
4422dd97fddeSJ. Bruce Fields 
44232825a7f9SJ. Bruce Fields 	rqstp->rq_next_page = resp->xdr.page_ptr + 1;
44242825a7f9SJ. Bruce Fields 
44251da177e4SLinus Torvalds 	p = resp->tagp;
44261da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
44271da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
44281da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
44291da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
44301da177e4SLinus Torvalds 
4431b607664eSTrond Myklebust 	nfsd4_sequence_done(resp);
44321da177e4SLinus Torvalds 	return 1;
44331da177e4SLinus Torvalds }
44341da177e4SLinus Torvalds 
44351da177e4SLinus Torvalds /*
44361da177e4SLinus Torvalds  * Local variables:
44371da177e4SLinus Torvalds  *  c-basic-offset: 8
44381da177e4SLinus Torvalds  * End:
44391da177e4SLinus Torvalds  */
4440