xref: /openbmc/linux/fs/nfsd/nfs4xdr.c (revision c0d6fc8a2d55a8235c301aeb6d5254d5992895d1)
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 
431da177e4SLinus Torvalds #include <linux/param.h>
441da177e4SLinus Torvalds #include <linux/smp.h>
451da177e4SLinus Torvalds #include <linux/fs.h>
461da177e4SLinus Torvalds #include <linux/namei.h>
471da177e4SLinus Torvalds #include <linux/vfs.h>
480733d213SAndy Adamson #include <linux/utsname.h>
491da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h>
501da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
511da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
521da177e4SLinus Torvalds #include <linux/nfsd/nfsd.h>
531da177e4SLinus Torvalds #include <linux/nfsd/state.h>
541da177e4SLinus Torvalds #include <linux/nfsd/xdr4.h>
551da177e4SLinus Torvalds #include <linux/nfsd_idmap.h>
561da177e4SLinus Torvalds #include <linux/nfs4.h>
571da177e4SLinus Torvalds #include <linux/nfs4_acl.h>
58dcb488a3SAndy Adamson #include <linux/sunrpc/gss_api.h>
594796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
621da177e4SLinus Torvalds 
6342ca0993SJ.Bruce Fields /*
6442ca0993SJ.Bruce Fields  * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
6542ca0993SJ.Bruce Fields  * directory in order to indicate to the client that a filesystem boundary is present
6642ca0993SJ.Bruce Fields  * We use a fixed fsid for a referral
6742ca0993SJ.Bruce Fields  */
6842ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
6942ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL
7042ca0993SJ.Bruce Fields 
71b37ad28bSAl Viro static __be32
72b37ad28bSAl Viro check_filename(char *str, int len, __be32 err)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	int i;
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	if (len == 0)
771da177e4SLinus Torvalds 		return nfserr_inval;
781da177e4SLinus Torvalds 	if (isdotent(str, len))
791da177e4SLinus Torvalds 		return err;
801da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
811da177e4SLinus Torvalds 		if (str[i] == '/')
821da177e4SLinus Torvalds 			return err;
831da177e4SLinus Torvalds 	return 0;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  * START OF "GENERIC" DECODE ROUTINES.
881da177e4SLinus Torvalds  *   These may look a little ugly since they are imported from a "generic"
891da177e4SLinus Torvalds  * set of XDR encode/decode routines which are intended to be shared by
901da177e4SLinus Torvalds  * all of our NFSv4 implementations (OpenBSD, MacOS X...).
911da177e4SLinus Torvalds  *
921da177e4SLinus Torvalds  * If the pain of reading these is too great, it should be a straightforward
931da177e4SLinus Torvalds  * task to translate them into Linux-specific versions which are more
941da177e4SLinus Torvalds  * consistent with the style used in NFSv2/v3...
951da177e4SLinus Torvalds  */
961da177e4SLinus Torvalds #define DECODE_HEAD				\
972ebbc012SAl Viro 	__be32 *p;				\
98b37ad28bSAl Viro 	__be32 status
991da177e4SLinus Torvalds #define DECODE_TAIL				\
1001da177e4SLinus Torvalds 	status = 0;				\
1011da177e4SLinus Torvalds out:						\
1021da177e4SLinus Torvalds 	return status;				\
1031da177e4SLinus Torvalds xdr_error:					\
104817cb9d4SChuck Lever 	dprintk("NFSD: xdr error (%s:%d)\n",	\
105817cb9d4SChuck Lever 			__FILE__, __LINE__);	\
1061da177e4SLinus Torvalds 	status = nfserr_bad_xdr;		\
1071da177e4SLinus Torvalds 	goto out
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds #define READ32(x)         (x) = ntohl(*p++)
1101da177e4SLinus Torvalds #define READ64(x)         do {			\
1111da177e4SLinus Torvalds 	(x) = (u64)ntohl(*p++) << 32;		\
1121da177e4SLinus Torvalds 	(x) |= ntohl(*p++);			\
1131da177e4SLinus Torvalds } while (0)
1141da177e4SLinus Torvalds #define READTIME(x)       do {			\
1151da177e4SLinus Torvalds 	p++;					\
1161da177e4SLinus Torvalds 	(x) = ntohl(*p++);			\
1171da177e4SLinus Torvalds 	p++;					\
1181da177e4SLinus Torvalds } while (0)
1191da177e4SLinus Torvalds #define READMEM(x,nbytes) do {			\
1201da177e4SLinus Torvalds 	x = (char *)p;				\
1211da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1221da177e4SLinus Torvalds } while (0)
1231da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do {			\
1241da177e4SLinus Torvalds 	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
1251da177e4SLinus Torvalds  		savemem(argp, p, nbytes) :	\
1261da177e4SLinus Torvalds  		(char *)p)) {			\
127817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
128817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1291da177e4SLinus Torvalds 		goto xdr_error;			\
1301da177e4SLinus Torvalds 		}				\
1311da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1321da177e4SLinus Torvalds } while (0)
1331da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do {			\
1341da177e4SLinus Torvalds 	memcpy((x), p, nbytes);			\
1351da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1361da177e4SLinus Torvalds } while (0)
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
1391da177e4SLinus Torvalds #define READ_BUF(nbytes)  do {			\
1401da177e4SLinus Torvalds 	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
1411da177e4SLinus Torvalds 		p = argp->p;			\
1421da177e4SLinus Torvalds 		argp->p += XDR_QUADLEN(nbytes);	\
1431da177e4SLinus Torvalds 	} else if (!(p = read_buf(argp, nbytes))) { \
144817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
145817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1461da177e4SLinus Torvalds 		goto xdr_error;			\
1471da177e4SLinus Torvalds 	}					\
1481da177e4SLinus Torvalds } while (0)
1491da177e4SLinus Torvalds 
150ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
1511da177e4SLinus Torvalds {
1521da177e4SLinus Torvalds 	/* We want more bytes than seem to be available.
1531da177e4SLinus Torvalds 	 * Maybe we need a new page, maybe we have just run out
1541da177e4SLinus Torvalds 	 */
155ca2a05aaSJ. Bruce Fields 	unsigned int avail = (char *)argp->end - (char *)argp->p;
1562ebbc012SAl Viro 	__be32 *p;
1571da177e4SLinus Torvalds 	if (avail + argp->pagelen < nbytes)
1581da177e4SLinus Torvalds 		return NULL;
1591da177e4SLinus Torvalds 	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
1601da177e4SLinus Torvalds 		return NULL;
1611da177e4SLinus Torvalds 	/* ok, we can do it with the current plus the next page */
1621da177e4SLinus Torvalds 	if (nbytes <= sizeof(argp->tmp))
1631da177e4SLinus Torvalds 		p = argp->tmp;
1641da177e4SLinus Torvalds 	else {
1651da177e4SLinus Torvalds 		kfree(argp->tmpp);
1661da177e4SLinus Torvalds 		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
1671da177e4SLinus Torvalds 		if (!p)
1681da177e4SLinus Torvalds 			return NULL;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	}
171ca2a05aaSJ. Bruce Fields 	/*
172ca2a05aaSJ. Bruce Fields 	 * The following memcpy is safe because read_buf is always
173ca2a05aaSJ. Bruce Fields 	 * called with nbytes > avail, and the two cases above both
174ca2a05aaSJ. Bruce Fields 	 * guarantee p points to at least nbytes bytes.
175ca2a05aaSJ. Bruce Fields 	 */
1761da177e4SLinus Torvalds 	memcpy(p, argp->p, avail);
1771da177e4SLinus Torvalds 	/* step to next page */
1781da177e4SLinus Torvalds 	argp->p = page_address(argp->pagelist[0]);
1791da177e4SLinus Torvalds 	argp->pagelist++;
1801da177e4SLinus Torvalds 	if (argp->pagelen < PAGE_SIZE) {
1811da177e4SLinus Torvalds 		argp->end = p + (argp->pagelen>>2);
1821da177e4SLinus Torvalds 		argp->pagelen = 0;
1831da177e4SLinus Torvalds 	} else {
1841da177e4SLinus Torvalds 		argp->end = p + (PAGE_SIZE>>2);
1851da177e4SLinus Torvalds 		argp->pagelen -= PAGE_SIZE;
1861da177e4SLinus Torvalds 	}
1871da177e4SLinus Torvalds 	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
1881da177e4SLinus Torvalds 	argp->p += XDR_QUADLEN(nbytes - avail);
1891da177e4SLinus Torvalds 	return p;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
19260adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
19360adfc50SAndy Adamson {
19460adfc50SAndy Adamson 	return (clid->cl_boot == 0) && (clid->cl_id == 0);
19560adfc50SAndy Adamson }
19660adfc50SAndy Adamson 
1971da177e4SLinus Torvalds static int
1981da177e4SLinus Torvalds defer_free(struct nfsd4_compoundargs *argp,
1991da177e4SLinus Torvalds 		void (*release)(const void *), void *p)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	struct tmpbuf *tb;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	tb = kmalloc(sizeof(*tb), GFP_KERNEL);
2041da177e4SLinus Torvalds 	if (!tb)
2051da177e4SLinus Torvalds 		return -ENOMEM;
2061da177e4SLinus Torvalds 	tb->buf = p;
2071da177e4SLinus Torvalds 	tb->release = release;
2081da177e4SLinus Torvalds 	tb->next = argp->to_free;
2091da177e4SLinus Torvalds 	argp->to_free = tb;
2101da177e4SLinus Torvalds 	return 0;
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
2132ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
2141da177e4SLinus Torvalds {
2151da177e4SLinus Torvalds 	if (p == argp->tmp) {
216a4db5fe5SJ. Bruce Fields 		p = kmalloc(nbytes, GFP_KERNEL);
217a4db5fe5SJ. Bruce Fields 		if (!p)
218a4db5fe5SJ. Bruce Fields 			return NULL;
2191da177e4SLinus Torvalds 		memcpy(p, argp->tmp, nbytes);
2201da177e4SLinus Torvalds 	} else {
22173dff8beSEric Sesterhenn 		BUG_ON(p != argp->tmpp);
2221da177e4SLinus Torvalds 		argp->tmpp = NULL;
2231da177e4SLinus Torvalds 	}
2241da177e4SLinus Torvalds 	if (defer_free(argp, kfree, p)) {
225a4db5fe5SJ. Bruce Fields 		kfree(p);
2261da177e4SLinus Torvalds 		return NULL;
2271da177e4SLinus Torvalds 	} else
2281da177e4SLinus Torvalds 		return (char *)p;
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
231b37ad28bSAl Viro static __be32
2321da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	u32 bmlen;
2351da177e4SLinus Torvalds 	DECODE_HEAD;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	bmval[0] = 0;
2381da177e4SLinus Torvalds 	bmval[1] = 0;
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	READ_BUF(4);
2411da177e4SLinus Torvalds 	READ32(bmlen);
2421da177e4SLinus Torvalds 	if (bmlen > 1000)
2431da177e4SLinus Torvalds 		goto xdr_error;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	READ_BUF(bmlen << 2);
2461da177e4SLinus Torvalds 	if (bmlen > 0)
2471da177e4SLinus Torvalds 		READ32(bmval[0]);
2481da177e4SLinus Torvalds 	if (bmlen > 1)
2491da177e4SLinus Torvalds 		READ32(bmval[1]);
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	DECODE_TAIL;
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
254*c0d6fc8aSBenny Halevy static u32 nfsd_attrmask[] = {
255*c0d6fc8aSBenny Halevy 	NFSD_WRITEABLE_ATTRS_WORD0,
256*c0d6fc8aSBenny Halevy 	NFSD_WRITEABLE_ATTRS_WORD1
257*c0d6fc8aSBenny Halevy };
258*c0d6fc8aSBenny Halevy 
259b37ad28bSAl Viro static __be32
260*c0d6fc8aSBenny Halevy nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
261*c0d6fc8aSBenny Halevy 		   struct iattr *iattr, struct nfs4_acl **acl)
2621da177e4SLinus Torvalds {
2631da177e4SLinus Torvalds 	int expected_len, len = 0;
2641da177e4SLinus Torvalds 	u32 dummy32;
2651da177e4SLinus Torvalds 	char *buf;
266b8dd7b9aSAl Viro 	int host_err;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	DECODE_HEAD;
2691da177e4SLinus Torvalds 	iattr->ia_valid = 0;
2701da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
2711da177e4SLinus Torvalds 		return status;
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	/*
274f34f9242SJ. Bruce Fields 	 * According to spec, unsupported attributes return ERR_ATTRNOTSUPP;
2751da177e4SLinus Torvalds 	 * read-only attributes return ERR_INVAL.
2761da177e4SLinus Torvalds 	 */
2771da177e4SLinus Torvalds 	if ((bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) || (bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1))
2781da177e4SLinus Torvalds 		return nfserr_attrnotsupp;
279*c0d6fc8aSBenny Halevy 	if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]))
2801da177e4SLinus Torvalds 		return nfserr_inval;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	READ_BUF(4);
2831da177e4SLinus Torvalds 	READ32(expected_len);
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
2861da177e4SLinus Torvalds 		READ_BUF(8);
2871da177e4SLinus Torvalds 		len += 8;
2881da177e4SLinus Torvalds 		READ64(iattr->ia_size);
2891da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
2901da177e4SLinus Torvalds 	}
2911da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
29228e05dd8SJ. Bruce Fields 		int nace;
29328e05dd8SJ. Bruce Fields 		struct nfs4_ace *ace;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 		READ_BUF(4); len += 4;
2961da177e4SLinus Torvalds 		READ32(nace);
2971da177e4SLinus Torvalds 
29828e05dd8SJ. Bruce Fields 		if (nace > NFS4_ACL_MAX)
29928e05dd8SJ. Bruce Fields 			return nfserr_resource;
30028e05dd8SJ. Bruce Fields 
30128e05dd8SJ. Bruce Fields 		*acl = nfs4_acl_new(nace);
3021da177e4SLinus Torvalds 		if (*acl == NULL) {
303b8dd7b9aSAl Viro 			host_err = -ENOMEM;
3041da177e4SLinus Torvalds 			goto out_nfserr;
3051da177e4SLinus Torvalds 		}
30628e05dd8SJ. Bruce Fields 		defer_free(argp, kfree, *acl);
3071da177e4SLinus Torvalds 
30828e05dd8SJ. Bruce Fields 		(*acl)->naces = nace;
30928e05dd8SJ. Bruce Fields 		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
3101da177e4SLinus Torvalds 			READ_BUF(16); len += 16;
31128e05dd8SJ. Bruce Fields 			READ32(ace->type);
31228e05dd8SJ. Bruce Fields 			READ32(ace->flag);
31328e05dd8SJ. Bruce Fields 			READ32(ace->access_mask);
3141da177e4SLinus Torvalds 			READ32(dummy32);
3151da177e4SLinus Torvalds 			READ_BUF(dummy32);
3161da177e4SLinus Torvalds 			len += XDR_QUADLEN(dummy32) << 2;
3171da177e4SLinus Torvalds 			READMEM(buf, dummy32);
31828e05dd8SJ. Bruce Fields 			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
319b8dd7b9aSAl Viro 			host_err = 0;
32028e05dd8SJ. Bruce Fields 			if (ace->whotype != NFS4_ACL_WHO_NAMED)
32128e05dd8SJ. Bruce Fields 				ace->who = 0;
32228e05dd8SJ. Bruce Fields 			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
323b8dd7b9aSAl Viro 				host_err = nfsd_map_name_to_gid(argp->rqstp,
32428e05dd8SJ. Bruce Fields 						buf, dummy32, &ace->who);
3251da177e4SLinus Torvalds 			else
326b8dd7b9aSAl Viro 				host_err = nfsd_map_name_to_uid(argp->rqstp,
32728e05dd8SJ. Bruce Fields 						buf, dummy32, &ace->who);
328b8dd7b9aSAl Viro 			if (host_err)
3291da177e4SLinus Torvalds 				goto out_nfserr;
3301da177e4SLinus Torvalds 		}
3311da177e4SLinus Torvalds 	} else
3321da177e4SLinus Torvalds 		*acl = NULL;
3331da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
3341da177e4SLinus Torvalds 		READ_BUF(4);
3351da177e4SLinus Torvalds 		len += 4;
3361da177e4SLinus Torvalds 		READ32(iattr->ia_mode);
3371da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
3381da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
3391da177e4SLinus Torvalds 	}
3401da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
3411da177e4SLinus Torvalds 		READ_BUF(4);
3421da177e4SLinus Torvalds 		len += 4;
3431da177e4SLinus Torvalds 		READ32(dummy32);
3441da177e4SLinus Torvalds 		READ_BUF(dummy32);
3451da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3461da177e4SLinus Torvalds 		READMEM(buf, dummy32);
347b8dd7b9aSAl Viro 		if ((host_err = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
3481da177e4SLinus Torvalds 			goto out_nfserr;
3491da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
3501da177e4SLinus Torvalds 	}
3511da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
3521da177e4SLinus Torvalds 		READ_BUF(4);
3531da177e4SLinus Torvalds 		len += 4;
3541da177e4SLinus Torvalds 		READ32(dummy32);
3551da177e4SLinus Torvalds 		READ_BUF(dummy32);
3561da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3571da177e4SLinus Torvalds 		READMEM(buf, dummy32);
358b8dd7b9aSAl Viro 		if ((host_err = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
3591da177e4SLinus Torvalds 			goto out_nfserr;
3601da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
3611da177e4SLinus Torvalds 	}
3621da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
3631da177e4SLinus Torvalds 		READ_BUF(4);
3641da177e4SLinus Torvalds 		len += 4;
3651da177e4SLinus Torvalds 		READ32(dummy32);
3661da177e4SLinus Torvalds 		switch (dummy32) {
3671da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3681da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3691da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3701da177e4SLinus Torvalds 			READ_BUF(12);
3711da177e4SLinus Torvalds 			len += 12;
3721da177e4SLinus Torvalds 			READ32(dummy32);
3731da177e4SLinus Torvalds 			if (dummy32)
3741da177e4SLinus Torvalds 				return nfserr_inval;
3751da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_sec);
3761da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_nsec);
3771da177e4SLinus Torvalds 			if (iattr->ia_atime.tv_nsec >= (u32)1000000000)
3781da177e4SLinus Torvalds 				return nfserr_inval;
3791da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
3801da177e4SLinus Torvalds 			break;
3811da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3821da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
3831da177e4SLinus Torvalds 			break;
3841da177e4SLinus Torvalds 		default:
3851da177e4SLinus Torvalds 			goto xdr_error;
3861da177e4SLinus Torvalds 		}
3871da177e4SLinus Torvalds 	}
3881da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
3891da177e4SLinus Torvalds 		READ_BUF(4);
3901da177e4SLinus Torvalds 		len += 4;
3911da177e4SLinus Torvalds 		READ32(dummy32);
3921da177e4SLinus Torvalds 		switch (dummy32) {
3931da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3941da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3951da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3961da177e4SLinus Torvalds 			READ_BUF(12);
3971da177e4SLinus Torvalds 			len += 12;
3981da177e4SLinus Torvalds 			READ32(dummy32);
3991da177e4SLinus Torvalds 			if (dummy32)
4001da177e4SLinus Torvalds 				return nfserr_inval;
4011da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_sec);
4021da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_nsec);
4031da177e4SLinus Torvalds 			if (iattr->ia_mtime.tv_nsec >= (u32)1000000000)
4041da177e4SLinus Torvalds 				return nfserr_inval;
4051da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
4061da177e4SLinus Torvalds 			break;
4071da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
4081da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
4091da177e4SLinus Torvalds 			break;
4101da177e4SLinus Torvalds 		default:
4111da177e4SLinus Torvalds 			goto xdr_error;
4121da177e4SLinus Torvalds 		}
4131da177e4SLinus Torvalds 	}
4141da177e4SLinus Torvalds 	if (len != expected_len)
4151da177e4SLinus Torvalds 		goto xdr_error;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	DECODE_TAIL;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds out_nfserr:
420b8dd7b9aSAl Viro 	status = nfserrno(host_err);
4211da177e4SLinus Torvalds 	goto out;
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds 
424b37ad28bSAl Viro static __be32
425e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
426e31a1b66SBenny Halevy {
427e31a1b66SBenny Halevy 	DECODE_HEAD;
428e31a1b66SBenny Halevy 
429e31a1b66SBenny Halevy 	READ_BUF(sizeof(stateid_t));
430e31a1b66SBenny Halevy 	READ32(sid->si_generation);
431e31a1b66SBenny Halevy 	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
432e31a1b66SBenny Halevy 
433e31a1b66SBenny Halevy 	DECODE_TAIL;
434e31a1b66SBenny Halevy }
435e31a1b66SBenny Halevy 
436e31a1b66SBenny Halevy static __be32
4371da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
4381da177e4SLinus Torvalds {
4391da177e4SLinus Torvalds 	DECODE_HEAD;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	READ_BUF(4);
4421da177e4SLinus Torvalds 	READ32(access->ac_req_access);
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	DECODE_TAIL;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
447b37ad28bSAl Viro static __be32
4481da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
4491da177e4SLinus Torvalds {
4501da177e4SLinus Torvalds 	DECODE_HEAD;
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	close->cl_stateowner = NULL;
453e31a1b66SBenny Halevy 	READ_BUF(4);
4541da177e4SLinus Torvalds 	READ32(close->cl_seqid);
455e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &close->cl_stateid);
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	DECODE_TAIL;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 
461b37ad28bSAl Viro static __be32
4621da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
4631da177e4SLinus Torvalds {
4641da177e4SLinus Torvalds 	DECODE_HEAD;
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	READ_BUF(12);
4671da177e4SLinus Torvalds 	READ64(commit->co_offset);
4681da177e4SLinus Torvalds 	READ32(commit->co_count);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	DECODE_TAIL;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
473b37ad28bSAl Viro static __be32
4741da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
4751da177e4SLinus Torvalds {
4761da177e4SLinus Torvalds 	DECODE_HEAD;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	READ_BUF(4);
4791da177e4SLinus Torvalds 	READ32(create->cr_type);
4801da177e4SLinus Torvalds 	switch (create->cr_type) {
4811da177e4SLinus Torvalds 	case NF4LNK:
4821da177e4SLinus Torvalds 		READ_BUF(4);
4831da177e4SLinus Torvalds 		READ32(create->cr_linklen);
4841da177e4SLinus Torvalds 		READ_BUF(create->cr_linklen);
4851da177e4SLinus Torvalds 		SAVEMEM(create->cr_linkname, create->cr_linklen);
4861da177e4SLinus Torvalds 		break;
4871da177e4SLinus Torvalds 	case NF4BLK:
4881da177e4SLinus Torvalds 	case NF4CHR:
4891da177e4SLinus Torvalds 		READ_BUF(8);
4901da177e4SLinus Torvalds 		READ32(create->cr_specdata1);
4911da177e4SLinus Torvalds 		READ32(create->cr_specdata2);
4921da177e4SLinus Torvalds 		break;
4931da177e4SLinus Torvalds 	case NF4SOCK:
4941da177e4SLinus Torvalds 	case NF4FIFO:
4951da177e4SLinus Torvalds 	case NF4DIR:
4961da177e4SLinus Torvalds 	default:
4971da177e4SLinus Torvalds 		break;
4981da177e4SLinus Torvalds 	}
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	READ_BUF(4);
5011da177e4SLinus Torvalds 	READ32(create->cr_namelen);
5021da177e4SLinus Torvalds 	READ_BUF(create->cr_namelen);
5031da177e4SLinus Torvalds 	SAVEMEM(create->cr_name, create->cr_namelen);
5041da177e4SLinus Torvalds 	if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
5051da177e4SLinus Torvalds 		return status;
5061da177e4SLinus Torvalds 
507*c0d6fc8aSBenny Halevy 	status = nfsd4_decode_fattr(argp, create->cr_bmval, nfsd_attrmask,
508*c0d6fc8aSBenny Halevy 				    &create->cr_iattr, &create->cr_acl);
509*c0d6fc8aSBenny Halevy 	if (status)
5101da177e4SLinus Torvalds 		goto out;
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	DECODE_TAIL;
5131da177e4SLinus Torvalds }
5141da177e4SLinus Torvalds 
515b37ad28bSAl Viro static inline __be32
5161da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
5171da177e4SLinus Torvalds {
518e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
5191da177e4SLinus Torvalds }
5201da177e4SLinus Torvalds 
521b37ad28bSAl Viro static inline __be32
5221da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
5231da177e4SLinus Torvalds {
5241da177e4SLinus Torvalds 	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
527b37ad28bSAl Viro static __be32
5281da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	DECODE_HEAD;
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	READ_BUF(4);
5331da177e4SLinus Torvalds 	READ32(link->li_namelen);
5341da177e4SLinus Torvalds 	READ_BUF(link->li_namelen);
5351da177e4SLinus Torvalds 	SAVEMEM(link->li_name, link->li_namelen);
5361da177e4SLinus Torvalds 	if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval)))
5371da177e4SLinus Torvalds 		return status;
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 	DECODE_TAIL;
5401da177e4SLinus Torvalds }
5411da177e4SLinus Torvalds 
542b37ad28bSAl Viro static __be32
5431da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
5441da177e4SLinus Torvalds {
5451da177e4SLinus Torvalds 	DECODE_HEAD;
5461da177e4SLinus Torvalds 
5473a65588aSJ. Bruce Fields 	lock->lk_replay_owner = NULL;
5481da177e4SLinus Torvalds 	/*
5491da177e4SLinus Torvalds 	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
5501da177e4SLinus Torvalds 	*/
5511da177e4SLinus Torvalds 	READ_BUF(28);
5521da177e4SLinus Torvalds 	READ32(lock->lk_type);
5531da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
5541da177e4SLinus Torvalds 		goto xdr_error;
5551da177e4SLinus Torvalds 	READ32(lock->lk_reclaim);
5561da177e4SLinus Torvalds 	READ64(lock->lk_offset);
5571da177e4SLinus Torvalds 	READ64(lock->lk_length);
5581da177e4SLinus Torvalds 	READ32(lock->lk_is_new);
5591da177e4SLinus Torvalds 
5601da177e4SLinus Torvalds 	if (lock->lk_is_new) {
561e31a1b66SBenny Halevy 		READ_BUF(4);
5621da177e4SLinus Torvalds 		READ32(lock->lk_new_open_seqid);
563e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
564e31a1b66SBenny Halevy 		if (status)
565e31a1b66SBenny Halevy 			return status;
566e31a1b66SBenny Halevy 		READ_BUF(8 + sizeof(clientid_t));
5671da177e4SLinus Torvalds 		READ32(lock->lk_new_lock_seqid);
5681da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
5691da177e4SLinus Torvalds 		READ32(lock->lk_new_owner.len);
5701da177e4SLinus Torvalds 		READ_BUF(lock->lk_new_owner.len);
5711da177e4SLinus Torvalds 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
5721da177e4SLinus Torvalds 	} else {
573e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
574e31a1b66SBenny Halevy 		if (status)
575e31a1b66SBenny Halevy 			return status;
576e31a1b66SBenny Halevy 		READ_BUF(4);
5771da177e4SLinus Torvalds 		READ32(lock->lk_old_lock_seqid);
5781da177e4SLinus Torvalds 	}
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 	DECODE_TAIL;
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
583b37ad28bSAl Viro static __be32
5841da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
5851da177e4SLinus Torvalds {
5861da177e4SLinus Torvalds 	DECODE_HEAD;
5871da177e4SLinus Torvalds 
5881da177e4SLinus Torvalds 	READ_BUF(32);
5891da177e4SLinus Torvalds 	READ32(lockt->lt_type);
5901da177e4SLinus Torvalds 	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
5911da177e4SLinus Torvalds 		goto xdr_error;
5921da177e4SLinus Torvalds 	READ64(lockt->lt_offset);
5931da177e4SLinus Torvalds 	READ64(lockt->lt_length);
5941da177e4SLinus Torvalds 	COPYMEM(&lockt->lt_clientid, 8);
5951da177e4SLinus Torvalds 	READ32(lockt->lt_owner.len);
5961da177e4SLinus Torvalds 	READ_BUF(lockt->lt_owner.len);
5971da177e4SLinus Torvalds 	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
5981da177e4SLinus Torvalds 
59960adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&lockt->lt_clientid))
60060adfc50SAndy Adamson 		return nfserr_inval;
6011da177e4SLinus Torvalds 	DECODE_TAIL;
6021da177e4SLinus Torvalds }
6031da177e4SLinus Torvalds 
604b37ad28bSAl Viro static __be32
6051da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
6061da177e4SLinus Torvalds {
6071da177e4SLinus Torvalds 	DECODE_HEAD;
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	locku->lu_stateowner = NULL;
610e31a1b66SBenny Halevy 	READ_BUF(8);
6111da177e4SLinus Torvalds 	READ32(locku->lu_type);
6121da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
6131da177e4SLinus Torvalds 		goto xdr_error;
6141da177e4SLinus Torvalds 	READ32(locku->lu_seqid);
615e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
616e31a1b66SBenny Halevy 	if (status)
617e31a1b66SBenny Halevy 		return status;
618e31a1b66SBenny Halevy 	READ_BUF(16);
6191da177e4SLinus Torvalds 	READ64(locku->lu_offset);
6201da177e4SLinus Torvalds 	READ64(locku->lu_length);
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	DECODE_TAIL;
6231da177e4SLinus Torvalds }
6241da177e4SLinus Torvalds 
625b37ad28bSAl Viro static __be32
6261da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
6271da177e4SLinus Torvalds {
6281da177e4SLinus Torvalds 	DECODE_HEAD;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	READ_BUF(4);
6311da177e4SLinus Torvalds 	READ32(lookup->lo_len);
6321da177e4SLinus Torvalds 	READ_BUF(lookup->lo_len);
6331da177e4SLinus Torvalds 	SAVEMEM(lookup->lo_name, lookup->lo_len);
6341da177e4SLinus Torvalds 	if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent)))
6351da177e4SLinus Torvalds 		return status;
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	DECODE_TAIL;
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds 
640b37ad28bSAl Viro static __be32
6411da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
6421da177e4SLinus Torvalds {
6431da177e4SLinus Torvalds 	DECODE_HEAD;
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 	memset(open->op_bmval, 0, sizeof(open->op_bmval));
6461da177e4SLinus Torvalds 	open->op_iattr.ia_valid = 0;
6471da177e4SLinus Torvalds 	open->op_stateowner = NULL;
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 	/* seqid, share_access, share_deny, clientid, ownerlen */
6501da177e4SLinus Torvalds 	READ_BUF(16 + sizeof(clientid_t));
6511da177e4SLinus Torvalds 	READ32(open->op_seqid);
6521da177e4SLinus Torvalds 	READ32(open->op_share_access);
6531da177e4SLinus Torvalds 	READ32(open->op_share_deny);
6541da177e4SLinus Torvalds 	COPYMEM(&open->op_clientid, sizeof(clientid_t));
6551da177e4SLinus Torvalds 	READ32(open->op_owner.len);
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	/* owner, open_flag */
6581da177e4SLinus Torvalds 	READ_BUF(open->op_owner.len + 4);
6591da177e4SLinus Torvalds 	SAVEMEM(open->op_owner.data, open->op_owner.len);
6601da177e4SLinus Torvalds 	READ32(open->op_create);
6611da177e4SLinus Torvalds 	switch (open->op_create) {
6621da177e4SLinus Torvalds 	case NFS4_OPEN_NOCREATE:
6631da177e4SLinus Torvalds 		break;
6641da177e4SLinus Torvalds 	case NFS4_OPEN_CREATE:
6651da177e4SLinus Torvalds 		READ_BUF(4);
6661da177e4SLinus Torvalds 		READ32(open->op_createmode);
6671da177e4SLinus Torvalds 		switch (open->op_createmode) {
6681da177e4SLinus Torvalds 		case NFS4_CREATE_UNCHECKED:
6691da177e4SLinus Torvalds 		case NFS4_CREATE_GUARDED:
670*c0d6fc8aSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
671*c0d6fc8aSBenny Halevy 				nfsd_attrmask, &open->op_iattr, &open->op_acl);
672*c0d6fc8aSBenny Halevy 			if (status)
6731da177e4SLinus Torvalds 				goto out;
6741da177e4SLinus Torvalds 			break;
6751da177e4SLinus Torvalds 		case NFS4_CREATE_EXCLUSIVE:
6761da177e4SLinus Torvalds 			READ_BUF(8);
6771da177e4SLinus Torvalds 			COPYMEM(open->op_verf.data, 8);
6781da177e4SLinus Torvalds 			break;
6791da177e4SLinus Torvalds 		default:
6801da177e4SLinus Torvalds 			goto xdr_error;
6811da177e4SLinus Torvalds 		}
6821da177e4SLinus Torvalds 		break;
6831da177e4SLinus Torvalds 	default:
6841da177e4SLinus Torvalds 		goto xdr_error;
6851da177e4SLinus Torvalds 	}
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds 	/* open_claim */
6881da177e4SLinus Torvalds 	READ_BUF(4);
6891da177e4SLinus Torvalds 	READ32(open->op_claim_type);
6901da177e4SLinus Torvalds 	switch (open->op_claim_type) {
6911da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_NULL:
6921da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
6931da177e4SLinus Torvalds 		READ_BUF(4);
6941da177e4SLinus Torvalds 		READ32(open->op_fname.len);
6951da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
6961da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
6971da177e4SLinus Torvalds 		if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
6981da177e4SLinus Torvalds 			return status;
6991da177e4SLinus Torvalds 		break;
7001da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_PREVIOUS:
7011da177e4SLinus Torvalds 		READ_BUF(4);
7021da177e4SLinus Torvalds 		READ32(open->op_delegate_type);
7031da177e4SLinus Torvalds 		break;
7041da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
705e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
706e31a1b66SBenny Halevy 		if (status)
707e31a1b66SBenny Halevy 			return status;
708e31a1b66SBenny Halevy 		READ_BUF(4);
7091da177e4SLinus Torvalds 		READ32(open->op_fname.len);
7101da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
7111da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
7121da177e4SLinus Torvalds 		if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
7131da177e4SLinus Torvalds 			return status;
7141da177e4SLinus Torvalds 		break;
7151da177e4SLinus Torvalds 	default:
7161da177e4SLinus Torvalds 		goto xdr_error;
7171da177e4SLinus Torvalds 	}
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds 	DECODE_TAIL;
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds 
722b37ad28bSAl Viro static __be32
7231da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
7241da177e4SLinus Torvalds {
7251da177e4SLinus Torvalds 	DECODE_HEAD;
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 	open_conf->oc_stateowner = NULL;
728e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
729e31a1b66SBenny Halevy 	if (status)
730e31a1b66SBenny Halevy 		return status;
731e31a1b66SBenny Halevy 	READ_BUF(4);
7321da177e4SLinus Torvalds 	READ32(open_conf->oc_seqid);
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds 	DECODE_TAIL;
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
737b37ad28bSAl Viro static __be32
7381da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
7391da177e4SLinus Torvalds {
7401da177e4SLinus Torvalds 	DECODE_HEAD;
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 	open_down->od_stateowner = NULL;
743e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
744e31a1b66SBenny Halevy 	if (status)
745e31a1b66SBenny Halevy 		return status;
746e31a1b66SBenny Halevy 	READ_BUF(12);
7471da177e4SLinus Torvalds 	READ32(open_down->od_seqid);
7481da177e4SLinus Torvalds 	READ32(open_down->od_share_access);
7491da177e4SLinus Torvalds 	READ32(open_down->od_share_deny);
7501da177e4SLinus Torvalds 
7511da177e4SLinus Torvalds 	DECODE_TAIL;
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds 
754b37ad28bSAl Viro static __be32
7551da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
7561da177e4SLinus Torvalds {
7571da177e4SLinus Torvalds 	DECODE_HEAD;
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	READ_BUF(4);
7601da177e4SLinus Torvalds 	READ32(putfh->pf_fhlen);
7611da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
7621da177e4SLinus Torvalds 		goto xdr_error;
7631da177e4SLinus Torvalds 	READ_BUF(putfh->pf_fhlen);
7641da177e4SLinus Torvalds 	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
7651da177e4SLinus Torvalds 
7661da177e4SLinus Torvalds 	DECODE_TAIL;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds 
769b37ad28bSAl Viro static __be32
7701da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
7711da177e4SLinus Torvalds {
7721da177e4SLinus Torvalds 	DECODE_HEAD;
7731da177e4SLinus Torvalds 
774e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
775e31a1b66SBenny Halevy 	if (status)
776e31a1b66SBenny Halevy 		return status;
777e31a1b66SBenny Halevy 	READ_BUF(12);
7781da177e4SLinus Torvalds 	READ64(read->rd_offset);
7791da177e4SLinus Torvalds 	READ32(read->rd_length);
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	DECODE_TAIL;
7821da177e4SLinus Torvalds }
7831da177e4SLinus Torvalds 
784b37ad28bSAl Viro static __be32
7851da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
7861da177e4SLinus Torvalds {
7871da177e4SLinus Torvalds 	DECODE_HEAD;
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds 	READ_BUF(24);
7901da177e4SLinus Torvalds 	READ64(readdir->rd_cookie);
7911da177e4SLinus Torvalds 	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
7921da177e4SLinus Torvalds 	READ32(readdir->rd_dircount);    /* just in case you needed a useless field... */
7931da177e4SLinus Torvalds 	READ32(readdir->rd_maxcount);
7941da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
7951da177e4SLinus Torvalds 		goto out;
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	DECODE_TAIL;
7981da177e4SLinus Torvalds }
7991da177e4SLinus Torvalds 
800b37ad28bSAl Viro static __be32
8011da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
8021da177e4SLinus Torvalds {
8031da177e4SLinus Torvalds 	DECODE_HEAD;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	READ_BUF(4);
8061da177e4SLinus Torvalds 	READ32(remove->rm_namelen);
8071da177e4SLinus Torvalds 	READ_BUF(remove->rm_namelen);
8081da177e4SLinus Torvalds 	SAVEMEM(remove->rm_name, remove->rm_namelen);
8091da177e4SLinus Torvalds 	if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent)))
8101da177e4SLinus Torvalds 		return status;
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds 	DECODE_TAIL;
8131da177e4SLinus Torvalds }
8141da177e4SLinus Torvalds 
815b37ad28bSAl Viro static __be32
8161da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
8171da177e4SLinus Torvalds {
8181da177e4SLinus Torvalds 	DECODE_HEAD;
8191da177e4SLinus Torvalds 
8201da177e4SLinus Torvalds 	READ_BUF(4);
8211da177e4SLinus Torvalds 	READ32(rename->rn_snamelen);
8221da177e4SLinus Torvalds 	READ_BUF(rename->rn_snamelen + 4);
8231da177e4SLinus Torvalds 	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
8241da177e4SLinus Torvalds 	READ32(rename->rn_tnamelen);
8251da177e4SLinus Torvalds 	READ_BUF(rename->rn_tnamelen);
8261da177e4SLinus Torvalds 	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
8271da177e4SLinus Torvalds 	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent)))
8281da177e4SLinus Torvalds 		return status;
8291da177e4SLinus Torvalds 	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval)))
8301da177e4SLinus Torvalds 		return status;
8311da177e4SLinus Torvalds 
8321da177e4SLinus Torvalds 	DECODE_TAIL;
8331da177e4SLinus Torvalds }
8341da177e4SLinus Torvalds 
835b37ad28bSAl Viro static __be32
8361da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
8371da177e4SLinus Torvalds {
8381da177e4SLinus Torvalds 	DECODE_HEAD;
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	READ_BUF(sizeof(clientid_t));
8411da177e4SLinus Torvalds 	COPYMEM(clientid, sizeof(clientid_t));
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds 	DECODE_TAIL;
8441da177e4SLinus Torvalds }
8451da177e4SLinus Torvalds 
846b37ad28bSAl Viro static __be32
847dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
848dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
849dcb488a3SAndy Adamson {
850dcb488a3SAndy Adamson 	DECODE_HEAD;
851dcb488a3SAndy Adamson 
852dcb488a3SAndy Adamson 	READ_BUF(4);
853dcb488a3SAndy Adamson 	READ32(secinfo->si_namelen);
854dcb488a3SAndy Adamson 	READ_BUF(secinfo->si_namelen);
855dcb488a3SAndy Adamson 	SAVEMEM(secinfo->si_name, secinfo->si_namelen);
856dcb488a3SAndy Adamson 	status = check_filename(secinfo->si_name, secinfo->si_namelen,
857dcb488a3SAndy Adamson 								nfserr_noent);
858dcb488a3SAndy Adamson 	if (status)
859dcb488a3SAndy Adamson 		return status;
860dcb488a3SAndy Adamson 	DECODE_TAIL;
861dcb488a3SAndy Adamson }
862dcb488a3SAndy Adamson 
863dcb488a3SAndy Adamson static __be32
8641da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
8651da177e4SLinus Torvalds {
866e31a1b66SBenny Halevy 	__be32 status;
8671da177e4SLinus Torvalds 
868e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
869e31a1b66SBenny Halevy 	if (status)
870e31a1b66SBenny Halevy 		return status;
871*c0d6fc8aSBenny Halevy 	return nfsd4_decode_fattr(argp, setattr->sa_bmval, nfsd_attrmask,
872e31a1b66SBenny Halevy 				  &setattr->sa_iattr, &setattr->sa_acl);
8731da177e4SLinus Torvalds }
8741da177e4SLinus Torvalds 
875b37ad28bSAl Viro static __be32
8761da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
8771da177e4SLinus Torvalds {
8781da177e4SLinus Torvalds 	DECODE_HEAD;
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	READ_BUF(12);
8811da177e4SLinus Torvalds 	COPYMEM(setclientid->se_verf.data, 8);
8821da177e4SLinus Torvalds 	READ32(setclientid->se_namelen);
8831da177e4SLinus Torvalds 
8841da177e4SLinus Torvalds 	READ_BUF(setclientid->se_namelen + 8);
8851da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_name, setclientid->se_namelen);
8861da177e4SLinus Torvalds 	READ32(setclientid->se_callback_prog);
8871da177e4SLinus Torvalds 	READ32(setclientid->se_callback_netid_len);
8881da177e4SLinus Torvalds 
8891da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_netid_len + 4);
8901da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
8911da177e4SLinus Torvalds 	READ32(setclientid->se_callback_addr_len);
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_addr_len + 4);
8941da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
8951da177e4SLinus Torvalds 	READ32(setclientid->se_callback_ident);
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 	DECODE_TAIL;
8981da177e4SLinus Torvalds }
8991da177e4SLinus Torvalds 
900b37ad28bSAl Viro static __be32
9011da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
9021da177e4SLinus Torvalds {
9031da177e4SLinus Torvalds 	DECODE_HEAD;
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	READ_BUF(8 + sizeof(nfs4_verifier));
9061da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_clientid, 8);
9071da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier));
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds 	DECODE_TAIL;
9101da177e4SLinus Torvalds }
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds /* Also used for NVERIFY */
913b37ad28bSAl Viro static __be32
9141da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
9151da177e4SLinus Torvalds {
9161da177e4SLinus Torvalds #if 0
9171da177e4SLinus Torvalds 	struct nfsd4_compoundargs save = {
9181da177e4SLinus Torvalds 		.p = argp->p,
9191da177e4SLinus Torvalds 		.end = argp->end,
9201da177e4SLinus Torvalds 		.rqstp = argp->rqstp,
9211da177e4SLinus Torvalds 	};
9221da177e4SLinus Torvalds 	u32             ve_bmval[2];
9231da177e4SLinus Torvalds 	struct iattr    ve_iattr;           /* request */
9241da177e4SLinus Torvalds 	struct nfs4_acl *ve_acl;            /* request */
9251da177e4SLinus Torvalds #endif
9261da177e4SLinus Torvalds 	DECODE_HEAD;
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
9291da177e4SLinus Torvalds 		goto out;
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
9321da177e4SLinus Torvalds 	 * nfsd4_proc_verify; however we still decode here just to return
9331da177e4SLinus Torvalds 	 * correct error in case of bad xdr. */
9341da177e4SLinus Torvalds #if 0
9351da177e4SLinus Torvalds 	status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl);
9361da177e4SLinus Torvalds 	if (status == nfserr_inval) {
9371da177e4SLinus Torvalds 		status = nfserrno(status);
9381da177e4SLinus Torvalds 		goto out;
9391da177e4SLinus Torvalds 	}
9401da177e4SLinus Torvalds #endif
9411da177e4SLinus Torvalds 	READ_BUF(4);
9421da177e4SLinus Torvalds 	READ32(verify->ve_attrlen);
9431da177e4SLinus Torvalds 	READ_BUF(verify->ve_attrlen);
9441da177e4SLinus Torvalds 	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 	DECODE_TAIL;
9471da177e4SLinus Torvalds }
9481da177e4SLinus Torvalds 
949b37ad28bSAl Viro static __be32
9501da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
9511da177e4SLinus Torvalds {
9521da177e4SLinus Torvalds 	int avail;
9531da177e4SLinus Torvalds 	int v;
9541da177e4SLinus Torvalds 	int len;
9551da177e4SLinus Torvalds 	DECODE_HEAD;
9561da177e4SLinus Torvalds 
957e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
958e31a1b66SBenny Halevy 	if (status)
959e31a1b66SBenny Halevy 		return status;
960e31a1b66SBenny Halevy 	READ_BUF(16);
9611da177e4SLinus Torvalds 	READ64(write->wr_offset);
9621da177e4SLinus Torvalds 	READ32(write->wr_stable_how);
9631da177e4SLinus Torvalds 	if (write->wr_stable_how > 2)
9641da177e4SLinus Torvalds 		goto xdr_error;
9651da177e4SLinus Torvalds 	READ32(write->wr_buflen);
9661da177e4SLinus Torvalds 
9671da177e4SLinus Torvalds 	/* Sorry .. no magic macros for this.. *
9681da177e4SLinus Torvalds 	 * READ_BUF(write->wr_buflen);
9691da177e4SLinus Torvalds 	 * SAVEMEM(write->wr_buf, write->wr_buflen);
9701da177e4SLinus Torvalds 	 */
9711da177e4SLinus Torvalds 	avail = (char*)argp->end - (char*)argp->p;
9721da177e4SLinus Torvalds 	if (avail + argp->pagelen < write->wr_buflen) {
973817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n",
974817cb9d4SChuck Lever 				__FILE__, __LINE__);
9751da177e4SLinus Torvalds 		goto xdr_error;
9761da177e4SLinus Torvalds 	}
9773cc03b16SNeilBrown 	argp->rqstp->rq_vec[0].iov_base = p;
9783cc03b16SNeilBrown 	argp->rqstp->rq_vec[0].iov_len = avail;
9791da177e4SLinus Torvalds 	v = 0;
9801da177e4SLinus Torvalds 	len = write->wr_buflen;
9813cc03b16SNeilBrown 	while (len > argp->rqstp->rq_vec[v].iov_len) {
9823cc03b16SNeilBrown 		len -= argp->rqstp->rq_vec[v].iov_len;
9831da177e4SLinus Torvalds 		v++;
9843cc03b16SNeilBrown 		argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]);
9851da177e4SLinus Torvalds 		argp->pagelist++;
9861da177e4SLinus Torvalds 		if (argp->pagelen >= PAGE_SIZE) {
9873cc03b16SNeilBrown 			argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE;
9881da177e4SLinus Torvalds 			argp->pagelen -= PAGE_SIZE;
9891da177e4SLinus Torvalds 		} else {
9903cc03b16SNeilBrown 			argp->rqstp->rq_vec[v].iov_len = argp->pagelen;
9911da177e4SLinus Torvalds 			argp->pagelen -= len;
9921da177e4SLinus Torvalds 		}
9931da177e4SLinus Torvalds 	}
9942ebbc012SAl Viro 	argp->end = (__be32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len);
9952ebbc012SAl Viro 	argp->p = (__be32*)  (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2));
9963cc03b16SNeilBrown 	argp->rqstp->rq_vec[v].iov_len = len;
9971da177e4SLinus Torvalds 	write->wr_vlen = v+1;
9981da177e4SLinus Torvalds 
9991da177e4SLinus Torvalds 	DECODE_TAIL;
10001da177e4SLinus Torvalds }
10011da177e4SLinus Torvalds 
1002b37ad28bSAl Viro static __be32
10031da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
10041da177e4SLinus Torvalds {
10051da177e4SLinus Torvalds 	DECODE_HEAD;
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 	READ_BUF(12);
10081da177e4SLinus Torvalds 	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
10091da177e4SLinus Torvalds 	READ32(rlockowner->rl_owner.len);
10101da177e4SLinus Torvalds 	READ_BUF(rlockowner->rl_owner.len);
10111da177e4SLinus Torvalds 	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
10121da177e4SLinus Torvalds 
101360adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
101460adfc50SAndy Adamson 		return nfserr_inval;
10151da177e4SLinus Torvalds 	DECODE_TAIL;
10161da177e4SLinus Torvalds }
10171da177e4SLinus Torvalds 
1018b37ad28bSAl Viro static __be32
10192db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
10200733d213SAndy Adamson 			 struct nfsd4_exchange_id *exid)
10212db134ebSAndy Adamson {
10220733d213SAndy Adamson 	int dummy;
10230733d213SAndy Adamson 	DECODE_HEAD;
10240733d213SAndy Adamson 
10250733d213SAndy Adamson 	READ_BUF(NFS4_VERIFIER_SIZE);
10260733d213SAndy Adamson 	COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE);
10270733d213SAndy Adamson 
10280733d213SAndy Adamson 	READ_BUF(4);
10290733d213SAndy Adamson 	READ32(exid->clname.len);
10300733d213SAndy Adamson 
10310733d213SAndy Adamson 	READ_BUF(exid->clname.len);
10320733d213SAndy Adamson 	SAVEMEM(exid->clname.data, exid->clname.len);
10330733d213SAndy Adamson 
10340733d213SAndy Adamson 	READ_BUF(4);
10350733d213SAndy Adamson 	READ32(exid->flags);
10360733d213SAndy Adamson 
10370733d213SAndy Adamson 	/* Ignore state_protect4_a */
10380733d213SAndy Adamson 	READ_BUF(4);
10390733d213SAndy Adamson 	READ32(exid->spa_how);
10400733d213SAndy Adamson 	switch (exid->spa_how) {
10410733d213SAndy Adamson 	case SP4_NONE:
10420733d213SAndy Adamson 		break;
10430733d213SAndy Adamson 	case SP4_MACH_CRED:
10440733d213SAndy Adamson 		/* spo_must_enforce */
10450733d213SAndy Adamson 		READ_BUF(4);
10460733d213SAndy Adamson 		READ32(dummy);
10470733d213SAndy Adamson 		READ_BUF(dummy * 4);
10480733d213SAndy Adamson 		p += dummy;
10490733d213SAndy Adamson 
10500733d213SAndy Adamson 		/* spo_must_allow */
10510733d213SAndy Adamson 		READ_BUF(4);
10520733d213SAndy Adamson 		READ32(dummy);
10530733d213SAndy Adamson 		READ_BUF(dummy * 4);
10540733d213SAndy Adamson 		p += dummy;
10550733d213SAndy Adamson 		break;
10560733d213SAndy Adamson 	case SP4_SSV:
10570733d213SAndy Adamson 		/* ssp_ops */
10580733d213SAndy Adamson 		READ_BUF(4);
10590733d213SAndy Adamson 		READ32(dummy);
10600733d213SAndy Adamson 		READ_BUF(dummy * 4);
10610733d213SAndy Adamson 		p += dummy;
10620733d213SAndy Adamson 
10630733d213SAndy Adamson 		READ_BUF(4);
10640733d213SAndy Adamson 		READ32(dummy);
10650733d213SAndy Adamson 		READ_BUF(dummy * 4);
10660733d213SAndy Adamson 		p += dummy;
10670733d213SAndy Adamson 
10680733d213SAndy Adamson 		/* ssp_hash_algs<> */
10690733d213SAndy Adamson 		READ_BUF(4);
10700733d213SAndy Adamson 		READ32(dummy);
10710733d213SAndy Adamson 		READ_BUF(dummy);
10720733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
10730733d213SAndy Adamson 
10740733d213SAndy Adamson 		/* ssp_encr_algs<> */
10750733d213SAndy Adamson 		READ_BUF(4);
10760733d213SAndy Adamson 		READ32(dummy);
10770733d213SAndy Adamson 		READ_BUF(dummy);
10780733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
10790733d213SAndy Adamson 
10800733d213SAndy Adamson 		/* ssp_window and ssp_num_gss_handles */
10810733d213SAndy Adamson 		READ_BUF(8);
10820733d213SAndy Adamson 		READ32(dummy);
10830733d213SAndy Adamson 		READ32(dummy);
10840733d213SAndy Adamson 		break;
10850733d213SAndy Adamson 	default:
10860733d213SAndy Adamson 		goto xdr_error;
10870733d213SAndy Adamson 	}
10880733d213SAndy Adamson 
10890733d213SAndy Adamson 	/* Ignore Implementation ID */
10900733d213SAndy Adamson 	READ_BUF(4);    /* nfs_impl_id4 array length */
10910733d213SAndy Adamson 	READ32(dummy);
10920733d213SAndy Adamson 
10930733d213SAndy Adamson 	if (dummy > 1)
10940733d213SAndy Adamson 		goto xdr_error;
10950733d213SAndy Adamson 
10960733d213SAndy Adamson 	if (dummy == 1) {
10970733d213SAndy Adamson 		/* nii_domain */
10980733d213SAndy Adamson 		READ_BUF(4);
10990733d213SAndy Adamson 		READ32(dummy);
11000733d213SAndy Adamson 		READ_BUF(dummy);
11010733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
11020733d213SAndy Adamson 
11030733d213SAndy Adamson 		/* nii_name */
11040733d213SAndy Adamson 		READ_BUF(4);
11050733d213SAndy Adamson 		READ32(dummy);
11060733d213SAndy Adamson 		READ_BUF(dummy);
11070733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
11080733d213SAndy Adamson 
11090733d213SAndy Adamson 		/* nii_date */
11100733d213SAndy Adamson 		READ_BUF(12);
11110733d213SAndy Adamson 		p += 3;
11120733d213SAndy Adamson 	}
11130733d213SAndy Adamson 	DECODE_TAIL;
11142db134ebSAndy Adamson }
11152db134ebSAndy Adamson 
11162db134ebSAndy Adamson static __be32
11172db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
11182db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
11192db134ebSAndy Adamson {
1120ec6b5d7bSAndy Adamson 	DECODE_HEAD;
1121ec6b5d7bSAndy Adamson 
1122ec6b5d7bSAndy Adamson 	u32 dummy;
1123ec6b5d7bSAndy Adamson 	char *machine_name;
1124ec6b5d7bSAndy Adamson 	int i;
1125ec6b5d7bSAndy Adamson 	int nr_secflavs;
1126ec6b5d7bSAndy Adamson 
1127ec6b5d7bSAndy Adamson 	READ_BUF(16);
1128ec6b5d7bSAndy Adamson 	COPYMEM(&sess->clientid, 8);
1129ec6b5d7bSAndy Adamson 	READ32(sess->seqid);
1130ec6b5d7bSAndy Adamson 	READ32(sess->flags);
1131ec6b5d7bSAndy Adamson 
1132ec6b5d7bSAndy Adamson 	/* Fore channel attrs */
1133ec6b5d7bSAndy Adamson 	READ_BUF(28);
1134ec6b5d7bSAndy Adamson 	READ32(dummy); /* headerpadsz is always 0 */
1135ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxreq_sz);
1136ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxresp_sz);
1137ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxresp_cached);
1138ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxops);
1139ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxreqs);
1140ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.nr_rdma_attrs);
1141ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs == 1) {
1142ec6b5d7bSAndy Adamson 		READ_BUF(4);
1143ec6b5d7bSAndy Adamson 		READ32(sess->fore_channel.rdma_attrs);
1144ec6b5d7bSAndy Adamson 	} else if (sess->fore_channel.nr_rdma_attrs > 1) {
1145ec6b5d7bSAndy Adamson 		dprintk("Too many fore channel attr bitmaps!\n");
1146ec6b5d7bSAndy Adamson 		goto xdr_error;
1147ec6b5d7bSAndy Adamson 	}
1148ec6b5d7bSAndy Adamson 
1149ec6b5d7bSAndy Adamson 	/* Back channel attrs */
1150ec6b5d7bSAndy Adamson 	READ_BUF(28);
1151ec6b5d7bSAndy Adamson 	READ32(dummy); /* headerpadsz is always 0 */
1152ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxreq_sz);
1153ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxresp_sz);
1154ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxresp_cached);
1155ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxops);
1156ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxreqs);
1157ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.nr_rdma_attrs);
1158ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs == 1) {
1159ec6b5d7bSAndy Adamson 		READ_BUF(4);
1160ec6b5d7bSAndy Adamson 		READ32(sess->back_channel.rdma_attrs);
1161ec6b5d7bSAndy Adamson 	} else if (sess->back_channel.nr_rdma_attrs > 1) {
1162ec6b5d7bSAndy Adamson 		dprintk("Too many back channel attr bitmaps!\n");
1163ec6b5d7bSAndy Adamson 		goto xdr_error;
1164ec6b5d7bSAndy Adamson 	}
1165ec6b5d7bSAndy Adamson 
1166ec6b5d7bSAndy Adamson 	READ_BUF(8);
1167ec6b5d7bSAndy Adamson 	READ32(sess->callback_prog);
1168ec6b5d7bSAndy Adamson 
1169ec6b5d7bSAndy Adamson 	/* callback_sec_params4 */
1170ec6b5d7bSAndy Adamson 	READ32(nr_secflavs);
1171ec6b5d7bSAndy Adamson 	for (i = 0; i < nr_secflavs; ++i) {
1172ec6b5d7bSAndy Adamson 		READ_BUF(4);
1173ec6b5d7bSAndy Adamson 		READ32(dummy);
1174ec6b5d7bSAndy Adamson 		switch (dummy) {
1175ec6b5d7bSAndy Adamson 		case RPC_AUTH_NULL:
1176ec6b5d7bSAndy Adamson 			/* Nothing to read */
1177ec6b5d7bSAndy Adamson 			break;
1178ec6b5d7bSAndy Adamson 		case RPC_AUTH_UNIX:
1179ec6b5d7bSAndy Adamson 			READ_BUF(8);
1180ec6b5d7bSAndy Adamson 			/* stamp */
1181ec6b5d7bSAndy Adamson 			READ32(dummy);
1182ec6b5d7bSAndy Adamson 
1183ec6b5d7bSAndy Adamson 			/* machine name */
1184ec6b5d7bSAndy Adamson 			READ32(dummy);
1185ec6b5d7bSAndy Adamson 			READ_BUF(dummy);
1186ec6b5d7bSAndy Adamson 			SAVEMEM(machine_name, dummy);
1187ec6b5d7bSAndy Adamson 
1188ec6b5d7bSAndy Adamson 			/* uid, gid */
1189ec6b5d7bSAndy Adamson 			READ_BUF(8);
1190ec6b5d7bSAndy Adamson 			READ32(sess->uid);
1191ec6b5d7bSAndy Adamson 			READ32(sess->gid);
1192ec6b5d7bSAndy Adamson 
1193ec6b5d7bSAndy Adamson 			/* more gids */
1194ec6b5d7bSAndy Adamson 			READ_BUF(4);
1195ec6b5d7bSAndy Adamson 			READ32(dummy);
1196ec6b5d7bSAndy Adamson 			READ_BUF(dummy * 4);
1197ec6b5d7bSAndy Adamson 			for (i = 0; i < dummy; ++i)
1198ec6b5d7bSAndy Adamson 				READ32(dummy);
1199ec6b5d7bSAndy Adamson 			break;
1200ec6b5d7bSAndy Adamson 		case RPC_AUTH_GSS:
1201ec6b5d7bSAndy Adamson 			dprintk("RPC_AUTH_GSS callback secflavor "
1202ec6b5d7bSAndy Adamson 				"not supported!\n");
1203ec6b5d7bSAndy Adamson 			READ_BUF(8);
1204ec6b5d7bSAndy Adamson 			/* gcbp_service */
1205ec6b5d7bSAndy Adamson 			READ32(dummy);
1206ec6b5d7bSAndy Adamson 			/* gcbp_handle_from_server */
1207ec6b5d7bSAndy Adamson 			READ32(dummy);
1208ec6b5d7bSAndy Adamson 			READ_BUF(dummy);
1209ec6b5d7bSAndy Adamson 			p += XDR_QUADLEN(dummy);
1210ec6b5d7bSAndy Adamson 			/* gcbp_handle_from_client */
1211ec6b5d7bSAndy Adamson 			READ_BUF(4);
1212ec6b5d7bSAndy Adamson 			READ32(dummy);
1213ec6b5d7bSAndy Adamson 			READ_BUF(dummy);
1214ec6b5d7bSAndy Adamson 			p += XDR_QUADLEN(dummy);
1215ec6b5d7bSAndy Adamson 			break;
1216ec6b5d7bSAndy Adamson 		default:
1217ec6b5d7bSAndy Adamson 			dprintk("Illegal callback secflavor\n");
1218ec6b5d7bSAndy Adamson 			return nfserr_inval;
1219ec6b5d7bSAndy Adamson 		}
1220ec6b5d7bSAndy Adamson 	}
1221ec6b5d7bSAndy Adamson 	DECODE_TAIL;
12222db134ebSAndy Adamson }
12232db134ebSAndy Adamson 
12242db134ebSAndy Adamson static __be32
12252db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
12262db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
12272db134ebSAndy Adamson {
1228e10e0cfcSBenny Halevy 	DECODE_HEAD;
1229e10e0cfcSBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN);
1230e10e0cfcSBenny Halevy 	COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1231e10e0cfcSBenny Halevy 
1232e10e0cfcSBenny Halevy 	DECODE_TAIL;
12332db134ebSAndy Adamson }
12342db134ebSAndy Adamson 
12352db134ebSAndy Adamson static __be32
12362db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
12372db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
12382db134ebSAndy Adamson {
1239b85d4c01SBenny Halevy 	DECODE_HEAD;
1240b85d4c01SBenny Halevy 
1241b85d4c01SBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
1242b85d4c01SBenny Halevy 	COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1243b85d4c01SBenny Halevy 	READ32(seq->seqid);
1244b85d4c01SBenny Halevy 	READ32(seq->slotid);
1245b85d4c01SBenny Halevy 	READ32(seq->maxslots);
1246b85d4c01SBenny Halevy 	READ32(seq->cachethis);
1247b85d4c01SBenny Halevy 
1248b85d4c01SBenny Halevy 	DECODE_TAIL;
12492db134ebSAndy Adamson }
12502db134ebSAndy Adamson 
12512db134ebSAndy Adamson static __be32
1252347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
1253347e0ad9SBenny Halevy {
1254347e0ad9SBenny Halevy 	return nfs_ok;
1255347e0ad9SBenny Halevy }
1256347e0ad9SBenny Halevy 
12573c375c6fSBenny Halevy static __be32
12583c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p)
12593c375c6fSBenny Halevy {
12601e685ec2SBenny Halevy 	return nfserr_notsupp;
12613c375c6fSBenny Halevy }
12623c375c6fSBenny Halevy 
1263347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *);
1264347e0ad9SBenny Halevy 
1265347e0ad9SBenny Halevy static nfsd4_dec nfsd4_dec_ops[] = {
1266ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
1267ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
1268ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
1269ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
1270ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
1271ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
1272ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
1273ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
1274ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
1275ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
1276ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
1277ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
1278ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
1279ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
1280ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1281ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
1282ad1060c8SJ. Bruce Fields 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
1283ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_open_confirm,
1284ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
1285ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
1286a1c8c4d1SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_noop,
1287ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
1288ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
1289ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
1290ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
1291ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
1292ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
1293ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_renew,
1294ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
1295ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
1296ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
1297ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
1298ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_setclientid,
1299ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm,
1300ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1301ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
1302ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_release_lockowner,
1303347e0ad9SBenny Halevy };
1304347e0ad9SBenny Halevy 
13052db134ebSAndy Adamson static nfsd4_dec nfsd41_dec_ops[] = {
13062db134ebSAndy Adamson 	[OP_ACCESS]		(nfsd4_dec)nfsd4_decode_access,
13072db134ebSAndy Adamson 	[OP_CLOSE]		(nfsd4_dec)nfsd4_decode_close,
13082db134ebSAndy Adamson 	[OP_COMMIT]		(nfsd4_dec)nfsd4_decode_commit,
13092db134ebSAndy Adamson 	[OP_CREATE]		(nfsd4_dec)nfsd4_decode_create,
13102db134ebSAndy Adamson 	[OP_DELEGPURGE]		(nfsd4_dec)nfsd4_decode_notsupp,
13112db134ebSAndy Adamson 	[OP_DELEGRETURN]	(nfsd4_dec)nfsd4_decode_delegreturn,
13122db134ebSAndy Adamson 	[OP_GETATTR]		(nfsd4_dec)nfsd4_decode_getattr,
13132db134ebSAndy Adamson 	[OP_GETFH]		(nfsd4_dec)nfsd4_decode_noop,
13142db134ebSAndy Adamson 	[OP_LINK]		(nfsd4_dec)nfsd4_decode_link,
13152db134ebSAndy Adamson 	[OP_LOCK]		(nfsd4_dec)nfsd4_decode_lock,
13162db134ebSAndy Adamson 	[OP_LOCKT]		(nfsd4_dec)nfsd4_decode_lockt,
13172db134ebSAndy Adamson 	[OP_LOCKU]		(nfsd4_dec)nfsd4_decode_locku,
13182db134ebSAndy Adamson 	[OP_LOOKUP]		(nfsd4_dec)nfsd4_decode_lookup,
13192db134ebSAndy Adamson 	[OP_LOOKUPP]		(nfsd4_dec)nfsd4_decode_noop,
13202db134ebSAndy Adamson 	[OP_NVERIFY]		(nfsd4_dec)nfsd4_decode_verify,
13212db134ebSAndy Adamson 	[OP_OPEN]		(nfsd4_dec)nfsd4_decode_open,
13222db134ebSAndy Adamson 	[OP_OPENATTR]		(nfsd4_dec)nfsd4_decode_notsupp,
13232db134ebSAndy Adamson 	[OP_OPEN_CONFIRM]	(nfsd4_dec)nfsd4_decode_notsupp,
13242db134ebSAndy Adamson 	[OP_OPEN_DOWNGRADE]	(nfsd4_dec)nfsd4_decode_open_downgrade,
13252db134ebSAndy Adamson 	[OP_PUTFH]		(nfsd4_dec)nfsd4_decode_putfh,
13262db134ebSAndy Adamson 	[OP_PUTPUBFH]		(nfsd4_dec)nfsd4_decode_notsupp,
13272db134ebSAndy Adamson 	[OP_PUTROOTFH]		(nfsd4_dec)nfsd4_decode_noop,
13282db134ebSAndy Adamson 	[OP_READ]		(nfsd4_dec)nfsd4_decode_read,
13292db134ebSAndy Adamson 	[OP_READDIR]		(nfsd4_dec)nfsd4_decode_readdir,
13302db134ebSAndy Adamson 	[OP_READLINK]		(nfsd4_dec)nfsd4_decode_noop,
13312db134ebSAndy Adamson 	[OP_REMOVE]		(nfsd4_dec)nfsd4_decode_remove,
13322db134ebSAndy Adamson 	[OP_RENAME]		(nfsd4_dec)nfsd4_decode_rename,
13332db134ebSAndy Adamson 	[OP_RENEW]		(nfsd4_dec)nfsd4_decode_notsupp,
13342db134ebSAndy Adamson 	[OP_RESTOREFH]		(nfsd4_dec)nfsd4_decode_noop,
13352db134ebSAndy Adamson 	[OP_SAVEFH]		(nfsd4_dec)nfsd4_decode_noop,
13362db134ebSAndy Adamson 	[OP_SECINFO]		(nfsd4_dec)nfsd4_decode_secinfo,
13372db134ebSAndy Adamson 	[OP_SETATTR]		(nfsd4_dec)nfsd4_decode_setattr,
13382db134ebSAndy Adamson 	[OP_SETCLIENTID]	(nfsd4_dec)nfsd4_decode_notsupp,
13392db134ebSAndy Adamson 	[OP_SETCLIENTID_CONFIRM](nfsd4_dec)nfsd4_decode_notsupp,
13402db134ebSAndy Adamson 	[OP_VERIFY]		(nfsd4_dec)nfsd4_decode_verify,
13412db134ebSAndy Adamson 	[OP_WRITE]		(nfsd4_dec)nfsd4_decode_write,
13422db134ebSAndy Adamson 	[OP_RELEASE_LOCKOWNER]	(nfsd4_dec)nfsd4_decode_notsupp,
13432db134ebSAndy Adamson 
13442db134ebSAndy Adamson 	/* new operations for NFSv4.1 */
13452db134ebSAndy Adamson 	[OP_BACKCHANNEL_CTL]	(nfsd4_dec)nfsd4_decode_notsupp,
13462db134ebSAndy Adamson 	[OP_BIND_CONN_TO_SESSION](nfsd4_dec)nfsd4_decode_notsupp,
13472db134ebSAndy Adamson 	[OP_EXCHANGE_ID]	(nfsd4_dec)nfsd4_decode_exchange_id,
13482db134ebSAndy Adamson 	[OP_CREATE_SESSION]	(nfsd4_dec)nfsd4_decode_create_session,
13492db134ebSAndy Adamson 	[OP_DESTROY_SESSION]	(nfsd4_dec)nfsd4_decode_destroy_session,
13502db134ebSAndy Adamson 	[OP_FREE_STATEID]	(nfsd4_dec)nfsd4_decode_notsupp,
13512db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	(nfsd4_dec)nfsd4_decode_notsupp,
13522db134ebSAndy Adamson 	[OP_GETDEVICEINFO]	(nfsd4_dec)nfsd4_decode_notsupp,
13532db134ebSAndy Adamson 	[OP_GETDEVICELIST]	(nfsd4_dec)nfsd4_decode_notsupp,
13542db134ebSAndy Adamson 	[OP_LAYOUTCOMMIT]	(nfsd4_dec)nfsd4_decode_notsupp,
13552db134ebSAndy Adamson 	[OP_LAYOUTGET]		(nfsd4_dec)nfsd4_decode_notsupp,
13562db134ebSAndy Adamson 	[OP_LAYOUTRETURN]	(nfsd4_dec)nfsd4_decode_notsupp,
13572db134ebSAndy Adamson 	[OP_SECINFO_NO_NAME]	(nfsd4_dec)nfsd4_decode_notsupp,
13582db134ebSAndy Adamson 	[OP_SEQUENCE]		(nfsd4_dec)nfsd4_decode_sequence,
13592db134ebSAndy Adamson 	[OP_SET_SSV]		(nfsd4_dec)nfsd4_decode_notsupp,
13602db134ebSAndy Adamson 	[OP_TEST_STATEID]	(nfsd4_dec)nfsd4_decode_notsupp,
13612db134ebSAndy Adamson 	[OP_WANT_DELEGATION]	(nfsd4_dec)nfsd4_decode_notsupp,
13622db134ebSAndy Adamson 	[OP_DESTROY_CLIENTID]	(nfsd4_dec)nfsd4_decode_notsupp,
13632db134ebSAndy Adamson 	[OP_RECLAIM_COMPLETE]	(nfsd4_dec)nfsd4_decode_notsupp,
13642db134ebSAndy Adamson };
13652db134ebSAndy Adamson 
1366f2feb96bSBenny Halevy struct nfsd4_minorversion_ops {
1367f2feb96bSBenny Halevy 	nfsd4_dec *decoders;
1368f2feb96bSBenny Halevy 	int nops;
1369f2feb96bSBenny Halevy };
1370f2feb96bSBenny Halevy 
1371f2feb96bSBenny Halevy static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
1372ad1060c8SJ. Bruce Fields 	[0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
13732db134ebSAndy Adamson 	[1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
1374f2feb96bSBenny Halevy };
1375f2feb96bSBenny Halevy 
1376347e0ad9SBenny Halevy static __be32
13771da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
13781da177e4SLinus Torvalds {
13791da177e4SLinus Torvalds 	DECODE_HEAD;
13801da177e4SLinus Torvalds 	struct nfsd4_op *op;
1381f2feb96bSBenny Halevy 	struct nfsd4_minorversion_ops *ops;
13821da177e4SLinus Torvalds 	int i;
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 	/*
13851da177e4SLinus Torvalds 	 * XXX: According to spec, we should check the tag
13861da177e4SLinus Torvalds 	 * for UTF-8 compliance.  I'm postponing this for
13871da177e4SLinus Torvalds 	 * now because it seems that some clients do use
13881da177e4SLinus Torvalds 	 * binary tags.
13891da177e4SLinus Torvalds 	 */
13901da177e4SLinus Torvalds 	READ_BUF(4);
13911da177e4SLinus Torvalds 	READ32(argp->taglen);
13921da177e4SLinus Torvalds 	READ_BUF(argp->taglen + 8);
13931da177e4SLinus Torvalds 	SAVEMEM(argp->tag, argp->taglen);
13941da177e4SLinus Torvalds 	READ32(argp->minorversion);
13951da177e4SLinus Torvalds 	READ32(argp->opcnt);
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds 	if (argp->taglen > NFSD4_MAX_TAGLEN)
13981da177e4SLinus Torvalds 		goto xdr_error;
13991da177e4SLinus Torvalds 	if (argp->opcnt > 100)
14001da177e4SLinus Torvalds 		goto xdr_error;
14011da177e4SLinus Torvalds 
1402e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
14031da177e4SLinus Torvalds 		argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
14041da177e4SLinus Torvalds 		if (!argp->ops) {
14051da177e4SLinus Torvalds 			argp->ops = argp->iops;
1406817cb9d4SChuck Lever 			dprintk("nfsd: couldn't allocate room for COMPOUND\n");
14071da177e4SLinus Torvalds 			goto xdr_error;
14081da177e4SLinus Torvalds 		}
14091da177e4SLinus Torvalds 	}
14101da177e4SLinus Torvalds 
1411f2feb96bSBenny Halevy 	if (argp->minorversion >= ARRAY_SIZE(nfsd4_minorversion))
141230cff1ffSBenny Halevy 		argp->opcnt = 0;
141330cff1ffSBenny Halevy 
1414f2feb96bSBenny Halevy 	ops = &nfsd4_minorversion[argp->minorversion];
14151da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
14161da177e4SLinus Torvalds 		op = &argp->ops[i];
14171da177e4SLinus Torvalds 		op->replay = NULL;
14181da177e4SLinus Torvalds 
14191da177e4SLinus Torvalds 		/*
14201da177e4SLinus Torvalds 		 * We can't use READ_BUF() here because we need to handle
14211da177e4SLinus Torvalds 		 * a missing opcode as an OP_WRITE + 1. So we need to check
14221da177e4SLinus Torvalds 		 * to see if we're truly at the end of our buffer or if there
14231da177e4SLinus Torvalds 		 * is another page we need to flip to.
14241da177e4SLinus Torvalds 		 */
14251da177e4SLinus Torvalds 
14261da177e4SLinus Torvalds 		if (argp->p == argp->end) {
14271da177e4SLinus Torvalds 			if (argp->pagelen < 4) {
14281da177e4SLinus Torvalds 				/* There isn't an opcode still on the wire */
14291da177e4SLinus Torvalds 				op->opnum = OP_WRITE + 1;
14301da177e4SLinus Torvalds 				op->status = nfserr_bad_xdr;
14311da177e4SLinus Torvalds 				argp->opcnt = i+1;
14321da177e4SLinus Torvalds 				break;
14331da177e4SLinus Torvalds 			}
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds 			/*
14361da177e4SLinus Torvalds 			 * False alarm. We just hit a page boundary, but there
14371da177e4SLinus Torvalds 			 * is still data available.  Move pointer across page
14381da177e4SLinus Torvalds 			 * boundary.  *snip from READ_BUF*
14391da177e4SLinus Torvalds 			 */
14401da177e4SLinus Torvalds 			argp->p = page_address(argp->pagelist[0]);
14411da177e4SLinus Torvalds 			argp->pagelist++;
14421da177e4SLinus Torvalds 			if (argp->pagelen < PAGE_SIZE) {
14431da177e4SLinus Torvalds 				argp->end = p + (argp->pagelen>>2);
14441da177e4SLinus Torvalds 				argp->pagelen = 0;
14451da177e4SLinus Torvalds 			} else {
14461da177e4SLinus Torvalds 				argp->end = p + (PAGE_SIZE>>2);
14471da177e4SLinus Torvalds 				argp->pagelen -= PAGE_SIZE;
14481da177e4SLinus Torvalds 			}
14491da177e4SLinus Torvalds 		}
14501da177e4SLinus Torvalds 		op->opnum = ntohl(*argp->p++);
14511da177e4SLinus Torvalds 
1452f2feb96bSBenny Halevy 		if (op->opnum >= OP_ACCESS && op->opnum < ops->nops)
1453f2feb96bSBenny Halevy 			op->status = ops->decoders[op->opnum](argp, &op->u);
1454347e0ad9SBenny Halevy 		else {
14551da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
14561da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
14571da177e4SLinus Torvalds 		}
14581da177e4SLinus Torvalds 
14591da177e4SLinus Torvalds 		if (op->status) {
14601da177e4SLinus Torvalds 			argp->opcnt = i+1;
14611da177e4SLinus Torvalds 			break;
14621da177e4SLinus Torvalds 		}
14631da177e4SLinus Torvalds 	}
14641da177e4SLinus Torvalds 
14651da177e4SLinus Torvalds 	DECODE_TAIL;
14661da177e4SLinus Torvalds }
14671da177e4SLinus Torvalds /*
14681da177e4SLinus Torvalds  * END OF "GENERIC" DECODE ROUTINES.
14691da177e4SLinus Torvalds  */
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds /*
14721da177e4SLinus Torvalds  * START OF "GENERIC" ENCODE ROUTINES.
14731da177e4SLinus Torvalds  *   These may look a little ugly since they are imported from a "generic"
14741da177e4SLinus Torvalds  * set of XDR encode/decode routines which are intended to be shared by
14751da177e4SLinus Torvalds  * all of our NFSv4 implementations (OpenBSD, MacOS X...).
14761da177e4SLinus Torvalds  *
14771da177e4SLinus Torvalds  * If the pain of reading these is too great, it should be a straightforward
14781da177e4SLinus Torvalds  * task to translate them into Linux-specific versions which are more
14791da177e4SLinus Torvalds  * consistent with the style used in NFSv2/v3...
14801da177e4SLinus Torvalds  */
14812ebbc012SAl Viro #define ENCODE_HEAD              __be32 *p
14821da177e4SLinus Torvalds 
14831da177e4SLinus Torvalds #define WRITE32(n)               *p++ = htonl(n)
14841da177e4SLinus Torvalds #define WRITE64(n)               do {				\
14851da177e4SLinus Torvalds 	*p++ = htonl((u32)((n) >> 32));				\
14861da177e4SLinus Torvalds 	*p++ = htonl((u32)(n));					\
14871da177e4SLinus Torvalds } while (0)
14885108b276SHarvey Harrison #define WRITEMEM(ptr,nbytes)     do { if (nbytes > 0) {		\
14891da177e4SLinus Torvalds 	*(p + XDR_QUADLEN(nbytes) -1) = 0;                      \
14901da177e4SLinus Torvalds 	memcpy(p, ptr, nbytes);					\
14911da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);				\
14925108b276SHarvey Harrison }} while (0)
14931da177e4SLinus Torvalds #define WRITECINFO(c)		do {				\
14941da177e4SLinus Torvalds 	*p++ = htonl(c.atomic);					\
14951da177e4SLinus Torvalds 	*p++ = htonl(c.before_ctime_sec);				\
14961da177e4SLinus Torvalds 	*p++ = htonl(c.before_ctime_nsec);				\
14971da177e4SLinus Torvalds 	*p++ = htonl(c.after_ctime_sec);				\
14981da177e4SLinus Torvalds 	*p++ = htonl(c.after_ctime_nsec);				\
14991da177e4SLinus Torvalds } while (0)
15001da177e4SLinus Torvalds 
15011da177e4SLinus Torvalds #define RESERVE_SPACE(nbytes)	do {				\
15021da177e4SLinus Torvalds 	p = resp->p;						\
15031da177e4SLinus Torvalds 	BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end);		\
15041da177e4SLinus Torvalds } while (0)
15051da177e4SLinus Torvalds #define ADJUST_ARGS()		resp->p = p
15061da177e4SLinus Torvalds 
15071da177e4SLinus Torvalds /*
15081da177e4SLinus Torvalds  * Header routine to setup seqid operation replay cache
15091da177e4SLinus Torvalds  */
15101da177e4SLinus Torvalds #define ENCODE_SEQID_OP_HEAD					\
15112ebbc012SAl Viro 	__be32 *save;						\
15121da177e4SLinus Torvalds 								\
15131da177e4SLinus Torvalds 	save = resp->p;
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds /*
15167fb64ceeSNeilBrown  * Routine for encoding the result of a "seqid-mutating" NFSv4 operation.  This
15177fb64ceeSNeilBrown  * is where sequence id's are incremented, and the replay cache is filled.
15187fb64ceeSNeilBrown  * Note that we increment sequence id's here, at the last moment, so we're sure
15197fb64ceeSNeilBrown  * we know whether the error to be returned is a sequence id mutating error.
15201da177e4SLinus Torvalds  */
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds #define ENCODE_SEQID_OP_TAIL(stateowner) do {			\
15231da177e4SLinus Torvalds 	if (seqid_mutating_err(nfserr) && stateowner) { 	\
15241da177e4SLinus Torvalds 		stateowner->so_seqid++;				\
15251da177e4SLinus Torvalds 		stateowner->so_replay.rp_status = nfserr;   	\
15261da177e4SLinus Torvalds 		stateowner->so_replay.rp_buflen = 		\
15271da177e4SLinus Torvalds 			  (((char *)(resp)->p - (char *)save)); \
15281da177e4SLinus Torvalds 		memcpy(stateowner->so_replay.rp_buf, save,      \
15291da177e4SLinus Torvalds  			stateowner->so_replay.rp_buflen); 	\
15301da177e4SLinus Torvalds 	} } while (0);
15311da177e4SLinus Torvalds 
153281c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
153381c3f413SJ.Bruce Fields  * seperated @sep.
153481c3f413SJ.Bruce Fields  */
1535b37ad28bSAl Viro static __be32 nfsd4_encode_components(char sep, char *components,
15362ebbc012SAl Viro 				   __be32 **pp, int *buflen)
153781c3f413SJ.Bruce Fields {
15382ebbc012SAl Viro 	__be32 *p = *pp;
15392ebbc012SAl Viro 	__be32 *countp = p;
154081c3f413SJ.Bruce Fields 	int strlen, count=0;
154181c3f413SJ.Bruce Fields 	char *str, *end;
154281c3f413SJ.Bruce Fields 
154381c3f413SJ.Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
154481c3f413SJ.Bruce Fields 	if ((*buflen -= 4) < 0)
154581c3f413SJ.Bruce Fields 		return nfserr_resource;
154681c3f413SJ.Bruce Fields 	WRITE32(0); /* We will fill this in with @count later */
154781c3f413SJ.Bruce Fields 	end = str = components;
154881c3f413SJ.Bruce Fields 	while (*end) {
154981c3f413SJ.Bruce Fields 		for (; *end && (*end != sep); end++)
155081c3f413SJ.Bruce Fields 			; /* Point to end of component */
155181c3f413SJ.Bruce Fields 		strlen = end - str;
155281c3f413SJ.Bruce Fields 		if (strlen) {
155381c3f413SJ.Bruce Fields 			if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
155481c3f413SJ.Bruce Fields 				return nfserr_resource;
155581c3f413SJ.Bruce Fields 			WRITE32(strlen);
155681c3f413SJ.Bruce Fields 			WRITEMEM(str, strlen);
155781c3f413SJ.Bruce Fields 			count++;
155881c3f413SJ.Bruce Fields 		}
155981c3f413SJ.Bruce Fields 		else
156081c3f413SJ.Bruce Fields 			end++;
156181c3f413SJ.Bruce Fields 		str = end;
156281c3f413SJ.Bruce Fields 	}
156381c3f413SJ.Bruce Fields 	*pp = p;
156481c3f413SJ.Bruce Fields 	p = countp;
156581c3f413SJ.Bruce Fields 	WRITE32(count);
156681c3f413SJ.Bruce Fields 	return 0;
156781c3f413SJ.Bruce Fields }
156881c3f413SJ.Bruce Fields 
156981c3f413SJ.Bruce Fields /*
157081c3f413SJ.Bruce Fields  * encode a location element of a fs_locations structure
157181c3f413SJ.Bruce Fields  */
1572b37ad28bSAl Viro static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
15732ebbc012SAl Viro 				    __be32 **pp, int *buflen)
157481c3f413SJ.Bruce Fields {
1575b37ad28bSAl Viro 	__be32 status;
15762ebbc012SAl Viro 	__be32 *p = *pp;
157781c3f413SJ.Bruce Fields 
157881c3f413SJ.Bruce Fields 	status = nfsd4_encode_components(':', location->hosts, &p, buflen);
157981c3f413SJ.Bruce Fields 	if (status)
158081c3f413SJ.Bruce Fields 		return status;
158181c3f413SJ.Bruce Fields 	status = nfsd4_encode_components('/', location->path, &p, buflen);
158281c3f413SJ.Bruce Fields 	if (status)
158381c3f413SJ.Bruce Fields 		return status;
158481c3f413SJ.Bruce Fields 	*pp = p;
158581c3f413SJ.Bruce Fields 	return 0;
158681c3f413SJ.Bruce Fields }
158781c3f413SJ.Bruce Fields 
158881c3f413SJ.Bruce Fields /*
158981c3f413SJ.Bruce Fields  * Return the path to an export point in the pseudo filesystem namespace
159081c3f413SJ.Bruce Fields  * Returned string is safe to use as long as the caller holds a reference
159181c3f413SJ.Bruce Fields  * to @exp.
159281c3f413SJ.Bruce Fields  */
1593b37ad28bSAl Viro static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
159481c3f413SJ.Bruce Fields {
159581c3f413SJ.Bruce Fields 	struct svc_fh tmp_fh;
159681c3f413SJ.Bruce Fields 	char *path, *rootpath;
159781c3f413SJ.Bruce Fields 
159881c3f413SJ.Bruce Fields 	fh_init(&tmp_fh, NFS4_FHSIZE);
1599df547efbSJ. Bruce Fields 	*stat = exp_pseudoroot(rqstp, &tmp_fh);
1600cc45f017SAl Viro 	if (*stat)
1601cc45f017SAl Viro 		return NULL;
160254775491SJan Blunck 	rootpath = tmp_fh.fh_export->ex_pathname;
160381c3f413SJ.Bruce Fields 
160454775491SJan Blunck 	path = exp->ex_pathname;
160581c3f413SJ.Bruce Fields 
160681c3f413SJ.Bruce Fields 	if (strncmp(path, rootpath, strlen(rootpath))) {
1607817cb9d4SChuck Lever 		dprintk("nfsd: fs_locations failed;"
160881c3f413SJ.Bruce Fields 			"%s is not contained in %s\n", path, rootpath);
1609cc45f017SAl Viro 		*stat = nfserr_notsupp;
1610cc45f017SAl Viro 		return NULL;
161181c3f413SJ.Bruce Fields 	}
161281c3f413SJ.Bruce Fields 
161381c3f413SJ.Bruce Fields 	return path + strlen(rootpath);
161481c3f413SJ.Bruce Fields }
161581c3f413SJ.Bruce Fields 
161681c3f413SJ.Bruce Fields /*
161781c3f413SJ.Bruce Fields  *  encode a fs_locations structure
161881c3f413SJ.Bruce Fields  */
1619b37ad28bSAl Viro static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
162081c3f413SJ.Bruce Fields 				     struct svc_export *exp,
16212ebbc012SAl Viro 				     __be32 **pp, int *buflen)
162281c3f413SJ.Bruce Fields {
1623b37ad28bSAl Viro 	__be32 status;
1624cc45f017SAl Viro 	int i;
16252ebbc012SAl Viro 	__be32 *p = *pp;
162681c3f413SJ.Bruce Fields 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
1627cc45f017SAl Viro 	char *root = nfsd4_path(rqstp, exp, &status);
162881c3f413SJ.Bruce Fields 
1629cc45f017SAl Viro 	if (status)
1630cc45f017SAl Viro 		return status;
163181c3f413SJ.Bruce Fields 	status = nfsd4_encode_components('/', root, &p, buflen);
163281c3f413SJ.Bruce Fields 	if (status)
163381c3f413SJ.Bruce Fields 		return status;
163481c3f413SJ.Bruce Fields 	if ((*buflen -= 4) < 0)
163581c3f413SJ.Bruce Fields 		return nfserr_resource;
163681c3f413SJ.Bruce Fields 	WRITE32(fslocs->locations_count);
163781c3f413SJ.Bruce Fields 	for (i=0; i<fslocs->locations_count; i++) {
163881c3f413SJ.Bruce Fields 		status = nfsd4_encode_fs_location4(&fslocs->locations[i],
163981c3f413SJ.Bruce Fields 						   &p, buflen);
164081c3f413SJ.Bruce Fields 		if (status)
164181c3f413SJ.Bruce Fields 			return status;
164281c3f413SJ.Bruce Fields 	}
164381c3f413SJ.Bruce Fields 	*pp = p;
164481c3f413SJ.Bruce Fields 	return 0;
164581c3f413SJ.Bruce Fields }
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds static u32 nfs4_ftypes[16] = {
16481da177e4SLinus Torvalds         NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
16491da177e4SLinus Torvalds         NF4DIR,  NF4BAD,  NF4BLK, NF4BAD,
16501da177e4SLinus Torvalds         NF4REG,  NF4BAD,  NF4LNK, NF4BAD,
16511da177e4SLinus Torvalds         NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
16521da177e4SLinus Torvalds };
16531da177e4SLinus Torvalds 
1654b37ad28bSAl Viro static __be32
16551da177e4SLinus Torvalds nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
16562ebbc012SAl Viro 			__be32 **p, int *buflen)
16571da177e4SLinus Torvalds {
16581da177e4SLinus Torvalds 	int status;
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds 	if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
16611da177e4SLinus Torvalds 		return nfserr_resource;
16621da177e4SLinus Torvalds 	if (whotype != NFS4_ACL_WHO_NAMED)
16631da177e4SLinus Torvalds 		status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
16641da177e4SLinus Torvalds 	else if (group)
16651da177e4SLinus Torvalds 		status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
16661da177e4SLinus Torvalds 	else
16671da177e4SLinus Torvalds 		status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
16681da177e4SLinus Torvalds 	if (status < 0)
16691da177e4SLinus Torvalds 		return nfserrno(status);
16701da177e4SLinus Torvalds 	*p = xdr_encode_opaque(*p, NULL, status);
16711da177e4SLinus Torvalds 	*buflen -= (XDR_QUADLEN(status) << 2) + 4;
16721da177e4SLinus Torvalds 	BUG_ON(*buflen < 0);
16731da177e4SLinus Torvalds 	return 0;
16741da177e4SLinus Torvalds }
16751da177e4SLinus Torvalds 
1676b37ad28bSAl Viro static inline __be32
16772ebbc012SAl Viro nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen)
16781da177e4SLinus Torvalds {
16791da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
16801da177e4SLinus Torvalds }
16811da177e4SLinus Torvalds 
1682b37ad28bSAl Viro static inline __be32
16832ebbc012SAl Viro nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen)
16841da177e4SLinus Torvalds {
16851da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
16861da177e4SLinus Torvalds }
16871da177e4SLinus Torvalds 
1688b37ad28bSAl Viro static inline __be32
16891da177e4SLinus Torvalds nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
16902ebbc012SAl Viro 		__be32 **p, int *buflen)
16911da177e4SLinus Torvalds {
16921da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
16931da177e4SLinus Torvalds }
16941da177e4SLinus Torvalds 
169542ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
169642ca0993SJ.Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
169742ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
169842ca0993SJ.Bruce Fields 
1699b37ad28bSAl Viro static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
170042ca0993SJ.Bruce Fields {
170142ca0993SJ.Bruce Fields 	/* As per referral draft:  */
170242ca0993SJ.Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
170342ca0993SJ.Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
170442ca0993SJ.Bruce Fields 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
170542ca0993SJ.Bruce Fields 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
170642ca0993SJ.Bruce Fields 			*rdattr_err = NFSERR_MOVED;
170742ca0993SJ.Bruce Fields 		else
170842ca0993SJ.Bruce Fields 			return nfserr_moved;
170942ca0993SJ.Bruce Fields 	}
171042ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
171142ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
171242ca0993SJ.Bruce Fields 	return 0;
171342ca0993SJ.Bruce Fields }
17141da177e4SLinus Torvalds 
17151da177e4SLinus Torvalds /*
17161da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
17171da177e4SLinus Torvalds  * ourselves.
17181da177e4SLinus Torvalds  *
17191da177e4SLinus Torvalds  * @countp is the buffer size in _words_; upon successful return this becomes
17201da177e4SLinus Torvalds  * replaced with the number of words written.
17211da177e4SLinus Torvalds  */
1722b37ad28bSAl Viro __be32
17231da177e4SLinus Torvalds nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
17242ebbc012SAl Viro 		struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval,
1725406a7ea9SFrank Filz 		struct svc_rqst *rqstp, int ignore_crossmnt)
17261da177e4SLinus Torvalds {
17271da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
17281da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
17291da177e4SLinus Torvalds 	struct kstat stat;
17301da177e4SLinus Torvalds 	struct svc_fh tempfh;
17311da177e4SLinus Torvalds 	struct kstatfs statfs;
17321da177e4SLinus Torvalds 	int buflen = *countp << 2;
17332ebbc012SAl Viro 	__be32 *attrlenp;
17341da177e4SLinus Torvalds 	u32 dummy;
17351da177e4SLinus Torvalds 	u64 dummy64;
173642ca0993SJ.Bruce Fields 	u32 rdattr_err = 0;
17372ebbc012SAl Viro 	__be32 *p = buffer;
1738b37ad28bSAl Viro 	__be32 status;
1739b8dd7b9aSAl Viro 	int err;
17401da177e4SLinus Torvalds 	int aclsupport = 0;
17411da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
17421da177e4SLinus Torvalds 
17431da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
17441da177e4SLinus Torvalds 	BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
17451da177e4SLinus Torvalds 	BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1);
17461da177e4SLinus Torvalds 
174742ca0993SJ.Bruce Fields 	if (exp->ex_fslocs.migrated) {
174842ca0993SJ.Bruce Fields 		status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err);
174942ca0993SJ.Bruce Fields 		if (status)
175042ca0993SJ.Bruce Fields 			goto out;
175142ca0993SJ.Bruce Fields 	}
175242ca0993SJ.Bruce Fields 
175354775491SJan Blunck 	err = vfs_getattr(exp->ex_path.mnt, dentry, &stat);
1754b8dd7b9aSAl Viro 	if (err)
17551da177e4SLinus Torvalds 		goto out_nfserr;
1756a16e92edSJ. Bruce Fields 	if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL |
1757a16e92edSJ. Bruce Fields 			FATTR4_WORD0_MAXNAME)) ||
17581da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
17591da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
1760b8dd7b9aSAl Viro 		err = vfs_statfs(dentry, &statfs);
1761b8dd7b9aSAl Viro 		if (err)
17621da177e4SLinus Torvalds 			goto out_nfserr;
17631da177e4SLinus Torvalds 	}
17641da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
17651da177e4SLinus Torvalds 		fh_init(&tempfh, NFS4_FHSIZE);
17661da177e4SLinus Torvalds 		status = fh_compose(&tempfh, exp, dentry, NULL);
17671da177e4SLinus Torvalds 		if (status)
17681da177e4SLinus Torvalds 			goto out;
17691da177e4SLinus Torvalds 		fhp = &tempfh;
17701da177e4SLinus Torvalds 	}
17711da177e4SLinus Torvalds 	if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
17721da177e4SLinus Torvalds 			| FATTR4_WORD0_SUPPORTED_ATTRS)) {
1773b8dd7b9aSAl Viro 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
1774b8dd7b9aSAl Viro 		aclsupport = (err == 0);
17751da177e4SLinus Torvalds 		if (bmval0 & FATTR4_WORD0_ACL) {
1776b8dd7b9aSAl Viro 			if (err == -EOPNOTSUPP)
17771da177e4SLinus Torvalds 				bmval0 &= ~FATTR4_WORD0_ACL;
1778b8dd7b9aSAl Viro 			else if (err == -EINVAL) {
17791da177e4SLinus Torvalds 				status = nfserr_attrnotsupp;
17801da177e4SLinus Torvalds 				goto out;
1781b8dd7b9aSAl Viro 			} else if (err != 0)
17821da177e4SLinus Torvalds 				goto out_nfserr;
17831da177e4SLinus Torvalds 		}
17841da177e4SLinus Torvalds 	}
178581c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
178681c3f413SJ.Bruce Fields 		if (exp->ex_fslocs.locations == NULL) {
178781c3f413SJ.Bruce Fields 			bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS;
178881c3f413SJ.Bruce Fields 		}
178981c3f413SJ.Bruce Fields 	}
17901da177e4SLinus Torvalds 	if ((buflen -= 16) < 0)
17911da177e4SLinus Torvalds 		goto out_resource;
17921da177e4SLinus Torvalds 
17931da177e4SLinus Torvalds 	WRITE32(2);
17941da177e4SLinus Torvalds 	WRITE32(bmval0);
17951da177e4SLinus Torvalds 	WRITE32(bmval1);
17961da177e4SLinus Torvalds 	attrlenp = p++;                /* to be backfilled later */
17971da177e4SLinus Torvalds 
17981da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
179942ca0993SJ.Bruce Fields 		u32 word0 = NFSD_SUPPORTED_ATTRS_WORD0;
18001da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
18011da177e4SLinus Torvalds 			goto out_resource;
180242ca0993SJ.Bruce Fields 		if (!aclsupport)
180342ca0993SJ.Bruce Fields 			word0 &= ~FATTR4_WORD0_ACL;
180442ca0993SJ.Bruce Fields 		if (!exp->ex_fslocs.locations)
180542ca0993SJ.Bruce Fields 			word0 &= ~FATTR4_WORD0_FS_LOCATIONS;
18061da177e4SLinus Torvalds 		WRITE32(2);
180742ca0993SJ.Bruce Fields 		WRITE32(word0);
18081da177e4SLinus Torvalds 		WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
18091da177e4SLinus Torvalds 	}
18101da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
18111da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18121da177e4SLinus Torvalds 			goto out_resource;
18131da177e4SLinus Torvalds 		dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12];
18141da177e4SLinus Torvalds 		if (dummy == NF4BAD)
18151da177e4SLinus Torvalds 			goto out_serverfault;
18161da177e4SLinus Torvalds 		WRITE32(dummy);
18171da177e4SLinus Torvalds 	}
18181da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
18191da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18201da177e4SLinus Torvalds 			goto out_resource;
182149640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
1822e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT);
182349640001SNeilBrown 		else
1824e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
18251da177e4SLinus Torvalds 	}
18261da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
18271da177e4SLinus Torvalds 		/*
18281da177e4SLinus Torvalds 		 * Note: This _must_ be consistent with the scheme for writing
18291da177e4SLinus Torvalds 		 * change_info, so any changes made here must be reflected there
18301da177e4SLinus Torvalds 		 * as well.  (See xdr4.h:set_change_info() and the WRITECINFO()
18311da177e4SLinus Torvalds 		 * macro above.)
18321da177e4SLinus Torvalds 		 */
18331da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
18341da177e4SLinus Torvalds 			goto out_resource;
18351da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_sec);
18361da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_nsec);
18371da177e4SLinus Torvalds 	}
18381da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
18391da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
18401da177e4SLinus Torvalds 			goto out_resource;
18411da177e4SLinus Torvalds 		WRITE64(stat.size);
18421da177e4SLinus Torvalds 	}
18431da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
18441da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18451da177e4SLinus Torvalds 			goto out_resource;
18461da177e4SLinus Torvalds 		WRITE32(1);
18471da177e4SLinus Torvalds 	}
18481da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
18491da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18501da177e4SLinus Torvalds 			goto out_resource;
18511da177e4SLinus Torvalds 		WRITE32(1);
18521da177e4SLinus Torvalds 	}
18531da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
18541da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18551da177e4SLinus Torvalds 			goto out_resource;
18561da177e4SLinus Torvalds 		WRITE32(0);
18571da177e4SLinus Torvalds 	}
18581da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
18591da177e4SLinus Torvalds 		if ((buflen -= 16) < 0)
18601da177e4SLinus Torvalds 			goto out_resource;
186142ca0993SJ.Bruce Fields 		if (exp->ex_fslocs.migrated) {
186242ca0993SJ.Bruce Fields 			WRITE64(NFS4_REFERRAL_FSID_MAJOR);
186342ca0993SJ.Bruce Fields 			WRITE64(NFS4_REFERRAL_FSID_MINOR);
1864af6a4e28SNeilBrown 		} else switch(fsid_source(fhp)) {
1865af6a4e28SNeilBrown 		case FSIDSOURCE_FSID:
18661da177e4SLinus Torvalds 			WRITE64((u64)exp->ex_fsid);
18671da177e4SLinus Torvalds 			WRITE64((u64)0);
1868af6a4e28SNeilBrown 			break;
1869af6a4e28SNeilBrown 		case FSIDSOURCE_DEV:
18701da177e4SLinus Torvalds 			WRITE32(0);
18711da177e4SLinus Torvalds 			WRITE32(MAJOR(stat.dev));
18721da177e4SLinus Torvalds 			WRITE32(0);
18731da177e4SLinus Torvalds 			WRITE32(MINOR(stat.dev));
1874af6a4e28SNeilBrown 			break;
1875af6a4e28SNeilBrown 		case FSIDSOURCE_UUID:
1876af6a4e28SNeilBrown 			WRITEMEM(exp->ex_uuid, 16);
1877af6a4e28SNeilBrown 			break;
18781da177e4SLinus Torvalds 		}
18791da177e4SLinus Torvalds 	}
18801da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
18811da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18821da177e4SLinus Torvalds 			goto out_resource;
18831da177e4SLinus Torvalds 		WRITE32(0);
18841da177e4SLinus Torvalds 	}
18851da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
18861da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18871da177e4SLinus Torvalds 			goto out_resource;
18881da177e4SLinus Torvalds 		WRITE32(NFSD_LEASE_TIME);
18891da177e4SLinus Torvalds 	}
18901da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
18911da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18921da177e4SLinus Torvalds 			goto out_resource;
189342ca0993SJ.Bruce Fields 		WRITE32(rdattr_err);
18941da177e4SLinus Torvalds 	}
18951da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
18961da177e4SLinus Torvalds 		struct nfs4_ace *ace;
18971da177e4SLinus Torvalds 
18981da177e4SLinus Torvalds 		if (acl == NULL) {
18991da177e4SLinus Torvalds 			if ((buflen -= 4) < 0)
19001da177e4SLinus Torvalds 				goto out_resource;
19011da177e4SLinus Torvalds 
19021da177e4SLinus Torvalds 			WRITE32(0);
19031da177e4SLinus Torvalds 			goto out_acl;
19041da177e4SLinus Torvalds 		}
19051da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19061da177e4SLinus Torvalds 			goto out_resource;
19071da177e4SLinus Torvalds 		WRITE32(acl->naces);
19081da177e4SLinus Torvalds 
190928e05dd8SJ. Bruce Fields 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
19101da177e4SLinus Torvalds 			if ((buflen -= 4*3) < 0)
19111da177e4SLinus Torvalds 				goto out_resource;
19121da177e4SLinus Torvalds 			WRITE32(ace->type);
19131da177e4SLinus Torvalds 			WRITE32(ace->flag);
19141da177e4SLinus Torvalds 			WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
19151da177e4SLinus Torvalds 			status = nfsd4_encode_aclname(rqstp, ace->whotype,
19161da177e4SLinus Torvalds 				ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
19171da177e4SLinus Torvalds 				&p, &buflen);
19181da177e4SLinus Torvalds 			if (status == nfserr_resource)
19191da177e4SLinus Torvalds 				goto out_resource;
19201da177e4SLinus Torvalds 			if (status)
19211da177e4SLinus Torvalds 				goto out;
19221da177e4SLinus Torvalds 		}
19231da177e4SLinus Torvalds 	}
19241da177e4SLinus Torvalds out_acl:
19251da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
19261da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19271da177e4SLinus Torvalds 			goto out_resource;
19281da177e4SLinus Torvalds 		WRITE32(aclsupport ?
19291da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
19301da177e4SLinus Torvalds 	}
19311da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
19321da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19331da177e4SLinus Torvalds 			goto out_resource;
19341da177e4SLinus Torvalds 		WRITE32(1);
19351da177e4SLinus Torvalds 	}
19361da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
19371da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19381da177e4SLinus Torvalds 			goto out_resource;
19391da177e4SLinus Torvalds 		WRITE32(1);
19401da177e4SLinus Torvalds 	}
19411da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
19421da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19431da177e4SLinus Torvalds 			goto out_resource;
19441da177e4SLinus Torvalds 		WRITE32(1);
19451da177e4SLinus Torvalds 	}
19461da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
19471da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19481da177e4SLinus Torvalds 			goto out_resource;
19491da177e4SLinus Torvalds 		WRITE32(1);
19501da177e4SLinus Torvalds 	}
19511da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
19521da177e4SLinus Torvalds 		buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4;
19531da177e4SLinus Torvalds 		if (buflen < 0)
19541da177e4SLinus Torvalds 			goto out_resource;
19551da177e4SLinus Torvalds 		WRITE32(fhp->fh_handle.fh_size);
19561da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
19571da177e4SLinus Torvalds 	}
19581da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
19591da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19601da177e4SLinus Torvalds 			goto out_resource;
196140ee5dc6SPeter Staubach 		WRITE64(stat.ino);
19621da177e4SLinus Torvalds 	}
19631da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
19641da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19651da177e4SLinus Torvalds 			goto out_resource;
19661da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
19671da177e4SLinus Torvalds 	}
19681da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
19691da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19701da177e4SLinus Torvalds 			goto out_resource;
19711da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
19721da177e4SLinus Torvalds 	}
19731da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
19741da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19751da177e4SLinus Torvalds 			goto out_resource;
19761da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_files);
19771da177e4SLinus Torvalds 	}
197881c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
197981c3f413SJ.Bruce Fields 		status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
198081c3f413SJ.Bruce Fields 		if (status == nfserr_resource)
198181c3f413SJ.Bruce Fields 			goto out_resource;
198281c3f413SJ.Bruce Fields 		if (status)
198381c3f413SJ.Bruce Fields 			goto out;
198481c3f413SJ.Bruce Fields 	}
19851da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
19861da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19871da177e4SLinus Torvalds 			goto out_resource;
19881da177e4SLinus Torvalds 		WRITE32(1);
19891da177e4SLinus Torvalds 	}
19901da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
19911da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19921da177e4SLinus Torvalds 			goto out_resource;
19931da177e4SLinus Torvalds 		WRITE64(~(u64)0);
19941da177e4SLinus Torvalds 	}
19951da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
19961da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19971da177e4SLinus Torvalds 			goto out_resource;
19981da177e4SLinus Torvalds 		WRITE32(255);
19991da177e4SLinus Torvalds 	}
20001da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
20011da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20021da177e4SLinus Torvalds 			goto out_resource;
2003a16e92edSJ. Bruce Fields 		WRITE32(statfs.f_namelen);
20041da177e4SLinus Torvalds 	}
20051da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
20061da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20071da177e4SLinus Torvalds 			goto out_resource;
20087adae489SGreg Banks 		WRITE64((u64) svc_max_payload(rqstp));
20091da177e4SLinus Torvalds 	}
20101da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
20111da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20121da177e4SLinus Torvalds 			goto out_resource;
20137adae489SGreg Banks 		WRITE64((u64) svc_max_payload(rqstp));
20141da177e4SLinus Torvalds 	}
20151da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
20161da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20171da177e4SLinus Torvalds 			goto out_resource;
20181da177e4SLinus Torvalds 		WRITE32(stat.mode & S_IALLUGO);
20191da177e4SLinus Torvalds 	}
20201da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
20211da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20221da177e4SLinus Torvalds 			goto out_resource;
20231da177e4SLinus Torvalds 		WRITE32(1);
20241da177e4SLinus Torvalds 	}
20251da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
20261da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20271da177e4SLinus Torvalds 			goto out_resource;
20281da177e4SLinus Torvalds 		WRITE32(stat.nlink);
20291da177e4SLinus Torvalds 	}
20301da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
20311da177e4SLinus Torvalds 		status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
20321da177e4SLinus Torvalds 		if (status == nfserr_resource)
20331da177e4SLinus Torvalds 			goto out_resource;
20341da177e4SLinus Torvalds 		if (status)
20351da177e4SLinus Torvalds 			goto out;
20361da177e4SLinus Torvalds 	}
20371da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
20381da177e4SLinus Torvalds 		status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
20391da177e4SLinus Torvalds 		if (status == nfserr_resource)
20401da177e4SLinus Torvalds 			goto out_resource;
20411da177e4SLinus Torvalds 		if (status)
20421da177e4SLinus Torvalds 			goto out;
20431da177e4SLinus Torvalds 	}
20441da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
20451da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20461da177e4SLinus Torvalds 			goto out_resource;
20471da177e4SLinus Torvalds 		WRITE32((u32) MAJOR(stat.rdev));
20481da177e4SLinus Torvalds 		WRITE32((u32) MINOR(stat.rdev));
20491da177e4SLinus Torvalds 	}
20501da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
20511da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20521da177e4SLinus Torvalds 			goto out_resource;
20531da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
20541da177e4SLinus Torvalds 		WRITE64(dummy64);
20551da177e4SLinus Torvalds 	}
20561da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
20571da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20581da177e4SLinus Torvalds 			goto out_resource;
20591da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
20601da177e4SLinus Torvalds 		WRITE64(dummy64);
20611da177e4SLinus Torvalds 	}
20621da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
20631da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20641da177e4SLinus Torvalds 			goto out_resource;
20651da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
20661da177e4SLinus Torvalds 		WRITE64(dummy64);
20671da177e4SLinus Torvalds 	}
20681da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
20691da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20701da177e4SLinus Torvalds 			goto out_resource;
20711da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
20721da177e4SLinus Torvalds 		WRITE64(dummy64);
20731da177e4SLinus Torvalds 	}
20741da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
20751da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
20761da177e4SLinus Torvalds 			goto out_resource;
20771da177e4SLinus Torvalds 		WRITE32(0);
20781da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_sec);
20791da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_nsec);
20801da177e4SLinus Torvalds 	}
20811da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
20821da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
20831da177e4SLinus Torvalds 			goto out_resource;
20841da177e4SLinus Torvalds 		WRITE32(0);
20851da177e4SLinus Torvalds 		WRITE32(1);
20861da177e4SLinus Torvalds 		WRITE32(0);
20871da177e4SLinus Torvalds 	}
20881da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
20891da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
20901da177e4SLinus Torvalds 			goto out_resource;
20911da177e4SLinus Torvalds 		WRITE32(0);
20921da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_sec);
20931da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_nsec);
20941da177e4SLinus Torvalds 	}
20951da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
20961da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
20971da177e4SLinus Torvalds 			goto out_resource;
20981da177e4SLinus Torvalds 		WRITE32(0);
20991da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_sec);
21001da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_nsec);
21011da177e4SLinus Torvalds 	}
21021da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
21031da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
21041da177e4SLinus Torvalds                 	goto out_resource;
2105406a7ea9SFrank Filz 		/*
2106406a7ea9SFrank Filz 		 * Get parent's attributes if not ignoring crossmount
2107406a7ea9SFrank Filz 		 * and this is the root of a cross-mounted filesystem.
2108406a7ea9SFrank Filz 		 */
2109406a7ea9SFrank Filz 		if (ignore_crossmnt == 0 &&
211054775491SJan Blunck 		    exp->ex_path.mnt->mnt_root->d_inode == dentry->d_inode) {
211154775491SJan Blunck 			err = vfs_getattr(exp->ex_path.mnt->mnt_parent,
211254775491SJan Blunck 				exp->ex_path.mnt->mnt_mountpoint, &stat);
211340ee5dc6SPeter Staubach 			if (err)
211440ee5dc6SPeter Staubach 				goto out_nfserr;
211540ee5dc6SPeter Staubach 		}
211640ee5dc6SPeter Staubach 		WRITE64(stat.ino);
21171da177e4SLinus Torvalds 	}
21181da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
21191da177e4SLinus Torvalds 	*countp = p - buffer;
21201da177e4SLinus Torvalds 	status = nfs_ok;
21211da177e4SLinus Torvalds 
21221da177e4SLinus Torvalds out:
212328e05dd8SJ. Bruce Fields 	kfree(acl);
21241da177e4SLinus Torvalds 	if (fhp == &tempfh)
21251da177e4SLinus Torvalds 		fh_put(&tempfh);
21261da177e4SLinus Torvalds 	return status;
21271da177e4SLinus Torvalds out_nfserr:
2128b8dd7b9aSAl Viro 	status = nfserrno(err);
21291da177e4SLinus Torvalds 	goto out;
21301da177e4SLinus Torvalds out_resource:
21311da177e4SLinus Torvalds 	*countp = 0;
21321da177e4SLinus Torvalds 	status = nfserr_resource;
21331da177e4SLinus Torvalds 	goto out;
21341da177e4SLinus Torvalds out_serverfault:
21351da177e4SLinus Torvalds 	status = nfserr_serverfault;
21361da177e4SLinus Torvalds 	goto out;
21371da177e4SLinus Torvalds }
21381da177e4SLinus Torvalds 
2139c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
2140c0ce6ec8SJ. Bruce Fields {
2141c0ce6ec8SJ. Bruce Fields 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
2142c0ce6ec8SJ. Bruce Fields 		return 1;
2143c0ce6ec8SJ. Bruce Fields 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
2144c0ce6ec8SJ. Bruce Fields 		return 1;
2145c0ce6ec8SJ. Bruce Fields 	return 0;
2146c0ce6ec8SJ. Bruce Fields }
2147c0ce6ec8SJ. Bruce Fields 
2148b37ad28bSAl Viro static __be32
21491da177e4SLinus Torvalds nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
21502ebbc012SAl Viro 		const char *name, int namlen, __be32 *p, int *buflen)
21511da177e4SLinus Torvalds {
21521da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
21531da177e4SLinus Torvalds 	struct dentry *dentry;
2154b37ad28bSAl Viro 	__be32 nfserr;
2155406a7ea9SFrank Filz 	int ignore_crossmnt = 0;
21561da177e4SLinus Torvalds 
21571da177e4SLinus Torvalds 	dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
21581da177e4SLinus Torvalds 	if (IS_ERR(dentry))
21591da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
21601da177e4SLinus Torvalds 
21611da177e4SLinus Torvalds 	exp_get(exp);
2162406a7ea9SFrank Filz 	/*
2163406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
2164406a7ea9SFrank Filz 	 * attributes that are only properties of the underlying filesystem
2165406a7ea9SFrank Filz 	 * as opposed to the cross-mounted file system. In such a case,
2166406a7ea9SFrank Filz 	 * we will not follow the cross mount and will fill the attribtutes
2167406a7ea9SFrank Filz 	 * directly from the mountpoint dentry.
2168406a7ea9SFrank Filz 	 */
2169c0ce6ec8SJ. Bruce Fields 	if (d_mountpoint(dentry) && !attributes_need_mount(cd->rd_bmval))
2170406a7ea9SFrank Filz 		ignore_crossmnt = 1;
2171406a7ea9SFrank Filz 	else if (d_mountpoint(dentry)) {
2172021d3a72SJ.Bruce Fields 		int err;
2173021d3a72SJ.Bruce Fields 
2174dcb488a3SAndy Adamson 		/*
2175dcb488a3SAndy Adamson 		 * Why the heck aren't we just using nfsd_lookup??
2176dcb488a3SAndy Adamson 		 * Different "."/".." handling?  Something else?
2177dcb488a3SAndy Adamson 		 * At least, add a comment here to explain....
2178dcb488a3SAndy Adamson 		 */
2179021d3a72SJ.Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
2180021d3a72SJ.Bruce Fields 		if (err) {
2181021d3a72SJ.Bruce Fields 			nfserr = nfserrno(err);
21821da177e4SLinus Torvalds 			goto out_put;
21831da177e4SLinus Torvalds 		}
2184dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
2185dcb488a3SAndy Adamson 		if (nfserr)
2186dcb488a3SAndy Adamson 			goto out_put;
21871da177e4SLinus Torvalds 
21881da177e4SLinus Torvalds 	}
21891da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
2190406a7ea9SFrank Filz 					cd->rd_rqstp, ignore_crossmnt);
21911da177e4SLinus Torvalds out_put:
21921da177e4SLinus Torvalds 	dput(dentry);
21931da177e4SLinus Torvalds 	exp_put(exp);
21941da177e4SLinus Torvalds 	return nfserr;
21951da177e4SLinus Torvalds }
21961da177e4SLinus Torvalds 
21972ebbc012SAl Viro static __be32 *
2198b37ad28bSAl Viro nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr)
21991da177e4SLinus Torvalds {
22002ebbc012SAl Viro 	__be32 *attrlenp;
22011da177e4SLinus Torvalds 
22021da177e4SLinus Torvalds 	if (buflen < 6)
22031da177e4SLinus Torvalds 		return NULL;
22041da177e4SLinus Torvalds 	*p++ = htonl(2);
22051da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
22061da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
22071da177e4SLinus Torvalds 
22081da177e4SLinus Torvalds 	attrlenp = p++;
22091da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
22101da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
22111da177e4SLinus Torvalds 	return p;
22121da177e4SLinus Torvalds }
22131da177e4SLinus Torvalds 
22141da177e4SLinus Torvalds static int
2215a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2216a0ad13efSNeilBrown 		    loff_t offset, u64 ino, unsigned int d_type)
22171da177e4SLinus Torvalds {
2218a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
22191da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
22201da177e4SLinus Torvalds 	int buflen;
22212ebbc012SAl Viro 	__be32 *p = cd->buffer;
2222b37ad28bSAl Viro 	__be32 nfserr = nfserr_toosmall;
22231da177e4SLinus Torvalds 
22241da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
22251da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
22261da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
22271da177e4SLinus Torvalds 		return 0;
22281da177e4SLinus Torvalds 	}
22291da177e4SLinus Torvalds 
22301da177e4SLinus Torvalds 	if (cd->offset)
22311da177e4SLinus Torvalds 		xdr_encode_hyper(cd->offset, (u64) offset);
22321da177e4SLinus Torvalds 
22331da177e4SLinus Torvalds 	buflen = cd->buflen - 4 - XDR_QUADLEN(namlen);
22341da177e4SLinus Torvalds 	if (buflen < 0)
22351da177e4SLinus Torvalds 		goto fail;
22361da177e4SLinus Torvalds 
22371da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
22381da177e4SLinus Torvalds 	cd->offset = p;                             /* remember pointer */
22391da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
22401da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
22411da177e4SLinus Torvalds 
22421da177e4SLinus Torvalds 	nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen);
22431da177e4SLinus Torvalds 	switch (nfserr) {
22441da177e4SLinus Torvalds 	case nfs_ok:
22451da177e4SLinus Torvalds 		p += buflen;
22461da177e4SLinus Torvalds 		break;
22471da177e4SLinus Torvalds 	case nfserr_resource:
22481da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
22491da177e4SLinus Torvalds 		goto fail;
22501da177e4SLinus Torvalds 	case nfserr_dropit:
22511da177e4SLinus Torvalds 		goto fail;
22521da177e4SLinus Torvalds 	default:
22531da177e4SLinus Torvalds 		/*
22541da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
22551da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
22561da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
22571da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
22581da177e4SLinus Torvalds 		 * entire READDIR operation(!)
22591da177e4SLinus Torvalds 		 */
22601da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
22611da177e4SLinus Torvalds 			goto fail;
22621da177e4SLinus Torvalds 		p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
226334081efcSFred Isaman 		if (p == NULL) {
226434081efcSFred Isaman 			nfserr = nfserr_toosmall;
22651da177e4SLinus Torvalds 			goto fail;
22661da177e4SLinus Torvalds 		}
226734081efcSFred Isaman 	}
22681da177e4SLinus Torvalds 	cd->buflen -= (p - cd->buffer);
22691da177e4SLinus Torvalds 	cd->buffer = p;
22701da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
22711da177e4SLinus Torvalds 	return 0;
22721da177e4SLinus Torvalds fail:
22731da177e4SLinus Torvalds 	cd->common.err = nfserr;
22741da177e4SLinus Torvalds 	return -EINVAL;
22751da177e4SLinus Torvalds }
22761da177e4SLinus Torvalds 
2277e2f282b9SBenny Halevy static void
2278e2f282b9SBenny Halevy nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
2279e2f282b9SBenny Halevy {
2280e2f282b9SBenny Halevy 	ENCODE_HEAD;
2281e2f282b9SBenny Halevy 
2282e2f282b9SBenny Halevy 	RESERVE_SPACE(sizeof(stateid_t));
2283e2f282b9SBenny Halevy 	WRITE32(sid->si_generation);
2284e2f282b9SBenny Halevy 	WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
2285e2f282b9SBenny Halevy 	ADJUST_ARGS();
2286e2f282b9SBenny Halevy }
2287e2f282b9SBenny Halevy 
2288695e12f8SBenny Halevy static __be32
2289b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
22901da177e4SLinus Torvalds {
22911da177e4SLinus Torvalds 	ENCODE_HEAD;
22921da177e4SLinus Torvalds 
22931da177e4SLinus Torvalds 	if (!nfserr) {
22941da177e4SLinus Torvalds 		RESERVE_SPACE(8);
22951da177e4SLinus Torvalds 		WRITE32(access->ac_supported);
22961da177e4SLinus Torvalds 		WRITE32(access->ac_resp_access);
22971da177e4SLinus Torvalds 		ADJUST_ARGS();
22981da177e4SLinus Torvalds 	}
2299695e12f8SBenny Halevy 	return nfserr;
23001da177e4SLinus Torvalds }
23011da177e4SLinus Torvalds 
2302695e12f8SBenny Halevy static __be32
2303b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
23041da177e4SLinus Torvalds {
23051da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
23061da177e4SLinus Torvalds 
2307e2f282b9SBenny Halevy 	if (!nfserr)
2308e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &close->cl_stateid);
2309e2f282b9SBenny Halevy 
23101da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
2311695e12f8SBenny Halevy 	return nfserr;
23121da177e4SLinus Torvalds }
23131da177e4SLinus Torvalds 
23141da177e4SLinus Torvalds 
2315695e12f8SBenny Halevy static __be32
2316b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
23171da177e4SLinus Torvalds {
23181da177e4SLinus Torvalds 	ENCODE_HEAD;
23191da177e4SLinus Torvalds 
23201da177e4SLinus Torvalds 	if (!nfserr) {
23211da177e4SLinus Torvalds 		RESERVE_SPACE(8);
23221da177e4SLinus Torvalds 		WRITEMEM(commit->co_verf.data, 8);
23231da177e4SLinus Torvalds 		ADJUST_ARGS();
23241da177e4SLinus Torvalds 	}
2325695e12f8SBenny Halevy 	return nfserr;
23261da177e4SLinus Torvalds }
23271da177e4SLinus Torvalds 
2328695e12f8SBenny Halevy static __be32
2329b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
23301da177e4SLinus Torvalds {
23311da177e4SLinus Torvalds 	ENCODE_HEAD;
23321da177e4SLinus Torvalds 
23331da177e4SLinus Torvalds 	if (!nfserr) {
23341da177e4SLinus Torvalds 		RESERVE_SPACE(32);
23351da177e4SLinus Torvalds 		WRITECINFO(create->cr_cinfo);
23361da177e4SLinus Torvalds 		WRITE32(2);
23371da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[0]);
23381da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[1]);
23391da177e4SLinus Torvalds 		ADJUST_ARGS();
23401da177e4SLinus Torvalds 	}
2341695e12f8SBenny Halevy 	return nfserr;
23421da177e4SLinus Torvalds }
23431da177e4SLinus Torvalds 
2344b37ad28bSAl Viro static __be32
2345b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
23461da177e4SLinus Torvalds {
23471da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
23481da177e4SLinus Torvalds 	int buflen;
23491da177e4SLinus Torvalds 
23501da177e4SLinus Torvalds 	if (nfserr)
23511da177e4SLinus Torvalds 		return nfserr;
23521da177e4SLinus Torvalds 
23531da177e4SLinus Torvalds 	buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2);
23541da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
23551da177e4SLinus Torvalds 				    resp->p, &buflen, getattr->ga_bmval,
2356406a7ea9SFrank Filz 				    resp->rqstp, 0);
23571da177e4SLinus Torvalds 	if (!nfserr)
23581da177e4SLinus Torvalds 		resp->p += buflen;
23591da177e4SLinus Torvalds 	return nfserr;
23601da177e4SLinus Torvalds }
23611da177e4SLinus Torvalds 
2362695e12f8SBenny Halevy static __be32
2363695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
23641da177e4SLinus Torvalds {
2365695e12f8SBenny Halevy 	struct svc_fh *fhp = *fhpp;
23661da177e4SLinus Torvalds 	unsigned int len;
23671da177e4SLinus Torvalds 	ENCODE_HEAD;
23681da177e4SLinus Torvalds 
23691da177e4SLinus Torvalds 	if (!nfserr) {
23701da177e4SLinus Torvalds 		len = fhp->fh_handle.fh_size;
23711da177e4SLinus Torvalds 		RESERVE_SPACE(len + 4);
23721da177e4SLinus Torvalds 		WRITE32(len);
23731da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, len);
23741da177e4SLinus Torvalds 		ADJUST_ARGS();
23751da177e4SLinus Torvalds 	}
2376695e12f8SBenny Halevy 	return nfserr;
23771da177e4SLinus Torvalds }
23781da177e4SLinus Torvalds 
23791da177e4SLinus Torvalds /*
23801da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
23811da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
23821da177e4SLinus Torvalds */
23831da177e4SLinus Torvalds static void
23841da177e4SLinus Torvalds nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
23851da177e4SLinus Torvalds {
23861da177e4SLinus Torvalds 	ENCODE_HEAD;
23871da177e4SLinus Torvalds 
23881da177e4SLinus Torvalds 	RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
23891da177e4SLinus Torvalds 	WRITE64(ld->ld_start);
23901da177e4SLinus Torvalds 	WRITE64(ld->ld_length);
23911da177e4SLinus Torvalds 	WRITE32(ld->ld_type);
23921da177e4SLinus Torvalds 	if (ld->ld_sop) {
23931da177e4SLinus Torvalds 		WRITEMEM(&ld->ld_clientid, 8);
23941da177e4SLinus Torvalds 		WRITE32(ld->ld_sop->so_owner.len);
23951da177e4SLinus Torvalds 		WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
23961da177e4SLinus Torvalds 		kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
23971da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
23981da177e4SLinus Torvalds 		WRITE64((u64)0); /* clientid */
23991da177e4SLinus Torvalds 		WRITE32(0); /* length of owner name */
24001da177e4SLinus Torvalds 	}
24011da177e4SLinus Torvalds 	ADJUST_ARGS();
24021da177e4SLinus Torvalds }
24031da177e4SLinus Torvalds 
2404695e12f8SBenny Halevy static __be32
2405b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
24061da177e4SLinus Torvalds {
24071da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
24081da177e4SLinus Torvalds 
2409e2f282b9SBenny Halevy 	if (!nfserr)
2410e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &lock->lk_resp_stateid);
2411e2f282b9SBenny Halevy 	else if (nfserr == nfserr_denied)
24121da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lock->lk_denied);
24131da177e4SLinus Torvalds 
24143a65588aSJ. Bruce Fields 	ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
2415695e12f8SBenny Halevy 	return nfserr;
24161da177e4SLinus Torvalds }
24171da177e4SLinus Torvalds 
2418695e12f8SBenny Halevy static __be32
2419b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
24201da177e4SLinus Torvalds {
24211da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
24221da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lockt->lt_denied);
2423695e12f8SBenny Halevy 	return nfserr;
24241da177e4SLinus Torvalds }
24251da177e4SLinus Torvalds 
2426695e12f8SBenny Halevy static __be32
2427b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
24281da177e4SLinus Torvalds {
24291da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
24301da177e4SLinus Torvalds 
2431e2f282b9SBenny Halevy 	if (!nfserr)
2432e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &locku->lu_stateid);
24331da177e4SLinus Torvalds 
24341da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
2435695e12f8SBenny Halevy 	return nfserr;
24361da177e4SLinus Torvalds }
24371da177e4SLinus Torvalds 
24381da177e4SLinus Torvalds 
2439695e12f8SBenny Halevy static __be32
2440b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
24411da177e4SLinus Torvalds {
24421da177e4SLinus Torvalds 	ENCODE_HEAD;
24431da177e4SLinus Torvalds 
24441da177e4SLinus Torvalds 	if (!nfserr) {
24451da177e4SLinus Torvalds 		RESERVE_SPACE(20);
24461da177e4SLinus Torvalds 		WRITECINFO(link->li_cinfo);
24471da177e4SLinus Torvalds 		ADJUST_ARGS();
24481da177e4SLinus Torvalds 	}
2449695e12f8SBenny Halevy 	return nfserr;
24501da177e4SLinus Torvalds }
24511da177e4SLinus Torvalds 
24521da177e4SLinus Torvalds 
2453695e12f8SBenny Halevy static __be32
2454b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
24551da177e4SLinus Torvalds {
24561b6b2257SBenny Halevy 	ENCODE_HEAD;
24571da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
24581da177e4SLinus Torvalds 
24591da177e4SLinus Torvalds 	if (nfserr)
24601da177e4SLinus Torvalds 		goto out;
24611da177e4SLinus Torvalds 
2462e2f282b9SBenny Halevy 	nfsd4_encode_stateid(resp, &open->op_stateid);
2463e2f282b9SBenny Halevy 	RESERVE_SPACE(40);
24641da177e4SLinus Torvalds 	WRITECINFO(open->op_cinfo);
24651da177e4SLinus Torvalds 	WRITE32(open->op_rflags);
24661da177e4SLinus Torvalds 	WRITE32(2);
24671da177e4SLinus Torvalds 	WRITE32(open->op_bmval[0]);
24681da177e4SLinus Torvalds 	WRITE32(open->op_bmval[1]);
24691da177e4SLinus Torvalds 	WRITE32(open->op_delegate_type);
24701da177e4SLinus Torvalds 	ADJUST_ARGS();
24711da177e4SLinus Torvalds 
24721da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
24731da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
24741da177e4SLinus Torvalds 		break;
24751da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
2476e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
2477e2f282b9SBenny Halevy 		RESERVE_SPACE(20);
24787b190fecSNeilBrown 		WRITE32(open->op_recall);
24791da177e4SLinus Torvalds 
24801da177e4SLinus Torvalds 		/*
24811da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
24821da177e4SLinus Torvalds 		 */
24831da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
24841da177e4SLinus Torvalds 		WRITE32(0);
24851da177e4SLinus Torvalds 		WRITE32(0);
24861da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
24871da177e4SLinus Torvalds 		ADJUST_ARGS();
24881da177e4SLinus Torvalds 		break;
24891da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
2490e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
2491e2f282b9SBenny Halevy 		RESERVE_SPACE(32);
24921da177e4SLinus Torvalds 		WRITE32(0);
24931da177e4SLinus Torvalds 
24941da177e4SLinus Torvalds 		/*
24951da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
24961da177e4SLinus Torvalds 		 */
24971da177e4SLinus Torvalds 		WRITE32(NFS4_LIMIT_SIZE);
24981da177e4SLinus Torvalds 		WRITE32(~(u32)0);
24991da177e4SLinus Torvalds 		WRITE32(~(u32)0);
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds 		/*
25021da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
25031da177e4SLinus Torvalds 		 */
25041da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
25051da177e4SLinus Torvalds 		WRITE32(0);
25061da177e4SLinus Torvalds 		WRITE32(0);
25071da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
25081da177e4SLinus Torvalds 		ADJUST_ARGS();
25091da177e4SLinus Torvalds 		break;
25101da177e4SLinus Torvalds 	default:
25111da177e4SLinus Torvalds 		BUG();
25121da177e4SLinus Torvalds 	}
25131da177e4SLinus Torvalds 	/* XXX save filehandle here */
25141da177e4SLinus Torvalds out:
25151da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(open->op_stateowner);
2516695e12f8SBenny Halevy 	return nfserr;
25171da177e4SLinus Torvalds }
25181da177e4SLinus Torvalds 
2519695e12f8SBenny Halevy static __be32
2520b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
25211da177e4SLinus Torvalds {
25221da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
25231da177e4SLinus Torvalds 
2524e2f282b9SBenny Halevy 	if (!nfserr)
2525e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
25261da177e4SLinus Torvalds 
25271da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
2528695e12f8SBenny Halevy 	return nfserr;
25291da177e4SLinus Torvalds }
25301da177e4SLinus Torvalds 
2531695e12f8SBenny Halevy static __be32
2532b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
25331da177e4SLinus Torvalds {
25341da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
25351da177e4SLinus Torvalds 
2536e2f282b9SBenny Halevy 	if (!nfserr)
2537e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &od->od_stateid);
25381da177e4SLinus Torvalds 
25391da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(od->od_stateowner);
2540695e12f8SBenny Halevy 	return nfserr;
25411da177e4SLinus Torvalds }
25421da177e4SLinus Torvalds 
2543b37ad28bSAl Viro static __be32
2544b37ad28bSAl Viro nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
254544524359SNeilBrown 		  struct nfsd4_read *read)
25461da177e4SLinus Torvalds {
25471da177e4SLinus Torvalds 	u32 eof;
25481da177e4SLinus Torvalds 	int v, pn;
25491da177e4SLinus Torvalds 	unsigned long maxcount;
25501da177e4SLinus Torvalds 	long len;
25511da177e4SLinus Torvalds 	ENCODE_HEAD;
25521da177e4SLinus Torvalds 
25531da177e4SLinus Torvalds 	if (nfserr)
25541da177e4SLinus Torvalds 		return nfserr;
25551da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
25561da177e4SLinus Torvalds 		return nfserr_resource;
25571da177e4SLinus Torvalds 
25581da177e4SLinus Torvalds 	RESERVE_SPACE(8); /* eof flag and byte count */
25591da177e4SLinus Torvalds 
25607adae489SGreg Banks 	maxcount = svc_max_payload(resp->rqstp);
25611da177e4SLinus Torvalds 	if (maxcount > read->rd_length)
25621da177e4SLinus Torvalds 		maxcount = read->rd_length;
25631da177e4SLinus Torvalds 
25641da177e4SLinus Torvalds 	len = maxcount;
25651da177e4SLinus Torvalds 	v = 0;
25661da177e4SLinus Torvalds 	while (len > 0) {
256744524359SNeilBrown 		pn = resp->rqstp->rq_resused++;
25683cc03b16SNeilBrown 		resp->rqstp->rq_vec[v].iov_base =
256944524359SNeilBrown 			page_address(resp->rqstp->rq_respages[pn]);
25703cc03b16SNeilBrown 		resp->rqstp->rq_vec[v].iov_len =
257144524359SNeilBrown 			len < PAGE_SIZE ? len : PAGE_SIZE;
25721da177e4SLinus Torvalds 		v++;
25731da177e4SLinus Torvalds 		len -= PAGE_SIZE;
25741da177e4SLinus Torvalds 	}
25751da177e4SLinus Torvalds 	read->rd_vlen = v;
25761da177e4SLinus Torvalds 
25771da177e4SLinus Torvalds 	nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp,
25783cc03b16SNeilBrown 			read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
25791da177e4SLinus Torvalds 			&maxcount);
25801da177e4SLinus Torvalds 
25811da177e4SLinus Torvalds 	if (nfserr == nfserr_symlink)
25821da177e4SLinus Torvalds 		nfserr = nfserr_inval;
25831da177e4SLinus Torvalds 	if (nfserr)
25841da177e4SLinus Torvalds 		return nfserr;
258544524359SNeilBrown 	eof = (read->rd_offset + maxcount >=
258644524359SNeilBrown 	       read->rd_fhp->fh_dentry->d_inode->i_size);
25871da177e4SLinus Torvalds 
25881da177e4SLinus Torvalds 	WRITE32(eof);
25891da177e4SLinus Torvalds 	WRITE32(maxcount);
25901da177e4SLinus Torvalds 	ADJUST_ARGS();
25916ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
25926ed6deccSNeilBrown 					- (char*)resp->xbuf->head[0].iov_base;
25931da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
25941da177e4SLinus Torvalds 
25956ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
25966ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
25971da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
25981da177e4SLinus Torvalds 	if (maxcount&3) {
25996ed6deccSNeilBrown 		RESERVE_SPACE(4);
26006ed6deccSNeilBrown 		WRITE32(0);
26011da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
26021da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
26036ed6deccSNeilBrown 		ADJUST_ARGS();
26041da177e4SLinus Torvalds 	}
26051da177e4SLinus Torvalds 	return 0;
26061da177e4SLinus Torvalds }
26071da177e4SLinus Torvalds 
2608b37ad28bSAl Viro static __be32
2609b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
26101da177e4SLinus Torvalds {
26111da177e4SLinus Torvalds 	int maxcount;
26121da177e4SLinus Torvalds 	char *page;
26131da177e4SLinus Torvalds 	ENCODE_HEAD;
26141da177e4SLinus Torvalds 
26151da177e4SLinus Torvalds 	if (nfserr)
26161da177e4SLinus Torvalds 		return nfserr;
26171da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
26181da177e4SLinus Torvalds 		return nfserr_resource;
26191da177e4SLinus Torvalds 
262044524359SNeilBrown 	page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
26211da177e4SLinus Torvalds 
26221da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
26231da177e4SLinus Torvalds 	RESERVE_SPACE(4);
26241da177e4SLinus Torvalds 
26251da177e4SLinus Torvalds 	/*
26261da177e4SLinus Torvalds 	 * XXX: By default, the ->readlink() VFS op will truncate symlinks
26271da177e4SLinus Torvalds 	 * if they would overflow the buffer.  Is this kosher in NFSv4?  If
26281da177e4SLinus Torvalds 	 * not, one easy fix is: if ->readlink() precisely fills the buffer,
26291da177e4SLinus Torvalds 	 * assume that truncation occurred, and return NFS4ERR_RESOURCE.
26301da177e4SLinus Torvalds 	 */
26311da177e4SLinus Torvalds 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
26321da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
26331da177e4SLinus Torvalds 		return nfserr_inval;
26341da177e4SLinus Torvalds 	if (nfserr)
26351da177e4SLinus Torvalds 		return nfserr;
26361da177e4SLinus Torvalds 
26371da177e4SLinus Torvalds 	WRITE32(maxcount);
26381da177e4SLinus Torvalds 	ADJUST_ARGS();
26396ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
26406ed6deccSNeilBrown 				- (char*)resp->xbuf->head[0].iov_base;
26411da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
26426ed6deccSNeilBrown 
26436ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
26446ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
26456ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_len = 0;
26461da177e4SLinus Torvalds 	if (maxcount&3) {
26476ed6deccSNeilBrown 		RESERVE_SPACE(4);
26486ed6deccSNeilBrown 		WRITE32(0);
26491da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
26501da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
26516ed6deccSNeilBrown 		ADJUST_ARGS();
26521da177e4SLinus Torvalds 	}
26531da177e4SLinus Torvalds 	return 0;
26541da177e4SLinus Torvalds }
26551da177e4SLinus Torvalds 
2656b37ad28bSAl Viro static __be32
2657b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
26581da177e4SLinus Torvalds {
26591da177e4SLinus Torvalds 	int maxcount;
26601da177e4SLinus Torvalds 	loff_t offset;
26612ebbc012SAl Viro 	__be32 *page, *savep, *tailbase;
26621da177e4SLinus Torvalds 	ENCODE_HEAD;
26631da177e4SLinus Torvalds 
26641da177e4SLinus Torvalds 	if (nfserr)
26651da177e4SLinus Torvalds 		return nfserr;
26661da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
26671da177e4SLinus Torvalds 		return nfserr_resource;
26681da177e4SLinus Torvalds 
26691da177e4SLinus Torvalds 	RESERVE_SPACE(8);  /* verifier */
26701da177e4SLinus Torvalds 	savep = p;
26711da177e4SLinus Torvalds 
26721da177e4SLinus Torvalds 	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
26731da177e4SLinus Torvalds 	WRITE32(0);
26741da177e4SLinus Torvalds 	WRITE32(0);
26751da177e4SLinus Torvalds 	ADJUST_ARGS();
26761da177e4SLinus Torvalds 	resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
2677bb6e8a9fSNeilBrown 	tailbase = p;
26781da177e4SLinus Torvalds 
26791da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
26801da177e4SLinus Torvalds 	if (maxcount > readdir->rd_maxcount)
26811da177e4SLinus Torvalds 		maxcount = readdir->rd_maxcount;
26821da177e4SLinus Torvalds 
26831da177e4SLinus Torvalds 	/*
26841da177e4SLinus Torvalds 	 * Convert from bytes to words, account for the two words already
26851da177e4SLinus Torvalds 	 * written, make sure to leave two words at the end for the next
26861da177e4SLinus Torvalds 	 * pointer and eof field.
26871da177e4SLinus Torvalds 	 */
26881da177e4SLinus Torvalds 	maxcount = (maxcount >> 2) - 4;
26891da177e4SLinus Torvalds 	if (maxcount < 0) {
26901da177e4SLinus Torvalds 		nfserr =  nfserr_toosmall;
26911da177e4SLinus Torvalds 		goto err_no_verf;
26921da177e4SLinus Torvalds 	}
26931da177e4SLinus Torvalds 
269444524359SNeilBrown 	page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
26951da177e4SLinus Torvalds 	readdir->common.err = 0;
26961da177e4SLinus Torvalds 	readdir->buflen = maxcount;
26971da177e4SLinus Torvalds 	readdir->buffer = page;
26981da177e4SLinus Torvalds 	readdir->offset = NULL;
26991da177e4SLinus Torvalds 
27001da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
27011da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
27021da177e4SLinus Torvalds 			      &offset,
27031da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
27041da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
27051da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
27061da177e4SLinus Torvalds 	    readdir->buffer == page)
27071da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
27081da177e4SLinus Torvalds 	if (nfserr == nfserr_symlink)
27091da177e4SLinus Torvalds 		nfserr = nfserr_notdir;
27101da177e4SLinus Torvalds 	if (nfserr)
27111da177e4SLinus Torvalds 		goto err_no_verf;
27121da177e4SLinus Torvalds 
27131da177e4SLinus Torvalds 	if (readdir->offset)
27141da177e4SLinus Torvalds 		xdr_encode_hyper(readdir->offset, offset);
27151da177e4SLinus Torvalds 
27161da177e4SLinus Torvalds 	p = readdir->buffer;
27171da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
27181da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
271944524359SNeilBrown 	resp->xbuf->page_len = ((char*)p) - (char*)page_address(
272044524359SNeilBrown 		resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
27211da177e4SLinus Torvalds 
2722bb6e8a9fSNeilBrown 	/* Use rest of head for padding and remaining ops: */
2723bb6e8a9fSNeilBrown 	resp->xbuf->tail[0].iov_base = tailbase;
27241da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
27251da177e4SLinus Torvalds 	resp->p = resp->xbuf->tail[0].iov_base;
2726bb6e8a9fSNeilBrown 	resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
27271da177e4SLinus Torvalds 
27281da177e4SLinus Torvalds 	return 0;
27291da177e4SLinus Torvalds err_no_verf:
27301da177e4SLinus Torvalds 	p = savep;
27311da177e4SLinus Torvalds 	ADJUST_ARGS();
27321da177e4SLinus Torvalds 	return nfserr;
27331da177e4SLinus Torvalds }
27341da177e4SLinus Torvalds 
2735695e12f8SBenny Halevy static __be32
2736b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
27371da177e4SLinus Torvalds {
27381da177e4SLinus Torvalds 	ENCODE_HEAD;
27391da177e4SLinus Torvalds 
27401da177e4SLinus Torvalds 	if (!nfserr) {
27411da177e4SLinus Torvalds 		RESERVE_SPACE(20);
27421da177e4SLinus Torvalds 		WRITECINFO(remove->rm_cinfo);
27431da177e4SLinus Torvalds 		ADJUST_ARGS();
27441da177e4SLinus Torvalds 	}
2745695e12f8SBenny Halevy 	return nfserr;
27461da177e4SLinus Torvalds }
27471da177e4SLinus Torvalds 
2748695e12f8SBenny Halevy static __be32
2749b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
27501da177e4SLinus Torvalds {
27511da177e4SLinus Torvalds 	ENCODE_HEAD;
27521da177e4SLinus Torvalds 
27531da177e4SLinus Torvalds 	if (!nfserr) {
27541da177e4SLinus Torvalds 		RESERVE_SPACE(40);
27551da177e4SLinus Torvalds 		WRITECINFO(rename->rn_sinfo);
27561da177e4SLinus Torvalds 		WRITECINFO(rename->rn_tinfo);
27571da177e4SLinus Torvalds 		ADJUST_ARGS();
27581da177e4SLinus Torvalds 	}
2759695e12f8SBenny Halevy 	return nfserr;
27601da177e4SLinus Torvalds }
27611da177e4SLinus Torvalds 
2762695e12f8SBenny Halevy static __be32
2763ca5c8cdeSAl Viro nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
2764dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
2765dcb488a3SAndy Adamson {
2766dcb488a3SAndy Adamson 	int i = 0;
2767dcb488a3SAndy Adamson 	struct svc_export *exp = secinfo->si_exp;
27684796f457SJ. Bruce Fields 	u32 nflavs;
27694796f457SJ. Bruce Fields 	struct exp_flavor_info *flavs;
27704796f457SJ. Bruce Fields 	struct exp_flavor_info def_flavs[2];
2771dcb488a3SAndy Adamson 	ENCODE_HEAD;
2772dcb488a3SAndy Adamson 
2773dcb488a3SAndy Adamson 	if (nfserr)
2774dcb488a3SAndy Adamson 		goto out;
27754796f457SJ. Bruce Fields 	if (exp->ex_nflavors) {
27764796f457SJ. Bruce Fields 		flavs = exp->ex_flavors;
27774796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
27784796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
27794796f457SJ. Bruce Fields 		flavs = def_flavs;
27804796f457SJ. Bruce Fields 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
27814796f457SJ. Bruce Fields 			nflavs = 2;
27824796f457SJ. Bruce Fields 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
27834796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
27844796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
27854796f457SJ. Bruce Fields 			nflavs = 1;
27864796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
27874796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
27884796f457SJ. Bruce Fields 		} else {
27894796f457SJ. Bruce Fields 			nflavs = 1;
27904796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
27914796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
27924796f457SJ. Bruce Fields 		}
27934796f457SJ. Bruce Fields 	}
27944796f457SJ. Bruce Fields 
2795dcb488a3SAndy Adamson 	RESERVE_SPACE(4);
27964796f457SJ. Bruce Fields 	WRITE32(nflavs);
2797dcb488a3SAndy Adamson 	ADJUST_ARGS();
27984796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
27994796f457SJ. Bruce Fields 		u32 flav = flavs[i].pseudoflavor;
2800dcb488a3SAndy Adamson 		struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
2801dcb488a3SAndy Adamson 
2802dcb488a3SAndy Adamson 		if (gm) {
2803dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2804dcb488a3SAndy Adamson 			WRITE32(RPC_AUTH_GSS);
2805dcb488a3SAndy Adamson 			ADJUST_ARGS();
2806dcb488a3SAndy Adamson 			RESERVE_SPACE(4 + gm->gm_oid.len);
2807dcb488a3SAndy Adamson 			WRITE32(gm->gm_oid.len);
2808dcb488a3SAndy Adamson 			WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
2809dcb488a3SAndy Adamson 			ADJUST_ARGS();
2810dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2811dcb488a3SAndy Adamson 			WRITE32(0); /* qop */
2812dcb488a3SAndy Adamson 			ADJUST_ARGS();
2813dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2814dcb488a3SAndy Adamson 			WRITE32(gss_pseudoflavor_to_service(gm, flav));
2815dcb488a3SAndy Adamson 			ADJUST_ARGS();
2816dcb488a3SAndy Adamson 			gss_mech_put(gm);
2817dcb488a3SAndy Adamson 		} else {
2818dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2819dcb488a3SAndy Adamson 			WRITE32(flav);
2820dcb488a3SAndy Adamson 			ADJUST_ARGS();
2821dcb488a3SAndy Adamson 		}
2822dcb488a3SAndy Adamson 	}
2823dcb488a3SAndy Adamson out:
2824dcb488a3SAndy Adamson 	if (exp)
2825dcb488a3SAndy Adamson 		exp_put(exp);
2826695e12f8SBenny Halevy 	return nfserr;
2827dcb488a3SAndy Adamson }
2828dcb488a3SAndy Adamson 
28291da177e4SLinus Torvalds /*
28301da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
28311da177e4SLinus Torvalds  * regardless of the error status.
28321da177e4SLinus Torvalds  */
2833695e12f8SBenny Halevy static __be32
2834b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
28351da177e4SLinus Torvalds {
28361da177e4SLinus Torvalds 	ENCODE_HEAD;
28371da177e4SLinus Torvalds 
28381da177e4SLinus Torvalds 	RESERVE_SPACE(12);
28391da177e4SLinus Torvalds 	if (nfserr) {
28401da177e4SLinus Torvalds 		WRITE32(2);
28411da177e4SLinus Torvalds 		WRITE32(0);
28421da177e4SLinus Torvalds 		WRITE32(0);
28431da177e4SLinus Torvalds 	}
28441da177e4SLinus Torvalds 	else {
28451da177e4SLinus Torvalds 		WRITE32(2);
28461da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[0]);
28471da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[1]);
28481da177e4SLinus Torvalds 	}
28491da177e4SLinus Torvalds 	ADJUST_ARGS();
2850695e12f8SBenny Halevy 	return nfserr;
28511da177e4SLinus Torvalds }
28521da177e4SLinus Torvalds 
2853695e12f8SBenny Halevy static __be32
2854b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
28551da177e4SLinus Torvalds {
28561da177e4SLinus Torvalds 	ENCODE_HEAD;
28571da177e4SLinus Torvalds 
28581da177e4SLinus Torvalds 	if (!nfserr) {
28591da177e4SLinus Torvalds 		RESERVE_SPACE(8 + sizeof(nfs4_verifier));
28601da177e4SLinus Torvalds 		WRITEMEM(&scd->se_clientid, 8);
28611da177e4SLinus Torvalds 		WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier));
28621da177e4SLinus Torvalds 		ADJUST_ARGS();
28631da177e4SLinus Torvalds 	}
28641da177e4SLinus Torvalds 	else if (nfserr == nfserr_clid_inuse) {
28651da177e4SLinus Torvalds 		RESERVE_SPACE(8);
28661da177e4SLinus Torvalds 		WRITE32(0);
28671da177e4SLinus Torvalds 		WRITE32(0);
28681da177e4SLinus Torvalds 		ADJUST_ARGS();
28691da177e4SLinus Torvalds 	}
2870695e12f8SBenny Halevy 	return nfserr;
28711da177e4SLinus Torvalds }
28721da177e4SLinus Torvalds 
2873695e12f8SBenny Halevy static __be32
2874b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
28751da177e4SLinus Torvalds {
28761da177e4SLinus Torvalds 	ENCODE_HEAD;
28771da177e4SLinus Torvalds 
28781da177e4SLinus Torvalds 	if (!nfserr) {
28791da177e4SLinus Torvalds 		RESERVE_SPACE(16);
28801da177e4SLinus Torvalds 		WRITE32(write->wr_bytes_written);
28811da177e4SLinus Torvalds 		WRITE32(write->wr_how_written);
28821da177e4SLinus Torvalds 		WRITEMEM(write->wr_verifier.data, 8);
28831da177e4SLinus Torvalds 		ADJUST_ARGS();
28841da177e4SLinus Torvalds 	}
2885695e12f8SBenny Halevy 	return nfserr;
28861da177e4SLinus Torvalds }
28871da177e4SLinus Torvalds 
2888695e12f8SBenny Halevy static __be32
28892db134ebSAndy Adamson nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
28902db134ebSAndy Adamson 			 struct nfsd4_exchange_id *exid)
28912db134ebSAndy Adamson {
28920733d213SAndy Adamson 	ENCODE_HEAD;
28930733d213SAndy Adamson 	char *major_id;
28940733d213SAndy Adamson 	char *server_scope;
28950733d213SAndy Adamson 	int major_id_sz;
28960733d213SAndy Adamson 	int server_scope_sz;
28970733d213SAndy Adamson 	uint64_t minor_id = 0;
28980733d213SAndy Adamson 
28990733d213SAndy Adamson 	if (nfserr)
29002db134ebSAndy Adamson 		return nfserr;
29010733d213SAndy Adamson 
29020733d213SAndy Adamson 	major_id = utsname()->nodename;
29030733d213SAndy Adamson 	major_id_sz = strlen(major_id);
29040733d213SAndy Adamson 	server_scope = utsname()->nodename;
29050733d213SAndy Adamson 	server_scope_sz = strlen(server_scope);
29060733d213SAndy Adamson 
29070733d213SAndy Adamson 	RESERVE_SPACE(
29080733d213SAndy Adamson 		8 /* eir_clientid */ +
29090733d213SAndy Adamson 		4 /* eir_sequenceid */ +
29100733d213SAndy Adamson 		4 /* eir_flags */ +
29110733d213SAndy Adamson 		4 /* spr_how (SP4_NONE) */ +
29120733d213SAndy Adamson 		8 /* so_minor_id */ +
29130733d213SAndy Adamson 		4 /* so_major_id.len */ +
29140733d213SAndy Adamson 		(XDR_QUADLEN(major_id_sz) * 4) +
29150733d213SAndy Adamson 		4 /* eir_server_scope.len */ +
29160733d213SAndy Adamson 		(XDR_QUADLEN(server_scope_sz) * 4) +
29170733d213SAndy Adamson 		4 /* eir_server_impl_id.count (0) */);
29180733d213SAndy Adamson 
29190733d213SAndy Adamson 	WRITEMEM(&exid->clientid, 8);
29200733d213SAndy Adamson 	WRITE32(exid->seqid);
29210733d213SAndy Adamson 	WRITE32(exid->flags);
29220733d213SAndy Adamson 
29230733d213SAndy Adamson 	/* state_protect4_r. Currently only support SP4_NONE */
29240733d213SAndy Adamson 	BUG_ON(exid->spa_how != SP4_NONE);
29250733d213SAndy Adamson 	WRITE32(exid->spa_how);
29260733d213SAndy Adamson 
29270733d213SAndy Adamson 	/* The server_owner struct */
29280733d213SAndy Adamson 	WRITE64(minor_id);      /* Minor id */
29290733d213SAndy Adamson 	/* major id */
29300733d213SAndy Adamson 	WRITE32(major_id_sz);
29310733d213SAndy Adamson 	WRITEMEM(major_id, major_id_sz);
29320733d213SAndy Adamson 
29330733d213SAndy Adamson 	/* Server scope */
29340733d213SAndy Adamson 	WRITE32(server_scope_sz);
29350733d213SAndy Adamson 	WRITEMEM(server_scope, server_scope_sz);
29360733d213SAndy Adamson 
29370733d213SAndy Adamson 	/* Implementation id */
29380733d213SAndy Adamson 	WRITE32(0);	/* zero length nfs_impl_id4 array */
29390733d213SAndy Adamson 	ADJUST_ARGS();
29400733d213SAndy Adamson 	return 0;
29412db134ebSAndy Adamson }
29422db134ebSAndy Adamson 
29432db134ebSAndy Adamson static __be32
29442db134ebSAndy Adamson nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
29452db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
29462db134ebSAndy Adamson {
2947ec6b5d7bSAndy Adamson 	ENCODE_HEAD;
2948ec6b5d7bSAndy Adamson 
2949ec6b5d7bSAndy Adamson 	if (nfserr)
29502db134ebSAndy Adamson 		return nfserr;
2951ec6b5d7bSAndy Adamson 
2952ec6b5d7bSAndy Adamson 	RESERVE_SPACE(24);
2953ec6b5d7bSAndy Adamson 	WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN);
2954ec6b5d7bSAndy Adamson 	WRITE32(sess->seqid);
2955ec6b5d7bSAndy Adamson 	WRITE32(sess->flags);
2956ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
2957ec6b5d7bSAndy Adamson 
2958ec6b5d7bSAndy Adamson 	RESERVE_SPACE(28);
2959ec6b5d7bSAndy Adamson 	WRITE32(0); /* headerpadsz */
2960ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxreq_sz);
2961ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxresp_sz);
2962ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxresp_cached);
2963ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxops);
2964ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxreqs);
2965ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.nr_rdma_attrs);
2966ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
2967ec6b5d7bSAndy Adamson 
2968ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs) {
2969ec6b5d7bSAndy Adamson 		RESERVE_SPACE(4);
2970ec6b5d7bSAndy Adamson 		WRITE32(sess->fore_channel.rdma_attrs);
2971ec6b5d7bSAndy Adamson 		ADJUST_ARGS();
2972ec6b5d7bSAndy Adamson 	}
2973ec6b5d7bSAndy Adamson 
2974ec6b5d7bSAndy Adamson 	RESERVE_SPACE(28);
2975ec6b5d7bSAndy Adamson 	WRITE32(0); /* headerpadsz */
2976ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxreq_sz);
2977ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxresp_sz);
2978ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxresp_cached);
2979ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxops);
2980ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxreqs);
2981ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.nr_rdma_attrs);
2982ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
2983ec6b5d7bSAndy Adamson 
2984ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs) {
2985ec6b5d7bSAndy Adamson 		RESERVE_SPACE(4);
2986ec6b5d7bSAndy Adamson 		WRITE32(sess->back_channel.rdma_attrs);
2987ec6b5d7bSAndy Adamson 		ADJUST_ARGS();
2988ec6b5d7bSAndy Adamson 	}
2989ec6b5d7bSAndy Adamson 	return 0;
29902db134ebSAndy Adamson }
29912db134ebSAndy Adamson 
29922db134ebSAndy Adamson static __be32
29932db134ebSAndy Adamson nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
29942db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
29952db134ebSAndy Adamson {
29962db134ebSAndy Adamson 	return nfserr;
29972db134ebSAndy Adamson }
29982db134ebSAndy Adamson 
2999bf864a31SAndy Adamson __be32
30002db134ebSAndy Adamson nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
30012db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
30022db134ebSAndy Adamson {
3003b85d4c01SBenny Halevy 	ENCODE_HEAD;
3004b85d4c01SBenny Halevy 
3005b85d4c01SBenny Halevy 	if (nfserr)
30062db134ebSAndy Adamson 		return nfserr;
3007b85d4c01SBenny Halevy 
3008b85d4c01SBenny Halevy 	RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 20);
3009b85d4c01SBenny Halevy 	WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
3010b85d4c01SBenny Halevy 	WRITE32(seq->seqid);
3011b85d4c01SBenny Halevy 	WRITE32(seq->slotid);
3012b85d4c01SBenny Halevy 	WRITE32(seq->maxslots);
3013b85d4c01SBenny Halevy 	/*
3014b85d4c01SBenny Halevy 	 * FIXME: for now:
3015b85d4c01SBenny Halevy 	 *   target_maxslots = maxslots
3016b85d4c01SBenny Halevy 	 *   status_flags = 0
3017b85d4c01SBenny Halevy 	 */
3018b85d4c01SBenny Halevy 	WRITE32(seq->maxslots);
3019b85d4c01SBenny Halevy 	WRITE32(0);
3020b85d4c01SBenny Halevy 
3021b85d4c01SBenny Halevy 	ADJUST_ARGS();
3022b85d4c01SBenny Halevy 	return 0;
30232db134ebSAndy Adamson }
30242db134ebSAndy Adamson 
30252db134ebSAndy Adamson static __be32
3026695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
3027695e12f8SBenny Halevy {
3028695e12f8SBenny Halevy 	return nfserr;
3029695e12f8SBenny Halevy }
3030695e12f8SBenny Halevy 
3031695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
3032695e12f8SBenny Halevy 
30332db134ebSAndy Adamson /*
30342db134ebSAndy Adamson  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
30352db134ebSAndy Adamson  * since we don't need to filter out obsolete ops as this is
30362db134ebSAndy Adamson  * done in the decoding phase.
30372db134ebSAndy Adamson  */
3038695e12f8SBenny Halevy static nfsd4_enc nfsd4_enc_ops[] = {
3039ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_enc)nfsd4_encode_access,
3040ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_enc)nfsd4_encode_close,
3041ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_enc)nfsd4_encode_commit,
3042ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_enc)nfsd4_encode_create,
3043ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_enc)nfsd4_encode_noop,
3044ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
3045ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_enc)nfsd4_encode_getattr,
3046ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_enc)nfsd4_encode_getfh,
3047ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_enc)nfsd4_encode_link,
3048ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_enc)nfsd4_encode_lock,
3049ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_enc)nfsd4_encode_lockt,
3050ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_enc)nfsd4_encode_locku,
3051ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_enc)nfsd4_encode_noop,
3052ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_enc)nfsd4_encode_noop,
3053ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
3054ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_enc)nfsd4_encode_open,
305584f09f46SBenny Halevy 	[OP_OPENATTR]		= (nfsd4_enc)nfsd4_encode_noop,
3056ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_enc)nfsd4_encode_open_confirm,
3057ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_enc)nfsd4_encode_open_downgrade,
3058ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_enc)nfsd4_encode_noop,
3059ad1060c8SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_enc)nfsd4_encode_noop,
3060ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_enc)nfsd4_encode_noop,
3061ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_enc)nfsd4_encode_read,
3062ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_enc)nfsd4_encode_readdir,
3063ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_enc)nfsd4_encode_readlink,
3064ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_enc)nfsd4_encode_remove,
3065ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_enc)nfsd4_encode_rename,
3066ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_enc)nfsd4_encode_noop,
3067ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_enc)nfsd4_encode_noop,
3068ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_enc)nfsd4_encode_noop,
3069ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_enc)nfsd4_encode_secinfo,
3070ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_enc)nfsd4_encode_setattr,
3071ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_enc)nfsd4_encode_setclientid,
3072ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop,
3073ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
3074ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_enc)nfsd4_encode_write,
3075ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_enc)nfsd4_encode_noop,
30762db134ebSAndy Adamson 
30772db134ebSAndy Adamson 	/* NFSv4.1 operations */
30782db134ebSAndy Adamson 	[OP_BACKCHANNEL_CTL]	= (nfsd4_enc)nfsd4_encode_noop,
30792db134ebSAndy Adamson 	[OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_noop,
30802db134ebSAndy Adamson 	[OP_EXCHANGE_ID]	= (nfsd4_enc)nfsd4_encode_exchange_id,
30812db134ebSAndy Adamson 	[OP_CREATE_SESSION]	= (nfsd4_enc)nfsd4_encode_create_session,
30822db134ebSAndy Adamson 	[OP_DESTROY_SESSION]	= (nfsd4_enc)nfsd4_encode_destroy_session,
30832db134ebSAndy Adamson 	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
30842db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
30852db134ebSAndy Adamson 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_noop,
30862db134ebSAndy Adamson 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
30872db134ebSAndy Adamson 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_noop,
30882db134ebSAndy Adamson 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_noop,
30892db134ebSAndy Adamson 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
30902db134ebSAndy Adamson 	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_noop,
30912db134ebSAndy Adamson 	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
30922db134ebSAndy Adamson 	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
30932db134ebSAndy Adamson 	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
30942db134ebSAndy Adamson 	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
30952db134ebSAndy Adamson 	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
30962db134ebSAndy Adamson 	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
3097695e12f8SBenny Halevy };
3098695e12f8SBenny Halevy 
3099496c262cSAndy Adamson /*
3100496c262cSAndy Adamson  * Calculate the total amount of memory that the compound response has taken
3101496c262cSAndy Adamson  * after encoding the current operation.
3102496c262cSAndy Adamson  *
3103496c262cSAndy Adamson  * pad: add on 8 bytes for the next operation's op_code and status so that
3104496c262cSAndy Adamson  * there is room to cache a failure on the next operation.
3105496c262cSAndy Adamson  *
3106496c262cSAndy Adamson  * Compare this length to the session se_fmaxresp_cached.
3107496c262cSAndy Adamson  *
3108496c262cSAndy Adamson  * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
3109496c262cSAndy Adamson  * will be at least a page and will therefore hold the xdr_buf head.
3110496c262cSAndy Adamson  */
3111496c262cSAndy Adamson static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
3112496c262cSAndy Adamson {
3113496c262cSAndy Adamson 	int status = 0;
3114496c262cSAndy Adamson 	struct xdr_buf *xb = &resp->rqstp->rq_res;
3115496c262cSAndy Adamson 	struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
3116496c262cSAndy Adamson 	struct nfsd4_session *session = NULL;
3117496c262cSAndy Adamson 	struct nfsd4_slot *slot = resp->cstate.slot;
3118496c262cSAndy Adamson 	u32 length, tlen = 0, pad = 8;
3119496c262cSAndy Adamson 
3120496c262cSAndy Adamson 	if (!nfsd4_has_session(&resp->cstate))
3121496c262cSAndy Adamson 		return status;
3122496c262cSAndy Adamson 
3123496c262cSAndy Adamson 	session = resp->cstate.session;
3124496c262cSAndy Adamson 	if (session == NULL || slot->sl_cache_entry.ce_cachethis == 0)
3125496c262cSAndy Adamson 		return status;
3126496c262cSAndy Adamson 
3127496c262cSAndy Adamson 	if (resp->opcnt >= args->opcnt)
3128496c262cSAndy Adamson 		pad = 0; /* this is the last operation */
3129496c262cSAndy Adamson 
3130496c262cSAndy Adamson 	if (xb->page_len == 0) {
3131496c262cSAndy Adamson 		length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
3132496c262cSAndy Adamson 	} else {
3133496c262cSAndy Adamson 		if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0)
3134496c262cSAndy Adamson 			tlen = (char *)resp->p - (char *)xb->tail[0].iov_base;
3135496c262cSAndy Adamson 
3136496c262cSAndy Adamson 		length = xb->head[0].iov_len + xb->page_len + tlen + pad;
3137496c262cSAndy Adamson 	}
3138496c262cSAndy Adamson 	dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
3139496c262cSAndy Adamson 		length, xb->page_len, tlen, pad);
3140496c262cSAndy Adamson 
3141496c262cSAndy Adamson 	if (length <= session->se_fmaxresp_cached)
3142496c262cSAndy Adamson 		return status;
3143496c262cSAndy Adamson 	else
3144496c262cSAndy Adamson 		return nfserr_rep_too_big_to_cache;
3145496c262cSAndy Adamson }
3146496c262cSAndy Adamson 
31471da177e4SLinus Torvalds void
31481da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
31491da177e4SLinus Torvalds {
31502ebbc012SAl Viro 	__be32 *statp;
31511da177e4SLinus Torvalds 	ENCODE_HEAD;
31521da177e4SLinus Torvalds 
31531da177e4SLinus Torvalds 	RESERVE_SPACE(8);
31541da177e4SLinus Torvalds 	WRITE32(op->opnum);
31551da177e4SLinus Torvalds 	statp = p++;	/* to be backfilled at the end */
31561da177e4SLinus Torvalds 	ADJUST_ARGS();
31571da177e4SLinus Torvalds 
3158695e12f8SBenny Halevy 	if (op->opnum == OP_ILLEGAL)
3159695e12f8SBenny Halevy 		goto status;
3160695e12f8SBenny Halevy 	BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
3161695e12f8SBenny Halevy 	       !nfsd4_enc_ops[op->opnum]);
3162695e12f8SBenny Halevy 	op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
3163496c262cSAndy Adamson 	/* nfsd4_check_drc_limit guarantees enough room for error status */
3164496c262cSAndy Adamson 	if (!op->status && nfsd4_check_drc_limit(resp))
3165496c262cSAndy Adamson 		op->status = nfserr_rep_too_big_to_cache;
3166695e12f8SBenny Halevy status:
31671da177e4SLinus Torvalds 	/*
31681da177e4SLinus Torvalds 	 * Note: We write the status directly, instead of using WRITE32(),
31691da177e4SLinus Torvalds 	 * since it is already in network byte order.
31701da177e4SLinus Torvalds 	 */
31711da177e4SLinus Torvalds 	*statp = op->status;
31721da177e4SLinus Torvalds }
31731da177e4SLinus Torvalds 
31741da177e4SLinus Torvalds /*
31751da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
31761da177e4SLinus Torvalds  *
31771da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
31781da177e4SLinus Torvalds  * previously sent already encoded operation.
31791da177e4SLinus Torvalds  *
31801da177e4SLinus Torvalds  * called with nfs4_lock_state() held
31811da177e4SLinus Torvalds  */
31821da177e4SLinus Torvalds void
31831da177e4SLinus Torvalds nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
31841da177e4SLinus Torvalds {
31851da177e4SLinus Torvalds 	ENCODE_HEAD;
31861da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
31871da177e4SLinus Torvalds 
31881da177e4SLinus Torvalds 	BUG_ON(!rp);
31891da177e4SLinus Torvalds 
31901da177e4SLinus Torvalds 	RESERVE_SPACE(8);
31911da177e4SLinus Torvalds 	WRITE32(op->opnum);
31921da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
31931da177e4SLinus Torvalds 	ADJUST_ARGS();
31941da177e4SLinus Torvalds 
31951da177e4SLinus Torvalds 	RESERVE_SPACE(rp->rp_buflen);
31961da177e4SLinus Torvalds 	WRITEMEM(rp->rp_buf, rp->rp_buflen);
31971da177e4SLinus Torvalds 	ADJUST_ARGS();
31981da177e4SLinus Torvalds }
31991da177e4SLinus Torvalds 
32001da177e4SLinus Torvalds /*
32011da177e4SLinus Torvalds  * END OF "GENERIC" ENCODE ROUTINES.
32021da177e4SLinus Torvalds  */
32031da177e4SLinus Torvalds 
32041da177e4SLinus Torvalds int
32052ebbc012SAl Viro nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
32061da177e4SLinus Torvalds {
32071da177e4SLinus Torvalds         return xdr_ressize_check(rqstp, p);
32081da177e4SLinus Torvalds }
32091da177e4SLinus Torvalds 
32101da177e4SLinus Torvalds void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
32111da177e4SLinus Torvalds {
32121da177e4SLinus Torvalds 	if (args->ops != args->iops) {
32131da177e4SLinus Torvalds 		kfree(args->ops);
32141da177e4SLinus Torvalds 		args->ops = args->iops;
32151da177e4SLinus Torvalds 	}
32161da177e4SLinus Torvalds 	kfree(args->tmpp);
32171da177e4SLinus Torvalds 	args->tmpp = NULL;
32181da177e4SLinus Torvalds 	while (args->to_free) {
32191da177e4SLinus Torvalds 		struct tmpbuf *tb = args->to_free;
32201da177e4SLinus Torvalds 		args->to_free = tb->next;
32211da177e4SLinus Torvalds 		tb->release(tb->buf);
32221da177e4SLinus Torvalds 		kfree(tb);
32231da177e4SLinus Torvalds 	}
32241da177e4SLinus Torvalds }
32251da177e4SLinus Torvalds 
32261da177e4SLinus Torvalds int
32272ebbc012SAl Viro nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
32281da177e4SLinus Torvalds {
3229b37ad28bSAl Viro 	__be32 status;
32301da177e4SLinus Torvalds 
32311da177e4SLinus Torvalds 	args->p = p;
32321da177e4SLinus Torvalds 	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
32331da177e4SLinus Torvalds 	args->pagelist = rqstp->rq_arg.pages;
32341da177e4SLinus Torvalds 	args->pagelen = rqstp->rq_arg.page_len;
32351da177e4SLinus Torvalds 	args->tmpp = NULL;
32361da177e4SLinus Torvalds 	args->to_free = NULL;
32371da177e4SLinus Torvalds 	args->ops = args->iops;
32381da177e4SLinus Torvalds 	args->rqstp = rqstp;
32391da177e4SLinus Torvalds 
32401da177e4SLinus Torvalds 	status = nfsd4_decode_compound(args);
32411da177e4SLinus Torvalds 	if (status) {
32421da177e4SLinus Torvalds 		nfsd4_release_compoundargs(args);
32431da177e4SLinus Torvalds 	}
32441da177e4SLinus Torvalds 	return !status;
32451da177e4SLinus Torvalds }
32461da177e4SLinus Torvalds 
32471da177e4SLinus Torvalds int
32482ebbc012SAl Viro nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp)
32491da177e4SLinus Torvalds {
32501da177e4SLinus Torvalds 	/*
32511da177e4SLinus Torvalds 	 * All that remains is to write the tag and operation count...
32521da177e4SLinus Torvalds 	 */
32531da177e4SLinus Torvalds 	struct kvec *iov;
32541da177e4SLinus Torvalds 	p = resp->tagp;
32551da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
32561da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
32571da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
32581da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
32591da177e4SLinus Torvalds 
32601da177e4SLinus Torvalds 	if (rqstp->rq_res.page_len)
32611da177e4SLinus Torvalds 		iov = &rqstp->rq_res.tail[0];
32621da177e4SLinus Torvalds 	else
32631da177e4SLinus Torvalds 		iov = &rqstp->rq_res.head[0];
32641da177e4SLinus Torvalds 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
32651da177e4SLinus Torvalds 	BUG_ON(iov->iov_len > PAGE_SIZE);
32666668958fSAndy Adamson 	if (nfsd4_has_session(&resp->cstate)) {
3267bf864a31SAndy Adamson 		if (resp->cstate.status == nfserr_replay_cache &&
3268bf864a31SAndy Adamson 				!nfsd4_not_cached(resp)) {
3269da3846a2SAndy Adamson 			iov->iov_len = resp->cstate.iovlen;
3270da3846a2SAndy Adamson 		} else {
3271da3846a2SAndy Adamson 			nfsd4_store_cache_entry(resp);
3272da3846a2SAndy Adamson 			dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
3273da3846a2SAndy Adamson 			resp->cstate.slot->sl_inuse = 0;
3274da3846a2SAndy Adamson 		}
3275da3846a2SAndy Adamson 		if (resp->cstate.session)
3276da3846a2SAndy Adamson 			nfsd4_put_session(resp->cstate.session);
3277da3846a2SAndy Adamson 	}
32781da177e4SLinus Torvalds 	return 1;
32791da177e4SLinus Torvalds }
32801da177e4SLinus Torvalds 
32811da177e4SLinus Torvalds /*
32821da177e4SLinus Torvalds  * Local variables:
32831da177e4SLinus Torvalds  *  c-basic-offset: 8
32841da177e4SLinus Torvalds  * End:
32851da177e4SLinus Torvalds  */
3286