xref: /linux/fs/nfsd/nfs4xdr.c (revision 57266a6e916e2522ea61758a3ee5576b60156791)
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  * TODO: Neil Brown made the following observation:  We currently
361da177e4SLinus Torvalds  * initially reserve NFSD_BUFSIZE space on the transmit queue and
371da177e4SLinus Torvalds  * never release any of that until the request is complete.
381da177e4SLinus Torvalds  * It would be good to calculate a new maximum response size while
391da177e4SLinus Torvalds  * decoding the COMPOUND, and call svc_reserve with this number
401da177e4SLinus Torvalds  * at the end of nfs4svc_decode_compoundargs.
411da177e4SLinus Torvalds  */
421da177e4SLinus Torvalds 
435a0e3ad6STejun Heo #include <linux/slab.h>
441da177e4SLinus Torvalds #include <linux/namei.h>
45341eb184SBoaz Harrosh #include <linux/statfs.h>
460733d213SAndy Adamson #include <linux/utsname.h>
4717456804SBryan Schumaker #include <linux/pagemap.h>
484796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h>
499a74af21SBoaz Harrosh 
502ca72e17SJ. Bruce Fields #include "idmap.h"
512ca72e17SJ. Bruce Fields #include "acl.h"
529a74af21SBoaz Harrosh #include "xdr4.h"
530a3adadeSJ. Bruce Fields #include "vfs.h"
5417456804SBryan Schumaker #include "state.h"
551091006cSJ. Bruce Fields #include "cache.h"
563d733711SStanislav Kinsbursky #include "netns.h"
572ca72e17SJ. Bruce Fields 
5818032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
5918032ca0SDavid Quigley #include <linux/security.h>
6018032ca0SDavid Quigley #endif
6118032ca0SDavid Quigley 
6218032ca0SDavid Quigley 
631da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
641da177e4SLinus Torvalds 
6542ca0993SJ.Bruce Fields /*
6642ca0993SJ.Bruce Fields  * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
6742ca0993SJ.Bruce Fields  * directory in order to indicate to the client that a filesystem boundary is present
6842ca0993SJ.Bruce Fields  * We use a fixed fsid for a referral
6942ca0993SJ.Bruce Fields  */
7042ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
7142ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL
7242ca0993SJ.Bruce Fields 
73b37ad28bSAl Viro static __be32
74a36b1725SJ. Bruce Fields check_filename(char *str, int len)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	int i;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	if (len == 0)
791da177e4SLinus Torvalds 		return nfserr_inval;
801da177e4SLinus Torvalds 	if (isdotent(str, len))
81a36b1725SJ. Bruce Fields 		return nfserr_badname;
821da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
831da177e4SLinus Torvalds 		if (str[i] == '/')
84a36b1725SJ. Bruce Fields 			return nfserr_badname;
851da177e4SLinus Torvalds 	return 0;
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds #define DECODE_HEAD				\
892ebbc012SAl Viro 	__be32 *p;				\
90b37ad28bSAl Viro 	__be32 status
911da177e4SLinus Torvalds #define DECODE_TAIL				\
921da177e4SLinus Torvalds 	status = 0;				\
931da177e4SLinus Torvalds out:						\
941da177e4SLinus Torvalds 	return status;				\
951da177e4SLinus Torvalds xdr_error:					\
96817cb9d4SChuck Lever 	dprintk("NFSD: xdr error (%s:%d)\n",	\
97817cb9d4SChuck Lever 			__FILE__, __LINE__);	\
981da177e4SLinus Torvalds 	status = nfserr_bad_xdr;		\
991da177e4SLinus Torvalds 	goto out
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds #define READ32(x)         (x) = ntohl(*p++)
1021da177e4SLinus Torvalds #define READ64(x)         do {			\
1031da177e4SLinus Torvalds 	(x) = (u64)ntohl(*p++) << 32;		\
1041da177e4SLinus Torvalds 	(x) |= ntohl(*p++);			\
1051da177e4SLinus Torvalds } while (0)
1061da177e4SLinus Torvalds #define READTIME(x)       do {			\
1071da177e4SLinus Torvalds 	p++;					\
1081da177e4SLinus Torvalds 	(x) = ntohl(*p++);			\
1091da177e4SLinus Torvalds 	p++;					\
1101da177e4SLinus Torvalds } while (0)
1111da177e4SLinus Torvalds #define READMEM(x,nbytes) do {			\
1121da177e4SLinus Torvalds 	x = (char *)p;				\
1131da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1141da177e4SLinus Torvalds } while (0)
1151da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do {			\
1161da177e4SLinus Torvalds 	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
1171da177e4SLinus Torvalds  		savemem(argp, p, nbytes) :	\
1181da177e4SLinus Torvalds  		(char *)p)) {			\
119817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
120817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1211da177e4SLinus Torvalds 		goto xdr_error;			\
1221da177e4SLinus Torvalds 		}				\
1231da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1241da177e4SLinus Torvalds } while (0)
1251da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do {			\
1261da177e4SLinus Torvalds 	memcpy((x), p, nbytes);			\
1271da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1281da177e4SLinus Torvalds } while (0)
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
1311da177e4SLinus Torvalds #define READ_BUF(nbytes)  do {			\
1321da177e4SLinus Torvalds 	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
1331da177e4SLinus Torvalds 		p = argp->p;			\
1341da177e4SLinus Torvalds 		argp->p += XDR_QUADLEN(nbytes);	\
1351da177e4SLinus Torvalds 	} else if (!(p = read_buf(argp, nbytes))) { \
136817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
137817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1381da177e4SLinus Torvalds 		goto xdr_error;			\
1391da177e4SLinus Torvalds 	}					\
1401da177e4SLinus Torvalds } while (0)
1411da177e4SLinus Torvalds 
142ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
1431da177e4SLinus Torvalds {
1441da177e4SLinus Torvalds 	/* We want more bytes than seem to be available.
1451da177e4SLinus Torvalds 	 * Maybe we need a new page, maybe we have just run out
1461da177e4SLinus Torvalds 	 */
147ca2a05aaSJ. Bruce Fields 	unsigned int avail = (char *)argp->end - (char *)argp->p;
1482ebbc012SAl Viro 	__be32 *p;
1491da177e4SLinus Torvalds 	if (avail + argp->pagelen < nbytes)
1501da177e4SLinus Torvalds 		return NULL;
1511da177e4SLinus Torvalds 	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
1521da177e4SLinus Torvalds 		return NULL;
1531da177e4SLinus Torvalds 	/* ok, we can do it with the current plus the next page */
1541da177e4SLinus Torvalds 	if (nbytes <= sizeof(argp->tmp))
1551da177e4SLinus Torvalds 		p = argp->tmp;
1561da177e4SLinus Torvalds 	else {
1571da177e4SLinus Torvalds 		kfree(argp->tmpp);
1581da177e4SLinus Torvalds 		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
1591da177e4SLinus Torvalds 		if (!p)
1601da177e4SLinus Torvalds 			return NULL;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	}
163ca2a05aaSJ. Bruce Fields 	/*
164ca2a05aaSJ. Bruce Fields 	 * The following memcpy is safe because read_buf is always
165ca2a05aaSJ. Bruce Fields 	 * called with nbytes > avail, and the two cases above both
166ca2a05aaSJ. Bruce Fields 	 * guarantee p points to at least nbytes bytes.
167ca2a05aaSJ. Bruce Fields 	 */
1681da177e4SLinus Torvalds 	memcpy(p, argp->p, avail);
1691da177e4SLinus Torvalds 	/* step to next page */
1701da177e4SLinus Torvalds 	argp->p = page_address(argp->pagelist[0]);
1711da177e4SLinus Torvalds 	argp->pagelist++;
1721da177e4SLinus Torvalds 	if (argp->pagelen < PAGE_SIZE) {
1732bc3c117SNeil Brown 		argp->end = argp->p + (argp->pagelen>>2);
1741da177e4SLinus Torvalds 		argp->pagelen = 0;
1751da177e4SLinus Torvalds 	} else {
1762bc3c117SNeil Brown 		argp->end = argp->p + (PAGE_SIZE>>2);
1771da177e4SLinus Torvalds 		argp->pagelen -= PAGE_SIZE;
1781da177e4SLinus Torvalds 	}
1791da177e4SLinus Torvalds 	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
1801da177e4SLinus Torvalds 	argp->p += XDR_QUADLEN(nbytes - avail);
1811da177e4SLinus Torvalds 	return p;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
18460adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
18560adfc50SAndy Adamson {
18660adfc50SAndy Adamson 	return (clid->cl_boot == 0) && (clid->cl_id == 0);
18760adfc50SAndy Adamson }
18860adfc50SAndy Adamson 
1891da177e4SLinus Torvalds static int
1901da177e4SLinus Torvalds defer_free(struct nfsd4_compoundargs *argp,
1911da177e4SLinus Torvalds 		void (*release)(const void *), void *p)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds 	struct tmpbuf *tb;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	tb = kmalloc(sizeof(*tb), GFP_KERNEL);
1961da177e4SLinus Torvalds 	if (!tb)
1971da177e4SLinus Torvalds 		return -ENOMEM;
1981da177e4SLinus Torvalds 	tb->buf = p;
1991da177e4SLinus Torvalds 	tb->release = release;
2001da177e4SLinus Torvalds 	tb->next = argp->to_free;
2011da177e4SLinus Torvalds 	argp->to_free = tb;
2021da177e4SLinus Torvalds 	return 0;
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds 
2052ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
2061da177e4SLinus Torvalds {
2071da177e4SLinus Torvalds 	if (p == argp->tmp) {
20867114fe6SThomas Meyer 		p = kmemdup(argp->tmp, nbytes, GFP_KERNEL);
209a4db5fe5SJ. Bruce Fields 		if (!p)
210a4db5fe5SJ. Bruce Fields 			return NULL;
2111da177e4SLinus Torvalds 	} else {
21273dff8beSEric Sesterhenn 		BUG_ON(p != argp->tmpp);
2131da177e4SLinus Torvalds 		argp->tmpp = NULL;
2141da177e4SLinus Torvalds 	}
2151da177e4SLinus Torvalds 	if (defer_free(argp, kfree, p)) {
216a4db5fe5SJ. Bruce Fields 		kfree(p);
2171da177e4SLinus Torvalds 		return NULL;
2181da177e4SLinus Torvalds 	} else
2191da177e4SLinus Torvalds 		return (char *)p;
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
222b37ad28bSAl Viro static __be32
2231da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
2241da177e4SLinus Torvalds {
2251da177e4SLinus Torvalds 	u32 bmlen;
2261da177e4SLinus Torvalds 	DECODE_HEAD;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	bmval[0] = 0;
2291da177e4SLinus Torvalds 	bmval[1] = 0;
2307e705706SAndy Adamson 	bmval[2] = 0;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	READ_BUF(4);
2331da177e4SLinus Torvalds 	READ32(bmlen);
2341da177e4SLinus Torvalds 	if (bmlen > 1000)
2351da177e4SLinus Torvalds 		goto xdr_error;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	READ_BUF(bmlen << 2);
2381da177e4SLinus Torvalds 	if (bmlen > 0)
2391da177e4SLinus Torvalds 		READ32(bmval[0]);
2401da177e4SLinus Torvalds 	if (bmlen > 1)
2411da177e4SLinus Torvalds 		READ32(bmval[1]);
2427e705706SAndy Adamson 	if (bmlen > 2)
2437e705706SAndy Adamson 		READ32(bmval[2]);
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	DECODE_TAIL;
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
248b37ad28bSAl Viro static __be32
2493c8e0316SYu Zhiguo nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
25018032ca0SDavid Quigley 		   struct iattr *iattr, struct nfs4_acl **acl,
25118032ca0SDavid Quigley 		   struct xdr_netobj *label)
2521da177e4SLinus Torvalds {
2531da177e4SLinus Torvalds 	int expected_len, len = 0;
2541da177e4SLinus Torvalds 	u32 dummy32;
2551da177e4SLinus Torvalds 	char *buf;
256b8dd7b9aSAl Viro 	int host_err;
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	DECODE_HEAD;
2591da177e4SLinus Torvalds 	iattr->ia_valid = 0;
2601da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
2611da177e4SLinus Torvalds 		return status;
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	READ_BUF(4);
2641da177e4SLinus Torvalds 	READ32(expected_len);
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
2671da177e4SLinus Torvalds 		READ_BUF(8);
2681da177e4SLinus Torvalds 		len += 8;
2691da177e4SLinus Torvalds 		READ64(iattr->ia_size);
2701da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
2711da177e4SLinus Torvalds 	}
2721da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
27364a817cfSJ. Bruce Fields 		u32 nace;
27428e05dd8SJ. Bruce Fields 		struct nfs4_ace *ace;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 		READ_BUF(4); len += 4;
2771da177e4SLinus Torvalds 		READ32(nace);
2781da177e4SLinus Torvalds 
27928e05dd8SJ. Bruce Fields 		if (nace > NFS4_ACL_MAX)
28028e05dd8SJ. Bruce Fields 			return nfserr_resource;
28128e05dd8SJ. Bruce Fields 
28228e05dd8SJ. Bruce Fields 		*acl = nfs4_acl_new(nace);
2831da177e4SLinus Torvalds 		if (*acl == NULL) {
284b8dd7b9aSAl Viro 			host_err = -ENOMEM;
2851da177e4SLinus Torvalds 			goto out_nfserr;
2861da177e4SLinus Torvalds 		}
28728e05dd8SJ. Bruce Fields 		defer_free(argp, kfree, *acl);
2881da177e4SLinus Torvalds 
28928e05dd8SJ. Bruce Fields 		(*acl)->naces = nace;
29028e05dd8SJ. Bruce Fields 		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
2911da177e4SLinus Torvalds 			READ_BUF(16); len += 16;
29228e05dd8SJ. Bruce Fields 			READ32(ace->type);
29328e05dd8SJ. Bruce Fields 			READ32(ace->flag);
29428e05dd8SJ. Bruce Fields 			READ32(ace->access_mask);
2951da177e4SLinus Torvalds 			READ32(dummy32);
2961da177e4SLinus Torvalds 			READ_BUF(dummy32);
2971da177e4SLinus Torvalds 			len += XDR_QUADLEN(dummy32) << 2;
2981da177e4SLinus Torvalds 			READMEM(buf, dummy32);
29928e05dd8SJ. Bruce Fields 			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
3003c726023SJ. Bruce Fields 			status = nfs_ok;
30128e05dd8SJ. Bruce Fields 			if (ace->whotype != NFS4_ACL_WHO_NAMED)
302ab8e4aeeSEric W. Biederman 				;
30328e05dd8SJ. Bruce Fields 			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
3043c726023SJ. Bruce Fields 				status = nfsd_map_name_to_gid(argp->rqstp,
305ab8e4aeeSEric W. Biederman 						buf, dummy32, &ace->who_gid);
3061da177e4SLinus Torvalds 			else
3073c726023SJ. Bruce Fields 				status = nfsd_map_name_to_uid(argp->rqstp,
308ab8e4aeeSEric W. Biederman 						buf, dummy32, &ace->who_uid);
3093c726023SJ. Bruce Fields 			if (status)
3103c726023SJ. Bruce Fields 				return status;
3111da177e4SLinus Torvalds 		}
3121da177e4SLinus Torvalds 	} else
3131da177e4SLinus Torvalds 		*acl = NULL;
3141da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
3151da177e4SLinus Torvalds 		READ_BUF(4);
3161da177e4SLinus Torvalds 		len += 4;
3171da177e4SLinus Torvalds 		READ32(iattr->ia_mode);
3181da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
3191da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
3221da177e4SLinus Torvalds 		READ_BUF(4);
3231da177e4SLinus Torvalds 		len += 4;
3241da177e4SLinus Torvalds 		READ32(dummy32);
3251da177e4SLinus Torvalds 		READ_BUF(dummy32);
3261da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3271da177e4SLinus Torvalds 		READMEM(buf, dummy32);
32847c85291SNeilBrown 		if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
32947c85291SNeilBrown 			return status;
3301da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
3311da177e4SLinus Torvalds 	}
3321da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
3331da177e4SLinus Torvalds 		READ_BUF(4);
3341da177e4SLinus Torvalds 		len += 4;
3351da177e4SLinus Torvalds 		READ32(dummy32);
3361da177e4SLinus Torvalds 		READ_BUF(dummy32);
3371da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3381da177e4SLinus Torvalds 		READMEM(buf, dummy32);
33947c85291SNeilBrown 		if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
34047c85291SNeilBrown 			return status;
3411da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
3421da177e4SLinus Torvalds 	}
3431da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
3441da177e4SLinus Torvalds 		READ_BUF(4);
3451da177e4SLinus Torvalds 		len += 4;
3461da177e4SLinus Torvalds 		READ32(dummy32);
3471da177e4SLinus Torvalds 		switch (dummy32) {
3481da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3491da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3501da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3511da177e4SLinus Torvalds 			READ_BUF(12);
3521da177e4SLinus Torvalds 			len += 12;
353bf8d9097SBryan Schumaker 			READ64(iattr->ia_atime.tv_sec);
3541da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_nsec);
3551da177e4SLinus Torvalds 			if (iattr->ia_atime.tv_nsec >= (u32)1000000000)
3561da177e4SLinus Torvalds 				return nfserr_inval;
3571da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
3581da177e4SLinus Torvalds 			break;
3591da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3601da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
3611da177e4SLinus Torvalds 			break;
3621da177e4SLinus Torvalds 		default:
3631da177e4SLinus Torvalds 			goto xdr_error;
3641da177e4SLinus Torvalds 		}
3651da177e4SLinus Torvalds 	}
3661da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
3671da177e4SLinus Torvalds 		READ_BUF(4);
3681da177e4SLinus Torvalds 		len += 4;
3691da177e4SLinus Torvalds 		READ32(dummy32);
3701da177e4SLinus Torvalds 		switch (dummy32) {
3711da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3721da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3731da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3741da177e4SLinus Torvalds 			READ_BUF(12);
3751da177e4SLinus Torvalds 			len += 12;
376bf8d9097SBryan Schumaker 			READ64(iattr->ia_mtime.tv_sec);
3771da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_nsec);
3781da177e4SLinus Torvalds 			if (iattr->ia_mtime.tv_nsec >= (u32)1000000000)
3791da177e4SLinus Torvalds 				return nfserr_inval;
3801da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
3811da177e4SLinus Torvalds 			break;
3821da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3831da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
3841da177e4SLinus Torvalds 			break;
3851da177e4SLinus Torvalds 		default:
3861da177e4SLinus Torvalds 			goto xdr_error;
3871da177e4SLinus Torvalds 		}
3881da177e4SLinus Torvalds 	}
38918032ca0SDavid Quigley 
39018032ca0SDavid Quigley 	label->len = 0;
39118032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
39218032ca0SDavid Quigley 	if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
39318032ca0SDavid Quigley 		READ_BUF(4);
39418032ca0SDavid Quigley 		len += 4;
39518032ca0SDavid Quigley 		READ32(dummy32); /* lfs: we don't use it */
39618032ca0SDavid Quigley 		READ_BUF(4);
39718032ca0SDavid Quigley 		len += 4;
39818032ca0SDavid Quigley 		READ32(dummy32); /* pi: we don't use it either */
39918032ca0SDavid Quigley 		READ_BUF(4);
40018032ca0SDavid Quigley 		len += 4;
40118032ca0SDavid Quigley 		READ32(dummy32);
40218032ca0SDavid Quigley 		READ_BUF(dummy32);
40318032ca0SDavid Quigley 		if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN)
40418032ca0SDavid Quigley 			return nfserr_badlabel;
40518032ca0SDavid Quigley 		len += (XDR_QUADLEN(dummy32) << 2);
40618032ca0SDavid Quigley 		READMEM(buf, dummy32);
40718032ca0SDavid Quigley 		label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
40818032ca0SDavid Quigley 		if (!label->data)
40918032ca0SDavid Quigley 			return nfserr_jukebox;
41018032ca0SDavid Quigley 		defer_free(argp, kfree, label->data);
41118032ca0SDavid Quigley 		memcpy(label->data, buf, dummy32);
41218032ca0SDavid Quigley 	}
41318032ca0SDavid Quigley #endif
41418032ca0SDavid Quigley 
4153c8e0316SYu Zhiguo 	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
4163c8e0316SYu Zhiguo 	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
4173c8e0316SYu Zhiguo 	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
4183c8e0316SYu Zhiguo 		READ_BUF(expected_len - len);
4193c8e0316SYu Zhiguo 	else if (len != expected_len)
4201da177e4SLinus Torvalds 		goto xdr_error;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	DECODE_TAIL;
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds out_nfserr:
425b8dd7b9aSAl Viro 	status = nfserrno(host_err);
4261da177e4SLinus Torvalds 	goto out;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
429b37ad28bSAl Viro static __be32
430e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
431e31a1b66SBenny Halevy {
432e31a1b66SBenny Halevy 	DECODE_HEAD;
433e31a1b66SBenny Halevy 
434e31a1b66SBenny Halevy 	READ_BUF(sizeof(stateid_t));
435e31a1b66SBenny Halevy 	READ32(sid->si_generation);
436e31a1b66SBenny Halevy 	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
437e31a1b66SBenny Halevy 
438e31a1b66SBenny Halevy 	DECODE_TAIL;
439e31a1b66SBenny Halevy }
440e31a1b66SBenny Halevy 
441e31a1b66SBenny Halevy static __be32
4421da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
4431da177e4SLinus Torvalds {
4441da177e4SLinus Torvalds 	DECODE_HEAD;
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	READ_BUF(4);
4471da177e4SLinus Torvalds 	READ32(access->ac_req_access);
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	DECODE_TAIL;
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds 
452acb2887eSJ. Bruce Fields static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
453acb2887eSJ. Bruce Fields {
454acb2887eSJ. Bruce Fields 	DECODE_HEAD;
45512fc3e92SJ. Bruce Fields 	u32 dummy, uid, gid;
456acb2887eSJ. Bruce Fields 	char *machine_name;
457acb2887eSJ. Bruce Fields 	int i;
458acb2887eSJ. Bruce Fields 	int nr_secflavs;
459acb2887eSJ. Bruce Fields 
460acb2887eSJ. Bruce Fields 	/* callback_sec_params4 */
461acb2887eSJ. Bruce Fields 	READ_BUF(4);
462acb2887eSJ. Bruce Fields 	READ32(nr_secflavs);
46312fc3e92SJ. Bruce Fields 	cbs->flavor = (u32)(-1);
464acb2887eSJ. Bruce Fields 	for (i = 0; i < nr_secflavs; ++i) {
465acb2887eSJ. Bruce Fields 		READ_BUF(4);
466acb2887eSJ. Bruce Fields 		READ32(dummy);
467acb2887eSJ. Bruce Fields 		switch (dummy) {
468acb2887eSJ. Bruce Fields 		case RPC_AUTH_NULL:
469acb2887eSJ. Bruce Fields 			/* Nothing to read */
47012fc3e92SJ. Bruce Fields 			if (cbs->flavor == (u32)(-1))
47112fc3e92SJ. Bruce Fields 				cbs->flavor = RPC_AUTH_NULL;
472acb2887eSJ. Bruce Fields 			break;
473acb2887eSJ. Bruce Fields 		case RPC_AUTH_UNIX:
474acb2887eSJ. Bruce Fields 			READ_BUF(8);
475acb2887eSJ. Bruce Fields 			/* stamp */
476acb2887eSJ. Bruce Fields 			READ32(dummy);
477acb2887eSJ. Bruce Fields 
478acb2887eSJ. Bruce Fields 			/* machine name */
479acb2887eSJ. Bruce Fields 			READ32(dummy);
480acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
481acb2887eSJ. Bruce Fields 			SAVEMEM(machine_name, dummy);
482acb2887eSJ. Bruce Fields 
483acb2887eSJ. Bruce Fields 			/* uid, gid */
484acb2887eSJ. Bruce Fields 			READ_BUF(8);
48512fc3e92SJ. Bruce Fields 			READ32(uid);
48612fc3e92SJ. Bruce Fields 			READ32(gid);
487acb2887eSJ. Bruce Fields 
488acb2887eSJ. Bruce Fields 			/* more gids */
489acb2887eSJ. Bruce Fields 			READ_BUF(4);
490acb2887eSJ. Bruce Fields 			READ32(dummy);
491acb2887eSJ. Bruce Fields 			READ_BUF(dummy * 4);
49212fc3e92SJ. Bruce Fields 			if (cbs->flavor == (u32)(-1)) {
49303bc6d1cSEric W. Biederman 				kuid_t kuid = make_kuid(&init_user_ns, uid);
49403bc6d1cSEric W. Biederman 				kgid_t kgid = make_kgid(&init_user_ns, gid);
49503bc6d1cSEric W. Biederman 				if (uid_valid(kuid) && gid_valid(kgid)) {
49603bc6d1cSEric W. Biederman 					cbs->uid = kuid;
49703bc6d1cSEric W. Biederman 					cbs->gid = kgid;
49812fc3e92SJ. Bruce Fields 					cbs->flavor = RPC_AUTH_UNIX;
49903bc6d1cSEric W. Biederman 				} else {
50003bc6d1cSEric W. Biederman 					dprintk("RPC_AUTH_UNIX with invalid"
50103bc6d1cSEric W. Biederman 						"uid or gid ignoring!\n");
50203bc6d1cSEric W. Biederman 				}
50312fc3e92SJ. Bruce Fields 			}
504acb2887eSJ. Bruce Fields 			break;
505acb2887eSJ. Bruce Fields 		case RPC_AUTH_GSS:
506acb2887eSJ. Bruce Fields 			dprintk("RPC_AUTH_GSS callback secflavor "
507acb2887eSJ. Bruce Fields 				"not supported!\n");
508acb2887eSJ. Bruce Fields 			READ_BUF(8);
509acb2887eSJ. Bruce Fields 			/* gcbp_service */
510acb2887eSJ. Bruce Fields 			READ32(dummy);
511acb2887eSJ. Bruce Fields 			/* gcbp_handle_from_server */
512acb2887eSJ. Bruce Fields 			READ32(dummy);
513acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
514acb2887eSJ. Bruce Fields 			p += XDR_QUADLEN(dummy);
515acb2887eSJ. Bruce Fields 			/* gcbp_handle_from_client */
516acb2887eSJ. Bruce Fields 			READ_BUF(4);
517acb2887eSJ. Bruce Fields 			READ32(dummy);
518acb2887eSJ. Bruce Fields 			READ_BUF(dummy);
519acb2887eSJ. Bruce Fields 			break;
520acb2887eSJ. Bruce Fields 		default:
521acb2887eSJ. Bruce Fields 			dprintk("Illegal callback secflavor\n");
522acb2887eSJ. Bruce Fields 			return nfserr_inval;
523acb2887eSJ. Bruce Fields 		}
524acb2887eSJ. Bruce Fields 	}
525acb2887eSJ. Bruce Fields 	DECODE_TAIL;
526acb2887eSJ. Bruce Fields }
527acb2887eSJ. Bruce Fields 
528cb73a9f4SJ. Bruce Fields static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc)
529cb73a9f4SJ. Bruce Fields {
530cb73a9f4SJ. Bruce Fields 	DECODE_HEAD;
531cb73a9f4SJ. Bruce Fields 
532cb73a9f4SJ. Bruce Fields 	READ_BUF(4);
533cb73a9f4SJ. Bruce Fields 	READ32(bc->bc_cb_program);
534cb73a9f4SJ. Bruce Fields 	nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
535cb73a9f4SJ. Bruce Fields 
536cb73a9f4SJ. Bruce Fields 	DECODE_TAIL;
537cb73a9f4SJ. Bruce Fields }
538cb73a9f4SJ. Bruce Fields 
5391d1bc8f2SJ. Bruce Fields static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts)
5401d1bc8f2SJ. Bruce Fields {
5411d1bc8f2SJ. Bruce Fields 	DECODE_HEAD;
5421d1bc8f2SJ. Bruce Fields 
5431d1bc8f2SJ. Bruce Fields 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 8);
5441d1bc8f2SJ. Bruce Fields 	COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
5451d1bc8f2SJ. Bruce Fields 	READ32(bcts->dir);
5466ce2357fSBryan Schumaker 	/* XXX: skipping ctsa_use_conn_in_rdma_mode.  Perhaps Tom Tucker
5476ce2357fSBryan Schumaker 	 * could help us figure out we should be using it. */
5481d1bc8f2SJ. Bruce Fields 	DECODE_TAIL;
5491d1bc8f2SJ. Bruce Fields }
5501d1bc8f2SJ. Bruce Fields 
551b37ad28bSAl Viro static __be32
5521da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
5531da177e4SLinus Torvalds {
5541da177e4SLinus Torvalds 	DECODE_HEAD;
5551da177e4SLinus Torvalds 
556e31a1b66SBenny Halevy 	READ_BUF(4);
5571da177e4SLinus Torvalds 	READ32(close->cl_seqid);
558e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &close->cl_stateid);
5591da177e4SLinus Torvalds 
5601da177e4SLinus Torvalds 	DECODE_TAIL;
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 
564b37ad28bSAl Viro static __be32
5651da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
5661da177e4SLinus Torvalds {
5671da177e4SLinus Torvalds 	DECODE_HEAD;
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	READ_BUF(12);
5701da177e4SLinus Torvalds 	READ64(commit->co_offset);
5711da177e4SLinus Torvalds 	READ32(commit->co_count);
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	DECODE_TAIL;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
576b37ad28bSAl Viro static __be32
5771da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
5781da177e4SLinus Torvalds {
5791da177e4SLinus Torvalds 	DECODE_HEAD;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	READ_BUF(4);
5821da177e4SLinus Torvalds 	READ32(create->cr_type);
5831da177e4SLinus Torvalds 	switch (create->cr_type) {
5841da177e4SLinus Torvalds 	case NF4LNK:
5851da177e4SLinus Torvalds 		READ_BUF(4);
5861da177e4SLinus Torvalds 		READ32(create->cr_linklen);
5871da177e4SLinus Torvalds 		READ_BUF(create->cr_linklen);
5881da177e4SLinus Torvalds 		SAVEMEM(create->cr_linkname, create->cr_linklen);
5891da177e4SLinus Torvalds 		break;
5901da177e4SLinus Torvalds 	case NF4BLK:
5911da177e4SLinus Torvalds 	case NF4CHR:
5921da177e4SLinus Torvalds 		READ_BUF(8);
5931da177e4SLinus Torvalds 		READ32(create->cr_specdata1);
5941da177e4SLinus Torvalds 		READ32(create->cr_specdata2);
5951da177e4SLinus Torvalds 		break;
5961da177e4SLinus Torvalds 	case NF4SOCK:
5971da177e4SLinus Torvalds 	case NF4FIFO:
5981da177e4SLinus Torvalds 	case NF4DIR:
5991da177e4SLinus Torvalds 	default:
6001da177e4SLinus Torvalds 		break;
6011da177e4SLinus Torvalds 	}
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	READ_BUF(4);
6041da177e4SLinus Torvalds 	READ32(create->cr_namelen);
6051da177e4SLinus Torvalds 	READ_BUF(create->cr_namelen);
6061da177e4SLinus Torvalds 	SAVEMEM(create->cr_name, create->cr_namelen);
607a36b1725SJ. Bruce Fields 	if ((status = check_filename(create->cr_name, create->cr_namelen)))
6081da177e4SLinus Torvalds 		return status;
6091da177e4SLinus Torvalds 
6103c8e0316SYu Zhiguo 	status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
61118032ca0SDavid Quigley 				    &create->cr_acl, &create->cr_label);
612c0d6fc8aSBenny Halevy 	if (status)
6131da177e4SLinus Torvalds 		goto out;
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	DECODE_TAIL;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
618b37ad28bSAl Viro static inline __be32
6191da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
6201da177e4SLinus Torvalds {
621e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
6221da177e4SLinus Torvalds }
6231da177e4SLinus Torvalds 
624b37ad28bSAl Viro static inline __be32
6251da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
6261da177e4SLinus Torvalds {
6271da177e4SLinus Torvalds 	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
6281da177e4SLinus Torvalds }
6291da177e4SLinus Torvalds 
630b37ad28bSAl Viro static __be32
6311da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
6321da177e4SLinus Torvalds {
6331da177e4SLinus Torvalds 	DECODE_HEAD;
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 	READ_BUF(4);
6361da177e4SLinus Torvalds 	READ32(link->li_namelen);
6371da177e4SLinus Torvalds 	READ_BUF(link->li_namelen);
6381da177e4SLinus Torvalds 	SAVEMEM(link->li_name, link->li_namelen);
639a36b1725SJ. Bruce Fields 	if ((status = check_filename(link->li_name, link->li_namelen)))
6401da177e4SLinus Torvalds 		return status;
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds 	DECODE_TAIL;
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds 
645b37ad28bSAl Viro static __be32
6461da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
6471da177e4SLinus Torvalds {
6481da177e4SLinus Torvalds 	DECODE_HEAD;
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 	/*
6511da177e4SLinus Torvalds 	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
6521da177e4SLinus Torvalds 	*/
6531da177e4SLinus Torvalds 	READ_BUF(28);
6541da177e4SLinus Torvalds 	READ32(lock->lk_type);
6551da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
6561da177e4SLinus Torvalds 		goto xdr_error;
6571da177e4SLinus Torvalds 	READ32(lock->lk_reclaim);
6581da177e4SLinus Torvalds 	READ64(lock->lk_offset);
6591da177e4SLinus Torvalds 	READ64(lock->lk_length);
6601da177e4SLinus Torvalds 	READ32(lock->lk_is_new);
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	if (lock->lk_is_new) {
663e31a1b66SBenny Halevy 		READ_BUF(4);
6641da177e4SLinus Torvalds 		READ32(lock->lk_new_open_seqid);
665e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
666e31a1b66SBenny Halevy 		if (status)
667e31a1b66SBenny Halevy 			return status;
668e31a1b66SBenny Halevy 		READ_BUF(8 + sizeof(clientid_t));
6691da177e4SLinus Torvalds 		READ32(lock->lk_new_lock_seqid);
6701da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
6711da177e4SLinus Torvalds 		READ32(lock->lk_new_owner.len);
6721da177e4SLinus Torvalds 		READ_BUF(lock->lk_new_owner.len);
6731da177e4SLinus Torvalds 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
6741da177e4SLinus Torvalds 	} else {
675e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
676e31a1b66SBenny Halevy 		if (status)
677e31a1b66SBenny Halevy 			return status;
678e31a1b66SBenny Halevy 		READ_BUF(4);
6791da177e4SLinus Torvalds 		READ32(lock->lk_old_lock_seqid);
6801da177e4SLinus Torvalds 	}
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 	DECODE_TAIL;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
685b37ad28bSAl Viro static __be32
6861da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
6871da177e4SLinus Torvalds {
6881da177e4SLinus Torvalds 	DECODE_HEAD;
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	READ_BUF(32);
6911da177e4SLinus Torvalds 	READ32(lockt->lt_type);
6921da177e4SLinus Torvalds 	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
6931da177e4SLinus Torvalds 		goto xdr_error;
6941da177e4SLinus Torvalds 	READ64(lockt->lt_offset);
6951da177e4SLinus Torvalds 	READ64(lockt->lt_length);
6961da177e4SLinus Torvalds 	COPYMEM(&lockt->lt_clientid, 8);
6971da177e4SLinus Torvalds 	READ32(lockt->lt_owner.len);
6981da177e4SLinus Torvalds 	READ_BUF(lockt->lt_owner.len);
6991da177e4SLinus Torvalds 	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds 	DECODE_TAIL;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
704b37ad28bSAl Viro static __be32
7051da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
7061da177e4SLinus Torvalds {
7071da177e4SLinus Torvalds 	DECODE_HEAD;
7081da177e4SLinus Torvalds 
709e31a1b66SBenny Halevy 	READ_BUF(8);
7101da177e4SLinus Torvalds 	READ32(locku->lu_type);
7111da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
7121da177e4SLinus Torvalds 		goto xdr_error;
7131da177e4SLinus Torvalds 	READ32(locku->lu_seqid);
714e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
715e31a1b66SBenny Halevy 	if (status)
716e31a1b66SBenny Halevy 		return status;
717e31a1b66SBenny Halevy 	READ_BUF(16);
7181da177e4SLinus Torvalds 	READ64(locku->lu_offset);
7191da177e4SLinus Torvalds 	READ64(locku->lu_length);
7201da177e4SLinus Torvalds 
7211da177e4SLinus Torvalds 	DECODE_TAIL;
7221da177e4SLinus Torvalds }
7231da177e4SLinus Torvalds 
724b37ad28bSAl Viro static __be32
7251da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
7261da177e4SLinus Torvalds {
7271da177e4SLinus Torvalds 	DECODE_HEAD;
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	READ_BUF(4);
7301da177e4SLinus Torvalds 	READ32(lookup->lo_len);
7311da177e4SLinus Torvalds 	READ_BUF(lookup->lo_len);
7321da177e4SLinus Torvalds 	SAVEMEM(lookup->lo_name, lookup->lo_len);
733a36b1725SJ. Bruce Fields 	if ((status = check_filename(lookup->lo_name, lookup->lo_len)))
7341da177e4SLinus Torvalds 		return status;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	DECODE_TAIL;
7371da177e4SLinus Torvalds }
7381da177e4SLinus Torvalds 
7392c8bd7e0SBenny Halevy static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when)
74004f9e664SJ. Bruce Fields {
74104f9e664SJ. Bruce Fields 	__be32 *p;
74204f9e664SJ. Bruce Fields 	u32 w;
74304f9e664SJ. Bruce Fields 
74404f9e664SJ. Bruce Fields 	READ_BUF(4);
74504f9e664SJ. Bruce Fields 	READ32(w);
7462c8bd7e0SBenny Halevy 	*share_access = w & NFS4_SHARE_ACCESS_MASK;
7472c8bd7e0SBenny Halevy 	*deleg_want = w & NFS4_SHARE_WANT_MASK;
7482c8bd7e0SBenny Halevy 	if (deleg_when)
7492c8bd7e0SBenny Halevy 		*deleg_when = w & NFS4_SHARE_WHEN_MASK;
7502c8bd7e0SBenny Halevy 
75104f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_ACCESS_MASK) {
75204f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_READ:
75304f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_WRITE:
75404f9e664SJ. Bruce Fields 	case NFS4_SHARE_ACCESS_BOTH:
75504f9e664SJ. Bruce Fields 		break;
75604f9e664SJ. Bruce Fields 	default:
75704f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
75804f9e664SJ. Bruce Fields 	}
759fc0d14feSBenny Halevy 	w &= ~NFS4_SHARE_ACCESS_MASK;
76004f9e664SJ. Bruce Fields 	if (!w)
76104f9e664SJ. Bruce Fields 		return nfs_ok;
76204f9e664SJ. Bruce Fields 	if (!argp->minorversion)
76304f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
76404f9e664SJ. Bruce Fields 	switch (w & NFS4_SHARE_WANT_MASK) {
76504f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_PREFERENCE:
76604f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_READ_DELEG:
76704f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_WRITE_DELEG:
76804f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_ANY_DELEG:
76904f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_NO_DELEG:
77004f9e664SJ. Bruce Fields 	case NFS4_SHARE_WANT_CANCEL:
77104f9e664SJ. Bruce Fields 		break;
77204f9e664SJ. Bruce Fields 	default:
77304f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
77404f9e664SJ. Bruce Fields 	}
77592bac8c5SBenny Halevy 	w &= ~NFS4_SHARE_WANT_MASK;
77604f9e664SJ. Bruce Fields 	if (!w)
77704f9e664SJ. Bruce Fields 		return nfs_ok;
7782c8bd7e0SBenny Halevy 
7792c8bd7e0SBenny Halevy 	if (!deleg_when)	/* open_downgrade */
7802c8bd7e0SBenny Halevy 		return nfserr_inval;
78104f9e664SJ. Bruce Fields 	switch (w) {
78204f9e664SJ. Bruce Fields 	case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
78304f9e664SJ. Bruce Fields 	case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
784c668fc6dSBenny Halevy 	case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
785c668fc6dSBenny Halevy 	      NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
78604f9e664SJ. Bruce Fields 		return nfs_ok;
78704f9e664SJ. Bruce Fields 	}
78804f9e664SJ. Bruce Fields xdr_error:
78904f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
79004f9e664SJ. Bruce Fields }
79104f9e664SJ. Bruce Fields 
79204f9e664SJ. Bruce Fields static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
79304f9e664SJ. Bruce Fields {
79404f9e664SJ. Bruce Fields 	__be32 *p;
79504f9e664SJ. Bruce Fields 
79604f9e664SJ. Bruce Fields 	READ_BUF(4);
79704f9e664SJ. Bruce Fields 	READ32(*x);
79804f9e664SJ. Bruce Fields 	/* Note: unlinke access bits, deny bits may be zero. */
79901cd4afaSDan Carpenter 	if (*x & ~NFS4_SHARE_DENY_BOTH)
80004f9e664SJ. Bruce Fields 		return nfserr_bad_xdr;
80104f9e664SJ. Bruce Fields 	return nfs_ok;
80204f9e664SJ. Bruce Fields xdr_error:
80304f9e664SJ. Bruce Fields 	return nfserr_bad_xdr;
80404f9e664SJ. Bruce Fields }
80504f9e664SJ. Bruce Fields 
806a084daf5SJ. Bruce Fields static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
807a084daf5SJ. Bruce Fields {
808a084daf5SJ. Bruce Fields 	__be32 *p;
809a084daf5SJ. Bruce Fields 
810a084daf5SJ. Bruce Fields 	READ_BUF(4);
811a084daf5SJ. Bruce Fields 	READ32(o->len);
812a084daf5SJ. Bruce Fields 
813a084daf5SJ. Bruce Fields 	if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT)
814a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
815a084daf5SJ. Bruce Fields 
816a084daf5SJ. Bruce Fields 	READ_BUF(o->len);
817a084daf5SJ. Bruce Fields 	SAVEMEM(o->data, o->len);
818a084daf5SJ. Bruce Fields 	return nfs_ok;
819a084daf5SJ. Bruce Fields xdr_error:
820a084daf5SJ. Bruce Fields 	return nfserr_bad_xdr;
821a084daf5SJ. Bruce Fields }
822a084daf5SJ. Bruce Fields 
823b37ad28bSAl Viro static __be32
8241da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
8251da177e4SLinus Torvalds {
8261da177e4SLinus Torvalds 	DECODE_HEAD;
8272c8bd7e0SBenny Halevy 	u32 dummy;
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	memset(open->op_bmval, 0, sizeof(open->op_bmval));
8301da177e4SLinus Torvalds 	open->op_iattr.ia_valid = 0;
831fe0750e5SJ. Bruce Fields 	open->op_openowner = NULL;
8321da177e4SLinus Torvalds 
8339d313b17SJ. Bruce Fields 	open->op_xdr_error = 0;
8341da177e4SLinus Torvalds 	/* seqid, share_access, share_deny, clientid, ownerlen */
83504f9e664SJ. Bruce Fields 	READ_BUF(4);
8361da177e4SLinus Torvalds 	READ32(open->op_seqid);
8372c8bd7e0SBenny Halevy 	/* decode, yet ignore deleg_when until supported */
8382c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open->op_share_access,
8392c8bd7e0SBenny Halevy 					   &open->op_deleg_want, &dummy);
84004f9e664SJ. Bruce Fields 	if (status)
84104f9e664SJ. Bruce Fields 		goto xdr_error;
84204f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
84304f9e664SJ. Bruce Fields 	if (status)
84404f9e664SJ. Bruce Fields 		goto xdr_error;
845a084daf5SJ. Bruce Fields 	READ_BUF(sizeof(clientid_t));
8461da177e4SLinus Torvalds 	COPYMEM(&open->op_clientid, sizeof(clientid_t));
847a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &open->op_owner);
848a084daf5SJ. Bruce Fields 	if (status)
849a084daf5SJ. Bruce Fields 		goto xdr_error;
850a084daf5SJ. Bruce Fields 	READ_BUF(4);
8511da177e4SLinus Torvalds 	READ32(open->op_create);
8521da177e4SLinus Torvalds 	switch (open->op_create) {
8531da177e4SLinus Torvalds 	case NFS4_OPEN_NOCREATE:
8541da177e4SLinus Torvalds 		break;
8551da177e4SLinus Torvalds 	case NFS4_OPEN_CREATE:
8561da177e4SLinus Torvalds 		READ_BUF(4);
8571da177e4SLinus Torvalds 		READ32(open->op_createmode);
8581da177e4SLinus Torvalds 		switch (open->op_createmode) {
8591da177e4SLinus Torvalds 		case NFS4_CREATE_UNCHECKED:
8601da177e4SLinus Torvalds 		case NFS4_CREATE_GUARDED:
861c0d6fc8aSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
86218032ca0SDavid Quigley 				&open->op_iattr, &open->op_acl, &open->op_label);
863c0d6fc8aSBenny Halevy 			if (status)
8641da177e4SLinus Torvalds 				goto out;
8651da177e4SLinus Torvalds 			break;
8661da177e4SLinus Torvalds 		case NFS4_CREATE_EXCLUSIVE:
867ab4684d1SChuck Lever 			READ_BUF(NFS4_VERIFIER_SIZE);
868ab4684d1SChuck Lever 			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
8691da177e4SLinus Torvalds 			break;
87079fb54abSBenny Halevy 		case NFS4_CREATE_EXCLUSIVE4_1:
87179fb54abSBenny Halevy 			if (argp->minorversion < 1)
87279fb54abSBenny Halevy 				goto xdr_error;
873ab4684d1SChuck Lever 			READ_BUF(NFS4_VERIFIER_SIZE);
874ab4684d1SChuck Lever 			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
87579fb54abSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
87618032ca0SDavid Quigley 				&open->op_iattr, &open->op_acl, &open->op_label);
87779fb54abSBenny Halevy 			if (status)
87879fb54abSBenny Halevy 				goto out;
87979fb54abSBenny Halevy 			break;
8801da177e4SLinus Torvalds 		default:
8811da177e4SLinus Torvalds 			goto xdr_error;
8821da177e4SLinus Torvalds 		}
8831da177e4SLinus Torvalds 		break;
8841da177e4SLinus Torvalds 	default:
8851da177e4SLinus Torvalds 		goto xdr_error;
8861da177e4SLinus Torvalds 	}
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 	/* open_claim */
8891da177e4SLinus Torvalds 	READ_BUF(4);
8901da177e4SLinus Torvalds 	READ32(open->op_claim_type);
8911da177e4SLinus Torvalds 	switch (open->op_claim_type) {
8921da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_NULL:
8931da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
8941da177e4SLinus Torvalds 		READ_BUF(4);
8951da177e4SLinus Torvalds 		READ32(open->op_fname.len);
8961da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
8971da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
898a36b1725SJ. Bruce Fields 		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
8991da177e4SLinus Torvalds 			return status;
9001da177e4SLinus Torvalds 		break;
9011da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_PREVIOUS:
9021da177e4SLinus Torvalds 		READ_BUF(4);
9031da177e4SLinus Torvalds 		READ32(open->op_delegate_type);
9041da177e4SLinus Torvalds 		break;
9051da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
906e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
907e31a1b66SBenny Halevy 		if (status)
908e31a1b66SBenny Halevy 			return status;
909e31a1b66SBenny Halevy 		READ_BUF(4);
9101da177e4SLinus Torvalds 		READ32(open->op_fname.len);
9111da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
9121da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
913a36b1725SJ. Bruce Fields 		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
9141da177e4SLinus Torvalds 			return status;
9151da177e4SLinus Torvalds 		break;
9168b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_FH:
9178b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
9188b289b2cSJ. Bruce Fields 		if (argp->minorversion < 1)
9198b289b2cSJ. Bruce Fields 			goto xdr_error;
9208b289b2cSJ. Bruce Fields 		/* void */
9218b289b2cSJ. Bruce Fields 		break;
9228b289b2cSJ. Bruce Fields 	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
9238b289b2cSJ. Bruce Fields 		if (argp->minorversion < 1)
9248b289b2cSJ. Bruce Fields 			goto xdr_error;
9258b289b2cSJ. Bruce Fields 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
9268b289b2cSJ. Bruce Fields 		if (status)
9278b289b2cSJ. Bruce Fields 			return status;
9288b289b2cSJ. Bruce Fields 		break;
9291da177e4SLinus Torvalds 	default:
9301da177e4SLinus Torvalds 		goto xdr_error;
9311da177e4SLinus Torvalds 	}
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 	DECODE_TAIL;
9341da177e4SLinus Torvalds }
9351da177e4SLinus Torvalds 
936b37ad28bSAl Viro static __be32
9371da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
9381da177e4SLinus Torvalds {
9391da177e4SLinus Torvalds 	DECODE_HEAD;
9401da177e4SLinus Torvalds 
941e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
942e31a1b66SBenny Halevy 	if (status)
943e31a1b66SBenny Halevy 		return status;
944e31a1b66SBenny Halevy 	READ_BUF(4);
9451da177e4SLinus Torvalds 	READ32(open_conf->oc_seqid);
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 	DECODE_TAIL;
9481da177e4SLinus Torvalds }
9491da177e4SLinus Torvalds 
950b37ad28bSAl Viro static __be32
9511da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
9521da177e4SLinus Torvalds {
9531da177e4SLinus Torvalds 	DECODE_HEAD;
9541da177e4SLinus Torvalds 
955e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
956e31a1b66SBenny Halevy 	if (status)
957e31a1b66SBenny Halevy 		return status;
95804f9e664SJ. Bruce Fields 	READ_BUF(4);
9591da177e4SLinus Torvalds 	READ32(open_down->od_seqid);
9602c8bd7e0SBenny Halevy 	status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
9612c8bd7e0SBenny Halevy 					   &open_down->od_deleg_want, NULL);
96204f9e664SJ. Bruce Fields 	if (status)
96304f9e664SJ. Bruce Fields 		return status;
96404f9e664SJ. Bruce Fields 	status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
96504f9e664SJ. Bruce Fields 	if (status)
96604f9e664SJ. Bruce Fields 		return status;
9671da177e4SLinus Torvalds 	DECODE_TAIL;
9681da177e4SLinus Torvalds }
9691da177e4SLinus Torvalds 
970b37ad28bSAl Viro static __be32
9711da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
9721da177e4SLinus Torvalds {
9731da177e4SLinus Torvalds 	DECODE_HEAD;
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 	READ_BUF(4);
9761da177e4SLinus Torvalds 	READ32(putfh->pf_fhlen);
9771da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
9781da177e4SLinus Torvalds 		goto xdr_error;
9791da177e4SLinus Torvalds 	READ_BUF(putfh->pf_fhlen);
9801da177e4SLinus Torvalds 	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	DECODE_TAIL;
9831da177e4SLinus Torvalds }
9841da177e4SLinus Torvalds 
985b37ad28bSAl Viro static __be32
9861da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
9871da177e4SLinus Torvalds {
9881da177e4SLinus Torvalds 	DECODE_HEAD;
9891da177e4SLinus Torvalds 
990e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
991e31a1b66SBenny Halevy 	if (status)
992e31a1b66SBenny Halevy 		return status;
993e31a1b66SBenny Halevy 	READ_BUF(12);
9941da177e4SLinus Torvalds 	READ64(read->rd_offset);
9951da177e4SLinus Torvalds 	READ32(read->rd_length);
9961da177e4SLinus Torvalds 
9971da177e4SLinus Torvalds 	DECODE_TAIL;
9981da177e4SLinus Torvalds }
9991da177e4SLinus Torvalds 
1000b37ad28bSAl Viro static __be32
10011da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
10021da177e4SLinus Torvalds {
10031da177e4SLinus Torvalds 	DECODE_HEAD;
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 	READ_BUF(24);
10061da177e4SLinus Torvalds 	READ64(readdir->rd_cookie);
10071da177e4SLinus Torvalds 	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
10081da177e4SLinus Torvalds 	READ32(readdir->rd_dircount);    /* just in case you needed a useless field... */
10091da177e4SLinus Torvalds 	READ32(readdir->rd_maxcount);
10101da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
10111da177e4SLinus Torvalds 		goto out;
10121da177e4SLinus Torvalds 
10131da177e4SLinus Torvalds 	DECODE_TAIL;
10141da177e4SLinus Torvalds }
10151da177e4SLinus Torvalds 
1016b37ad28bSAl Viro static __be32
10171da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
10181da177e4SLinus Torvalds {
10191da177e4SLinus Torvalds 	DECODE_HEAD;
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds 	READ_BUF(4);
10221da177e4SLinus Torvalds 	READ32(remove->rm_namelen);
10231da177e4SLinus Torvalds 	READ_BUF(remove->rm_namelen);
10241da177e4SLinus Torvalds 	SAVEMEM(remove->rm_name, remove->rm_namelen);
1025a36b1725SJ. Bruce Fields 	if ((status = check_filename(remove->rm_name, remove->rm_namelen)))
10261da177e4SLinus Torvalds 		return status;
10271da177e4SLinus Torvalds 
10281da177e4SLinus Torvalds 	DECODE_TAIL;
10291da177e4SLinus Torvalds }
10301da177e4SLinus Torvalds 
1031b37ad28bSAl Viro static __be32
10321da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
10331da177e4SLinus Torvalds {
10341da177e4SLinus Torvalds 	DECODE_HEAD;
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	READ_BUF(4);
10371da177e4SLinus Torvalds 	READ32(rename->rn_snamelen);
10381da177e4SLinus Torvalds 	READ_BUF(rename->rn_snamelen + 4);
10391da177e4SLinus Torvalds 	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
10401da177e4SLinus Torvalds 	READ32(rename->rn_tnamelen);
10411da177e4SLinus Torvalds 	READ_BUF(rename->rn_tnamelen);
10421da177e4SLinus Torvalds 	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
1043a36b1725SJ. Bruce Fields 	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen)))
10441da177e4SLinus Torvalds 		return status;
1045a36b1725SJ. Bruce Fields 	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen)))
10461da177e4SLinus Torvalds 		return status;
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds 	DECODE_TAIL;
10491da177e4SLinus Torvalds }
10501da177e4SLinus Torvalds 
1051b37ad28bSAl Viro static __be32
10521da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
10531da177e4SLinus Torvalds {
10541da177e4SLinus Torvalds 	DECODE_HEAD;
10551da177e4SLinus Torvalds 
10561da177e4SLinus Torvalds 	READ_BUF(sizeof(clientid_t));
10571da177e4SLinus Torvalds 	COPYMEM(clientid, sizeof(clientid_t));
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	DECODE_TAIL;
10601da177e4SLinus Torvalds }
10611da177e4SLinus Torvalds 
1062b37ad28bSAl Viro static __be32
1063dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
1064dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
1065dcb488a3SAndy Adamson {
1066dcb488a3SAndy Adamson 	DECODE_HEAD;
1067dcb488a3SAndy Adamson 
1068dcb488a3SAndy Adamson 	READ_BUF(4);
1069dcb488a3SAndy Adamson 	READ32(secinfo->si_namelen);
1070dcb488a3SAndy Adamson 	READ_BUF(secinfo->si_namelen);
1071dcb488a3SAndy Adamson 	SAVEMEM(secinfo->si_name, secinfo->si_namelen);
1072a36b1725SJ. Bruce Fields 	status = check_filename(secinfo->si_name, secinfo->si_namelen);
1073dcb488a3SAndy Adamson 	if (status)
1074dcb488a3SAndy Adamson 		return status;
1075dcb488a3SAndy Adamson 	DECODE_TAIL;
1076dcb488a3SAndy Adamson }
1077dcb488a3SAndy Adamson 
1078dcb488a3SAndy Adamson static __be32
107904f4ad16SJ. Bruce Fields nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
108004f4ad16SJ. Bruce Fields 		     struct nfsd4_secinfo_no_name *sin)
108104f4ad16SJ. Bruce Fields {
108204f4ad16SJ. Bruce Fields 	DECODE_HEAD;
108304f4ad16SJ. Bruce Fields 
108404f4ad16SJ. Bruce Fields 	READ_BUF(4);
108504f4ad16SJ. Bruce Fields 	READ32(sin->sin_style);
108604f4ad16SJ. Bruce Fields 	DECODE_TAIL;
108704f4ad16SJ. Bruce Fields }
108804f4ad16SJ. Bruce Fields 
108904f4ad16SJ. Bruce Fields static __be32
10901da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
10911da177e4SLinus Torvalds {
1092e31a1b66SBenny Halevy 	__be32 status;
10931da177e4SLinus Torvalds 
1094e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
1095e31a1b66SBenny Halevy 	if (status)
1096e31a1b66SBenny Halevy 		return status;
10973c8e0316SYu Zhiguo 	return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
109818032ca0SDavid Quigley 				  &setattr->sa_acl, &setattr->sa_label);
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds 
1101b37ad28bSAl Viro static __be32
11021da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
11031da177e4SLinus Torvalds {
11041da177e4SLinus Torvalds 	DECODE_HEAD;
11051da177e4SLinus Torvalds 
1106ab4684d1SChuck Lever 	READ_BUF(NFS4_VERIFIER_SIZE);
1107ab4684d1SChuck Lever 	COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE);
11081da177e4SLinus Torvalds 
1109a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &setclientid->se_name);
1110a084daf5SJ. Bruce Fields 	if (status)
1111a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
1112a084daf5SJ. Bruce Fields 	READ_BUF(8);
11131da177e4SLinus Torvalds 	READ32(setclientid->se_callback_prog);
11141da177e4SLinus Torvalds 	READ32(setclientid->se_callback_netid_len);
11151da177e4SLinus Torvalds 
11161da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_netid_len + 4);
11171da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
11181da177e4SLinus Torvalds 	READ32(setclientid->se_callback_addr_len);
11191da177e4SLinus Torvalds 
11201da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_addr_len + 4);
11211da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
11221da177e4SLinus Torvalds 	READ32(setclientid->se_callback_ident);
11231da177e4SLinus Torvalds 
11241da177e4SLinus Torvalds 	DECODE_TAIL;
11251da177e4SLinus Torvalds }
11261da177e4SLinus Torvalds 
1127b37ad28bSAl Viro static __be32
11281da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
11291da177e4SLinus Torvalds {
11301da177e4SLinus Torvalds 	DECODE_HEAD;
11311da177e4SLinus Torvalds 
1132ab4684d1SChuck Lever 	READ_BUF(8 + NFS4_VERIFIER_SIZE);
11331da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_clientid, 8);
1134ab4684d1SChuck Lever 	COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE);
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	DECODE_TAIL;
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds 
11391da177e4SLinus Torvalds /* Also used for NVERIFY */
1140b37ad28bSAl Viro static __be32
11411da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
11421da177e4SLinus Torvalds {
11431da177e4SLinus Torvalds 	DECODE_HEAD;
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
11461da177e4SLinus Torvalds 		goto out;
11471da177e4SLinus Torvalds 
11481da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
1149e5f95703SJ. Bruce Fields 	 * nfsd4_proc_verify */
1150e5f95703SJ. Bruce Fields 
11511da177e4SLinus Torvalds 	READ_BUF(4);
11521da177e4SLinus Torvalds 	READ32(verify->ve_attrlen);
11531da177e4SLinus Torvalds 	READ_BUF(verify->ve_attrlen);
11541da177e4SLinus Torvalds 	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds 	DECODE_TAIL;
11571da177e4SLinus Torvalds }
11581da177e4SLinus Torvalds 
1159b37ad28bSAl Viro static __be32
11601da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
11611da177e4SLinus Torvalds {
11621da177e4SLinus Torvalds 	int avail;
11631da177e4SLinus Torvalds 	int len;
11641da177e4SLinus Torvalds 	DECODE_HEAD;
11651da177e4SLinus Torvalds 
1166e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
1167e31a1b66SBenny Halevy 	if (status)
1168e31a1b66SBenny Halevy 		return status;
1169e31a1b66SBenny Halevy 	READ_BUF(16);
11701da177e4SLinus Torvalds 	READ64(write->wr_offset);
11711da177e4SLinus Torvalds 	READ32(write->wr_stable_how);
11721da177e4SLinus Torvalds 	if (write->wr_stable_how > 2)
11731da177e4SLinus Torvalds 		goto xdr_error;
11741da177e4SLinus Torvalds 	READ32(write->wr_buflen);
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	/* Sorry .. no magic macros for this.. *
11771da177e4SLinus Torvalds 	 * READ_BUF(write->wr_buflen);
11781da177e4SLinus Torvalds 	 * SAVEMEM(write->wr_buf, write->wr_buflen);
11791da177e4SLinus Torvalds 	 */
11801da177e4SLinus Torvalds 	avail = (char*)argp->end - (char*)argp->p;
11811da177e4SLinus Torvalds 	if (avail + argp->pagelen < write->wr_buflen) {
1182817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n",
1183817cb9d4SChuck Lever 				__FILE__, __LINE__);
11841da177e4SLinus Torvalds 		goto xdr_error;
11851da177e4SLinus Torvalds 	}
118670cc7f75SJ. Bruce Fields 	write->wr_head.iov_base = p;
118770cc7f75SJ. Bruce Fields 	write->wr_head.iov_len = avail;
11885a80a54dSJ. Bruce Fields 	WARN_ON(avail != (XDR_QUADLEN(avail) << 2));
118970cc7f75SJ. Bruce Fields 	write->wr_pagelist = argp->pagelist;
11905a80a54dSJ. Bruce Fields 
11915a80a54dSJ. Bruce Fields 	len = XDR_QUADLEN(write->wr_buflen) << 2;
11925a80a54dSJ. Bruce Fields 	if (len >= avail) {
11935a80a54dSJ. Bruce Fields 		int pages;
11945a80a54dSJ. Bruce Fields 
11955a80a54dSJ. Bruce Fields 		len -= avail;
11965a80a54dSJ. Bruce Fields 
11975a80a54dSJ. Bruce Fields 		pages = len >> PAGE_SHIFT;
11985a80a54dSJ. Bruce Fields 		argp->pagelist += pages;
11995a80a54dSJ. Bruce Fields 		argp->pagelen -= pages * PAGE_SIZE;
12005a80a54dSJ. Bruce Fields 		len -= pages * PAGE_SIZE;
12015a80a54dSJ. Bruce Fields 
12025a80a54dSJ. Bruce Fields 		argp->p = (__be32 *)page_address(argp->pagelist[0]);
12035a80a54dSJ. Bruce Fields 		argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
12041da177e4SLinus Torvalds 	}
12055a80a54dSJ. Bruce Fields 	argp->p += XDR_QUADLEN(len);
12061da177e4SLinus Torvalds 
12071da177e4SLinus Torvalds 	DECODE_TAIL;
12081da177e4SLinus Torvalds }
12091da177e4SLinus Torvalds 
1210b37ad28bSAl Viro static __be32
12111da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
12121da177e4SLinus Torvalds {
12131da177e4SLinus Torvalds 	DECODE_HEAD;
12141da177e4SLinus Torvalds 
12151da177e4SLinus Torvalds 	READ_BUF(12);
12161da177e4SLinus Torvalds 	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
12171da177e4SLinus Torvalds 	READ32(rlockowner->rl_owner.len);
12181da177e4SLinus Torvalds 	READ_BUF(rlockowner->rl_owner.len);
12191da177e4SLinus Torvalds 	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
12201da177e4SLinus Torvalds 
122160adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
122260adfc50SAndy Adamson 		return nfserr_inval;
12231da177e4SLinus Torvalds 	DECODE_TAIL;
12241da177e4SLinus Torvalds }
12251da177e4SLinus Torvalds 
1226b37ad28bSAl Viro static __be32
12272db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
12280733d213SAndy Adamson 			 struct nfsd4_exchange_id *exid)
12292db134ebSAndy Adamson {
12305afa040bSMi Jinlong 	int dummy, tmp;
12310733d213SAndy Adamson 	DECODE_HEAD;
12320733d213SAndy Adamson 
12330733d213SAndy Adamson 	READ_BUF(NFS4_VERIFIER_SIZE);
12340733d213SAndy Adamson 	COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE);
12350733d213SAndy Adamson 
1236a084daf5SJ. Bruce Fields 	status = nfsd4_decode_opaque(argp, &exid->clname);
1237a084daf5SJ. Bruce Fields 	if (status)
1238a084daf5SJ. Bruce Fields 		return nfserr_bad_xdr;
12390733d213SAndy Adamson 
12400733d213SAndy Adamson 	READ_BUF(4);
12410733d213SAndy Adamson 	READ32(exid->flags);
12420733d213SAndy Adamson 
12430733d213SAndy Adamson 	/* Ignore state_protect4_a */
12440733d213SAndy Adamson 	READ_BUF(4);
12450733d213SAndy Adamson 	READ32(exid->spa_how);
12460733d213SAndy Adamson 	switch (exid->spa_how) {
12470733d213SAndy Adamson 	case SP4_NONE:
12480733d213SAndy Adamson 		break;
12490733d213SAndy Adamson 	case SP4_MACH_CRED:
12500733d213SAndy Adamson 		/* spo_must_enforce */
12510733d213SAndy Adamson 		READ_BUF(4);
12520733d213SAndy Adamson 		READ32(dummy);
12530733d213SAndy Adamson 		READ_BUF(dummy * 4);
12540733d213SAndy Adamson 		p += dummy;
12550733d213SAndy Adamson 
12560733d213SAndy Adamson 		/* spo_must_allow */
12570733d213SAndy Adamson 		READ_BUF(4);
12580733d213SAndy Adamson 		READ32(dummy);
12590733d213SAndy Adamson 		READ_BUF(dummy * 4);
12600733d213SAndy Adamson 		p += dummy;
12610733d213SAndy Adamson 		break;
12620733d213SAndy Adamson 	case SP4_SSV:
12630733d213SAndy Adamson 		/* ssp_ops */
12640733d213SAndy Adamson 		READ_BUF(4);
12650733d213SAndy Adamson 		READ32(dummy);
12660733d213SAndy Adamson 		READ_BUF(dummy * 4);
12670733d213SAndy Adamson 		p += dummy;
12680733d213SAndy Adamson 
12690733d213SAndy Adamson 		READ_BUF(4);
12700733d213SAndy Adamson 		READ32(dummy);
12710733d213SAndy Adamson 		READ_BUF(dummy * 4);
12720733d213SAndy Adamson 		p += dummy;
12730733d213SAndy Adamson 
12740733d213SAndy Adamson 		/* ssp_hash_algs<> */
12750733d213SAndy Adamson 		READ_BUF(4);
12765afa040bSMi Jinlong 		READ32(tmp);
12775afa040bSMi Jinlong 		while (tmp--) {
12780733d213SAndy Adamson 			READ_BUF(4);
12790733d213SAndy Adamson 			READ32(dummy);
12800733d213SAndy Adamson 			READ_BUF(dummy);
12810733d213SAndy Adamson 			p += XDR_QUADLEN(dummy);
12825afa040bSMi Jinlong 		}
12835afa040bSMi Jinlong 
12845afa040bSMi Jinlong 		/* ssp_encr_algs<> */
12855afa040bSMi Jinlong 		READ_BUF(4);
12865afa040bSMi Jinlong 		READ32(tmp);
12875afa040bSMi Jinlong 		while (tmp--) {
12885afa040bSMi Jinlong 			READ_BUF(4);
12895afa040bSMi Jinlong 			READ32(dummy);
12905afa040bSMi Jinlong 			READ_BUF(dummy);
12915afa040bSMi Jinlong 			p += XDR_QUADLEN(dummy);
12925afa040bSMi Jinlong 		}
12930733d213SAndy Adamson 
12940733d213SAndy Adamson 		/* ssp_window and ssp_num_gss_handles */
12950733d213SAndy Adamson 		READ_BUF(8);
12960733d213SAndy Adamson 		READ32(dummy);
12970733d213SAndy Adamson 		READ32(dummy);
12980733d213SAndy Adamson 		break;
12990733d213SAndy Adamson 	default:
13000733d213SAndy Adamson 		goto xdr_error;
13010733d213SAndy Adamson 	}
13020733d213SAndy Adamson 
13030733d213SAndy Adamson 	/* Ignore Implementation ID */
13040733d213SAndy Adamson 	READ_BUF(4);    /* nfs_impl_id4 array length */
13050733d213SAndy Adamson 	READ32(dummy);
13060733d213SAndy Adamson 
13070733d213SAndy Adamson 	if (dummy > 1)
13080733d213SAndy Adamson 		goto xdr_error;
13090733d213SAndy Adamson 
13100733d213SAndy Adamson 	if (dummy == 1) {
13110733d213SAndy Adamson 		/* nii_domain */
13120733d213SAndy Adamson 		READ_BUF(4);
13130733d213SAndy Adamson 		READ32(dummy);
13140733d213SAndy Adamson 		READ_BUF(dummy);
13150733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
13160733d213SAndy Adamson 
13170733d213SAndy Adamson 		/* nii_name */
13180733d213SAndy Adamson 		READ_BUF(4);
13190733d213SAndy Adamson 		READ32(dummy);
13200733d213SAndy Adamson 		READ_BUF(dummy);
13210733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
13220733d213SAndy Adamson 
13230733d213SAndy Adamson 		/* nii_date */
13240733d213SAndy Adamson 		READ_BUF(12);
13250733d213SAndy Adamson 		p += 3;
13260733d213SAndy Adamson 	}
13270733d213SAndy Adamson 	DECODE_TAIL;
13282db134ebSAndy Adamson }
13292db134ebSAndy Adamson 
13302db134ebSAndy Adamson static __be32
13312db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
13322db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
13332db134ebSAndy Adamson {
1334ec6b5d7bSAndy Adamson 	DECODE_HEAD;
1335ec6b5d7bSAndy Adamson 	u32 dummy;
1336ec6b5d7bSAndy Adamson 
1337ec6b5d7bSAndy Adamson 	READ_BUF(16);
1338ec6b5d7bSAndy Adamson 	COPYMEM(&sess->clientid, 8);
1339ec6b5d7bSAndy Adamson 	READ32(sess->seqid);
1340ec6b5d7bSAndy Adamson 	READ32(sess->flags);
1341ec6b5d7bSAndy Adamson 
1342ec6b5d7bSAndy Adamson 	/* Fore channel attrs */
1343ec6b5d7bSAndy Adamson 	READ_BUF(28);
1344ec6b5d7bSAndy Adamson 	READ32(dummy); /* headerpadsz is always 0 */
1345ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxreq_sz);
1346ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxresp_sz);
1347ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxresp_cached);
1348ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxops);
1349ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxreqs);
1350ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.nr_rdma_attrs);
1351ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs == 1) {
1352ec6b5d7bSAndy Adamson 		READ_BUF(4);
1353ec6b5d7bSAndy Adamson 		READ32(sess->fore_channel.rdma_attrs);
1354ec6b5d7bSAndy Adamson 	} else if (sess->fore_channel.nr_rdma_attrs > 1) {
1355ec6b5d7bSAndy Adamson 		dprintk("Too many fore channel attr bitmaps!\n");
1356ec6b5d7bSAndy Adamson 		goto xdr_error;
1357ec6b5d7bSAndy Adamson 	}
1358ec6b5d7bSAndy Adamson 
1359ec6b5d7bSAndy Adamson 	/* Back channel attrs */
1360ec6b5d7bSAndy Adamson 	READ_BUF(28);
1361ec6b5d7bSAndy Adamson 	READ32(dummy); /* headerpadsz is always 0 */
1362ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxreq_sz);
1363ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxresp_sz);
1364ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxresp_cached);
1365ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxops);
1366ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxreqs);
1367ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.nr_rdma_attrs);
1368ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs == 1) {
1369ec6b5d7bSAndy Adamson 		READ_BUF(4);
1370ec6b5d7bSAndy Adamson 		READ32(sess->back_channel.rdma_attrs);
1371ec6b5d7bSAndy Adamson 	} else if (sess->back_channel.nr_rdma_attrs > 1) {
1372ec6b5d7bSAndy Adamson 		dprintk("Too many back channel attr bitmaps!\n");
1373ec6b5d7bSAndy Adamson 		goto xdr_error;
1374ec6b5d7bSAndy Adamson 	}
1375ec6b5d7bSAndy Adamson 
1376acb2887eSJ. Bruce Fields 	READ_BUF(4);
1377ec6b5d7bSAndy Adamson 	READ32(sess->callback_prog);
1378acb2887eSJ. Bruce Fields 	nfsd4_decode_cb_sec(argp, &sess->cb_sec);
1379ec6b5d7bSAndy Adamson 	DECODE_TAIL;
13802db134ebSAndy Adamson }
13812db134ebSAndy Adamson 
13822db134ebSAndy Adamson static __be32
13832db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
13842db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
13852db134ebSAndy Adamson {
1386e10e0cfcSBenny Halevy 	DECODE_HEAD;
1387e10e0cfcSBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN);
1388e10e0cfcSBenny Halevy 	COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1389e10e0cfcSBenny Halevy 
1390e10e0cfcSBenny Halevy 	DECODE_TAIL;
13912db134ebSAndy Adamson }
13922db134ebSAndy Adamson 
13932db134ebSAndy Adamson static __be32
1394e1ca12dfSBryan Schumaker nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
1395e1ca12dfSBryan Schumaker 			  struct nfsd4_free_stateid *free_stateid)
1396e1ca12dfSBryan Schumaker {
1397e1ca12dfSBryan Schumaker 	DECODE_HEAD;
1398e1ca12dfSBryan Schumaker 
1399e1ca12dfSBryan Schumaker 	READ_BUF(sizeof(stateid_t));
1400e1ca12dfSBryan Schumaker 	READ32(free_stateid->fr_stateid.si_generation);
1401e1ca12dfSBryan Schumaker 	COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
1402e1ca12dfSBryan Schumaker 
1403e1ca12dfSBryan Schumaker 	DECODE_TAIL;
1404e1ca12dfSBryan Schumaker }
1405e1ca12dfSBryan Schumaker 
1406e1ca12dfSBryan Schumaker static __be32
14072db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
14082db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
14092db134ebSAndy Adamson {
1410b85d4c01SBenny Halevy 	DECODE_HEAD;
1411b85d4c01SBenny Halevy 
1412b85d4c01SBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
1413b85d4c01SBenny Halevy 	COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1414b85d4c01SBenny Halevy 	READ32(seq->seqid);
1415b85d4c01SBenny Halevy 	READ32(seq->slotid);
1416b85d4c01SBenny Halevy 	READ32(seq->maxslots);
1417b85d4c01SBenny Halevy 	READ32(seq->cachethis);
1418b85d4c01SBenny Halevy 
1419b85d4c01SBenny Halevy 	DECODE_TAIL;
14202db134ebSAndy Adamson }
14212db134ebSAndy Adamson 
142217456804SBryan Schumaker static __be32
142317456804SBryan Schumaker nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
142417456804SBryan Schumaker {
142517456804SBryan Schumaker 	int i;
142603cfb420SBryan Schumaker 	__be32 *p, status;
142703cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid;
142817456804SBryan Schumaker 
142917456804SBryan Schumaker 	READ_BUF(4);
143017456804SBryan Schumaker 	test_stateid->ts_num_ids = ntohl(*p++);
143117456804SBryan Schumaker 
143203cfb420SBryan Schumaker 	INIT_LIST_HEAD(&test_stateid->ts_stateid_list);
143317456804SBryan Schumaker 
143417456804SBryan Schumaker 	for (i = 0; i < test_stateid->ts_num_ids; i++) {
143503cfb420SBryan Schumaker 		stateid = kmalloc(sizeof(struct nfsd4_test_stateid_id), GFP_KERNEL);
143603cfb420SBryan Schumaker 		if (!stateid) {
1437afcf6792SAl Viro 			status = nfserrno(-ENOMEM);
143803cfb420SBryan Schumaker 			goto out;
143903cfb420SBryan Schumaker 		}
144003cfb420SBryan Schumaker 
144103cfb420SBryan Schumaker 		defer_free(argp, kfree, stateid);
144203cfb420SBryan Schumaker 		INIT_LIST_HEAD(&stateid->ts_id_list);
144303cfb420SBryan Schumaker 		list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
144403cfb420SBryan Schumaker 
144503cfb420SBryan Schumaker 		status = nfsd4_decode_stateid(argp, &stateid->ts_id_stateid);
144617456804SBryan Schumaker 		if (status)
144703cfb420SBryan Schumaker 			goto out;
144817456804SBryan Schumaker 	}
144917456804SBryan Schumaker 
145017456804SBryan Schumaker 	status = 0;
145117456804SBryan Schumaker out:
145217456804SBryan Schumaker 	return status;
145317456804SBryan Schumaker xdr_error:
145417456804SBryan Schumaker 	dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
145517456804SBryan Schumaker 	status = nfserr_bad_xdr;
145617456804SBryan Schumaker 	goto out;
145717456804SBryan Schumaker }
145817456804SBryan Schumaker 
1459345c2842SMi Jinlong static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, struct nfsd4_destroy_clientid *dc)
1460345c2842SMi Jinlong {
1461345c2842SMi Jinlong 	DECODE_HEAD;
1462345c2842SMi Jinlong 
1463345c2842SMi Jinlong 	READ_BUF(8);
1464345c2842SMi Jinlong 	COPYMEM(&dc->clientid, 8);
1465345c2842SMi Jinlong 
1466345c2842SMi Jinlong 	DECODE_TAIL;
1467345c2842SMi Jinlong }
1468345c2842SMi Jinlong 
14694dc6ec00SJ. Bruce Fields static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
14704dc6ec00SJ. Bruce Fields {
14714dc6ec00SJ. Bruce Fields 	DECODE_HEAD;
14724dc6ec00SJ. Bruce Fields 
14734dc6ec00SJ. Bruce Fields 	READ_BUF(4);
14744dc6ec00SJ. Bruce Fields 	READ32(rc->rca_one_fs);
14754dc6ec00SJ. Bruce Fields 
14764dc6ec00SJ. Bruce Fields 	DECODE_TAIL;
14774dc6ec00SJ. Bruce Fields }
14784dc6ec00SJ. Bruce Fields 
14792db134ebSAndy Adamson static __be32
1480347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
1481347e0ad9SBenny Halevy {
1482347e0ad9SBenny Halevy 	return nfs_ok;
1483347e0ad9SBenny Halevy }
1484347e0ad9SBenny Halevy 
14853c375c6fSBenny Halevy static __be32
14863c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p)
14873c375c6fSBenny Halevy {
14881e685ec2SBenny Halevy 	return nfserr_notsupp;
14893c375c6fSBenny Halevy }
14903c375c6fSBenny Halevy 
1491347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *);
1492347e0ad9SBenny Halevy 
1493347e0ad9SBenny Halevy static nfsd4_dec nfsd4_dec_ops[] = {
1494ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
1495ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
1496ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
1497ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
1498ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
1499ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
1500ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
1501ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
1502ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
1503ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
1504ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
1505ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
1506ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
1507ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
1508ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1509ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
1510ad1060c8SJ. Bruce Fields 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
1511ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_open_confirm,
1512ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
1513ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
1514a1c8c4d1SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_noop,
1515ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
1516ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
1517ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
1518ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
1519ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
1520ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
1521ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_renew,
1522ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
1523ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
1524ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
1525ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
1526ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_setclientid,
1527ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm,
1528ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1529ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
1530ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_release_lockowner,
1531347e0ad9SBenny Halevy };
1532347e0ad9SBenny Halevy 
15332db134ebSAndy Adamson static nfsd4_dec nfsd41_dec_ops[] = {
15349064caaeSRandy Dunlap 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
15359064caaeSRandy Dunlap 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
15369064caaeSRandy Dunlap 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
15379064caaeSRandy Dunlap 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
15389064caaeSRandy Dunlap 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
15399064caaeSRandy Dunlap 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
15409064caaeSRandy Dunlap 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
15419064caaeSRandy Dunlap 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
15429064caaeSRandy Dunlap 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
15439064caaeSRandy Dunlap 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
15449064caaeSRandy Dunlap 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
15459064caaeSRandy Dunlap 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
15469064caaeSRandy Dunlap 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
15479064caaeSRandy Dunlap 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
15489064caaeSRandy Dunlap 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
15499064caaeSRandy Dunlap 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
15509064caaeSRandy Dunlap 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
15519064caaeSRandy Dunlap 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_notsupp,
15529064caaeSRandy Dunlap 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
15539064caaeSRandy Dunlap 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
15549064caaeSRandy Dunlap 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_notsupp,
15559064caaeSRandy Dunlap 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
15569064caaeSRandy Dunlap 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
15579064caaeSRandy Dunlap 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
15589064caaeSRandy Dunlap 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
15599064caaeSRandy Dunlap 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
15609064caaeSRandy Dunlap 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
15619064caaeSRandy Dunlap 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_notsupp,
15629064caaeSRandy Dunlap 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
15639064caaeSRandy Dunlap 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
15649064caaeSRandy Dunlap 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
15659064caaeSRandy Dunlap 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
15669064caaeSRandy Dunlap 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_notsupp,
15679064caaeSRandy Dunlap 	[OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp,
15689064caaeSRandy Dunlap 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
15699064caaeSRandy Dunlap 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
15709064caaeSRandy Dunlap 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_notsupp,
15712db134ebSAndy Adamson 
15722db134ebSAndy Adamson 	/* new operations for NFSv4.1 */
1573cb73a9f4SJ. Bruce Fields 	[OP_BACKCHANNEL_CTL]	= (nfsd4_dec)nfsd4_decode_backchannel_ctl,
15741d1bc8f2SJ. Bruce Fields 	[OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session,
15759064caaeSRandy Dunlap 	[OP_EXCHANGE_ID]	= (nfsd4_dec)nfsd4_decode_exchange_id,
15769064caaeSRandy Dunlap 	[OP_CREATE_SESSION]	= (nfsd4_dec)nfsd4_decode_create_session,
15779064caaeSRandy Dunlap 	[OP_DESTROY_SESSION]	= (nfsd4_dec)nfsd4_decode_destroy_session,
1578e1ca12dfSBryan Schumaker 	[OP_FREE_STATEID]	= (nfsd4_dec)nfsd4_decode_free_stateid,
15799064caaeSRandy Dunlap 	[OP_GET_DIR_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
15809064caaeSRandy Dunlap 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_notsupp,
15819064caaeSRandy Dunlap 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
15829064caaeSRandy Dunlap 	[OP_LAYOUTCOMMIT]	= (nfsd4_dec)nfsd4_decode_notsupp,
15839064caaeSRandy Dunlap 	[OP_LAYOUTGET]		= (nfsd4_dec)nfsd4_decode_notsupp,
15849064caaeSRandy Dunlap 	[OP_LAYOUTRETURN]	= (nfsd4_dec)nfsd4_decode_notsupp,
158504f4ad16SJ. Bruce Fields 	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_secinfo_no_name,
15869064caaeSRandy Dunlap 	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
15879064caaeSRandy Dunlap 	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
158817456804SBryan Schumaker 	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_test_stateid,
15899064caaeSRandy Dunlap 	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
1590345c2842SMi Jinlong 	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_destroy_clientid,
15914dc6ec00SJ. Bruce Fields 	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
15922db134ebSAndy Adamson };
15932db134ebSAndy Adamson 
1594f2feb96bSBenny Halevy struct nfsd4_minorversion_ops {
1595f2feb96bSBenny Halevy 	nfsd4_dec *decoders;
1596f2feb96bSBenny Halevy 	int nops;
1597f2feb96bSBenny Halevy };
1598f2feb96bSBenny Halevy 
1599f2feb96bSBenny Halevy static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
1600ad1060c8SJ. Bruce Fields 	[0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
16012db134ebSAndy Adamson 	[1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
16024bdc33edSSteve Dickson 	[2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
1603f2feb96bSBenny Halevy };
1604f2feb96bSBenny Halevy 
1605347e0ad9SBenny Halevy static __be32
16061da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
16071da177e4SLinus Torvalds {
16081da177e4SLinus Torvalds 	DECODE_HEAD;
16091da177e4SLinus Torvalds 	struct nfsd4_op *op;
1610f2feb96bSBenny Halevy 	struct nfsd4_minorversion_ops *ops;
16111091006cSJ. Bruce Fields 	bool cachethis = false;
16121da177e4SLinus Torvalds 	int i;
16131da177e4SLinus Torvalds 
16141da177e4SLinus Torvalds 	READ_BUF(4);
16151da177e4SLinus Torvalds 	READ32(argp->taglen);
16161da177e4SLinus Torvalds 	READ_BUF(argp->taglen + 8);
16171da177e4SLinus Torvalds 	SAVEMEM(argp->tag, argp->taglen);
16181da177e4SLinus Torvalds 	READ32(argp->minorversion);
16191da177e4SLinus Torvalds 	READ32(argp->opcnt);
16201da177e4SLinus Torvalds 
16211da177e4SLinus Torvalds 	if (argp->taglen > NFSD4_MAX_TAGLEN)
16221da177e4SLinus Torvalds 		goto xdr_error;
16231da177e4SLinus Torvalds 	if (argp->opcnt > 100)
16241da177e4SLinus Torvalds 		goto xdr_error;
16251da177e4SLinus Torvalds 
1626e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
16271da177e4SLinus Torvalds 		argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
16281da177e4SLinus Torvalds 		if (!argp->ops) {
16291da177e4SLinus Torvalds 			argp->ops = argp->iops;
1630817cb9d4SChuck Lever 			dprintk("nfsd: couldn't allocate room for COMPOUND\n");
16311da177e4SLinus Torvalds 			goto xdr_error;
16321da177e4SLinus Torvalds 		}
16331da177e4SLinus Torvalds 	}
16341da177e4SLinus Torvalds 
1635f2feb96bSBenny Halevy 	if (argp->minorversion >= ARRAY_SIZE(nfsd4_minorversion))
163630cff1ffSBenny Halevy 		argp->opcnt = 0;
163730cff1ffSBenny Halevy 
1638f2feb96bSBenny Halevy 	ops = &nfsd4_minorversion[argp->minorversion];
16391da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
16401da177e4SLinus Torvalds 		op = &argp->ops[i];
16411da177e4SLinus Torvalds 		op->replay = NULL;
16421da177e4SLinus Torvalds 
16438a61b18cSJ. Bruce Fields 		READ_BUF(4);
16448a61b18cSJ. Bruce Fields 		READ32(op->opnum);
16451da177e4SLinus Torvalds 
1646de3cab79SRicardo Labiaga 		if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP)
1647f2feb96bSBenny Halevy 			op->status = ops->decoders[op->opnum](argp, &op->u);
1648347e0ad9SBenny Halevy 		else {
16491da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
16501da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
16511da177e4SLinus Torvalds 		}
16521da177e4SLinus Torvalds 
16531da177e4SLinus Torvalds 		if (op->status) {
16541da177e4SLinus Torvalds 			argp->opcnt = i+1;
16551da177e4SLinus Torvalds 			break;
16561da177e4SLinus Torvalds 		}
16571091006cSJ. Bruce Fields 		/*
16581091006cSJ. Bruce Fields 		 * We'll try to cache the result in the DRC if any one
16591091006cSJ. Bruce Fields 		 * op in the compound wants to be cached:
16601091006cSJ. Bruce Fields 		 */
16611091006cSJ. Bruce Fields 		cachethis |= nfsd4_cache_this_op(op);
16621da177e4SLinus Torvalds 	}
16631091006cSJ. Bruce Fields 	/* Sessions make the DRC unnecessary: */
16641091006cSJ. Bruce Fields 	if (argp->minorversion)
16651091006cSJ. Bruce Fields 		cachethis = false;
16661091006cSJ. Bruce Fields 	argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
16671da177e4SLinus Torvalds 
16681da177e4SLinus Torvalds 	DECODE_TAIL;
16691da177e4SLinus Torvalds }
16701da177e4SLinus Torvalds 
16711da177e4SLinus Torvalds #define WRITE32(n)               *p++ = htonl(n)
16721da177e4SLinus Torvalds #define WRITE64(n)               do {				\
16731da177e4SLinus Torvalds 	*p++ = htonl((u32)((n) >> 32));				\
16741da177e4SLinus Torvalds 	*p++ = htonl((u32)(n));					\
16751da177e4SLinus Torvalds } while (0)
16765108b276SHarvey Harrison #define WRITEMEM(ptr,nbytes)     do { if (nbytes > 0) {		\
16771da177e4SLinus Torvalds 	*(p + XDR_QUADLEN(nbytes) -1) = 0;                      \
16781da177e4SLinus Torvalds 	memcpy(p, ptr, nbytes);					\
16791da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);				\
16805108b276SHarvey Harrison }} while (0)
1681c654b8a9SJ. Bruce Fields 
1682c654b8a9SJ. Bruce Fields static void write32(__be32 **p, u32 n)
1683c654b8a9SJ. Bruce Fields {
168445eaa1c1SJ. Bruce Fields 	*(*p)++ = htonl(n);
1685c654b8a9SJ. Bruce Fields }
1686c654b8a9SJ. Bruce Fields 
1687c654b8a9SJ. Bruce Fields static void write64(__be32 **p, u64 n)
1688c654b8a9SJ. Bruce Fields {
168945eaa1c1SJ. Bruce Fields 	write32(p, (n >> 32));
1690c654b8a9SJ. Bruce Fields 	write32(p, (u32)n);
1691c654b8a9SJ. Bruce Fields }
1692c654b8a9SJ. Bruce Fields 
1693c654b8a9SJ. Bruce Fields static void write_change(__be32 **p, struct kstat *stat, struct inode *inode)
1694c654b8a9SJ. Bruce Fields {
1695c654b8a9SJ. Bruce Fields 	if (IS_I_VERSION(inode)) {
1696c654b8a9SJ. Bruce Fields 		write64(p, inode->i_version);
1697c654b8a9SJ. Bruce Fields 	} else {
1698c654b8a9SJ. Bruce Fields 		write32(p, stat->ctime.tv_sec);
1699c654b8a9SJ. Bruce Fields 		write32(p, stat->ctime.tv_nsec);
1700c654b8a9SJ. Bruce Fields 	}
1701c654b8a9SJ. Bruce Fields }
1702c654b8a9SJ. Bruce Fields 
1703c654b8a9SJ. Bruce Fields static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
1704c654b8a9SJ. Bruce Fields {
1705c654b8a9SJ. Bruce Fields 	write32(p, c->atomic);
1706c654b8a9SJ. Bruce Fields 	if (c->change_supported) {
1707c654b8a9SJ. Bruce Fields 		write64(p, c->before_change);
1708c654b8a9SJ. Bruce Fields 		write64(p, c->after_change);
1709c654b8a9SJ. Bruce Fields 	} else {
1710c654b8a9SJ. Bruce Fields 		write32(p, c->before_ctime_sec);
1711c654b8a9SJ. Bruce Fields 		write32(p, c->before_ctime_nsec);
1712c654b8a9SJ. Bruce Fields 		write32(p, c->after_ctime_sec);
1713c654b8a9SJ. Bruce Fields 		write32(p, c->after_ctime_nsec);
1714c654b8a9SJ. Bruce Fields 	}
1715c654b8a9SJ. Bruce Fields }
17161da177e4SLinus Torvalds 
17171da177e4SLinus Torvalds #define RESERVE_SPACE(nbytes)	do {				\
17181da177e4SLinus Torvalds 	p = resp->p;						\
17191da177e4SLinus Torvalds 	BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end);		\
17201da177e4SLinus Torvalds } while (0)
17211da177e4SLinus Torvalds #define ADJUST_ARGS()		resp->p = p
17221da177e4SLinus Torvalds 
172381c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
1724e7a0444aSWeston Andros Adamson  * separated @sep, escaped with esc_enter and esc_exit.
172581c3f413SJ.Bruce Fields  */
1726e7a0444aSWeston Andros Adamson static __be32 nfsd4_encode_components_esc(char sep, char *components,
1727e7a0444aSWeston Andros Adamson 				   __be32 **pp, int *buflen,
1728e7a0444aSWeston Andros Adamson 				   char esc_enter, char esc_exit)
172981c3f413SJ.Bruce Fields {
17302ebbc012SAl Viro 	__be32 *p = *pp;
17312ebbc012SAl Viro 	__be32 *countp = p;
173281c3f413SJ.Bruce Fields 	int strlen, count=0;
1733e7a0444aSWeston Andros Adamson 	char *str, *end, *next;
173481c3f413SJ.Bruce Fields 
173581c3f413SJ.Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
173681c3f413SJ.Bruce Fields 	if ((*buflen -= 4) < 0)
173781c3f413SJ.Bruce Fields 		return nfserr_resource;
173881c3f413SJ.Bruce Fields 	WRITE32(0); /* We will fill this in with @count later */
173981c3f413SJ.Bruce Fields 	end = str = components;
174081c3f413SJ.Bruce Fields 	while (*end) {
1741e7a0444aSWeston Andros Adamson 		bool found_esc = false;
1742e7a0444aSWeston Andros Adamson 
1743e7a0444aSWeston Andros Adamson 		/* try to parse as esc_start, ..., esc_end, sep */
1744e7a0444aSWeston Andros Adamson 		if (*str == esc_enter) {
1745e7a0444aSWeston Andros Adamson 			for (; *end && (*end != esc_exit); end++)
1746e7a0444aSWeston Andros Adamson 				/* find esc_exit or end of string */;
1747e7a0444aSWeston Andros Adamson 			next = end + 1;
1748e7a0444aSWeston Andros Adamson 			if (*end && (!*next || *next == sep)) {
1749e7a0444aSWeston Andros Adamson 				str++;
1750e7a0444aSWeston Andros Adamson 				found_esc = true;
1751e7a0444aSWeston Andros Adamson 			}
1752e7a0444aSWeston Andros Adamson 		}
1753e7a0444aSWeston Andros Adamson 
1754e7a0444aSWeston Andros Adamson 		if (!found_esc)
175581c3f413SJ.Bruce Fields 			for (; *end && (*end != sep); end++)
1756e7a0444aSWeston Andros Adamson 				/* find sep or end of string */;
1757e7a0444aSWeston Andros Adamson 
175881c3f413SJ.Bruce Fields 		strlen = end - str;
175981c3f413SJ.Bruce Fields 		if (strlen) {
176081c3f413SJ.Bruce Fields 			if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
176181c3f413SJ.Bruce Fields 				return nfserr_resource;
176281c3f413SJ.Bruce Fields 			WRITE32(strlen);
176381c3f413SJ.Bruce Fields 			WRITEMEM(str, strlen);
176481c3f413SJ.Bruce Fields 			count++;
176581c3f413SJ.Bruce Fields 		}
176681c3f413SJ.Bruce Fields 		else
176781c3f413SJ.Bruce Fields 			end++;
176881c3f413SJ.Bruce Fields 		str = end;
176981c3f413SJ.Bruce Fields 	}
177081c3f413SJ.Bruce Fields 	*pp = p;
177181c3f413SJ.Bruce Fields 	p = countp;
177281c3f413SJ.Bruce Fields 	WRITE32(count);
177381c3f413SJ.Bruce Fields 	return 0;
177481c3f413SJ.Bruce Fields }
177581c3f413SJ.Bruce Fields 
1776e7a0444aSWeston Andros Adamson /* Encode as an array of strings the string given with components
1777e7a0444aSWeston Andros Adamson  * separated @sep.
1778e7a0444aSWeston Andros Adamson  */
1779e7a0444aSWeston Andros Adamson static __be32 nfsd4_encode_components(char sep, char *components,
1780e7a0444aSWeston Andros Adamson 				   __be32 **pp, int *buflen)
1781e7a0444aSWeston Andros Adamson {
1782e7a0444aSWeston Andros Adamson 	return nfsd4_encode_components_esc(sep, components, pp, buflen, 0, 0);
1783e7a0444aSWeston Andros Adamson }
1784e7a0444aSWeston Andros Adamson 
178581c3f413SJ.Bruce Fields /*
178681c3f413SJ.Bruce Fields  * encode a location element of a fs_locations structure
178781c3f413SJ.Bruce Fields  */
1788b37ad28bSAl Viro static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
17892ebbc012SAl Viro 				    __be32 **pp, int *buflen)
179081c3f413SJ.Bruce Fields {
1791b37ad28bSAl Viro 	__be32 status;
17922ebbc012SAl Viro 	__be32 *p = *pp;
179381c3f413SJ.Bruce Fields 
1794e7a0444aSWeston Andros Adamson 	status = nfsd4_encode_components_esc(':', location->hosts, &p, buflen,
1795e7a0444aSWeston Andros Adamson 						'[', ']');
179681c3f413SJ.Bruce Fields 	if (status)
179781c3f413SJ.Bruce Fields 		return status;
179881c3f413SJ.Bruce Fields 	status = nfsd4_encode_components('/', location->path, &p, buflen);
179981c3f413SJ.Bruce Fields 	if (status)
180081c3f413SJ.Bruce Fields 		return status;
180181c3f413SJ.Bruce Fields 	*pp = p;
180281c3f413SJ.Bruce Fields 	return 0;
180381c3f413SJ.Bruce Fields }
180481c3f413SJ.Bruce Fields 
180581c3f413SJ.Bruce Fields /*
1806ed748aacSTrond Myklebust  * Encode a path in RFC3530 'pathname4' format
180781c3f413SJ.Bruce Fields  */
1808ed748aacSTrond Myklebust static __be32 nfsd4_encode_path(const struct path *root,
1809ed748aacSTrond Myklebust 		const struct path *path, __be32 **pp, int *buflen)
181081c3f413SJ.Bruce Fields {
1811ed748aacSTrond Myklebust 	struct path cur = {
1812ed748aacSTrond Myklebust 		.mnt = path->mnt,
1813ed748aacSTrond Myklebust 		.dentry = path->dentry,
1814ed748aacSTrond Myklebust 	};
1815ed748aacSTrond Myklebust 	__be32 *p = *pp;
1816ed748aacSTrond Myklebust 	struct dentry **components = NULL;
1817ed748aacSTrond Myklebust 	unsigned int ncomponents = 0;
1818ed748aacSTrond Myklebust 	__be32 err = nfserr_jukebox;
181981c3f413SJ.Bruce Fields 
1820ed748aacSTrond Myklebust 	dprintk("nfsd4_encode_components(");
182181c3f413SJ.Bruce Fields 
1822ed748aacSTrond Myklebust 	path_get(&cur);
1823ed748aacSTrond Myklebust 	/* First walk the path up to the nfsd root, and store the
1824ed748aacSTrond Myklebust 	 * dentries/path components in an array.
1825ed748aacSTrond Myklebust 	 */
1826ed748aacSTrond Myklebust 	for (;;) {
1827ed748aacSTrond Myklebust 		if (cur.dentry == root->dentry && cur.mnt == root->mnt)
1828ed748aacSTrond Myklebust 			break;
1829ed748aacSTrond Myklebust 		if (cur.dentry == cur.mnt->mnt_root) {
1830ed748aacSTrond Myklebust 			if (follow_up(&cur))
1831ed748aacSTrond Myklebust 				continue;
1832ed748aacSTrond Myklebust 			goto out_free;
183381c3f413SJ.Bruce Fields 		}
1834ed748aacSTrond Myklebust 		if ((ncomponents & 15) == 0) {
1835ed748aacSTrond Myklebust 			struct dentry **new;
1836ed748aacSTrond Myklebust 			new = krealloc(components,
1837ed748aacSTrond Myklebust 					sizeof(*new) * (ncomponents + 16),
1838ed748aacSTrond Myklebust 					GFP_KERNEL);
1839ed748aacSTrond Myklebust 			if (!new)
1840ed748aacSTrond Myklebust 				goto out_free;
1841ed748aacSTrond Myklebust 			components = new;
1842ed748aacSTrond Myklebust 		}
1843ed748aacSTrond Myklebust 		components[ncomponents++] = cur.dentry;
1844ed748aacSTrond Myklebust 		cur.dentry = dget_parent(cur.dentry);
1845ed748aacSTrond Myklebust 	}
1846ed748aacSTrond Myklebust 
1847ed748aacSTrond Myklebust 	*buflen -= 4;
1848ed748aacSTrond Myklebust 	if (*buflen < 0)
1849ed748aacSTrond Myklebust 		goto out_free;
1850ed748aacSTrond Myklebust 	WRITE32(ncomponents);
1851ed748aacSTrond Myklebust 
1852ed748aacSTrond Myklebust 	while (ncomponents) {
1853ed748aacSTrond Myklebust 		struct dentry *dentry = components[ncomponents - 1];
1854ed748aacSTrond Myklebust 		unsigned int len = dentry->d_name.len;
1855ed748aacSTrond Myklebust 
1856ed748aacSTrond Myklebust 		*buflen -= 4 + (XDR_QUADLEN(len) << 2);
1857ed748aacSTrond Myklebust 		if (*buflen < 0)
1858ed748aacSTrond Myklebust 			goto out_free;
1859ed748aacSTrond Myklebust 		WRITE32(len);
1860ed748aacSTrond Myklebust 		WRITEMEM(dentry->d_name.name, len);
1861ed748aacSTrond Myklebust 		dprintk("/%s", dentry->d_name.name);
1862ed748aacSTrond Myklebust 		dput(dentry);
1863ed748aacSTrond Myklebust 		ncomponents--;
1864ed748aacSTrond Myklebust 	}
1865ed748aacSTrond Myklebust 
1866ed748aacSTrond Myklebust 	*pp = p;
1867ed748aacSTrond Myklebust 	err = 0;
1868ed748aacSTrond Myklebust out_free:
1869ed748aacSTrond Myklebust 	dprintk(")\n");
1870ed748aacSTrond Myklebust 	while (ncomponents)
1871ed748aacSTrond Myklebust 		dput(components[--ncomponents]);
1872ed748aacSTrond Myklebust 	kfree(components);
1873ed748aacSTrond Myklebust 	path_put(&cur);
1874ed748aacSTrond Myklebust 	return err;
1875ed748aacSTrond Myklebust }
1876ed748aacSTrond Myklebust 
1877ed748aacSTrond Myklebust static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
1878ed748aacSTrond Myklebust 		const struct path *path, __be32 **pp, int *buflen)
1879ed748aacSTrond Myklebust {
1880ed748aacSTrond Myklebust 	struct svc_export *exp_ps;
1881ed748aacSTrond Myklebust 	__be32 res;
1882ed748aacSTrond Myklebust 
1883ed748aacSTrond Myklebust 	exp_ps = rqst_find_fsidzero_export(rqstp);
1884ed748aacSTrond Myklebust 	if (IS_ERR(exp_ps))
1885ed748aacSTrond Myklebust 		return nfserrno(PTR_ERR(exp_ps));
1886ed748aacSTrond Myklebust 	res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen);
1887ed748aacSTrond Myklebust 	exp_put(exp_ps);
1888ed748aacSTrond Myklebust 	return res;
188981c3f413SJ.Bruce Fields }
189081c3f413SJ.Bruce Fields 
189181c3f413SJ.Bruce Fields /*
189281c3f413SJ.Bruce Fields  *  encode a fs_locations structure
189381c3f413SJ.Bruce Fields  */
1894b37ad28bSAl Viro static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
189581c3f413SJ.Bruce Fields 				     struct svc_export *exp,
18962ebbc012SAl Viro 				     __be32 **pp, int *buflen)
189781c3f413SJ.Bruce Fields {
1898b37ad28bSAl Viro 	__be32 status;
1899cc45f017SAl Viro 	int i;
19002ebbc012SAl Viro 	__be32 *p = *pp;
190181c3f413SJ.Bruce Fields 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
190281c3f413SJ.Bruce Fields 
1903ed748aacSTrond Myklebust 	status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen);
190481c3f413SJ.Bruce Fields 	if (status)
190581c3f413SJ.Bruce Fields 		return status;
190681c3f413SJ.Bruce Fields 	if ((*buflen -= 4) < 0)
190781c3f413SJ.Bruce Fields 		return nfserr_resource;
190881c3f413SJ.Bruce Fields 	WRITE32(fslocs->locations_count);
190981c3f413SJ.Bruce Fields 	for (i=0; i<fslocs->locations_count; i++) {
191081c3f413SJ.Bruce Fields 		status = nfsd4_encode_fs_location4(&fslocs->locations[i],
191181c3f413SJ.Bruce Fields 						   &p, buflen);
191281c3f413SJ.Bruce Fields 		if (status)
191381c3f413SJ.Bruce Fields 			return status;
191481c3f413SJ.Bruce Fields 	}
191581c3f413SJ.Bruce Fields 	*pp = p;
191681c3f413SJ.Bruce Fields 	return 0;
191781c3f413SJ.Bruce Fields }
19181da177e4SLinus Torvalds 
19193d2544b1SJ. Bruce Fields static u32 nfs4_file_type(umode_t mode)
19203d2544b1SJ. Bruce Fields {
19213d2544b1SJ. Bruce Fields 	switch (mode & S_IFMT) {
19223d2544b1SJ. Bruce Fields 	case S_IFIFO:	return NF4FIFO;
19233d2544b1SJ. Bruce Fields 	case S_IFCHR:	return NF4CHR;
19243d2544b1SJ. Bruce Fields 	case S_IFDIR:	return NF4DIR;
19253d2544b1SJ. Bruce Fields 	case S_IFBLK:	return NF4BLK;
19263d2544b1SJ. Bruce Fields 	case S_IFLNK:	return NF4LNK;
19273d2544b1SJ. Bruce Fields 	case S_IFREG:	return NF4REG;
19283d2544b1SJ. Bruce Fields 	case S_IFSOCK:	return NF4SOCK;
19293d2544b1SJ. Bruce Fields 	default:	return NF4BAD;
19301da177e4SLinus Torvalds 	};
19313d2544b1SJ. Bruce Fields }
19321da177e4SLinus Torvalds 
1933b37ad28bSAl Viro static __be32
1934ab8e4aeeSEric W. Biederman nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, kuid_t uid, kgid_t gid,
19352ebbc012SAl Viro 			__be32 **p, int *buflen)
19361da177e4SLinus Torvalds {
19371da177e4SLinus Torvalds 	int status;
19381da177e4SLinus Torvalds 
19391da177e4SLinus Torvalds 	if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
19401da177e4SLinus Torvalds 		return nfserr_resource;
19411da177e4SLinus Torvalds 	if (whotype != NFS4_ACL_WHO_NAMED)
19421da177e4SLinus Torvalds 		status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
1943ab8e4aeeSEric W. Biederman 	else if (gid_valid(gid))
1944ab8e4aeeSEric W. Biederman 		status = nfsd_map_gid_to_name(rqstp, gid, (u8 *)(*p + 1));
19451da177e4SLinus Torvalds 	else
1946ab8e4aeeSEric W. Biederman 		status = nfsd_map_uid_to_name(rqstp, uid, (u8 *)(*p + 1));
19471da177e4SLinus Torvalds 	if (status < 0)
19481da177e4SLinus Torvalds 		return nfserrno(status);
19491da177e4SLinus Torvalds 	*p = xdr_encode_opaque(*p, NULL, status);
19501da177e4SLinus Torvalds 	*buflen -= (XDR_QUADLEN(status) << 2) + 4;
19511da177e4SLinus Torvalds 	BUG_ON(*buflen < 0);
19521da177e4SLinus Torvalds 	return 0;
19531da177e4SLinus Torvalds }
19541da177e4SLinus Torvalds 
1955b37ad28bSAl Viro static inline __be32
1956ab8e4aeeSEric W. Biederman nfsd4_encode_user(struct svc_rqst *rqstp, kuid_t user, __be32 **p, int *buflen)
19571da177e4SLinus Torvalds {
1958ab8e4aeeSEric W. Biederman 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, user, INVALID_GID,
1959ab8e4aeeSEric W. Biederman 				 p, buflen);
19601da177e4SLinus Torvalds }
19611da177e4SLinus Torvalds 
1962b37ad28bSAl Viro static inline __be32
1963ab8e4aeeSEric W. Biederman nfsd4_encode_group(struct svc_rqst *rqstp, kgid_t group, __be32 **p, int *buflen)
19641da177e4SLinus Torvalds {
1965ab8e4aeeSEric W. Biederman 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, INVALID_UID, group,
1966ab8e4aeeSEric W. Biederman 				 p, buflen);
19671da177e4SLinus Torvalds }
19681da177e4SLinus Torvalds 
1969b37ad28bSAl Viro static inline __be32
1970ab8e4aeeSEric W. Biederman nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
19712ebbc012SAl Viro 		__be32 **p, int *buflen)
19721da177e4SLinus Torvalds {
1973ab8e4aeeSEric W. Biederman 	kuid_t uid = INVALID_UID;
1974ab8e4aeeSEric W. Biederman 	kgid_t gid = INVALID_GID;
1975ab8e4aeeSEric W. Biederman 
1976ab8e4aeeSEric W. Biederman 	if (ace->whotype == NFS4_ACL_WHO_NAMED) {
1977ab8e4aeeSEric W. Biederman 		if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
1978ab8e4aeeSEric W. Biederman 			gid = ace->who_gid;
1979ab8e4aeeSEric W. Biederman 		else
1980ab8e4aeeSEric W. Biederman 			uid = ace->who_uid;
1981ab8e4aeeSEric W. Biederman 	}
1982ab8e4aeeSEric W. Biederman 	return nfsd4_encode_name(rqstp, ace->whotype, uid, gid, p, buflen);
19831da177e4SLinus Torvalds }
19841da177e4SLinus Torvalds 
198542ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
198642ca0993SJ.Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
198742ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
198842ca0993SJ.Bruce Fields 
198918032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
199018032ca0SDavid Quigley static inline __be32
199118032ca0SDavid Quigley nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
199218032ca0SDavid Quigley {
199318032ca0SDavid Quigley 	__be32 *p = *pp;
199418032ca0SDavid Quigley 
199518032ca0SDavid Quigley 	if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4))
199618032ca0SDavid Quigley 		return nfserr_resource;
199718032ca0SDavid Quigley 
199818032ca0SDavid Quigley 	/*
199918032ca0SDavid Quigley 	 * For now we use a 0 here to indicate the null translation; in
200018032ca0SDavid Quigley 	 * the future we may place a call to translation code here.
200118032ca0SDavid Quigley 	 */
200218032ca0SDavid Quigley 	if ((*buflen -= 8) < 0)
200318032ca0SDavid Quigley 		return nfserr_resource;
200418032ca0SDavid Quigley 
200518032ca0SDavid Quigley 	WRITE32(0); /* lfs */
200618032ca0SDavid Quigley 	WRITE32(0); /* pi */
200718032ca0SDavid Quigley 	p = xdr_encode_opaque(p, context, len);
200818032ca0SDavid Quigley 	*buflen -= (XDR_QUADLEN(len) << 2) + 4;
200918032ca0SDavid Quigley 
201018032ca0SDavid Quigley 	*pp = p;
201118032ca0SDavid Quigley 	return 0;
201218032ca0SDavid Quigley }
201318032ca0SDavid Quigley #else
201418032ca0SDavid Quigley static inline __be32
2015ba4e55bbSJ. Bruce Fields nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
201618032ca0SDavid Quigley { return 0; }
201718032ca0SDavid Quigley #endif
201818032ca0SDavid Quigley 
2019b37ad28bSAl Viro static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
202042ca0993SJ.Bruce Fields {
202142ca0993SJ.Bruce Fields 	/* As per referral draft:  */
202242ca0993SJ.Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
202342ca0993SJ.Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
202442ca0993SJ.Bruce Fields 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
202542ca0993SJ.Bruce Fields 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
202642ca0993SJ.Bruce Fields 			*rdattr_err = NFSERR_MOVED;
202742ca0993SJ.Bruce Fields 		else
202842ca0993SJ.Bruce Fields 			return nfserr_moved;
202942ca0993SJ.Bruce Fields 	}
203042ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
203142ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
203242ca0993SJ.Bruce Fields 	return 0;
203342ca0993SJ.Bruce Fields }
20341da177e4SLinus Torvalds 
2035ae7095a7SJ. Bruce Fields 
2036ae7095a7SJ. Bruce Fields static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
2037ae7095a7SJ. Bruce Fields {
2038ae7095a7SJ. Bruce Fields 	struct path path = exp->ex_path;
2039ae7095a7SJ. Bruce Fields 	int err;
2040ae7095a7SJ. Bruce Fields 
2041ae7095a7SJ. Bruce Fields 	path_get(&path);
2042ae7095a7SJ. Bruce Fields 	while (follow_up(&path)) {
2043ae7095a7SJ. Bruce Fields 		if (path.dentry != path.mnt->mnt_root)
2044ae7095a7SJ. Bruce Fields 			break;
2045ae7095a7SJ. Bruce Fields 	}
20463dadecceSAl Viro 	err = vfs_getattr(&path, stat);
2047ae7095a7SJ. Bruce Fields 	path_put(&path);
2048ae7095a7SJ. Bruce Fields 	return err;
2049ae7095a7SJ. Bruce Fields }
2050ae7095a7SJ. Bruce Fields 
20511da177e4SLinus Torvalds /*
20521da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
20531da177e4SLinus Torvalds  * ourselves.
20541da177e4SLinus Torvalds  *
205584822d0bSJ. Bruce Fields  * countp is the buffer size in _words_
20561da177e4SLinus Torvalds  */
2057b37ad28bSAl Viro __be32
20581da177e4SLinus Torvalds nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
205984822d0bSJ. Bruce Fields 		struct dentry *dentry, __be32 **buffer, int count, u32 *bmval,
2060406a7ea9SFrank Filz 		struct svc_rqst *rqstp, int ignore_crossmnt)
20611da177e4SLinus Torvalds {
20621da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
20631da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
20647e705706SAndy Adamson 	u32 bmval2 = bmval[2];
20651da177e4SLinus Torvalds 	struct kstat stat;
20661da177e4SLinus Torvalds 	struct svc_fh tempfh;
20671da177e4SLinus Torvalds 	struct kstatfs statfs;
206884822d0bSJ. Bruce Fields 	int buflen = count << 2;
20692ebbc012SAl Viro 	__be32 *attrlenp;
20701da177e4SLinus Torvalds 	u32 dummy;
20711da177e4SLinus Torvalds 	u64 dummy64;
207242ca0993SJ.Bruce Fields 	u32 rdattr_err = 0;
207384822d0bSJ. Bruce Fields 	__be32 *p = *buffer;
2074b37ad28bSAl Viro 	__be32 status;
2075b8dd7b9aSAl Viro 	int err;
20761da177e4SLinus Torvalds 	int aclsupport = 0;
20771da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
207818032ca0SDavid Quigley 	void *context = NULL;
207918032ca0SDavid Quigley 	int contextlen;
208018032ca0SDavid Quigley 	bool contextsupport = false;
20817e705706SAndy Adamson 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
20827e705706SAndy Adamson 	u32 minorversion = resp->cstate.minorversion;
2083ebabe9a9SChristoph Hellwig 	struct path path = {
2084ebabe9a9SChristoph Hellwig 		.mnt	= exp->ex_path.mnt,
2085ebabe9a9SChristoph Hellwig 		.dentry	= dentry,
2086ebabe9a9SChristoph Hellwig 	};
20873d733711SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
20881da177e4SLinus Torvalds 
20891da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
20907e705706SAndy Adamson 	BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion));
20917e705706SAndy Adamson 	BUG_ON(bmval1 & ~nfsd_suppattrs1(minorversion));
20927e705706SAndy Adamson 	BUG_ON(bmval2 & ~nfsd_suppattrs2(minorversion));
20931da177e4SLinus Torvalds 
209442ca0993SJ.Bruce Fields 	if (exp->ex_fslocs.migrated) {
20957e705706SAndy Adamson 		BUG_ON(bmval[2]);
209642ca0993SJ.Bruce Fields 		status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err);
209742ca0993SJ.Bruce Fields 		if (status)
209842ca0993SJ.Bruce Fields 			goto out;
209942ca0993SJ.Bruce Fields 	}
210042ca0993SJ.Bruce Fields 
21013dadecceSAl Viro 	err = vfs_getattr(&path, &stat);
2102b8dd7b9aSAl Viro 	if (err)
21031da177e4SLinus Torvalds 		goto out_nfserr;
2104a16e92edSJ. Bruce Fields 	if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL |
2105a16e92edSJ. Bruce Fields 			FATTR4_WORD0_MAXNAME)) ||
21061da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
21071da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
2108ebabe9a9SChristoph Hellwig 		err = vfs_statfs(&path, &statfs);
2109b8dd7b9aSAl Viro 		if (err)
21101da177e4SLinus Torvalds 			goto out_nfserr;
21111da177e4SLinus Torvalds 	}
21121da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
21131da177e4SLinus Torvalds 		fh_init(&tempfh, NFS4_FHSIZE);
21141da177e4SLinus Torvalds 		status = fh_compose(&tempfh, exp, dentry, NULL);
21151da177e4SLinus Torvalds 		if (status)
21161da177e4SLinus Torvalds 			goto out;
21171da177e4SLinus Torvalds 		fhp = &tempfh;
21181da177e4SLinus Torvalds 	}
21191da177e4SLinus Torvalds 	if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
21201da177e4SLinus Torvalds 			| FATTR4_WORD0_SUPPORTED_ATTRS)) {
2121b8dd7b9aSAl Viro 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
2122b8dd7b9aSAl Viro 		aclsupport = (err == 0);
21231da177e4SLinus Torvalds 		if (bmval0 & FATTR4_WORD0_ACL) {
2124b8dd7b9aSAl Viro 			if (err == -EOPNOTSUPP)
21251da177e4SLinus Torvalds 				bmval0 &= ~FATTR4_WORD0_ACL;
2126b8dd7b9aSAl Viro 			else if (err == -EINVAL) {
21271da177e4SLinus Torvalds 				status = nfserr_attrnotsupp;
21281da177e4SLinus Torvalds 				goto out;
2129b8dd7b9aSAl Viro 			} else if (err != 0)
21301da177e4SLinus Torvalds 				goto out_nfserr;
21311da177e4SLinus Torvalds 		}
21321da177e4SLinus Torvalds 	}
21332b44f1baSBenny Halevy 
213418032ca0SDavid Quigley #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
213518032ca0SDavid Quigley 	if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) ||
213618032ca0SDavid Quigley 			bmval[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
213718032ca0SDavid Quigley 		err = security_inode_getsecctx(dentry->d_inode,
213818032ca0SDavid Quigley 						&context, &contextlen);
213918032ca0SDavid Quigley 		contextsupport = (err == 0);
214018032ca0SDavid Quigley 		if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
214118032ca0SDavid Quigley 			if (err == -EOPNOTSUPP)
214218032ca0SDavid Quigley 				bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
214318032ca0SDavid Quigley 			else if (err)
214418032ca0SDavid Quigley 				goto out_nfserr;
214518032ca0SDavid Quigley 		}
214618032ca0SDavid Quigley 	}
214718032ca0SDavid Quigley #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
214818032ca0SDavid Quigley 
21492b44f1baSBenny Halevy 	if (bmval2) {
21501da177e4SLinus Torvalds 		if ((buflen -= 16) < 0)
21511da177e4SLinus Torvalds 			goto out_resource;
21527e705706SAndy Adamson 		WRITE32(3);
21537e705706SAndy Adamson 		WRITE32(bmval0);
21547e705706SAndy Adamson 		WRITE32(bmval1);
21557e705706SAndy Adamson 		WRITE32(bmval2);
21562b44f1baSBenny Halevy 	} else if (bmval1) {
21572b44f1baSBenny Halevy 		if ((buflen -= 12) < 0)
21582b44f1baSBenny Halevy 			goto out_resource;
21591da177e4SLinus Torvalds 		WRITE32(2);
21601da177e4SLinus Torvalds 		WRITE32(bmval0);
21611da177e4SLinus Torvalds 		WRITE32(bmval1);
21627e705706SAndy Adamson 	} else {
21632b44f1baSBenny Halevy 		if ((buflen -= 8) < 0)
21642b44f1baSBenny Halevy 			goto out_resource;
21657e705706SAndy Adamson 		WRITE32(1);
21667e705706SAndy Adamson 		WRITE32(bmval0);
21677e705706SAndy Adamson 	}
21681da177e4SLinus Torvalds 	attrlenp = p++;                /* to be backfilled later */
21691da177e4SLinus Torvalds 
21701da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
21717e705706SAndy Adamson 		u32 word0 = nfsd_suppattrs0(minorversion);
21727e705706SAndy Adamson 		u32 word1 = nfsd_suppattrs1(minorversion);
21737e705706SAndy Adamson 		u32 word2 = nfsd_suppattrs2(minorversion);
21747e705706SAndy Adamson 
217542ca0993SJ.Bruce Fields 		if (!aclsupport)
217642ca0993SJ.Bruce Fields 			word0 &= ~FATTR4_WORD0_ACL;
217718032ca0SDavid Quigley 		if (!contextsupport)
217818032ca0SDavid Quigley 			word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
21797e705706SAndy Adamson 		if (!word2) {
21802b44f1baSBenny Halevy 			if ((buflen -= 12) < 0)
21812b44f1baSBenny Halevy 				goto out_resource;
21821da177e4SLinus Torvalds 			WRITE32(2);
218342ca0993SJ.Bruce Fields 			WRITE32(word0);
21847e705706SAndy Adamson 			WRITE32(word1);
21857e705706SAndy Adamson 		} else {
21862b44f1baSBenny Halevy 			if ((buflen -= 16) < 0)
21872b44f1baSBenny Halevy 				goto out_resource;
21887e705706SAndy Adamson 			WRITE32(3);
21897e705706SAndy Adamson 			WRITE32(word0);
21907e705706SAndy Adamson 			WRITE32(word1);
21917e705706SAndy Adamson 			WRITE32(word2);
21927e705706SAndy Adamson 		}
21931da177e4SLinus Torvalds 	}
21941da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
21951da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
21961da177e4SLinus Torvalds 			goto out_resource;
21973d2544b1SJ. Bruce Fields 		dummy = nfs4_file_type(stat.mode);
21981da177e4SLinus Torvalds 		if (dummy == NF4BAD)
21991da177e4SLinus Torvalds 			goto out_serverfault;
22001da177e4SLinus Torvalds 		WRITE32(dummy);
22011da177e4SLinus Torvalds 	}
22021da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
22031da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22041da177e4SLinus Torvalds 			goto out_resource;
220549640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
2206e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT);
220749640001SNeilBrown 		else
2208e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
22091da177e4SLinus Torvalds 	}
22101da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
22111da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
22121da177e4SLinus Torvalds 			goto out_resource;
2213c654b8a9SJ. Bruce Fields 		write_change(&p, &stat, dentry->d_inode);
22141da177e4SLinus Torvalds 	}
22151da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
22161da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
22171da177e4SLinus Torvalds 			goto out_resource;
22181da177e4SLinus Torvalds 		WRITE64(stat.size);
22191da177e4SLinus Torvalds 	}
22201da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
22211da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22221da177e4SLinus Torvalds 			goto out_resource;
22231da177e4SLinus Torvalds 		WRITE32(1);
22241da177e4SLinus Torvalds 	}
22251da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
22261da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22271da177e4SLinus Torvalds 			goto out_resource;
22281da177e4SLinus Torvalds 		WRITE32(1);
22291da177e4SLinus Torvalds 	}
22301da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
22311da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22321da177e4SLinus Torvalds 			goto out_resource;
22331da177e4SLinus Torvalds 		WRITE32(0);
22341da177e4SLinus Torvalds 	}
22351da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
22361da177e4SLinus Torvalds 		if ((buflen -= 16) < 0)
22371da177e4SLinus Torvalds 			goto out_resource;
223842ca0993SJ.Bruce Fields 		if (exp->ex_fslocs.migrated) {
223942ca0993SJ.Bruce Fields 			WRITE64(NFS4_REFERRAL_FSID_MAJOR);
224042ca0993SJ.Bruce Fields 			WRITE64(NFS4_REFERRAL_FSID_MINOR);
2241af6a4e28SNeilBrown 		} else switch(fsid_source(fhp)) {
2242af6a4e28SNeilBrown 		case FSIDSOURCE_FSID:
22431da177e4SLinus Torvalds 			WRITE64((u64)exp->ex_fsid);
22441da177e4SLinus Torvalds 			WRITE64((u64)0);
2245af6a4e28SNeilBrown 			break;
2246af6a4e28SNeilBrown 		case FSIDSOURCE_DEV:
22471da177e4SLinus Torvalds 			WRITE32(0);
22481da177e4SLinus Torvalds 			WRITE32(MAJOR(stat.dev));
22491da177e4SLinus Torvalds 			WRITE32(0);
22501da177e4SLinus Torvalds 			WRITE32(MINOR(stat.dev));
2251af6a4e28SNeilBrown 			break;
2252af6a4e28SNeilBrown 		case FSIDSOURCE_UUID:
2253af6a4e28SNeilBrown 			WRITEMEM(exp->ex_uuid, 16);
2254af6a4e28SNeilBrown 			break;
22551da177e4SLinus Torvalds 		}
22561da177e4SLinus Torvalds 	}
22571da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
22581da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22591da177e4SLinus Torvalds 			goto out_resource;
22601da177e4SLinus Torvalds 		WRITE32(0);
22611da177e4SLinus Torvalds 	}
22621da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
22631da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22641da177e4SLinus Torvalds 			goto out_resource;
22653d733711SStanislav Kinsbursky 		WRITE32(nn->nfsd4_lease);
22661da177e4SLinus Torvalds 	}
22671da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
22681da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22691da177e4SLinus Torvalds 			goto out_resource;
227042ca0993SJ.Bruce Fields 		WRITE32(rdattr_err);
22711da177e4SLinus Torvalds 	}
22721da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
22731da177e4SLinus Torvalds 		struct nfs4_ace *ace;
22741da177e4SLinus Torvalds 
22751da177e4SLinus Torvalds 		if (acl == NULL) {
22761da177e4SLinus Torvalds 			if ((buflen -= 4) < 0)
22771da177e4SLinus Torvalds 				goto out_resource;
22781da177e4SLinus Torvalds 
22791da177e4SLinus Torvalds 			WRITE32(0);
22801da177e4SLinus Torvalds 			goto out_acl;
22811da177e4SLinus Torvalds 		}
22821da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
22831da177e4SLinus Torvalds 			goto out_resource;
22841da177e4SLinus Torvalds 		WRITE32(acl->naces);
22851da177e4SLinus Torvalds 
228628e05dd8SJ. Bruce Fields 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
22871da177e4SLinus Torvalds 			if ((buflen -= 4*3) < 0)
22881da177e4SLinus Torvalds 				goto out_resource;
22891da177e4SLinus Torvalds 			WRITE32(ace->type);
22901da177e4SLinus Torvalds 			WRITE32(ace->flag);
22911da177e4SLinus Torvalds 			WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
2292ab8e4aeeSEric W. Biederman 			status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen);
22931da177e4SLinus Torvalds 			if (status == nfserr_resource)
22941da177e4SLinus Torvalds 				goto out_resource;
22951da177e4SLinus Torvalds 			if (status)
22961da177e4SLinus Torvalds 				goto out;
22971da177e4SLinus Torvalds 		}
22981da177e4SLinus Torvalds 	}
22991da177e4SLinus Torvalds out_acl:
23001da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
23011da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23021da177e4SLinus Torvalds 			goto out_resource;
23031da177e4SLinus Torvalds 		WRITE32(aclsupport ?
23041da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
23051da177e4SLinus Torvalds 	}
23061da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
23071da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23081da177e4SLinus Torvalds 			goto out_resource;
23091da177e4SLinus Torvalds 		WRITE32(1);
23101da177e4SLinus Torvalds 	}
23111da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
23121da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23131da177e4SLinus Torvalds 			goto out_resource;
23142930d381SJ. Bruce Fields 		WRITE32(0);
23151da177e4SLinus Torvalds 	}
23161da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
23171da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23181da177e4SLinus Torvalds 			goto out_resource;
23191da177e4SLinus Torvalds 		WRITE32(1);
23201da177e4SLinus Torvalds 	}
23211da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
23221da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23231da177e4SLinus Torvalds 			goto out_resource;
23241da177e4SLinus Torvalds 		WRITE32(1);
23251da177e4SLinus Torvalds 	}
23261da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
23271da177e4SLinus Torvalds 		buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4;
23281da177e4SLinus Torvalds 		if (buflen < 0)
23291da177e4SLinus Torvalds 			goto out_resource;
23301da177e4SLinus Torvalds 		WRITE32(fhp->fh_handle.fh_size);
23311da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
23321da177e4SLinus Torvalds 	}
23331da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
23341da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23351da177e4SLinus Torvalds 			goto out_resource;
233640ee5dc6SPeter Staubach 		WRITE64(stat.ino);
23371da177e4SLinus Torvalds 	}
23381da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
23391da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23401da177e4SLinus Torvalds 			goto out_resource;
23411da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
23421da177e4SLinus Torvalds 	}
23431da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
23441da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23451da177e4SLinus Torvalds 			goto out_resource;
23461da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
23471da177e4SLinus Torvalds 	}
23481da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
23491da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23501da177e4SLinus Torvalds 			goto out_resource;
23511da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_files);
23521da177e4SLinus Torvalds 	}
235381c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
235481c3f413SJ.Bruce Fields 		status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
235581c3f413SJ.Bruce Fields 		if (status == nfserr_resource)
235681c3f413SJ.Bruce Fields 			goto out_resource;
235781c3f413SJ.Bruce Fields 		if (status)
235881c3f413SJ.Bruce Fields 			goto out;
235981c3f413SJ.Bruce Fields 	}
23601da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
23611da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23621da177e4SLinus Torvalds 			goto out_resource;
23631da177e4SLinus Torvalds 		WRITE32(1);
23641da177e4SLinus Torvalds 	}
23651da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
23661da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23671da177e4SLinus Torvalds 			goto out_resource;
23681da177e4SLinus Torvalds 		WRITE64(~(u64)0);
23691da177e4SLinus Torvalds 	}
23701da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
23711da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23721da177e4SLinus Torvalds 			goto out_resource;
23731da177e4SLinus Torvalds 		WRITE32(255);
23741da177e4SLinus Torvalds 	}
23751da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
23761da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23771da177e4SLinus Torvalds 			goto out_resource;
2378a16e92edSJ. Bruce Fields 		WRITE32(statfs.f_namelen);
23791da177e4SLinus Torvalds 	}
23801da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
23811da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23821da177e4SLinus Torvalds 			goto out_resource;
23837adae489SGreg Banks 		WRITE64((u64) svc_max_payload(rqstp));
23841da177e4SLinus Torvalds 	}
23851da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
23861da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
23871da177e4SLinus Torvalds 			goto out_resource;
23887adae489SGreg Banks 		WRITE64((u64) svc_max_payload(rqstp));
23891da177e4SLinus Torvalds 	}
23901da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
23911da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23921da177e4SLinus Torvalds 			goto out_resource;
23931da177e4SLinus Torvalds 		WRITE32(stat.mode & S_IALLUGO);
23941da177e4SLinus Torvalds 	}
23951da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
23961da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
23971da177e4SLinus Torvalds 			goto out_resource;
23981da177e4SLinus Torvalds 		WRITE32(1);
23991da177e4SLinus Torvalds 	}
24001da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
24011da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
24021da177e4SLinus Torvalds 			goto out_resource;
24031da177e4SLinus Torvalds 		WRITE32(stat.nlink);
24041da177e4SLinus Torvalds 	}
24051da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
24061da177e4SLinus Torvalds 		status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
24071da177e4SLinus Torvalds 		if (status == nfserr_resource)
24081da177e4SLinus Torvalds 			goto out_resource;
24091da177e4SLinus Torvalds 		if (status)
24101da177e4SLinus Torvalds 			goto out;
24111da177e4SLinus Torvalds 	}
24121da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
24131da177e4SLinus Torvalds 		status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
24141da177e4SLinus Torvalds 		if (status == nfserr_resource)
24151da177e4SLinus Torvalds 			goto out_resource;
24161da177e4SLinus Torvalds 		if (status)
24171da177e4SLinus Torvalds 			goto out;
24181da177e4SLinus Torvalds 	}
24191da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
24201da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
24211da177e4SLinus Torvalds 			goto out_resource;
24221da177e4SLinus Torvalds 		WRITE32((u32) MAJOR(stat.rdev));
24231da177e4SLinus Torvalds 		WRITE32((u32) MINOR(stat.rdev));
24241da177e4SLinus Torvalds 	}
24251da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
24261da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
24271da177e4SLinus Torvalds 			goto out_resource;
24281da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
24291da177e4SLinus Torvalds 		WRITE64(dummy64);
24301da177e4SLinus Torvalds 	}
24311da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
24321da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
24331da177e4SLinus Torvalds 			goto out_resource;
24341da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
24351da177e4SLinus Torvalds 		WRITE64(dummy64);
24361da177e4SLinus Torvalds 	}
24371da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
24381da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
24391da177e4SLinus Torvalds 			goto out_resource;
24401da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
24411da177e4SLinus Torvalds 		WRITE64(dummy64);
24421da177e4SLinus Torvalds 	}
24431da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
24441da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
24451da177e4SLinus Torvalds 			goto out_resource;
24461da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
24471da177e4SLinus Torvalds 		WRITE64(dummy64);
24481da177e4SLinus Torvalds 	}
24491da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
24501da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
24511da177e4SLinus Torvalds 			goto out_resource;
2452bf8d9097SBryan Schumaker 		WRITE64((s64)stat.atime.tv_sec);
24531da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_nsec);
24541da177e4SLinus Torvalds 	}
24551da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
24561da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
24571da177e4SLinus Torvalds 			goto out_resource;
24581da177e4SLinus Torvalds 		WRITE32(0);
24591da177e4SLinus Torvalds 		WRITE32(1);
24601da177e4SLinus Torvalds 		WRITE32(0);
24611da177e4SLinus Torvalds 	}
24621da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
24631da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
24641da177e4SLinus Torvalds 			goto out_resource;
2465bf8d9097SBryan Schumaker 		WRITE64((s64)stat.ctime.tv_sec);
24661da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_nsec);
24671da177e4SLinus Torvalds 	}
24681da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
24691da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
24701da177e4SLinus Torvalds 			goto out_resource;
2471bf8d9097SBryan Schumaker 		WRITE64((s64)stat.mtime.tv_sec);
24721da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_nsec);
24731da177e4SLinus Torvalds 	}
24741da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
24751da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
24761da177e4SLinus Torvalds                 	goto out_resource;
2477406a7ea9SFrank Filz 		/*
2478406a7ea9SFrank Filz 		 * Get parent's attributes if not ignoring crossmount
2479406a7ea9SFrank Filz 		 * and this is the root of a cross-mounted filesystem.
2480406a7ea9SFrank Filz 		 */
2481406a7ea9SFrank Filz 		if (ignore_crossmnt == 0 &&
2482ae7095a7SJ. Bruce Fields 		    dentry == exp->ex_path.mnt->mnt_root)
2483ae7095a7SJ. Bruce Fields 			get_parent_attributes(exp, &stat);
248440ee5dc6SPeter Staubach 		WRITE64(stat.ino);
24851da177e4SLinus Torvalds 	}
248618032ca0SDavid Quigley 	if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
248718032ca0SDavid Quigley 		status = nfsd4_encode_security_label(rqstp, context,
248818032ca0SDavid Quigley 				contextlen, &p, &buflen);
248918032ca0SDavid Quigley 		if (status)
249018032ca0SDavid Quigley 			goto out;
249118032ca0SDavid Quigley 	}
24928c18f205SBenny Halevy 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
24938c18f205SBenny Halevy 		WRITE32(3);
24948c18f205SBenny Halevy 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
24958c18f205SBenny Halevy 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
24968c18f205SBenny Halevy 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD2);
24978c18f205SBenny Halevy 	}
24987e705706SAndy Adamson 
24991da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
250084822d0bSJ. Bruce Fields 	*buffer = p;
25011da177e4SLinus Torvalds 	status = nfs_ok;
25021da177e4SLinus Torvalds 
25031da177e4SLinus Torvalds out:
2504ba4e55bbSJ. Bruce Fields #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
250518032ca0SDavid Quigley 	if (context)
250618032ca0SDavid Quigley 		security_release_secctx(context, contextlen);
2507ba4e55bbSJ. Bruce Fields #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
250828e05dd8SJ. Bruce Fields 	kfree(acl);
25091da177e4SLinus Torvalds 	if (fhp == &tempfh)
25101da177e4SLinus Torvalds 		fh_put(&tempfh);
25111da177e4SLinus Torvalds 	return status;
25121da177e4SLinus Torvalds out_nfserr:
2513b8dd7b9aSAl Viro 	status = nfserrno(err);
25141da177e4SLinus Torvalds 	goto out;
25151da177e4SLinus Torvalds out_resource:
25161da177e4SLinus Torvalds 	status = nfserr_resource;
25171da177e4SLinus Torvalds 	goto out;
25181da177e4SLinus Torvalds out_serverfault:
25191da177e4SLinus Torvalds 	status = nfserr_serverfault;
25201da177e4SLinus Torvalds 	goto out;
25211da177e4SLinus Torvalds }
25221da177e4SLinus Torvalds 
2523c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
2524c0ce6ec8SJ. Bruce Fields {
2525c0ce6ec8SJ. Bruce Fields 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
2526c0ce6ec8SJ. Bruce Fields 		return 1;
2527c0ce6ec8SJ. Bruce Fields 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
2528c0ce6ec8SJ. Bruce Fields 		return 1;
2529c0ce6ec8SJ. Bruce Fields 	return 0;
2530c0ce6ec8SJ. Bruce Fields }
2531c0ce6ec8SJ. Bruce Fields 
2532b37ad28bSAl Viro static __be32
25331da177e4SLinus Torvalds nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
253484822d0bSJ. Bruce Fields 		const char *name, int namlen, __be32 **p, int buflen)
25351da177e4SLinus Torvalds {
25361da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
25371da177e4SLinus Torvalds 	struct dentry *dentry;
2538b37ad28bSAl Viro 	__be32 nfserr;
2539406a7ea9SFrank Filz 	int ignore_crossmnt = 0;
25401da177e4SLinus Torvalds 
25411da177e4SLinus Torvalds 	dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
25421da177e4SLinus Torvalds 	if (IS_ERR(dentry))
25431da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
2544b2c0cea6SJ. Bruce Fields 	if (!dentry->d_inode) {
2545b2c0cea6SJ. Bruce Fields 		/*
2546b2c0cea6SJ. Bruce Fields 		 * nfsd_buffered_readdir drops the i_mutex between
2547b2c0cea6SJ. Bruce Fields 		 * readdir and calling this callback, leaving a window
2548b2c0cea6SJ. Bruce Fields 		 * where this directory entry could have gone away.
2549b2c0cea6SJ. Bruce Fields 		 */
2550b2c0cea6SJ. Bruce Fields 		dput(dentry);
2551b2c0cea6SJ. Bruce Fields 		return nfserr_noent;
2552b2c0cea6SJ. Bruce Fields 	}
25531da177e4SLinus Torvalds 
25541da177e4SLinus Torvalds 	exp_get(exp);
2555406a7ea9SFrank Filz 	/*
2556406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
2557406a7ea9SFrank Filz 	 * attributes that are only properties of the underlying filesystem
2558406a7ea9SFrank Filz 	 * as opposed to the cross-mounted file system. In such a case,
2559406a7ea9SFrank Filz 	 * we will not follow the cross mount and will fill the attribtutes
2560406a7ea9SFrank Filz 	 * directly from the mountpoint dentry.
2561406a7ea9SFrank Filz 	 */
25623227fa41SJ. Bruce Fields 	if (nfsd_mountpoint(dentry, exp)) {
2563021d3a72SJ.Bruce Fields 		int err;
2564021d3a72SJ.Bruce Fields 
25653227fa41SJ. Bruce Fields 		if (!(exp->ex_flags & NFSEXP_V4ROOT)
25663227fa41SJ. Bruce Fields 				&& !attributes_need_mount(cd->rd_bmval)) {
25673227fa41SJ. Bruce Fields 			ignore_crossmnt = 1;
25683227fa41SJ. Bruce Fields 			goto out_encode;
25693227fa41SJ. Bruce Fields 		}
2570dcb488a3SAndy Adamson 		/*
2571dcb488a3SAndy Adamson 		 * Why the heck aren't we just using nfsd_lookup??
2572dcb488a3SAndy Adamson 		 * Different "."/".." handling?  Something else?
2573dcb488a3SAndy Adamson 		 * At least, add a comment here to explain....
2574dcb488a3SAndy Adamson 		 */
2575021d3a72SJ.Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
2576021d3a72SJ.Bruce Fields 		if (err) {
2577021d3a72SJ.Bruce Fields 			nfserr = nfserrno(err);
25781da177e4SLinus Torvalds 			goto out_put;
25791da177e4SLinus Torvalds 		}
2580dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
2581dcb488a3SAndy Adamson 		if (nfserr)
2582dcb488a3SAndy Adamson 			goto out_put;
25831da177e4SLinus Torvalds 
25841da177e4SLinus Torvalds 	}
25853227fa41SJ. Bruce Fields out_encode:
25861da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
2587406a7ea9SFrank Filz 					cd->rd_rqstp, ignore_crossmnt);
25881da177e4SLinus Torvalds out_put:
25891da177e4SLinus Torvalds 	dput(dentry);
25901da177e4SLinus Torvalds 	exp_put(exp);
25911da177e4SLinus Torvalds 	return nfserr;
25921da177e4SLinus Torvalds }
25931da177e4SLinus Torvalds 
25942ebbc012SAl Viro static __be32 *
2595b37ad28bSAl Viro nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr)
25961da177e4SLinus Torvalds {
25972ebbc012SAl Viro 	__be32 *attrlenp;
25981da177e4SLinus Torvalds 
25991da177e4SLinus Torvalds 	if (buflen < 6)
26001da177e4SLinus Torvalds 		return NULL;
26011da177e4SLinus Torvalds 	*p++ = htonl(2);
26021da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
26031da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
26041da177e4SLinus Torvalds 
26051da177e4SLinus Torvalds 	attrlenp = p++;
26061da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
26071da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
26081da177e4SLinus Torvalds 	return p;
26091da177e4SLinus Torvalds }
26101da177e4SLinus Torvalds 
26111da177e4SLinus Torvalds static int
2612a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2613a0ad13efSNeilBrown 		    loff_t offset, u64 ino, unsigned int d_type)
26141da177e4SLinus Torvalds {
2615a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
26161da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
26171da177e4SLinus Torvalds 	int buflen;
26182ebbc012SAl Viro 	__be32 *p = cd->buffer;
2619b2c0cea6SJ. Bruce Fields 	__be32 *cookiep;
2620b37ad28bSAl Viro 	__be32 nfserr = nfserr_toosmall;
26211da177e4SLinus Torvalds 
26221da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
26231da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
26241da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
26251da177e4SLinus Torvalds 		return 0;
26261da177e4SLinus Torvalds 	}
26271da177e4SLinus Torvalds 
26281da177e4SLinus Torvalds 	if (cd->offset)
26291da177e4SLinus Torvalds 		xdr_encode_hyper(cd->offset, (u64) offset);
26301da177e4SLinus Torvalds 
26311da177e4SLinus Torvalds 	buflen = cd->buflen - 4 - XDR_QUADLEN(namlen);
26321da177e4SLinus Torvalds 	if (buflen < 0)
26331da177e4SLinus Torvalds 		goto fail;
26341da177e4SLinus Torvalds 
26351da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
2636b2c0cea6SJ. Bruce Fields 	cookiep = p;
26371da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
26381da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
26391da177e4SLinus Torvalds 
264084822d0bSJ. Bruce Fields 	nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, &p, buflen);
26411da177e4SLinus Torvalds 	switch (nfserr) {
26421da177e4SLinus Torvalds 	case nfs_ok:
26431da177e4SLinus Torvalds 		break;
26441da177e4SLinus Torvalds 	case nfserr_resource:
26451da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
26461da177e4SLinus Torvalds 		goto fail;
2647b2c0cea6SJ. Bruce Fields 	case nfserr_noent:
2648b2c0cea6SJ. Bruce Fields 		goto skip_entry;
26491da177e4SLinus Torvalds 	default:
26501da177e4SLinus Torvalds 		/*
26511da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
26521da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
26531da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
26541da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
26551da177e4SLinus Torvalds 		 * entire READDIR operation(!)
26561da177e4SLinus Torvalds 		 */
26571da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
26581da177e4SLinus Torvalds 			goto fail;
26591da177e4SLinus Torvalds 		p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
266034081efcSFred Isaman 		if (p == NULL) {
266134081efcSFred Isaman 			nfserr = nfserr_toosmall;
26621da177e4SLinus Torvalds 			goto fail;
26631da177e4SLinus Torvalds 		}
266434081efcSFred Isaman 	}
26651da177e4SLinus Torvalds 	cd->buflen -= (p - cd->buffer);
26661da177e4SLinus Torvalds 	cd->buffer = p;
2667b2c0cea6SJ. Bruce Fields 	cd->offset = cookiep;
2668b2c0cea6SJ. Bruce Fields skip_entry:
26691da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
26701da177e4SLinus Torvalds 	return 0;
26711da177e4SLinus Torvalds fail:
26721da177e4SLinus Torvalds 	cd->common.err = nfserr;
26731da177e4SLinus Torvalds 	return -EINVAL;
26741da177e4SLinus Torvalds }
26751da177e4SLinus Torvalds 
2676e2f282b9SBenny Halevy static void
2677e2f282b9SBenny Halevy nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
2678e2f282b9SBenny Halevy {
2679bc749ca4SJ. Bruce Fields 	__be32 *p;
2680e2f282b9SBenny Halevy 
2681e2f282b9SBenny Halevy 	RESERVE_SPACE(sizeof(stateid_t));
2682e2f282b9SBenny Halevy 	WRITE32(sid->si_generation);
2683e2f282b9SBenny Halevy 	WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
2684e2f282b9SBenny Halevy 	ADJUST_ARGS();
2685e2f282b9SBenny Halevy }
2686e2f282b9SBenny Halevy 
2687695e12f8SBenny Halevy static __be32
2688b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
26891da177e4SLinus Torvalds {
2690bc749ca4SJ. Bruce Fields 	__be32 *p;
26911da177e4SLinus Torvalds 
26921da177e4SLinus Torvalds 	if (!nfserr) {
26931da177e4SLinus Torvalds 		RESERVE_SPACE(8);
26941da177e4SLinus Torvalds 		WRITE32(access->ac_supported);
26951da177e4SLinus Torvalds 		WRITE32(access->ac_resp_access);
26961da177e4SLinus Torvalds 		ADJUST_ARGS();
26971da177e4SLinus Torvalds 	}
2698695e12f8SBenny Halevy 	return nfserr;
26991da177e4SLinus Torvalds }
27001da177e4SLinus Torvalds 
27011d1bc8f2SJ. Bruce Fields static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts)
27021d1bc8f2SJ. Bruce Fields {
27031d1bc8f2SJ. Bruce Fields 	__be32 *p;
27041d1bc8f2SJ. Bruce Fields 
27051d1bc8f2SJ. Bruce Fields 	if (!nfserr) {
27061d1bc8f2SJ. Bruce Fields 		RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 8);
27071d1bc8f2SJ. Bruce Fields 		WRITEMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
27081d1bc8f2SJ. Bruce Fields 		WRITE32(bcts->dir);
27096e67b5d1SJ. Bruce Fields 		/* Sorry, we do not yet support RDMA over 4.1: */
27101d1bc8f2SJ. Bruce Fields 		WRITE32(0);
27111d1bc8f2SJ. Bruce Fields 		ADJUST_ARGS();
27121d1bc8f2SJ. Bruce Fields 	}
27131d1bc8f2SJ. Bruce Fields 	return nfserr;
27141d1bc8f2SJ. Bruce Fields }
27151d1bc8f2SJ. Bruce Fields 
2716695e12f8SBenny Halevy static __be32
2717b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
27181da177e4SLinus Torvalds {
2719e2f282b9SBenny Halevy 	if (!nfserr)
2720e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &close->cl_stateid);
2721e2f282b9SBenny Halevy 
2722695e12f8SBenny Halevy 	return nfserr;
27231da177e4SLinus Torvalds }
27241da177e4SLinus Torvalds 
27251da177e4SLinus Torvalds 
2726695e12f8SBenny Halevy static __be32
2727b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
27281da177e4SLinus Torvalds {
2729bc749ca4SJ. Bruce Fields 	__be32 *p;
27301da177e4SLinus Torvalds 
27311da177e4SLinus Torvalds 	if (!nfserr) {
2732ab4684d1SChuck Lever 		RESERVE_SPACE(NFS4_VERIFIER_SIZE);
2733ab4684d1SChuck Lever 		WRITEMEM(commit->co_verf.data, NFS4_VERIFIER_SIZE);
27341da177e4SLinus Torvalds 		ADJUST_ARGS();
27351da177e4SLinus Torvalds 	}
2736695e12f8SBenny Halevy 	return nfserr;
27371da177e4SLinus Torvalds }
27381da177e4SLinus Torvalds 
2739695e12f8SBenny Halevy static __be32
2740b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
27411da177e4SLinus Torvalds {
2742bc749ca4SJ. Bruce Fields 	__be32 *p;
27431da177e4SLinus Torvalds 
27441da177e4SLinus Torvalds 	if (!nfserr) {
27451da177e4SLinus Torvalds 		RESERVE_SPACE(32);
2746c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &create->cr_cinfo);
27471da177e4SLinus Torvalds 		WRITE32(2);
27481da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[0]);
27491da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[1]);
27501da177e4SLinus Torvalds 		ADJUST_ARGS();
27511da177e4SLinus Torvalds 	}
2752695e12f8SBenny Halevy 	return nfserr;
27531da177e4SLinus Torvalds }
27541da177e4SLinus Torvalds 
2755b37ad28bSAl Viro static __be32
2756b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
27571da177e4SLinus Torvalds {
27581da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
27591da177e4SLinus Torvalds 	int buflen;
27601da177e4SLinus Torvalds 
27611da177e4SLinus Torvalds 	if (nfserr)
27621da177e4SLinus Torvalds 		return nfserr;
27631da177e4SLinus Torvalds 
27641da177e4SLinus Torvalds 	buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2);
27651da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
276684822d0bSJ. Bruce Fields 				    &resp->p, buflen, getattr->ga_bmval,
2767406a7ea9SFrank Filz 				    resp->rqstp, 0);
27681da177e4SLinus Torvalds 	return nfserr;
27691da177e4SLinus Torvalds }
27701da177e4SLinus Torvalds 
2771695e12f8SBenny Halevy static __be32
2772695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
27731da177e4SLinus Torvalds {
2774695e12f8SBenny Halevy 	struct svc_fh *fhp = *fhpp;
27751da177e4SLinus Torvalds 	unsigned int len;
2776bc749ca4SJ. Bruce Fields 	__be32 *p;
27771da177e4SLinus Torvalds 
27781da177e4SLinus Torvalds 	if (!nfserr) {
27791da177e4SLinus Torvalds 		len = fhp->fh_handle.fh_size;
27801da177e4SLinus Torvalds 		RESERVE_SPACE(len + 4);
27811da177e4SLinus Torvalds 		WRITE32(len);
27821da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, len);
27831da177e4SLinus Torvalds 		ADJUST_ARGS();
27841da177e4SLinus Torvalds 	}
2785695e12f8SBenny Halevy 	return nfserr;
27861da177e4SLinus Torvalds }
27871da177e4SLinus Torvalds 
27881da177e4SLinus Torvalds /*
27891da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
27901da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
27911da177e4SLinus Torvalds */
27921da177e4SLinus Torvalds static void
27931da177e4SLinus Torvalds nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
27941da177e4SLinus Torvalds {
27957c13f344SJ. Bruce Fields 	struct xdr_netobj *conf = &ld->ld_owner;
2796bc749ca4SJ. Bruce Fields 	__be32 *p;
27971da177e4SLinus Torvalds 
27987c13f344SJ. Bruce Fields 	RESERVE_SPACE(32 + XDR_LEN(conf->len));
27991da177e4SLinus Torvalds 	WRITE64(ld->ld_start);
28001da177e4SLinus Torvalds 	WRITE64(ld->ld_length);
28011da177e4SLinus Torvalds 	WRITE32(ld->ld_type);
28027c13f344SJ. Bruce Fields 	if (conf->len) {
28031da177e4SLinus Torvalds 		WRITEMEM(&ld->ld_clientid, 8);
28047c13f344SJ. Bruce Fields 		WRITE32(conf->len);
28057c13f344SJ. Bruce Fields 		WRITEMEM(conf->data, conf->len);
28067c13f344SJ. Bruce Fields 		kfree(conf->data);
28071da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
28081da177e4SLinus Torvalds 		WRITE64((u64)0); /* clientid */
28091da177e4SLinus Torvalds 		WRITE32(0); /* length of owner name */
28101da177e4SLinus Torvalds 	}
28111da177e4SLinus Torvalds 	ADJUST_ARGS();
28121da177e4SLinus Torvalds }
28131da177e4SLinus Torvalds 
2814695e12f8SBenny Halevy static __be32
2815b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
28161da177e4SLinus Torvalds {
2817e2f282b9SBenny Halevy 	if (!nfserr)
2818e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &lock->lk_resp_stateid);
2819e2f282b9SBenny Halevy 	else if (nfserr == nfserr_denied)
28201da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lock->lk_denied);
28211da177e4SLinus Torvalds 
2822695e12f8SBenny Halevy 	return nfserr;
28231da177e4SLinus Torvalds }
28241da177e4SLinus Torvalds 
2825695e12f8SBenny Halevy static __be32
2826b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
28271da177e4SLinus Torvalds {
28281da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
28291da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lockt->lt_denied);
2830695e12f8SBenny Halevy 	return nfserr;
28311da177e4SLinus Torvalds }
28321da177e4SLinus Torvalds 
2833695e12f8SBenny Halevy static __be32
2834b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
28351da177e4SLinus Torvalds {
2836e2f282b9SBenny Halevy 	if (!nfserr)
2837e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &locku->lu_stateid);
28381da177e4SLinus Torvalds 
2839695e12f8SBenny Halevy 	return nfserr;
28401da177e4SLinus Torvalds }
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds 
2843695e12f8SBenny Halevy static __be32
2844b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
28451da177e4SLinus Torvalds {
2846bc749ca4SJ. Bruce Fields 	__be32 *p;
28471da177e4SLinus Torvalds 
28481da177e4SLinus Torvalds 	if (!nfserr) {
28491da177e4SLinus Torvalds 		RESERVE_SPACE(20);
2850c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &link->li_cinfo);
28511da177e4SLinus Torvalds 		ADJUST_ARGS();
28521da177e4SLinus Torvalds 	}
2853695e12f8SBenny Halevy 	return nfserr;
28541da177e4SLinus Torvalds }
28551da177e4SLinus Torvalds 
28561da177e4SLinus Torvalds 
2857695e12f8SBenny Halevy static __be32
2858b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
28591da177e4SLinus Torvalds {
2860bc749ca4SJ. Bruce Fields 	__be32 *p;
28611da177e4SLinus Torvalds 
28621da177e4SLinus Torvalds 	if (nfserr)
28631da177e4SLinus Torvalds 		goto out;
28641da177e4SLinus Torvalds 
2865e2f282b9SBenny Halevy 	nfsd4_encode_stateid(resp, &open->op_stateid);
2866e2f282b9SBenny Halevy 	RESERVE_SPACE(40);
2867c654b8a9SJ. Bruce Fields 	write_cinfo(&p, &open->op_cinfo);
28681da177e4SLinus Torvalds 	WRITE32(open->op_rflags);
28691da177e4SLinus Torvalds 	WRITE32(2);
28701da177e4SLinus Torvalds 	WRITE32(open->op_bmval[0]);
28711da177e4SLinus Torvalds 	WRITE32(open->op_bmval[1]);
28721da177e4SLinus Torvalds 	WRITE32(open->op_delegate_type);
28731da177e4SLinus Torvalds 	ADJUST_ARGS();
28741da177e4SLinus Torvalds 
28751da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
28761da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
28771da177e4SLinus Torvalds 		break;
28781da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
2879e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
2880e2f282b9SBenny Halevy 		RESERVE_SPACE(20);
28817b190fecSNeilBrown 		WRITE32(open->op_recall);
28821da177e4SLinus Torvalds 
28831da177e4SLinus Torvalds 		/*
28841da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
28851da177e4SLinus Torvalds 		 */
28861da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
28871da177e4SLinus Torvalds 		WRITE32(0);
28881da177e4SLinus Torvalds 		WRITE32(0);
28891da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
28901da177e4SLinus Torvalds 		ADJUST_ARGS();
28911da177e4SLinus Torvalds 		break;
28921da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
2893e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
2894e2f282b9SBenny Halevy 		RESERVE_SPACE(32);
28951da177e4SLinus Torvalds 		WRITE32(0);
28961da177e4SLinus Torvalds 
28971da177e4SLinus Torvalds 		/*
28981da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
28991da177e4SLinus Torvalds 		 */
29001da177e4SLinus Torvalds 		WRITE32(NFS4_LIMIT_SIZE);
29011da177e4SLinus Torvalds 		WRITE32(~(u32)0);
29021da177e4SLinus Torvalds 		WRITE32(~(u32)0);
29031da177e4SLinus Torvalds 
29041da177e4SLinus Torvalds 		/*
29051da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
29061da177e4SLinus Torvalds 		 */
29071da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
29081da177e4SLinus Torvalds 		WRITE32(0);
29091da177e4SLinus Torvalds 		WRITE32(0);
29101da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
29111da177e4SLinus Torvalds 		ADJUST_ARGS();
29121da177e4SLinus Torvalds 		break;
2913d24433cdSBenny Halevy 	case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
2914d24433cdSBenny Halevy 		switch (open->op_why_no_deleg) {
2915d24433cdSBenny Halevy 		case WND4_CONTENTION:
2916d24433cdSBenny Halevy 		case WND4_RESOURCE:
2917d24433cdSBenny Halevy 			RESERVE_SPACE(8);
2918d24433cdSBenny Halevy 			WRITE32(open->op_why_no_deleg);
2919d24433cdSBenny Halevy 			WRITE32(0);	/* deleg signaling not supported yet */
2920d24433cdSBenny Halevy 			break;
2921d24433cdSBenny Halevy 		default:
2922d24433cdSBenny Halevy 			RESERVE_SPACE(4);
2923d24433cdSBenny Halevy 			WRITE32(open->op_why_no_deleg);
2924d24433cdSBenny Halevy 		}
2925d24433cdSBenny Halevy 		ADJUST_ARGS();
2926d24433cdSBenny Halevy 		break;
29271da177e4SLinus Torvalds 	default:
29281da177e4SLinus Torvalds 		BUG();
29291da177e4SLinus Torvalds 	}
29301da177e4SLinus Torvalds 	/* XXX save filehandle here */
29311da177e4SLinus Torvalds out:
2932695e12f8SBenny Halevy 	return nfserr;
29331da177e4SLinus Torvalds }
29341da177e4SLinus Torvalds 
2935695e12f8SBenny Halevy static __be32
2936b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
29371da177e4SLinus Torvalds {
2938e2f282b9SBenny Halevy 	if (!nfserr)
2939e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
29401da177e4SLinus Torvalds 
2941695e12f8SBenny Halevy 	return nfserr;
29421da177e4SLinus Torvalds }
29431da177e4SLinus Torvalds 
2944695e12f8SBenny Halevy static __be32
2945b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
29461da177e4SLinus Torvalds {
2947e2f282b9SBenny Halevy 	if (!nfserr)
2948e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &od->od_stateid);
29491da177e4SLinus Torvalds 
2950695e12f8SBenny Halevy 	return nfserr;
29511da177e4SLinus Torvalds }
29521da177e4SLinus Torvalds 
2953b37ad28bSAl Viro static __be32
2954b37ad28bSAl Viro nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
295544524359SNeilBrown 		  struct nfsd4_read *read)
29561da177e4SLinus Torvalds {
29571da177e4SLinus Torvalds 	u32 eof;
2958afc59400SJ. Bruce Fields 	int v;
2959afc59400SJ. Bruce Fields 	struct page *page;
29601da177e4SLinus Torvalds 	unsigned long maxcount;
29611da177e4SLinus Torvalds 	long len;
2962bc749ca4SJ. Bruce Fields 	__be32 *p;
29631da177e4SLinus Torvalds 
29641da177e4SLinus Torvalds 	if (nfserr)
29651da177e4SLinus Torvalds 		return nfserr;
29661da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
29671da177e4SLinus Torvalds 		return nfserr_resource;
29681da177e4SLinus Torvalds 
29691da177e4SLinus Torvalds 	RESERVE_SPACE(8); /* eof flag and byte count */
29701da177e4SLinus Torvalds 
29717adae489SGreg Banks 	maxcount = svc_max_payload(resp->rqstp);
29721da177e4SLinus Torvalds 	if (maxcount > read->rd_length)
29731da177e4SLinus Torvalds 		maxcount = read->rd_length;
29741da177e4SLinus Torvalds 
29751da177e4SLinus Torvalds 	len = maxcount;
29761da177e4SLinus Torvalds 	v = 0;
29771da177e4SLinus Torvalds 	while (len > 0) {
2978afc59400SJ. Bruce Fields 		page = *(resp->rqstp->rq_next_page);
2979afc59400SJ. Bruce Fields 		if (!page) { /* ran out of pages */
2980d5f50b0cSJ. Bruce Fields 			maxcount -= len;
2981d5f50b0cSJ. Bruce Fields 			break;
2982d5f50b0cSJ. Bruce Fields 		}
2983afc59400SJ. Bruce Fields 		resp->rqstp->rq_vec[v].iov_base = page_address(page);
29843cc03b16SNeilBrown 		resp->rqstp->rq_vec[v].iov_len =
298544524359SNeilBrown 			len < PAGE_SIZE ? len : PAGE_SIZE;
2986afc59400SJ. Bruce Fields 		resp->rqstp->rq_next_page++;
29871da177e4SLinus Torvalds 		v++;
29881da177e4SLinus Torvalds 		len -= PAGE_SIZE;
29891da177e4SLinus Torvalds 	}
29901da177e4SLinus Torvalds 	read->rd_vlen = v;
29911da177e4SLinus Torvalds 
2992039a87caSJ. Bruce Fields 	nfserr = nfsd_read_file(read->rd_rqstp, read->rd_fhp, read->rd_filp,
29933cc03b16SNeilBrown 			read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
29941da177e4SLinus Torvalds 			&maxcount);
29951da177e4SLinus Torvalds 
29961da177e4SLinus Torvalds 	if (nfserr)
29971da177e4SLinus Torvalds 		return nfserr;
299844524359SNeilBrown 	eof = (read->rd_offset + maxcount >=
299944524359SNeilBrown 	       read->rd_fhp->fh_dentry->d_inode->i_size);
30001da177e4SLinus Torvalds 
30011da177e4SLinus Torvalds 	WRITE32(eof);
30021da177e4SLinus Torvalds 	WRITE32(maxcount);
30031da177e4SLinus Torvalds 	ADJUST_ARGS();
30046ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
30056ed6deccSNeilBrown 					- (char*)resp->xbuf->head[0].iov_base;
30061da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
30071da177e4SLinus Torvalds 
30086ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
30096ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
30101da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
30111da177e4SLinus Torvalds 	if (maxcount&3) {
30126ed6deccSNeilBrown 		RESERVE_SPACE(4);
30136ed6deccSNeilBrown 		WRITE32(0);
30141da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
30151da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
30166ed6deccSNeilBrown 		ADJUST_ARGS();
30171da177e4SLinus Torvalds 	}
30181da177e4SLinus Torvalds 	return 0;
30191da177e4SLinus Torvalds }
30201da177e4SLinus Torvalds 
3021b37ad28bSAl Viro static __be32
3022b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
30231da177e4SLinus Torvalds {
30241da177e4SLinus Torvalds 	int maxcount;
30251da177e4SLinus Torvalds 	char *page;
3026bc749ca4SJ. Bruce Fields 	__be32 *p;
30271da177e4SLinus Torvalds 
30281da177e4SLinus Torvalds 	if (nfserr)
30291da177e4SLinus Torvalds 		return nfserr;
30301da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
30311da177e4SLinus Torvalds 		return nfserr_resource;
3032afc59400SJ. Bruce Fields 	if (!*resp->rqstp->rq_next_page)
3033d5f50b0cSJ. Bruce Fields 		return nfserr_resource;
30341da177e4SLinus Torvalds 
3035afc59400SJ. Bruce Fields 	page = page_address(*(resp->rqstp->rq_next_page++));
30361da177e4SLinus Torvalds 
30371da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
30381da177e4SLinus Torvalds 	RESERVE_SPACE(4);
30391da177e4SLinus Torvalds 
30401da177e4SLinus Torvalds 	/*
30411da177e4SLinus Torvalds 	 * XXX: By default, the ->readlink() VFS op will truncate symlinks
30421da177e4SLinus Torvalds 	 * if they would overflow the buffer.  Is this kosher in NFSv4?  If
30431da177e4SLinus Torvalds 	 * not, one easy fix is: if ->readlink() precisely fills the buffer,
30441da177e4SLinus Torvalds 	 * assume that truncation occurred, and return NFS4ERR_RESOURCE.
30451da177e4SLinus Torvalds 	 */
30461da177e4SLinus Torvalds 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
30471da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
30481da177e4SLinus Torvalds 		return nfserr_inval;
30491da177e4SLinus Torvalds 	if (nfserr)
30501da177e4SLinus Torvalds 		return nfserr;
30511da177e4SLinus Torvalds 
30521da177e4SLinus Torvalds 	WRITE32(maxcount);
30531da177e4SLinus Torvalds 	ADJUST_ARGS();
30546ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
30556ed6deccSNeilBrown 				- (char*)resp->xbuf->head[0].iov_base;
30561da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
30576ed6deccSNeilBrown 
30586ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
30596ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
30606ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_len = 0;
30611da177e4SLinus Torvalds 	if (maxcount&3) {
30626ed6deccSNeilBrown 		RESERVE_SPACE(4);
30636ed6deccSNeilBrown 		WRITE32(0);
30641da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
30651da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
30666ed6deccSNeilBrown 		ADJUST_ARGS();
30671da177e4SLinus Torvalds 	}
30681da177e4SLinus Torvalds 	return 0;
30691da177e4SLinus Torvalds }
30701da177e4SLinus Torvalds 
3071b37ad28bSAl Viro static __be32
3072b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
30731da177e4SLinus Torvalds {
30741da177e4SLinus Torvalds 	int maxcount;
30751da177e4SLinus Torvalds 	loff_t offset;
30762ebbc012SAl Viro 	__be32 *page, *savep, *tailbase;
3077bc749ca4SJ. Bruce Fields 	__be32 *p;
30781da177e4SLinus Torvalds 
30791da177e4SLinus Torvalds 	if (nfserr)
30801da177e4SLinus Torvalds 		return nfserr;
30811da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
30821da177e4SLinus Torvalds 		return nfserr_resource;
3083afc59400SJ. Bruce Fields 	if (!*resp->rqstp->rq_next_page)
3084d5f50b0cSJ. Bruce Fields 		return nfserr_resource;
30851da177e4SLinus Torvalds 
3086ab4684d1SChuck Lever 	RESERVE_SPACE(NFS4_VERIFIER_SIZE);
30871da177e4SLinus Torvalds 	savep = p;
30881da177e4SLinus Torvalds 
30891da177e4SLinus Torvalds 	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
30901da177e4SLinus Torvalds 	WRITE32(0);
30911da177e4SLinus Torvalds 	WRITE32(0);
30921da177e4SLinus Torvalds 	ADJUST_ARGS();
30931da177e4SLinus Torvalds 	resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
3094bb6e8a9fSNeilBrown 	tailbase = p;
30951da177e4SLinus Torvalds 
30961da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
30971da177e4SLinus Torvalds 	if (maxcount > readdir->rd_maxcount)
30981da177e4SLinus Torvalds 		maxcount = readdir->rd_maxcount;
30991da177e4SLinus Torvalds 
31001da177e4SLinus Torvalds 	/*
31011da177e4SLinus Torvalds 	 * Convert from bytes to words, account for the two words already
31021da177e4SLinus Torvalds 	 * written, make sure to leave two words at the end for the next
31031da177e4SLinus Torvalds 	 * pointer and eof field.
31041da177e4SLinus Torvalds 	 */
31051da177e4SLinus Torvalds 	maxcount = (maxcount >> 2) - 4;
31061da177e4SLinus Torvalds 	if (maxcount < 0) {
31071da177e4SLinus Torvalds 		nfserr =  nfserr_toosmall;
31081da177e4SLinus Torvalds 		goto err_no_verf;
31091da177e4SLinus Torvalds 	}
31101da177e4SLinus Torvalds 
3111afc59400SJ. Bruce Fields 	page = page_address(*(resp->rqstp->rq_next_page++));
31121da177e4SLinus Torvalds 	readdir->common.err = 0;
31131da177e4SLinus Torvalds 	readdir->buflen = maxcount;
31141da177e4SLinus Torvalds 	readdir->buffer = page;
31151da177e4SLinus Torvalds 	readdir->offset = NULL;
31161da177e4SLinus Torvalds 
31171da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
31181da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
31191da177e4SLinus Torvalds 			      &offset,
31201da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
31211da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
31221da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
31231da177e4SLinus Torvalds 	    readdir->buffer == page)
31241da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
31251da177e4SLinus Torvalds 	if (nfserr)
31261da177e4SLinus Torvalds 		goto err_no_verf;
31271da177e4SLinus Torvalds 
31281da177e4SLinus Torvalds 	if (readdir->offset)
31291da177e4SLinus Torvalds 		xdr_encode_hyper(readdir->offset, offset);
31301da177e4SLinus Torvalds 
31311da177e4SLinus Torvalds 	p = readdir->buffer;
31321da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
31331da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
3134afc59400SJ. Bruce Fields 	resp->xbuf->page_len = ((char*)p) -
3135afc59400SJ. Bruce Fields 		(char*)page_address(*(resp->rqstp->rq_next_page-1));
31361da177e4SLinus Torvalds 
3137bb6e8a9fSNeilBrown 	/* Use rest of head for padding and remaining ops: */
3138bb6e8a9fSNeilBrown 	resp->xbuf->tail[0].iov_base = tailbase;
31391da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
31401da177e4SLinus Torvalds 	resp->p = resp->xbuf->tail[0].iov_base;
3141bb6e8a9fSNeilBrown 	resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
31421da177e4SLinus Torvalds 
31431da177e4SLinus Torvalds 	return 0;
31441da177e4SLinus Torvalds err_no_verf:
31451da177e4SLinus Torvalds 	p = savep;
31461da177e4SLinus Torvalds 	ADJUST_ARGS();
31471da177e4SLinus Torvalds 	return nfserr;
31481da177e4SLinus Torvalds }
31491da177e4SLinus Torvalds 
3150695e12f8SBenny Halevy static __be32
3151b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
31521da177e4SLinus Torvalds {
3153bc749ca4SJ. Bruce Fields 	__be32 *p;
31541da177e4SLinus Torvalds 
31551da177e4SLinus Torvalds 	if (!nfserr) {
31561da177e4SLinus Torvalds 		RESERVE_SPACE(20);
3157c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &remove->rm_cinfo);
31581da177e4SLinus Torvalds 		ADJUST_ARGS();
31591da177e4SLinus Torvalds 	}
3160695e12f8SBenny Halevy 	return nfserr;
31611da177e4SLinus Torvalds }
31621da177e4SLinus Torvalds 
3163695e12f8SBenny Halevy static __be32
3164b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
31651da177e4SLinus Torvalds {
3166bc749ca4SJ. Bruce Fields 	__be32 *p;
31671da177e4SLinus Torvalds 
31681da177e4SLinus Torvalds 	if (!nfserr) {
31691da177e4SLinus Torvalds 		RESERVE_SPACE(40);
3170c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &rename->rn_sinfo);
3171c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &rename->rn_tinfo);
31721da177e4SLinus Torvalds 		ADJUST_ARGS();
31731da177e4SLinus Torvalds 	}
3174695e12f8SBenny Halevy 	return nfserr;
31751da177e4SLinus Torvalds }
31761da177e4SLinus Torvalds 
3177695e12f8SBenny Halevy static __be32
317822b6dee8SMi Jinlong nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
317922b6dee8SMi Jinlong 			 __be32 nfserr, struct svc_export *exp)
3180dcb488a3SAndy Adamson {
3181676e4ebdSChuck Lever 	u32 i, nflavs, supported;
31824796f457SJ. Bruce Fields 	struct exp_flavor_info *flavs;
31834796f457SJ. Bruce Fields 	struct exp_flavor_info def_flavs[2];
3184676e4ebdSChuck Lever 	__be32 *p, *flavorsp;
3185676e4ebdSChuck Lever 	static bool report = true;
3186dcb488a3SAndy Adamson 
3187dcb488a3SAndy Adamson 	if (nfserr)
3188dcb488a3SAndy Adamson 		goto out;
31894796f457SJ. Bruce Fields 	if (exp->ex_nflavors) {
31904796f457SJ. Bruce Fields 		flavs = exp->ex_flavors;
31914796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
31924796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
31934796f457SJ. Bruce Fields 		flavs = def_flavs;
31944796f457SJ. Bruce Fields 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
31954796f457SJ. Bruce Fields 			nflavs = 2;
31964796f457SJ. Bruce Fields 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
31974796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
31984796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
31994796f457SJ. Bruce Fields 			nflavs = 1;
32004796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
32014796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
32024796f457SJ. Bruce Fields 		} else {
32034796f457SJ. Bruce Fields 			nflavs = 1;
32044796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
32054796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
32064796f457SJ. Bruce Fields 		}
32074796f457SJ. Bruce Fields 	}
32084796f457SJ. Bruce Fields 
3209676e4ebdSChuck Lever 	supported = 0;
3210dcb488a3SAndy Adamson 	RESERVE_SPACE(4);
3211676e4ebdSChuck Lever 	flavorsp = p++;		/* to be backfilled later */
3212dcb488a3SAndy Adamson 	ADJUST_ARGS();
3213676e4ebdSChuck Lever 
32144796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
3215676e4ebdSChuck Lever 		rpc_authflavor_t pf = flavs[i].pseudoflavor;
3216a77c806fSChuck Lever 		struct rpcsec_gss_info info;
3217dcb488a3SAndy Adamson 
3218676e4ebdSChuck Lever 		if (rpcauth_get_gssinfo(pf, &info) == 0) {
3219676e4ebdSChuck Lever 			supported++;
3220ed9411a0SChuck Lever 			RESERVE_SPACE(4 + 4 + info.oid.len + 4 + 4);
3221dcb488a3SAndy Adamson 			WRITE32(RPC_AUTH_GSS);
3222a77c806fSChuck Lever 			WRITE32(info.oid.len);
3223a77c806fSChuck Lever 			WRITEMEM(info.oid.data, info.oid.len);
3224a77c806fSChuck Lever 			WRITE32(info.qop);
3225a77c806fSChuck Lever 			WRITE32(info.service);
3226dcb488a3SAndy Adamson 			ADJUST_ARGS();
3227676e4ebdSChuck Lever 		} else if (pf < RPC_AUTH_MAXFLAVOR) {
3228676e4ebdSChuck Lever 			supported++;
3229dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
3230676e4ebdSChuck Lever 			WRITE32(pf);
3231dcb488a3SAndy Adamson 			ADJUST_ARGS();
3232676e4ebdSChuck Lever 		} else {
3233676e4ebdSChuck Lever 			if (report)
3234676e4ebdSChuck Lever 				pr_warn("NFS: SECINFO: security flavor %u "
3235676e4ebdSChuck Lever 					"is not supported\n", pf);
3236dcb488a3SAndy Adamson 		}
3237dcb488a3SAndy Adamson 	}
3238a77c806fSChuck Lever 
3239676e4ebdSChuck Lever 	if (nflavs != supported)
3240676e4ebdSChuck Lever 		report = false;
3241676e4ebdSChuck Lever 	*flavorsp = htonl(supported);
3242676e4ebdSChuck Lever 
3243dcb488a3SAndy Adamson out:
3244dcb488a3SAndy Adamson 	if (exp)
3245dcb488a3SAndy Adamson 		exp_put(exp);
3246695e12f8SBenny Halevy 	return nfserr;
3247dcb488a3SAndy Adamson }
3248dcb488a3SAndy Adamson 
324922b6dee8SMi Jinlong static __be32
325022b6dee8SMi Jinlong nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
325122b6dee8SMi Jinlong 		     struct nfsd4_secinfo *secinfo)
325222b6dee8SMi Jinlong {
325322b6dee8SMi Jinlong 	return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->si_exp);
325422b6dee8SMi Jinlong }
325522b6dee8SMi Jinlong 
325622b6dee8SMi Jinlong static __be32
325722b6dee8SMi Jinlong nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
325822b6dee8SMi Jinlong 		     struct nfsd4_secinfo_no_name *secinfo)
325922b6dee8SMi Jinlong {
326022b6dee8SMi Jinlong 	return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->sin_exp);
326122b6dee8SMi Jinlong }
326222b6dee8SMi Jinlong 
32631da177e4SLinus Torvalds /*
32641da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
32651da177e4SLinus Torvalds  * regardless of the error status.
32661da177e4SLinus Torvalds  */
3267695e12f8SBenny Halevy static __be32
3268b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
32691da177e4SLinus Torvalds {
3270bc749ca4SJ. Bruce Fields 	__be32 *p;
32711da177e4SLinus Torvalds 
327218032ca0SDavid Quigley 	RESERVE_SPACE(16);
32731da177e4SLinus Torvalds 	if (nfserr) {
327418032ca0SDavid Quigley 		WRITE32(3);
327518032ca0SDavid Quigley 		WRITE32(0);
32761da177e4SLinus Torvalds 		WRITE32(0);
32771da177e4SLinus Torvalds 		WRITE32(0);
32781da177e4SLinus Torvalds 	}
32791da177e4SLinus Torvalds 	else {
328018032ca0SDavid Quigley 		WRITE32(3);
32811da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[0]);
32821da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[1]);
328318032ca0SDavid Quigley 		WRITE32(setattr->sa_bmval[2]);
32841da177e4SLinus Torvalds 	}
32851da177e4SLinus Torvalds 	ADJUST_ARGS();
3286695e12f8SBenny Halevy 	return nfserr;
32871da177e4SLinus Torvalds }
32881da177e4SLinus Torvalds 
3289695e12f8SBenny Halevy static __be32
3290b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
32911da177e4SLinus Torvalds {
3292bc749ca4SJ. Bruce Fields 	__be32 *p;
32931da177e4SLinus Torvalds 
32941da177e4SLinus Torvalds 	if (!nfserr) {
3295ab4684d1SChuck Lever 		RESERVE_SPACE(8 + NFS4_VERIFIER_SIZE);
32961da177e4SLinus Torvalds 		WRITEMEM(&scd->se_clientid, 8);
3297ab4684d1SChuck Lever 		WRITEMEM(&scd->se_confirm, NFS4_VERIFIER_SIZE);
32981da177e4SLinus Torvalds 		ADJUST_ARGS();
32991da177e4SLinus Torvalds 	}
33001da177e4SLinus Torvalds 	else if (nfserr == nfserr_clid_inuse) {
33011da177e4SLinus Torvalds 		RESERVE_SPACE(8);
33021da177e4SLinus Torvalds 		WRITE32(0);
33031da177e4SLinus Torvalds 		WRITE32(0);
33041da177e4SLinus Torvalds 		ADJUST_ARGS();
33051da177e4SLinus Torvalds 	}
3306695e12f8SBenny Halevy 	return nfserr;
33071da177e4SLinus Torvalds }
33081da177e4SLinus Torvalds 
3309695e12f8SBenny Halevy static __be32
3310b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
33111da177e4SLinus Torvalds {
3312bc749ca4SJ. Bruce Fields 	__be32 *p;
33131da177e4SLinus Torvalds 
33141da177e4SLinus Torvalds 	if (!nfserr) {
33151da177e4SLinus Torvalds 		RESERVE_SPACE(16);
33161da177e4SLinus Torvalds 		WRITE32(write->wr_bytes_written);
33171da177e4SLinus Torvalds 		WRITE32(write->wr_how_written);
3318ab4684d1SChuck Lever 		WRITEMEM(write->wr_verifier.data, NFS4_VERIFIER_SIZE);
33191da177e4SLinus Torvalds 		ADJUST_ARGS();
33201da177e4SLinus Torvalds 	}
3321695e12f8SBenny Halevy 	return nfserr;
33221da177e4SLinus Torvalds }
33231da177e4SLinus Torvalds 
3324*57266a6eSJ. Bruce Fields static const u32 nfs4_minimal_spo_must_enforce[2] = {
3325*57266a6eSJ. Bruce Fields 	[1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
3326*57266a6eSJ. Bruce Fields 	      1 << (OP_EXCHANGE_ID - 32) |
3327*57266a6eSJ. Bruce Fields 	      1 << (OP_CREATE_SESSION - 32) |
3328*57266a6eSJ. Bruce Fields 	      1 << (OP_DESTROY_SESSION - 32) |
3329*57266a6eSJ. Bruce Fields 	      1 << (OP_DESTROY_CLIENTID - 32)
3330*57266a6eSJ. Bruce Fields };
3331*57266a6eSJ. Bruce Fields 
3332695e12f8SBenny Halevy static __be32
333357b7b43bSJ. Bruce Fields nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
33342db134ebSAndy Adamson 			 struct nfsd4_exchange_id *exid)
33352db134ebSAndy Adamson {
3336bc749ca4SJ. Bruce Fields 	__be32 *p;
33370733d213SAndy Adamson 	char *major_id;
33380733d213SAndy Adamson 	char *server_scope;
33390733d213SAndy Adamson 	int major_id_sz;
33400733d213SAndy Adamson 	int server_scope_sz;
33410733d213SAndy Adamson 	uint64_t minor_id = 0;
33420733d213SAndy Adamson 
33430733d213SAndy Adamson 	if (nfserr)
33442db134ebSAndy Adamson 		return nfserr;
33450733d213SAndy Adamson 
33460733d213SAndy Adamson 	major_id = utsname()->nodename;
33470733d213SAndy Adamson 	major_id_sz = strlen(major_id);
33480733d213SAndy Adamson 	server_scope = utsname()->nodename;
33490733d213SAndy Adamson 	server_scope_sz = strlen(server_scope);
33500733d213SAndy Adamson 
33510733d213SAndy Adamson 	RESERVE_SPACE(
33520733d213SAndy Adamson 		8 /* eir_clientid */ +
33530733d213SAndy Adamson 		4 /* eir_sequenceid */ +
33540733d213SAndy Adamson 		4 /* eir_flags */ +
33550733d213SAndy Adamson 		4 /* spr_how (SP4_NONE) */ +
33560733d213SAndy Adamson 		8 /* so_minor_id */ +
33570733d213SAndy Adamson 		4 /* so_major_id.len */ +
33580733d213SAndy Adamson 		(XDR_QUADLEN(major_id_sz) * 4) +
33590733d213SAndy Adamson 		4 /* eir_server_scope.len */ +
33600733d213SAndy Adamson 		(XDR_QUADLEN(server_scope_sz) * 4) +
33610733d213SAndy Adamson 		4 /* eir_server_impl_id.count (0) */);
33620733d213SAndy Adamson 
33630733d213SAndy Adamson 	WRITEMEM(&exid->clientid, 8);
33640733d213SAndy Adamson 	WRITE32(exid->seqid);
33650733d213SAndy Adamson 	WRITE32(exid->flags);
33660733d213SAndy Adamson 
33670733d213SAndy Adamson 	/* state_protect4_r. Currently only support SP4_NONE */
33680733d213SAndy Adamson 	BUG_ON(exid->spa_how != SP4_NONE);
33690733d213SAndy Adamson 	WRITE32(exid->spa_how);
3370*57266a6eSJ. Bruce Fields 	switch (exid->spa_how) {
3371*57266a6eSJ. Bruce Fields 	case SP4_NONE:
3372*57266a6eSJ. Bruce Fields 		break;
3373*57266a6eSJ. Bruce Fields 	case SP4_MACH_CRED:
3374*57266a6eSJ. Bruce Fields 		/* spo_must_enforce bitmap: */
3375*57266a6eSJ. Bruce Fields 		WRITE32(2);
3376*57266a6eSJ. Bruce Fields 		WRITE32(nfs4_minimal_spo_must_enforce[0]);
3377*57266a6eSJ. Bruce Fields 		WRITE32(nfs4_minimal_spo_must_enforce[1]);
3378*57266a6eSJ. Bruce Fields 		/* empty spo_must_allow bitmap: */
3379*57266a6eSJ. Bruce Fields 		WRITE32(0);
3380*57266a6eSJ. Bruce Fields 		break;
3381*57266a6eSJ. Bruce Fields 	default:
3382*57266a6eSJ. Bruce Fields 		WARN_ON_ONCE(1);
3383*57266a6eSJ. Bruce Fields 	}
33840733d213SAndy Adamson 
33850733d213SAndy Adamson 	/* The server_owner struct */
33860733d213SAndy Adamson 	WRITE64(minor_id);      /* Minor id */
33870733d213SAndy Adamson 	/* major id */
33880733d213SAndy Adamson 	WRITE32(major_id_sz);
33890733d213SAndy Adamson 	WRITEMEM(major_id, major_id_sz);
33900733d213SAndy Adamson 
33910733d213SAndy Adamson 	/* Server scope */
33920733d213SAndy Adamson 	WRITE32(server_scope_sz);
33930733d213SAndy Adamson 	WRITEMEM(server_scope, server_scope_sz);
33940733d213SAndy Adamson 
33950733d213SAndy Adamson 	/* Implementation id */
33960733d213SAndy Adamson 	WRITE32(0);	/* zero length nfs_impl_id4 array */
33970733d213SAndy Adamson 	ADJUST_ARGS();
33980733d213SAndy Adamson 	return 0;
33992db134ebSAndy Adamson }
34002db134ebSAndy Adamson 
34012db134ebSAndy Adamson static __be32
340257b7b43bSJ. Bruce Fields nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
34032db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
34042db134ebSAndy Adamson {
3405bc749ca4SJ. Bruce Fields 	__be32 *p;
3406ec6b5d7bSAndy Adamson 
3407ec6b5d7bSAndy Adamson 	if (nfserr)
34082db134ebSAndy Adamson 		return nfserr;
3409ec6b5d7bSAndy Adamson 
3410ec6b5d7bSAndy Adamson 	RESERVE_SPACE(24);
3411ec6b5d7bSAndy Adamson 	WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN);
3412ec6b5d7bSAndy Adamson 	WRITE32(sess->seqid);
3413ec6b5d7bSAndy Adamson 	WRITE32(sess->flags);
3414ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
3415ec6b5d7bSAndy Adamson 
3416ec6b5d7bSAndy Adamson 	RESERVE_SPACE(28);
3417ec6b5d7bSAndy Adamson 	WRITE32(0); /* headerpadsz */
3418ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxreq_sz);
3419ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxresp_sz);
3420ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxresp_cached);
3421ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxops);
3422ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxreqs);
3423ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.nr_rdma_attrs);
3424ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
3425ec6b5d7bSAndy Adamson 
3426ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs) {
3427ec6b5d7bSAndy Adamson 		RESERVE_SPACE(4);
3428ec6b5d7bSAndy Adamson 		WRITE32(sess->fore_channel.rdma_attrs);
3429ec6b5d7bSAndy Adamson 		ADJUST_ARGS();
3430ec6b5d7bSAndy Adamson 	}
3431ec6b5d7bSAndy Adamson 
3432ec6b5d7bSAndy Adamson 	RESERVE_SPACE(28);
3433ec6b5d7bSAndy Adamson 	WRITE32(0); /* headerpadsz */
3434ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxreq_sz);
3435ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxresp_sz);
3436ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxresp_cached);
3437ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxops);
3438ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxreqs);
3439ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.nr_rdma_attrs);
3440ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
3441ec6b5d7bSAndy Adamson 
3442ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs) {
3443ec6b5d7bSAndy Adamson 		RESERVE_SPACE(4);
3444ec6b5d7bSAndy Adamson 		WRITE32(sess->back_channel.rdma_attrs);
3445ec6b5d7bSAndy Adamson 		ADJUST_ARGS();
3446ec6b5d7bSAndy Adamson 	}
3447ec6b5d7bSAndy Adamson 	return 0;
34482db134ebSAndy Adamson }
34492db134ebSAndy Adamson 
34502db134ebSAndy Adamson static __be32
345157b7b43bSJ. Bruce Fields nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, __be32 nfserr,
34522db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
34532db134ebSAndy Adamson {
34542db134ebSAndy Adamson 	return nfserr;
34552db134ebSAndy Adamson }
34562db134ebSAndy Adamson 
3457c47d832bSDaniel Mack static __be32
3458d1829b38SJ. Bruce Fields nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
3459e1ca12dfSBryan Schumaker 			  struct nfsd4_free_stateid *free_stateid)
3460e1ca12dfSBryan Schumaker {
3461e1ca12dfSBryan Schumaker 	__be32 *p;
3462e1ca12dfSBryan Schumaker 
3463e1ca12dfSBryan Schumaker 	if (nfserr)
3464e1ca12dfSBryan Schumaker 		return nfserr;
3465e1ca12dfSBryan Schumaker 
3466e1ca12dfSBryan Schumaker 	RESERVE_SPACE(4);
3467d1829b38SJ. Bruce Fields 	*p++ = nfserr;
3468e1ca12dfSBryan Schumaker 	ADJUST_ARGS();
3469e1ca12dfSBryan Schumaker 	return nfserr;
3470e1ca12dfSBryan Schumaker }
3471e1ca12dfSBryan Schumaker 
3472e1ca12dfSBryan Schumaker static __be32
347357b7b43bSJ. Bruce Fields nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
34742db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
34752db134ebSAndy Adamson {
3476bc749ca4SJ. Bruce Fields 	__be32 *p;
3477b85d4c01SBenny Halevy 
3478b85d4c01SBenny Halevy 	if (nfserr)
34792db134ebSAndy Adamson 		return nfserr;
3480b85d4c01SBenny Halevy 
3481b85d4c01SBenny Halevy 	RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 20);
3482b85d4c01SBenny Halevy 	WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
3483b85d4c01SBenny Halevy 	WRITE32(seq->seqid);
3484b85d4c01SBenny Halevy 	WRITE32(seq->slotid);
3485b7d7ca35SJ. Bruce Fields 	/* Note slotid's are numbered from zero: */
3486b7d7ca35SJ. Bruce Fields 	WRITE32(seq->maxslots - 1); /* sr_highest_slotid */
3487b7d7ca35SJ. Bruce Fields 	WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */
34880d7bb719SJ. Bruce Fields 	WRITE32(seq->status_flags);
3489b85d4c01SBenny Halevy 
3490b85d4c01SBenny Halevy 	ADJUST_ARGS();
3491557ce264SAndy Adamson 	resp->cstate.datap = p; /* DRC cache data pointer */
3492b85d4c01SBenny Halevy 	return 0;
34932db134ebSAndy Adamson }
34942db134ebSAndy Adamson 
34952355c596SJ. Bruce Fields static __be32
349657b7b43bSJ. Bruce Fields nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
349717456804SBryan Schumaker 			  struct nfsd4_test_stateid *test_stateid)
349817456804SBryan Schumaker {
349903cfb420SBryan Schumaker 	struct nfsd4_test_stateid_id *stateid, *next;
350017456804SBryan Schumaker 	__be32 *p;
350117456804SBryan Schumaker 
350203cfb420SBryan Schumaker 	RESERVE_SPACE(4 + (4 * test_stateid->ts_num_ids));
350317456804SBryan Schumaker 	*p++ = htonl(test_stateid->ts_num_ids);
350417456804SBryan Schumaker 
350503cfb420SBryan Schumaker 	list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
350602f5fde5SAl Viro 		*p++ = stateid->ts_id_status;
350717456804SBryan Schumaker 	}
350817456804SBryan Schumaker 
350903cfb420SBryan Schumaker 	ADJUST_ARGS();
351017456804SBryan Schumaker 	return nfserr;
351117456804SBryan Schumaker }
351217456804SBryan Schumaker 
35132db134ebSAndy Adamson static __be32
3514695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
3515695e12f8SBenny Halevy {
3516695e12f8SBenny Halevy 	return nfserr;
3517695e12f8SBenny Halevy }
3518695e12f8SBenny Halevy 
3519695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
3520695e12f8SBenny Halevy 
35212db134ebSAndy Adamson /*
35222db134ebSAndy Adamson  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
35232db134ebSAndy Adamson  * since we don't need to filter out obsolete ops as this is
35242db134ebSAndy Adamson  * done in the decoding phase.
35252db134ebSAndy Adamson  */
3526695e12f8SBenny Halevy static nfsd4_enc nfsd4_enc_ops[] = {
3527ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_enc)nfsd4_encode_access,
3528ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_enc)nfsd4_encode_close,
3529ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_enc)nfsd4_encode_commit,
3530ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_enc)nfsd4_encode_create,
3531ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_enc)nfsd4_encode_noop,
3532ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
3533ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_enc)nfsd4_encode_getattr,
3534ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_enc)nfsd4_encode_getfh,
3535ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_enc)nfsd4_encode_link,
3536ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_enc)nfsd4_encode_lock,
3537ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_enc)nfsd4_encode_lockt,
3538ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_enc)nfsd4_encode_locku,
3539ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_enc)nfsd4_encode_noop,
3540ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_enc)nfsd4_encode_noop,
3541ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
3542ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_enc)nfsd4_encode_open,
354384f09f46SBenny Halevy 	[OP_OPENATTR]		= (nfsd4_enc)nfsd4_encode_noop,
3544ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_enc)nfsd4_encode_open_confirm,
3545ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_enc)nfsd4_encode_open_downgrade,
3546ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_enc)nfsd4_encode_noop,
3547ad1060c8SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_enc)nfsd4_encode_noop,
3548ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_enc)nfsd4_encode_noop,
3549ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_enc)nfsd4_encode_read,
3550ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_enc)nfsd4_encode_readdir,
3551ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_enc)nfsd4_encode_readlink,
3552ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_enc)nfsd4_encode_remove,
3553ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_enc)nfsd4_encode_rename,
3554ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_enc)nfsd4_encode_noop,
3555ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_enc)nfsd4_encode_noop,
3556ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_enc)nfsd4_encode_noop,
3557ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_enc)nfsd4_encode_secinfo,
3558ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_enc)nfsd4_encode_setattr,
3559ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_enc)nfsd4_encode_setclientid,
3560ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop,
3561ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
3562ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_enc)nfsd4_encode_write,
3563ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_enc)nfsd4_encode_noop,
35642db134ebSAndy Adamson 
35652db134ebSAndy Adamson 	/* NFSv4.1 operations */
35662db134ebSAndy Adamson 	[OP_BACKCHANNEL_CTL]	= (nfsd4_enc)nfsd4_encode_noop,
35671d1bc8f2SJ. Bruce Fields 	[OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session,
35682db134ebSAndy Adamson 	[OP_EXCHANGE_ID]	= (nfsd4_enc)nfsd4_encode_exchange_id,
35692db134ebSAndy Adamson 	[OP_CREATE_SESSION]	= (nfsd4_enc)nfsd4_encode_create_session,
35702db134ebSAndy Adamson 	[OP_DESTROY_SESSION]	= (nfsd4_enc)nfsd4_encode_destroy_session,
3571e1ca12dfSBryan Schumaker 	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_free_stateid,
35722db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
35732db134ebSAndy Adamson 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_noop,
35742db134ebSAndy Adamson 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
35752db134ebSAndy Adamson 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_noop,
35762db134ebSAndy Adamson 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_noop,
35772db134ebSAndy Adamson 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
357822b6dee8SMi Jinlong 	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_secinfo_no_name,
35792db134ebSAndy Adamson 	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
35802db134ebSAndy Adamson 	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
358117456804SBryan Schumaker 	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_test_stateid,
35822db134ebSAndy Adamson 	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
35832db134ebSAndy Adamson 	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
35842db134ebSAndy Adamson 	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
3585695e12f8SBenny Halevy };
3586695e12f8SBenny Halevy 
3587496c262cSAndy Adamson /*
3588496c262cSAndy Adamson  * Calculate the total amount of memory that the compound response has taken
358958e7b33aSMi Jinlong  * after encoding the current operation with pad.
3590496c262cSAndy Adamson  *
359158e7b33aSMi Jinlong  * pad: if operation is non-idempotent, pad was calculate by op_rsize_bop()
359258e7b33aSMi Jinlong  *      which was specified at nfsd4_operation, else pad is zero.
3593496c262cSAndy Adamson  *
359458e7b33aSMi Jinlong  * Compare this length to the session se_fmaxresp_sz and se_fmaxresp_cached.
3595496c262cSAndy Adamson  *
3596496c262cSAndy Adamson  * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
3597496c262cSAndy Adamson  * will be at least a page and will therefore hold the xdr_buf head.
3598496c262cSAndy Adamson  */
359957b7b43bSJ. Bruce Fields __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
3600496c262cSAndy Adamson {
3601496c262cSAndy Adamson 	struct xdr_buf *xb = &resp->rqstp->rq_res;
3602496c262cSAndy Adamson 	struct nfsd4_session *session = NULL;
3603496c262cSAndy Adamson 	struct nfsd4_slot *slot = resp->cstate.slot;
360458e7b33aSMi Jinlong 	u32 length, tlen = 0;
3605496c262cSAndy Adamson 
3606496c262cSAndy Adamson 	if (!nfsd4_has_session(&resp->cstate))
360758e7b33aSMi Jinlong 		return 0;
3608496c262cSAndy Adamson 
3609496c262cSAndy Adamson 	session = resp->cstate.session;
361058e7b33aSMi Jinlong 	if (session == NULL)
361158e7b33aSMi Jinlong 		return 0;
3612496c262cSAndy Adamson 
3613496c262cSAndy Adamson 	if (xb->page_len == 0) {
3614496c262cSAndy Adamson 		length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
3615496c262cSAndy Adamson 	} else {
3616496c262cSAndy Adamson 		if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0)
3617496c262cSAndy Adamson 			tlen = (char *)resp->p - (char *)xb->tail[0].iov_base;
3618496c262cSAndy Adamson 
3619496c262cSAndy Adamson 		length = xb->head[0].iov_len + xb->page_len + tlen + pad;
3620496c262cSAndy Adamson 	}
3621496c262cSAndy Adamson 	dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
3622496c262cSAndy Adamson 		length, xb->page_len, tlen, pad);
3623496c262cSAndy Adamson 
362458e7b33aSMi Jinlong 	if (length > session->se_fchannel.maxresp_sz)
362558e7b33aSMi Jinlong 		return nfserr_rep_too_big;
362658e7b33aSMi Jinlong 
362773e79482SJ. Bruce Fields 	if ((slot->sl_flags & NFSD4_SLOT_CACHETHIS) &&
362858e7b33aSMi Jinlong 	    length > session->se_fchannel.maxresp_cached)
3629496c262cSAndy Adamson 		return nfserr_rep_too_big_to_cache;
363058e7b33aSMi Jinlong 
363158e7b33aSMi Jinlong 	return 0;
3632496c262cSAndy Adamson }
3633496c262cSAndy Adamson 
36341da177e4SLinus Torvalds void
36351da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
36361da177e4SLinus Torvalds {
36379411b1d4SJ. Bruce Fields 	struct nfs4_stateowner *so = resp->cstate.replay_owner;
36382ebbc012SAl Viro 	__be32 *statp;
3639bc749ca4SJ. Bruce Fields 	__be32 *p;
36401da177e4SLinus Torvalds 
36411da177e4SLinus Torvalds 	RESERVE_SPACE(8);
36421da177e4SLinus Torvalds 	WRITE32(op->opnum);
36431da177e4SLinus Torvalds 	statp = p++;	/* to be backfilled at the end */
36441da177e4SLinus Torvalds 	ADJUST_ARGS();
36451da177e4SLinus Torvalds 
3646695e12f8SBenny Halevy 	if (op->opnum == OP_ILLEGAL)
3647695e12f8SBenny Halevy 		goto status;
3648695e12f8SBenny Halevy 	BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
3649695e12f8SBenny Halevy 	       !nfsd4_enc_ops[op->opnum]);
3650695e12f8SBenny Halevy 	op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
3651496c262cSAndy Adamson 	/* nfsd4_check_drc_limit guarantees enough room for error status */
365258e7b33aSMi Jinlong 	if (!op->status)
365358e7b33aSMi Jinlong 		op->status = nfsd4_check_resp_size(resp, 0);
36549411b1d4SJ. Bruce Fields 	if (so) {
36559411b1d4SJ. Bruce Fields 		so->so_replay.rp_status = op->status;
36569411b1d4SJ. Bruce Fields 		so->so_replay.rp_buflen = (char *)resp->p - (char *)(statp+1);
36579411b1d4SJ. Bruce Fields 		memcpy(so->so_replay.rp_buf, statp+1, so->so_replay.rp_buflen);
36589411b1d4SJ. Bruce Fields 	}
3659695e12f8SBenny Halevy status:
36601da177e4SLinus Torvalds 	/*
36611da177e4SLinus Torvalds 	 * Note: We write the status directly, instead of using WRITE32(),
36621da177e4SLinus Torvalds 	 * since it is already in network byte order.
36631da177e4SLinus Torvalds 	 */
36641da177e4SLinus Torvalds 	*statp = op->status;
36651da177e4SLinus Torvalds }
36661da177e4SLinus Torvalds 
36671da177e4SLinus Torvalds /*
36681da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
36691da177e4SLinus Torvalds  *
36701da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
36711da177e4SLinus Torvalds  * previously sent already encoded operation.
36721da177e4SLinus Torvalds  *
36731da177e4SLinus Torvalds  * called with nfs4_lock_state() held
36741da177e4SLinus Torvalds  */
36751da177e4SLinus Torvalds void
36761da177e4SLinus Torvalds nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
36771da177e4SLinus Torvalds {
3678bc749ca4SJ. Bruce Fields 	__be32 *p;
36791da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
36801da177e4SLinus Torvalds 
36811da177e4SLinus Torvalds 	BUG_ON(!rp);
36821da177e4SLinus Torvalds 
36831da177e4SLinus Torvalds 	RESERVE_SPACE(8);
36841da177e4SLinus Torvalds 	WRITE32(op->opnum);
36851da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
36861da177e4SLinus Torvalds 	ADJUST_ARGS();
36871da177e4SLinus Torvalds 
36881da177e4SLinus Torvalds 	RESERVE_SPACE(rp->rp_buflen);
36891da177e4SLinus Torvalds 	WRITEMEM(rp->rp_buf, rp->rp_buflen);
36901da177e4SLinus Torvalds 	ADJUST_ARGS();
36911da177e4SLinus Torvalds }
36921da177e4SLinus Torvalds 
36931da177e4SLinus Torvalds int
36942ebbc012SAl Viro nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
36951da177e4SLinus Torvalds {
36961da177e4SLinus Torvalds         return xdr_ressize_check(rqstp, p);
36971da177e4SLinus Torvalds }
36981da177e4SLinus Torvalds 
36993e98abffSJ. Bruce Fields int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp)
37001da177e4SLinus Torvalds {
37013e98abffSJ. Bruce Fields 	struct svc_rqst *rqstp = rq;
37023e98abffSJ. Bruce Fields 	struct nfsd4_compoundargs *args = rqstp->rq_argp;
37033e98abffSJ. Bruce Fields 
37041da177e4SLinus Torvalds 	if (args->ops != args->iops) {
37051da177e4SLinus Torvalds 		kfree(args->ops);
37061da177e4SLinus Torvalds 		args->ops = args->iops;
37071da177e4SLinus Torvalds 	}
37081da177e4SLinus Torvalds 	kfree(args->tmpp);
37091da177e4SLinus Torvalds 	args->tmpp = NULL;
37101da177e4SLinus Torvalds 	while (args->to_free) {
37111da177e4SLinus Torvalds 		struct tmpbuf *tb = args->to_free;
37121da177e4SLinus Torvalds 		args->to_free = tb->next;
37131da177e4SLinus Torvalds 		tb->release(tb->buf);
37141da177e4SLinus Torvalds 		kfree(tb);
37151da177e4SLinus Torvalds 	}
37163e98abffSJ. Bruce Fields 	return 1;
37171da177e4SLinus Torvalds }
37181da177e4SLinus Torvalds 
37191da177e4SLinus Torvalds int
37202ebbc012SAl Viro nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
37211da177e4SLinus Torvalds {
37221da177e4SLinus Torvalds 	args->p = p;
37231da177e4SLinus Torvalds 	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
37241da177e4SLinus Torvalds 	args->pagelist = rqstp->rq_arg.pages;
37251da177e4SLinus Torvalds 	args->pagelen = rqstp->rq_arg.page_len;
37261da177e4SLinus Torvalds 	args->tmpp = NULL;
37271da177e4SLinus Torvalds 	args->to_free = NULL;
37281da177e4SLinus Torvalds 	args->ops = args->iops;
37291da177e4SLinus Torvalds 	args->rqstp = rqstp;
37301da177e4SLinus Torvalds 
37313e98abffSJ. Bruce Fields 	return !nfsd4_decode_compound(args);
37321da177e4SLinus Torvalds }
37331da177e4SLinus Torvalds 
37341da177e4SLinus Torvalds int
37352ebbc012SAl Viro nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp)
37361da177e4SLinus Torvalds {
37371da177e4SLinus Torvalds 	/*
37381da177e4SLinus Torvalds 	 * All that remains is to write the tag and operation count...
37391da177e4SLinus Torvalds 	 */
3740557ce264SAndy Adamson 	struct nfsd4_compound_state *cs = &resp->cstate;
37411da177e4SLinus Torvalds 	struct kvec *iov;
37421da177e4SLinus Torvalds 	p = resp->tagp;
37431da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
37441da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
37451da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
37461da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
37471da177e4SLinus Torvalds 
37481da177e4SLinus Torvalds 	if (rqstp->rq_res.page_len)
37491da177e4SLinus Torvalds 		iov = &rqstp->rq_res.tail[0];
37501da177e4SLinus Torvalds 	else
37511da177e4SLinus Torvalds 		iov = &rqstp->rq_res.head[0];
37521da177e4SLinus Torvalds 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
37531da177e4SLinus Torvalds 	BUG_ON(iov->iov_len > PAGE_SIZE);
375426c0c75eSJ. Bruce Fields 	if (nfsd4_has_session(cs)) {
375526c0c75eSJ. Bruce Fields 		if (cs->status != nfserr_replay_cache) {
3756da3846a2SAndy Adamson 			nfsd4_store_cache_entry(resp);
375773e79482SJ. Bruce Fields 			cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
375826c0c75eSJ. Bruce Fields 		}
3759d7682988SBenny Halevy 		/* Renew the clientid on success and on replay */
3760221a6876SJ. Bruce Fields 		put_client_renew(cs->session->se_client);
3761221a6876SJ. Bruce Fields 		nfsd4_put_session(cs->session);
3762da3846a2SAndy Adamson 	}
37631da177e4SLinus Torvalds 	return 1;
37641da177e4SLinus Torvalds }
37651da177e4SLinus Torvalds 
37661da177e4SLinus Torvalds /*
37671da177e4SLinus Torvalds  * Local variables:
37681da177e4SLinus Torvalds  *  c-basic-offset: 8
37691da177e4SLinus Torvalds  * End:
37701da177e4SLinus Torvalds  */
3771