xref: /openbmc/linux/fs/nfsd/nfs4xdr.c (revision ebabe9a9001af0af56c0c2780ca1576246e7a74b)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Server-side XDR for NFSv4
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (c) 2002 The Regents of the University of Michigan.
51da177e4SLinus Torvalds  *  All rights reserved.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Kendrick Smith <kmsmith@umich.edu>
81da177e4SLinus Torvalds  *  Andy Adamson   <andros@umich.edu>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  Redistribution and use in source and binary forms, with or without
111da177e4SLinus Torvalds  *  modification, are permitted provided that the following conditions
121da177e4SLinus Torvalds  *  are met:
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  1. Redistributions of source code must retain the above copyright
151da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer.
161da177e4SLinus Torvalds  *  2. Redistributions in binary form must reproduce the above copyright
171da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer in the
181da177e4SLinus Torvalds  *     documentation and/or other materials provided with the distribution.
191da177e4SLinus Torvalds  *  3. Neither the name of the University nor the names of its
201da177e4SLinus Torvalds  *     contributors may be used to endorse or promote products derived
211da177e4SLinus Torvalds  *     from this software without specific prior written permission.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
241da177e4SLinus Torvalds  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
251da177e4SLinus Torvalds  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
261da177e4SLinus Torvalds  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271da177e4SLinus Torvalds  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
281da177e4SLinus Torvalds  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
291da177e4SLinus Torvalds  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
301da177e4SLinus Torvalds  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
311da177e4SLinus Torvalds  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
321da177e4SLinus Torvalds  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
331da177e4SLinus Torvalds  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  * TODO: Neil Brown made the following observation:  We currently
361da177e4SLinus Torvalds  * initially reserve NFSD_BUFSIZE space on the transmit queue and
371da177e4SLinus Torvalds  * never release any of that until the request is complete.
381da177e4SLinus Torvalds  * It would be good to calculate a new maximum response size while
391da177e4SLinus Torvalds  * decoding the COMPOUND, and call svc_reserve with this number
401da177e4SLinus Torvalds  * at the end of nfs4svc_decode_compoundargs.
411da177e4SLinus Torvalds  */
421da177e4SLinus Torvalds 
435a0e3ad6STejun Heo #include <linux/slab.h>
441da177e4SLinus Torvalds #include <linux/namei.h>
45341eb184SBoaz Harrosh #include <linux/statfs.h>
460733d213SAndy Adamson #include <linux/utsname.h>
471da177e4SLinus Torvalds #include <linux/nfsd_idmap.h>
481da177e4SLinus Torvalds #include <linux/nfs4_acl.h>
494796f457SJ. Bruce Fields #include <linux/sunrpc/svcauth_gss.h>
509a74af21SBoaz Harrosh 
519a74af21SBoaz Harrosh #include "xdr4.h"
520a3adadeSJ. Bruce Fields #include "vfs.h"
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
551da177e4SLinus Torvalds 
5642ca0993SJ.Bruce Fields /*
5742ca0993SJ.Bruce Fields  * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
5842ca0993SJ.Bruce Fields  * directory in order to indicate to the client that a filesystem boundary is present
5942ca0993SJ.Bruce Fields  * We use a fixed fsid for a referral
6042ca0993SJ.Bruce Fields  */
6142ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
6242ca0993SJ.Bruce Fields #define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL
6342ca0993SJ.Bruce Fields 
64b37ad28bSAl Viro static __be32
65b37ad28bSAl Viro check_filename(char *str, int len, __be32 err)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds 	int i;
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 	if (len == 0)
701da177e4SLinus Torvalds 		return nfserr_inval;
711da177e4SLinus Torvalds 	if (isdotent(str, len))
721da177e4SLinus Torvalds 		return err;
731da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
741da177e4SLinus Torvalds 		if (str[i] == '/')
751da177e4SLinus Torvalds 			return err;
761da177e4SLinus Torvalds 	return 0;
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds #define DECODE_HEAD				\
802ebbc012SAl Viro 	__be32 *p;				\
81b37ad28bSAl Viro 	__be32 status
821da177e4SLinus Torvalds #define DECODE_TAIL				\
831da177e4SLinus Torvalds 	status = 0;				\
841da177e4SLinus Torvalds out:						\
851da177e4SLinus Torvalds 	return status;				\
861da177e4SLinus Torvalds xdr_error:					\
87817cb9d4SChuck Lever 	dprintk("NFSD: xdr error (%s:%d)\n",	\
88817cb9d4SChuck Lever 			__FILE__, __LINE__);	\
891da177e4SLinus Torvalds 	status = nfserr_bad_xdr;		\
901da177e4SLinus Torvalds 	goto out
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds #define READ32(x)         (x) = ntohl(*p++)
931da177e4SLinus Torvalds #define READ64(x)         do {			\
941da177e4SLinus Torvalds 	(x) = (u64)ntohl(*p++) << 32;		\
951da177e4SLinus Torvalds 	(x) |= ntohl(*p++);			\
961da177e4SLinus Torvalds } while (0)
971da177e4SLinus Torvalds #define READTIME(x)       do {			\
981da177e4SLinus Torvalds 	p++;					\
991da177e4SLinus Torvalds 	(x) = ntohl(*p++);			\
1001da177e4SLinus Torvalds 	p++;					\
1011da177e4SLinus Torvalds } while (0)
1021da177e4SLinus Torvalds #define READMEM(x,nbytes) do {			\
1031da177e4SLinus Torvalds 	x = (char *)p;				\
1041da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1051da177e4SLinus Torvalds } while (0)
1061da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do {			\
1071da177e4SLinus Torvalds 	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
1081da177e4SLinus Torvalds  		savemem(argp, p, nbytes) :	\
1091da177e4SLinus Torvalds  		(char *)p)) {			\
110817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
111817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1121da177e4SLinus Torvalds 		goto xdr_error;			\
1131da177e4SLinus Torvalds 		}				\
1141da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1151da177e4SLinus Torvalds } while (0)
1161da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do {			\
1171da177e4SLinus Torvalds 	memcpy((x), p, nbytes);			\
1181da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1191da177e4SLinus Torvalds } while (0)
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
1221da177e4SLinus Torvalds #define READ_BUF(nbytes)  do {			\
1231da177e4SLinus Torvalds 	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
1241da177e4SLinus Torvalds 		p = argp->p;			\
1251da177e4SLinus Torvalds 		argp->p += XDR_QUADLEN(nbytes);	\
1261da177e4SLinus Torvalds 	} else if (!(p = read_buf(argp, nbytes))) { \
127817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n", \
128817cb9d4SChuck Lever 				__FILE__, __LINE__); \
1291da177e4SLinus Torvalds 		goto xdr_error;			\
1301da177e4SLinus Torvalds 	}					\
1311da177e4SLinus Torvalds } while (0)
1321da177e4SLinus Torvalds 
133ca2a05aaSJ. Bruce Fields static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
1341da177e4SLinus Torvalds {
1351da177e4SLinus Torvalds 	/* We want more bytes than seem to be available.
1361da177e4SLinus Torvalds 	 * Maybe we need a new page, maybe we have just run out
1371da177e4SLinus Torvalds 	 */
138ca2a05aaSJ. Bruce Fields 	unsigned int avail = (char *)argp->end - (char *)argp->p;
1392ebbc012SAl Viro 	__be32 *p;
1401da177e4SLinus Torvalds 	if (avail + argp->pagelen < nbytes)
1411da177e4SLinus Torvalds 		return NULL;
1421da177e4SLinus Torvalds 	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
1431da177e4SLinus Torvalds 		return NULL;
1441da177e4SLinus Torvalds 	/* ok, we can do it with the current plus the next page */
1451da177e4SLinus Torvalds 	if (nbytes <= sizeof(argp->tmp))
1461da177e4SLinus Torvalds 		p = argp->tmp;
1471da177e4SLinus Torvalds 	else {
1481da177e4SLinus Torvalds 		kfree(argp->tmpp);
1491da177e4SLinus Torvalds 		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
1501da177e4SLinus Torvalds 		if (!p)
1511da177e4SLinus Torvalds 			return NULL;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	}
154ca2a05aaSJ. Bruce Fields 	/*
155ca2a05aaSJ. Bruce Fields 	 * The following memcpy is safe because read_buf is always
156ca2a05aaSJ. Bruce Fields 	 * called with nbytes > avail, and the two cases above both
157ca2a05aaSJ. Bruce Fields 	 * guarantee p points to at least nbytes bytes.
158ca2a05aaSJ. Bruce Fields 	 */
1591da177e4SLinus Torvalds 	memcpy(p, argp->p, avail);
1601da177e4SLinus Torvalds 	/* step to next page */
1611da177e4SLinus Torvalds 	argp->p = page_address(argp->pagelist[0]);
1621da177e4SLinus Torvalds 	argp->pagelist++;
1631da177e4SLinus Torvalds 	if (argp->pagelen < PAGE_SIZE) {
1642bc3c117SNeil Brown 		argp->end = argp->p + (argp->pagelen>>2);
1651da177e4SLinus Torvalds 		argp->pagelen = 0;
1661da177e4SLinus Torvalds 	} else {
1672bc3c117SNeil Brown 		argp->end = argp->p + (PAGE_SIZE>>2);
1681da177e4SLinus Torvalds 		argp->pagelen -= PAGE_SIZE;
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds 	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
1711da177e4SLinus Torvalds 	argp->p += XDR_QUADLEN(nbytes - avail);
1721da177e4SLinus Torvalds 	return p;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
17560adfc50SAndy Adamson static int zero_clientid(clientid_t *clid)
17660adfc50SAndy Adamson {
17760adfc50SAndy Adamson 	return (clid->cl_boot == 0) && (clid->cl_id == 0);
17860adfc50SAndy Adamson }
17960adfc50SAndy Adamson 
1801da177e4SLinus Torvalds static int
1811da177e4SLinus Torvalds defer_free(struct nfsd4_compoundargs *argp,
1821da177e4SLinus Torvalds 		void (*release)(const void *), void *p)
1831da177e4SLinus Torvalds {
1841da177e4SLinus Torvalds 	struct tmpbuf *tb;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	tb = kmalloc(sizeof(*tb), GFP_KERNEL);
1871da177e4SLinus Torvalds 	if (!tb)
1881da177e4SLinus Torvalds 		return -ENOMEM;
1891da177e4SLinus Torvalds 	tb->buf = p;
1901da177e4SLinus Torvalds 	tb->release = release;
1911da177e4SLinus Torvalds 	tb->next = argp->to_free;
1921da177e4SLinus Torvalds 	argp->to_free = tb;
1931da177e4SLinus Torvalds 	return 0;
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
1962ebbc012SAl Viro static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
1971da177e4SLinus Torvalds {
1981da177e4SLinus Torvalds 	if (p == argp->tmp) {
199a4db5fe5SJ. Bruce Fields 		p = kmalloc(nbytes, GFP_KERNEL);
200a4db5fe5SJ. Bruce Fields 		if (!p)
201a4db5fe5SJ. Bruce Fields 			return NULL;
2021da177e4SLinus Torvalds 		memcpy(p, argp->tmp, nbytes);
2031da177e4SLinus Torvalds 	} else {
20473dff8beSEric Sesterhenn 		BUG_ON(p != argp->tmpp);
2051da177e4SLinus Torvalds 		argp->tmpp = NULL;
2061da177e4SLinus Torvalds 	}
2071da177e4SLinus Torvalds 	if (defer_free(argp, kfree, p)) {
208a4db5fe5SJ. Bruce Fields 		kfree(p);
2091da177e4SLinus Torvalds 		return NULL;
2101da177e4SLinus Torvalds 	} else
2111da177e4SLinus Torvalds 		return (char *)p;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
214b37ad28bSAl Viro static __be32
2151da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
2161da177e4SLinus Torvalds {
2171da177e4SLinus Torvalds 	u32 bmlen;
2181da177e4SLinus Torvalds 	DECODE_HEAD;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	bmval[0] = 0;
2211da177e4SLinus Torvalds 	bmval[1] = 0;
2227e705706SAndy Adamson 	bmval[2] = 0;
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds 	READ_BUF(4);
2251da177e4SLinus Torvalds 	READ32(bmlen);
2261da177e4SLinus Torvalds 	if (bmlen > 1000)
2271da177e4SLinus Torvalds 		goto xdr_error;
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	READ_BUF(bmlen << 2);
2301da177e4SLinus Torvalds 	if (bmlen > 0)
2311da177e4SLinus Torvalds 		READ32(bmval[0]);
2321da177e4SLinus Torvalds 	if (bmlen > 1)
2331da177e4SLinus Torvalds 		READ32(bmval[1]);
2347e705706SAndy Adamson 	if (bmlen > 2)
2357e705706SAndy Adamson 		READ32(bmval[2]);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	DECODE_TAIL;
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds 
240b37ad28bSAl Viro static __be32
2413c8e0316SYu Zhiguo nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
242c0d6fc8aSBenny Halevy 		   struct iattr *iattr, struct nfs4_acl **acl)
2431da177e4SLinus Torvalds {
2441da177e4SLinus Torvalds 	int expected_len, len = 0;
2451da177e4SLinus Torvalds 	u32 dummy32;
2461da177e4SLinus Torvalds 	char *buf;
247b8dd7b9aSAl Viro 	int host_err;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	DECODE_HEAD;
2501da177e4SLinus Torvalds 	iattr->ia_valid = 0;
2511da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
2521da177e4SLinus Torvalds 		return status;
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	READ_BUF(4);
2551da177e4SLinus Torvalds 	READ32(expected_len);
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
2581da177e4SLinus Torvalds 		READ_BUF(8);
2591da177e4SLinus Torvalds 		len += 8;
2601da177e4SLinus Torvalds 		READ64(iattr->ia_size);
2611da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
2621da177e4SLinus Torvalds 	}
2631da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
26428e05dd8SJ. Bruce Fields 		int nace;
26528e05dd8SJ. Bruce Fields 		struct nfs4_ace *ace;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 		READ_BUF(4); len += 4;
2681da177e4SLinus Torvalds 		READ32(nace);
2691da177e4SLinus Torvalds 
27028e05dd8SJ. Bruce Fields 		if (nace > NFS4_ACL_MAX)
27128e05dd8SJ. Bruce Fields 			return nfserr_resource;
27228e05dd8SJ. Bruce Fields 
27328e05dd8SJ. Bruce Fields 		*acl = nfs4_acl_new(nace);
2741da177e4SLinus Torvalds 		if (*acl == NULL) {
275b8dd7b9aSAl Viro 			host_err = -ENOMEM;
2761da177e4SLinus Torvalds 			goto out_nfserr;
2771da177e4SLinus Torvalds 		}
27828e05dd8SJ. Bruce Fields 		defer_free(argp, kfree, *acl);
2791da177e4SLinus Torvalds 
28028e05dd8SJ. Bruce Fields 		(*acl)->naces = nace;
28128e05dd8SJ. Bruce Fields 		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
2821da177e4SLinus Torvalds 			READ_BUF(16); len += 16;
28328e05dd8SJ. Bruce Fields 			READ32(ace->type);
28428e05dd8SJ. Bruce Fields 			READ32(ace->flag);
28528e05dd8SJ. Bruce Fields 			READ32(ace->access_mask);
2861da177e4SLinus Torvalds 			READ32(dummy32);
2871da177e4SLinus Torvalds 			READ_BUF(dummy32);
2881da177e4SLinus Torvalds 			len += XDR_QUADLEN(dummy32) << 2;
2891da177e4SLinus Torvalds 			READMEM(buf, dummy32);
29028e05dd8SJ. Bruce Fields 			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
291b8dd7b9aSAl Viro 			host_err = 0;
29228e05dd8SJ. Bruce Fields 			if (ace->whotype != NFS4_ACL_WHO_NAMED)
29328e05dd8SJ. Bruce Fields 				ace->who = 0;
29428e05dd8SJ. Bruce Fields 			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
295b8dd7b9aSAl Viro 				host_err = nfsd_map_name_to_gid(argp->rqstp,
29628e05dd8SJ. Bruce Fields 						buf, dummy32, &ace->who);
2971da177e4SLinus Torvalds 			else
298b8dd7b9aSAl Viro 				host_err = nfsd_map_name_to_uid(argp->rqstp,
29928e05dd8SJ. Bruce Fields 						buf, dummy32, &ace->who);
300b8dd7b9aSAl Viro 			if (host_err)
3011da177e4SLinus Torvalds 				goto out_nfserr;
3021da177e4SLinus Torvalds 		}
3031da177e4SLinus Torvalds 	} else
3041da177e4SLinus Torvalds 		*acl = NULL;
3051da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
3061da177e4SLinus Torvalds 		READ_BUF(4);
3071da177e4SLinus Torvalds 		len += 4;
3081da177e4SLinus Torvalds 		READ32(iattr->ia_mode);
3091da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
3101da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
3111da177e4SLinus Torvalds 	}
3121da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
3131da177e4SLinus Torvalds 		READ_BUF(4);
3141da177e4SLinus Torvalds 		len += 4;
3151da177e4SLinus Torvalds 		READ32(dummy32);
3161da177e4SLinus Torvalds 		READ_BUF(dummy32);
3171da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3181da177e4SLinus Torvalds 		READMEM(buf, dummy32);
319b8dd7b9aSAl Viro 		if ((host_err = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
3201da177e4SLinus Torvalds 			goto out_nfserr;
3211da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
3221da177e4SLinus Torvalds 	}
3231da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
3241da177e4SLinus Torvalds 		READ_BUF(4);
3251da177e4SLinus Torvalds 		len += 4;
3261da177e4SLinus Torvalds 		READ32(dummy32);
3271da177e4SLinus Torvalds 		READ_BUF(dummy32);
3281da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3291da177e4SLinus Torvalds 		READMEM(buf, dummy32);
330b8dd7b9aSAl Viro 		if ((host_err = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
3311da177e4SLinus Torvalds 			goto out_nfserr;
3321da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
3351da177e4SLinus Torvalds 		READ_BUF(4);
3361da177e4SLinus Torvalds 		len += 4;
3371da177e4SLinus Torvalds 		READ32(dummy32);
3381da177e4SLinus Torvalds 		switch (dummy32) {
3391da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3401da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3411da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3421da177e4SLinus Torvalds 			READ_BUF(12);
3431da177e4SLinus Torvalds 			len += 12;
3441da177e4SLinus Torvalds 			READ32(dummy32);
3451da177e4SLinus Torvalds 			if (dummy32)
3461da177e4SLinus Torvalds 				return nfserr_inval;
3471da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_sec);
3481da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_nsec);
3491da177e4SLinus Torvalds 			if (iattr->ia_atime.tv_nsec >= (u32)1000000000)
3501da177e4SLinus Torvalds 				return nfserr_inval;
3511da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
3521da177e4SLinus Torvalds 			break;
3531da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3541da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
3551da177e4SLinus Torvalds 			break;
3561da177e4SLinus Torvalds 		default:
3571da177e4SLinus Torvalds 			goto xdr_error;
3581da177e4SLinus Torvalds 		}
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
3611da177e4SLinus Torvalds 		READ_BUF(4);
3621da177e4SLinus Torvalds 		len += 4;
3631da177e4SLinus Torvalds 		READ32(dummy32);
3641da177e4SLinus Torvalds 		switch (dummy32) {
3651da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3661da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3671da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3681da177e4SLinus Torvalds 			READ_BUF(12);
3691da177e4SLinus Torvalds 			len += 12;
3701da177e4SLinus Torvalds 			READ32(dummy32);
3711da177e4SLinus Torvalds 			if (dummy32)
3721da177e4SLinus Torvalds 				return nfserr_inval;
3731da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_sec);
3741da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_nsec);
3751da177e4SLinus Torvalds 			if (iattr->ia_mtime.tv_nsec >= (u32)1000000000)
3761da177e4SLinus Torvalds 				return nfserr_inval;
3771da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
3781da177e4SLinus Torvalds 			break;
3791da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3801da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
3811da177e4SLinus Torvalds 			break;
3821da177e4SLinus Torvalds 		default:
3831da177e4SLinus Torvalds 			goto xdr_error;
3841da177e4SLinus Torvalds 		}
3851da177e4SLinus Torvalds 	}
3863c8e0316SYu Zhiguo 	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
3873c8e0316SYu Zhiguo 	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
3883c8e0316SYu Zhiguo 	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
3893c8e0316SYu Zhiguo 		READ_BUF(expected_len - len);
3903c8e0316SYu Zhiguo 	else if (len != expected_len)
3911da177e4SLinus Torvalds 		goto xdr_error;
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	DECODE_TAIL;
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds out_nfserr:
396b8dd7b9aSAl Viro 	status = nfserrno(host_err);
3971da177e4SLinus Torvalds 	goto out;
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
400b37ad28bSAl Viro static __be32
401e31a1b66SBenny Halevy nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
402e31a1b66SBenny Halevy {
403e31a1b66SBenny Halevy 	DECODE_HEAD;
404e31a1b66SBenny Halevy 
405e31a1b66SBenny Halevy 	READ_BUF(sizeof(stateid_t));
406e31a1b66SBenny Halevy 	READ32(sid->si_generation);
407e31a1b66SBenny Halevy 	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
408e31a1b66SBenny Halevy 
409e31a1b66SBenny Halevy 	DECODE_TAIL;
410e31a1b66SBenny Halevy }
411e31a1b66SBenny Halevy 
412e31a1b66SBenny Halevy static __be32
4131da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
4141da177e4SLinus Torvalds {
4151da177e4SLinus Torvalds 	DECODE_HEAD;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	READ_BUF(4);
4181da177e4SLinus Torvalds 	READ32(access->ac_req_access);
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	DECODE_TAIL;
4211da177e4SLinus Torvalds }
4221da177e4SLinus Torvalds 
423b37ad28bSAl Viro static __be32
4241da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
4251da177e4SLinus Torvalds {
4261da177e4SLinus Torvalds 	DECODE_HEAD;
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	close->cl_stateowner = NULL;
429e31a1b66SBenny Halevy 	READ_BUF(4);
4301da177e4SLinus Torvalds 	READ32(close->cl_seqid);
431e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &close->cl_stateid);
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	DECODE_TAIL;
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 
437b37ad28bSAl Viro static __be32
4381da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
4391da177e4SLinus Torvalds {
4401da177e4SLinus Torvalds 	DECODE_HEAD;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	READ_BUF(12);
4431da177e4SLinus Torvalds 	READ64(commit->co_offset);
4441da177e4SLinus Torvalds 	READ32(commit->co_count);
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	DECODE_TAIL;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
449b37ad28bSAl Viro static __be32
4501da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
4511da177e4SLinus Torvalds {
4521da177e4SLinus Torvalds 	DECODE_HEAD;
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	READ_BUF(4);
4551da177e4SLinus Torvalds 	READ32(create->cr_type);
4561da177e4SLinus Torvalds 	switch (create->cr_type) {
4571da177e4SLinus Torvalds 	case NF4LNK:
4581da177e4SLinus Torvalds 		READ_BUF(4);
4591da177e4SLinus Torvalds 		READ32(create->cr_linklen);
4601da177e4SLinus Torvalds 		READ_BUF(create->cr_linklen);
4611da177e4SLinus Torvalds 		SAVEMEM(create->cr_linkname, create->cr_linklen);
4621da177e4SLinus Torvalds 		break;
4631da177e4SLinus Torvalds 	case NF4BLK:
4641da177e4SLinus Torvalds 	case NF4CHR:
4651da177e4SLinus Torvalds 		READ_BUF(8);
4661da177e4SLinus Torvalds 		READ32(create->cr_specdata1);
4671da177e4SLinus Torvalds 		READ32(create->cr_specdata2);
4681da177e4SLinus Torvalds 		break;
4691da177e4SLinus Torvalds 	case NF4SOCK:
4701da177e4SLinus Torvalds 	case NF4FIFO:
4711da177e4SLinus Torvalds 	case NF4DIR:
4721da177e4SLinus Torvalds 	default:
4731da177e4SLinus Torvalds 		break;
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 	READ_BUF(4);
4771da177e4SLinus Torvalds 	READ32(create->cr_namelen);
4781da177e4SLinus Torvalds 	READ_BUF(create->cr_namelen);
4791da177e4SLinus Torvalds 	SAVEMEM(create->cr_name, create->cr_namelen);
4801da177e4SLinus Torvalds 	if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
4811da177e4SLinus Torvalds 		return status;
4821da177e4SLinus Torvalds 
4833c8e0316SYu Zhiguo 	status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
4843c8e0316SYu Zhiguo 				    &create->cr_acl);
485c0d6fc8aSBenny Halevy 	if (status)
4861da177e4SLinus Torvalds 		goto out;
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	DECODE_TAIL;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds 
491b37ad28bSAl Viro static inline __be32
4921da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
4931da177e4SLinus Torvalds {
494e31a1b66SBenny Halevy 	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
4951da177e4SLinus Torvalds }
4961da177e4SLinus Torvalds 
497b37ad28bSAl Viro static inline __be32
4981da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
4991da177e4SLinus Torvalds {
5001da177e4SLinus Torvalds 	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds 
503b37ad28bSAl Viro static __be32
5041da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
5051da177e4SLinus Torvalds {
5061da177e4SLinus Torvalds 	DECODE_HEAD;
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	READ_BUF(4);
5091da177e4SLinus Torvalds 	READ32(link->li_namelen);
5101da177e4SLinus Torvalds 	READ_BUF(link->li_namelen);
5111da177e4SLinus Torvalds 	SAVEMEM(link->li_name, link->li_namelen);
5121da177e4SLinus Torvalds 	if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval)))
5131da177e4SLinus Torvalds 		return status;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds 	DECODE_TAIL;
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
518b37ad28bSAl Viro static __be32
5191da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
5201da177e4SLinus Torvalds {
5211da177e4SLinus Torvalds 	DECODE_HEAD;
5221da177e4SLinus Torvalds 
5233a65588aSJ. Bruce Fields 	lock->lk_replay_owner = NULL;
5241da177e4SLinus Torvalds 	/*
5251da177e4SLinus Torvalds 	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
5261da177e4SLinus Torvalds 	*/
5271da177e4SLinus Torvalds 	READ_BUF(28);
5281da177e4SLinus Torvalds 	READ32(lock->lk_type);
5291da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
5301da177e4SLinus Torvalds 		goto xdr_error;
5311da177e4SLinus Torvalds 	READ32(lock->lk_reclaim);
5321da177e4SLinus Torvalds 	READ64(lock->lk_offset);
5331da177e4SLinus Torvalds 	READ64(lock->lk_length);
5341da177e4SLinus Torvalds 	READ32(lock->lk_is_new);
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 	if (lock->lk_is_new) {
537e31a1b66SBenny Halevy 		READ_BUF(4);
5381da177e4SLinus Torvalds 		READ32(lock->lk_new_open_seqid);
539e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
540e31a1b66SBenny Halevy 		if (status)
541e31a1b66SBenny Halevy 			return status;
542e31a1b66SBenny Halevy 		READ_BUF(8 + sizeof(clientid_t));
5431da177e4SLinus Torvalds 		READ32(lock->lk_new_lock_seqid);
5441da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
5451da177e4SLinus Torvalds 		READ32(lock->lk_new_owner.len);
5461da177e4SLinus Torvalds 		READ_BUF(lock->lk_new_owner.len);
5471da177e4SLinus Torvalds 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
5481da177e4SLinus Torvalds 	} else {
549e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
550e31a1b66SBenny Halevy 		if (status)
551e31a1b66SBenny Halevy 			return status;
552e31a1b66SBenny Halevy 		READ_BUF(4);
5531da177e4SLinus Torvalds 		READ32(lock->lk_old_lock_seqid);
5541da177e4SLinus Torvalds 	}
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 	DECODE_TAIL;
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds 
559b37ad28bSAl Viro static __be32
5601da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
5611da177e4SLinus Torvalds {
5621da177e4SLinus Torvalds 	DECODE_HEAD;
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	READ_BUF(32);
5651da177e4SLinus Torvalds 	READ32(lockt->lt_type);
5661da177e4SLinus Torvalds 	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
5671da177e4SLinus Torvalds 		goto xdr_error;
5681da177e4SLinus Torvalds 	READ64(lockt->lt_offset);
5691da177e4SLinus Torvalds 	READ64(lockt->lt_length);
5701da177e4SLinus Torvalds 	COPYMEM(&lockt->lt_clientid, 8);
5711da177e4SLinus Torvalds 	READ32(lockt->lt_owner.len);
5721da177e4SLinus Torvalds 	READ_BUF(lockt->lt_owner.len);
5731da177e4SLinus Torvalds 	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
5741da177e4SLinus Torvalds 
57560adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&lockt->lt_clientid))
57660adfc50SAndy Adamson 		return nfserr_inval;
5771da177e4SLinus Torvalds 	DECODE_TAIL;
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds 
580b37ad28bSAl Viro static __be32
5811da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	DECODE_HEAD;
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	locku->lu_stateowner = NULL;
586e31a1b66SBenny Halevy 	READ_BUF(8);
5871da177e4SLinus Torvalds 	READ32(locku->lu_type);
5881da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
5891da177e4SLinus Torvalds 		goto xdr_error;
5901da177e4SLinus Torvalds 	READ32(locku->lu_seqid);
591e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
592e31a1b66SBenny Halevy 	if (status)
593e31a1b66SBenny Halevy 		return status;
594e31a1b66SBenny Halevy 	READ_BUF(16);
5951da177e4SLinus Torvalds 	READ64(locku->lu_offset);
5961da177e4SLinus Torvalds 	READ64(locku->lu_length);
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds 	DECODE_TAIL;
5991da177e4SLinus Torvalds }
6001da177e4SLinus Torvalds 
601b37ad28bSAl Viro static __be32
6021da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
6031da177e4SLinus Torvalds {
6041da177e4SLinus Torvalds 	DECODE_HEAD;
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	READ_BUF(4);
6071da177e4SLinus Torvalds 	READ32(lookup->lo_len);
6081da177e4SLinus Torvalds 	READ_BUF(lookup->lo_len);
6091da177e4SLinus Torvalds 	SAVEMEM(lookup->lo_name, lookup->lo_len);
6101da177e4SLinus Torvalds 	if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent)))
6111da177e4SLinus Torvalds 		return status;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	DECODE_TAIL;
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds 
616b37ad28bSAl Viro static __be32
6171da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
6181da177e4SLinus Torvalds {
6191da177e4SLinus Torvalds 	DECODE_HEAD;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	memset(open->op_bmval, 0, sizeof(open->op_bmval));
6221da177e4SLinus Torvalds 	open->op_iattr.ia_valid = 0;
6231da177e4SLinus Torvalds 	open->op_stateowner = NULL;
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 	/* seqid, share_access, share_deny, clientid, ownerlen */
6261da177e4SLinus Torvalds 	READ_BUF(16 + sizeof(clientid_t));
6271da177e4SLinus Torvalds 	READ32(open->op_seqid);
6281da177e4SLinus Torvalds 	READ32(open->op_share_access);
6291da177e4SLinus Torvalds 	READ32(open->op_share_deny);
6301da177e4SLinus Torvalds 	COPYMEM(&open->op_clientid, sizeof(clientid_t));
6311da177e4SLinus Torvalds 	READ32(open->op_owner.len);
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	/* owner, open_flag */
6341da177e4SLinus Torvalds 	READ_BUF(open->op_owner.len + 4);
6351da177e4SLinus Torvalds 	SAVEMEM(open->op_owner.data, open->op_owner.len);
6361da177e4SLinus Torvalds 	READ32(open->op_create);
6371da177e4SLinus Torvalds 	switch (open->op_create) {
6381da177e4SLinus Torvalds 	case NFS4_OPEN_NOCREATE:
6391da177e4SLinus Torvalds 		break;
6401da177e4SLinus Torvalds 	case NFS4_OPEN_CREATE:
6411da177e4SLinus Torvalds 		READ_BUF(4);
6421da177e4SLinus Torvalds 		READ32(open->op_createmode);
6431da177e4SLinus Torvalds 		switch (open->op_createmode) {
6441da177e4SLinus Torvalds 		case NFS4_CREATE_UNCHECKED:
6451da177e4SLinus Torvalds 		case NFS4_CREATE_GUARDED:
646c0d6fc8aSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
6473c8e0316SYu Zhiguo 				&open->op_iattr, &open->op_acl);
648c0d6fc8aSBenny Halevy 			if (status)
6491da177e4SLinus Torvalds 				goto out;
6501da177e4SLinus Torvalds 			break;
6511da177e4SLinus Torvalds 		case NFS4_CREATE_EXCLUSIVE:
6521da177e4SLinus Torvalds 			READ_BUF(8);
6531da177e4SLinus Torvalds 			COPYMEM(open->op_verf.data, 8);
6541da177e4SLinus Torvalds 			break;
65579fb54abSBenny Halevy 		case NFS4_CREATE_EXCLUSIVE4_1:
65679fb54abSBenny Halevy 			if (argp->minorversion < 1)
65779fb54abSBenny Halevy 				goto xdr_error;
65879fb54abSBenny Halevy 			READ_BUF(8);
65979fb54abSBenny Halevy 			COPYMEM(open->op_verf.data, 8);
66079fb54abSBenny Halevy 			status = nfsd4_decode_fattr(argp, open->op_bmval,
6613c8e0316SYu Zhiguo 				&open->op_iattr, &open->op_acl);
66279fb54abSBenny Halevy 			if (status)
66379fb54abSBenny Halevy 				goto out;
66479fb54abSBenny Halevy 			break;
6651da177e4SLinus Torvalds 		default:
6661da177e4SLinus Torvalds 			goto xdr_error;
6671da177e4SLinus Torvalds 		}
6681da177e4SLinus Torvalds 		break;
6691da177e4SLinus Torvalds 	default:
6701da177e4SLinus Torvalds 		goto xdr_error;
6711da177e4SLinus Torvalds 	}
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 	/* open_claim */
6741da177e4SLinus Torvalds 	READ_BUF(4);
6751da177e4SLinus Torvalds 	READ32(open->op_claim_type);
6761da177e4SLinus Torvalds 	switch (open->op_claim_type) {
6771da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_NULL:
6781da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
6791da177e4SLinus Torvalds 		READ_BUF(4);
6801da177e4SLinus Torvalds 		READ32(open->op_fname.len);
6811da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
6821da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
6831da177e4SLinus Torvalds 		if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
6841da177e4SLinus Torvalds 			return status;
6851da177e4SLinus Torvalds 		break;
6861da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_PREVIOUS:
6871da177e4SLinus Torvalds 		READ_BUF(4);
6881da177e4SLinus Torvalds 		READ32(open->op_delegate_type);
6891da177e4SLinus Torvalds 		break;
6901da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
691e31a1b66SBenny Halevy 		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
692e31a1b66SBenny Halevy 		if (status)
693e31a1b66SBenny Halevy 			return status;
694e31a1b66SBenny Halevy 		READ_BUF(4);
6951da177e4SLinus Torvalds 		READ32(open->op_fname.len);
6961da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
6971da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
6981da177e4SLinus Torvalds 		if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
6991da177e4SLinus Torvalds 			return status;
7001da177e4SLinus Torvalds 		break;
7011da177e4SLinus Torvalds 	default:
7021da177e4SLinus Torvalds 		goto xdr_error;
7031da177e4SLinus Torvalds 	}
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	DECODE_TAIL;
7061da177e4SLinus Torvalds }
7071da177e4SLinus Torvalds 
708b37ad28bSAl Viro static __be32
7091da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
7101da177e4SLinus Torvalds {
7111da177e4SLinus Torvalds 	DECODE_HEAD;
7121da177e4SLinus Torvalds 
7131da177e4SLinus Torvalds 	open_conf->oc_stateowner = NULL;
714e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
715e31a1b66SBenny Halevy 	if (status)
716e31a1b66SBenny Halevy 		return status;
717e31a1b66SBenny Halevy 	READ_BUF(4);
7181da177e4SLinus Torvalds 	READ32(open_conf->oc_seqid);
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 	DECODE_TAIL;
7211da177e4SLinus Torvalds }
7221da177e4SLinus Torvalds 
723b37ad28bSAl Viro static __be32
7241da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
7251da177e4SLinus Torvalds {
7261da177e4SLinus Torvalds 	DECODE_HEAD;
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds 	open_down->od_stateowner = NULL;
729e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
730e31a1b66SBenny Halevy 	if (status)
731e31a1b66SBenny Halevy 		return status;
732e31a1b66SBenny Halevy 	READ_BUF(12);
7331da177e4SLinus Torvalds 	READ32(open_down->od_seqid);
7341da177e4SLinus Torvalds 	READ32(open_down->od_share_access);
7351da177e4SLinus Torvalds 	READ32(open_down->od_share_deny);
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	DECODE_TAIL;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
740b37ad28bSAl Viro static __be32
7411da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds 	DECODE_HEAD;
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	READ_BUF(4);
7461da177e4SLinus Torvalds 	READ32(putfh->pf_fhlen);
7471da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
7481da177e4SLinus Torvalds 		goto xdr_error;
7491da177e4SLinus Torvalds 	READ_BUF(putfh->pf_fhlen);
7501da177e4SLinus Torvalds 	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	DECODE_TAIL;
7531da177e4SLinus Torvalds }
7541da177e4SLinus Torvalds 
755b37ad28bSAl Viro static __be32
7561da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
7571da177e4SLinus Torvalds {
7581da177e4SLinus Torvalds 	DECODE_HEAD;
7591da177e4SLinus Torvalds 
760e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
761e31a1b66SBenny Halevy 	if (status)
762e31a1b66SBenny Halevy 		return status;
763e31a1b66SBenny Halevy 	READ_BUF(12);
7641da177e4SLinus Torvalds 	READ64(read->rd_offset);
7651da177e4SLinus Torvalds 	READ32(read->rd_length);
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	DECODE_TAIL;
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds 
770b37ad28bSAl Viro static __be32
7711da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
7721da177e4SLinus Torvalds {
7731da177e4SLinus Torvalds 	DECODE_HEAD;
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	READ_BUF(24);
7761da177e4SLinus Torvalds 	READ64(readdir->rd_cookie);
7771da177e4SLinus Torvalds 	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
7781da177e4SLinus Torvalds 	READ32(readdir->rd_dircount);    /* just in case you needed a useless field... */
7791da177e4SLinus Torvalds 	READ32(readdir->rd_maxcount);
7801da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
7811da177e4SLinus Torvalds 		goto out;
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	DECODE_TAIL;
7841da177e4SLinus Torvalds }
7851da177e4SLinus Torvalds 
786b37ad28bSAl Viro static __be32
7871da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
7881da177e4SLinus Torvalds {
7891da177e4SLinus Torvalds 	DECODE_HEAD;
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds 	READ_BUF(4);
7921da177e4SLinus Torvalds 	READ32(remove->rm_namelen);
7931da177e4SLinus Torvalds 	READ_BUF(remove->rm_namelen);
7941da177e4SLinus Torvalds 	SAVEMEM(remove->rm_name, remove->rm_namelen);
7951da177e4SLinus Torvalds 	if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent)))
7961da177e4SLinus Torvalds 		return status;
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 	DECODE_TAIL;
7991da177e4SLinus Torvalds }
8001da177e4SLinus Torvalds 
801b37ad28bSAl Viro static __be32
8021da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
8031da177e4SLinus Torvalds {
8041da177e4SLinus Torvalds 	DECODE_HEAD;
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds 	READ_BUF(4);
8071da177e4SLinus Torvalds 	READ32(rename->rn_snamelen);
8081da177e4SLinus Torvalds 	READ_BUF(rename->rn_snamelen + 4);
8091da177e4SLinus Torvalds 	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
8101da177e4SLinus Torvalds 	READ32(rename->rn_tnamelen);
8111da177e4SLinus Torvalds 	READ_BUF(rename->rn_tnamelen);
8121da177e4SLinus Torvalds 	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
8131da177e4SLinus Torvalds 	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent)))
8141da177e4SLinus Torvalds 		return status;
8151da177e4SLinus Torvalds 	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval)))
8161da177e4SLinus Torvalds 		return status;
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds 	DECODE_TAIL;
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds 
821b37ad28bSAl Viro static __be32
8221da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
8231da177e4SLinus Torvalds {
8241da177e4SLinus Torvalds 	DECODE_HEAD;
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds 	READ_BUF(sizeof(clientid_t));
8271da177e4SLinus Torvalds 	COPYMEM(clientid, sizeof(clientid_t));
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	DECODE_TAIL;
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds 
832b37ad28bSAl Viro static __be32
833dcb488a3SAndy Adamson nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
834dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
835dcb488a3SAndy Adamson {
836dcb488a3SAndy Adamson 	DECODE_HEAD;
837dcb488a3SAndy Adamson 
838dcb488a3SAndy Adamson 	READ_BUF(4);
839dcb488a3SAndy Adamson 	READ32(secinfo->si_namelen);
840dcb488a3SAndy Adamson 	READ_BUF(secinfo->si_namelen);
841dcb488a3SAndy Adamson 	SAVEMEM(secinfo->si_name, secinfo->si_namelen);
842dcb488a3SAndy Adamson 	status = check_filename(secinfo->si_name, secinfo->si_namelen,
843dcb488a3SAndy Adamson 								nfserr_noent);
844dcb488a3SAndy Adamson 	if (status)
845dcb488a3SAndy Adamson 		return status;
846dcb488a3SAndy Adamson 	DECODE_TAIL;
847dcb488a3SAndy Adamson }
848dcb488a3SAndy Adamson 
849dcb488a3SAndy Adamson static __be32
8501da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
8511da177e4SLinus Torvalds {
852e31a1b66SBenny Halevy 	__be32 status;
8531da177e4SLinus Torvalds 
854e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
855e31a1b66SBenny Halevy 	if (status)
856e31a1b66SBenny Halevy 		return status;
8573c8e0316SYu Zhiguo 	return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
8583c8e0316SYu Zhiguo 				  &setattr->sa_acl);
8591da177e4SLinus Torvalds }
8601da177e4SLinus Torvalds 
861b37ad28bSAl Viro static __be32
8621da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
8631da177e4SLinus Torvalds {
8641da177e4SLinus Torvalds 	DECODE_HEAD;
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 	READ_BUF(12);
8671da177e4SLinus Torvalds 	COPYMEM(setclientid->se_verf.data, 8);
8681da177e4SLinus Torvalds 	READ32(setclientid->se_namelen);
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds 	READ_BUF(setclientid->se_namelen + 8);
8711da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_name, setclientid->se_namelen);
8721da177e4SLinus Torvalds 	READ32(setclientid->se_callback_prog);
8731da177e4SLinus Torvalds 	READ32(setclientid->se_callback_netid_len);
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_netid_len + 4);
8761da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
8771da177e4SLinus Torvalds 	READ32(setclientid->se_callback_addr_len);
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_addr_len + 4);
8801da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
8811da177e4SLinus Torvalds 	READ32(setclientid->se_callback_ident);
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	DECODE_TAIL;
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds 
886b37ad28bSAl Viro static __be32
8871da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
8881da177e4SLinus Torvalds {
8891da177e4SLinus Torvalds 	DECODE_HEAD;
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 	READ_BUF(8 + sizeof(nfs4_verifier));
8921da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_clientid, 8);
8931da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier));
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	DECODE_TAIL;
8961da177e4SLinus Torvalds }
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds /* Also used for NVERIFY */
899b37ad28bSAl Viro static __be32
9001da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
9011da177e4SLinus Torvalds {
9021da177e4SLinus Torvalds #if 0
9031da177e4SLinus Torvalds 	struct nfsd4_compoundargs save = {
9041da177e4SLinus Torvalds 		.p = argp->p,
9051da177e4SLinus Torvalds 		.end = argp->end,
9061da177e4SLinus Torvalds 		.rqstp = argp->rqstp,
9071da177e4SLinus Torvalds 	};
9081da177e4SLinus Torvalds 	u32             ve_bmval[2];
9091da177e4SLinus Torvalds 	struct iattr    ve_iattr;           /* request */
9101da177e4SLinus Torvalds 	struct nfs4_acl *ve_acl;            /* request */
9111da177e4SLinus Torvalds #endif
9121da177e4SLinus Torvalds 	DECODE_HEAD;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
9151da177e4SLinus Torvalds 		goto out;
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
9181da177e4SLinus Torvalds 	 * nfsd4_proc_verify; however we still decode here just to return
9191da177e4SLinus Torvalds 	 * correct error in case of bad xdr. */
9201da177e4SLinus Torvalds #if 0
9211da177e4SLinus Torvalds 	status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl);
9221da177e4SLinus Torvalds 	if (status == nfserr_inval) {
9231da177e4SLinus Torvalds 		status = nfserrno(status);
9241da177e4SLinus Torvalds 		goto out;
9251da177e4SLinus Torvalds 	}
9261da177e4SLinus Torvalds #endif
9271da177e4SLinus Torvalds 	READ_BUF(4);
9281da177e4SLinus Torvalds 	READ32(verify->ve_attrlen);
9291da177e4SLinus Torvalds 	READ_BUF(verify->ve_attrlen);
9301da177e4SLinus Torvalds 	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 	DECODE_TAIL;
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds 
935b37ad28bSAl Viro static __be32
9361da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
9371da177e4SLinus Torvalds {
9381da177e4SLinus Torvalds 	int avail;
9391da177e4SLinus Torvalds 	int v;
9401da177e4SLinus Torvalds 	int len;
9411da177e4SLinus Torvalds 	DECODE_HEAD;
9421da177e4SLinus Torvalds 
943e31a1b66SBenny Halevy 	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
944e31a1b66SBenny Halevy 	if (status)
945e31a1b66SBenny Halevy 		return status;
946e31a1b66SBenny Halevy 	READ_BUF(16);
9471da177e4SLinus Torvalds 	READ64(write->wr_offset);
9481da177e4SLinus Torvalds 	READ32(write->wr_stable_how);
9491da177e4SLinus Torvalds 	if (write->wr_stable_how > 2)
9501da177e4SLinus Torvalds 		goto xdr_error;
9511da177e4SLinus Torvalds 	READ32(write->wr_buflen);
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	/* Sorry .. no magic macros for this.. *
9541da177e4SLinus Torvalds 	 * READ_BUF(write->wr_buflen);
9551da177e4SLinus Torvalds 	 * SAVEMEM(write->wr_buf, write->wr_buflen);
9561da177e4SLinus Torvalds 	 */
9571da177e4SLinus Torvalds 	avail = (char*)argp->end - (char*)argp->p;
9581da177e4SLinus Torvalds 	if (avail + argp->pagelen < write->wr_buflen) {
959817cb9d4SChuck Lever 		dprintk("NFSD: xdr error (%s:%d)\n",
960817cb9d4SChuck Lever 				__FILE__, __LINE__);
9611da177e4SLinus Torvalds 		goto xdr_error;
9621da177e4SLinus Torvalds 	}
9633cc03b16SNeilBrown 	argp->rqstp->rq_vec[0].iov_base = p;
9643cc03b16SNeilBrown 	argp->rqstp->rq_vec[0].iov_len = avail;
9651da177e4SLinus Torvalds 	v = 0;
9661da177e4SLinus Torvalds 	len = write->wr_buflen;
9673cc03b16SNeilBrown 	while (len > argp->rqstp->rq_vec[v].iov_len) {
9683cc03b16SNeilBrown 		len -= argp->rqstp->rq_vec[v].iov_len;
9691da177e4SLinus Torvalds 		v++;
9703cc03b16SNeilBrown 		argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]);
9711da177e4SLinus Torvalds 		argp->pagelist++;
9721da177e4SLinus Torvalds 		if (argp->pagelen >= PAGE_SIZE) {
9733cc03b16SNeilBrown 			argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE;
9741da177e4SLinus Torvalds 			argp->pagelen -= PAGE_SIZE;
9751da177e4SLinus Torvalds 		} else {
9763cc03b16SNeilBrown 			argp->rqstp->rq_vec[v].iov_len = argp->pagelen;
9771da177e4SLinus Torvalds 			argp->pagelen -= len;
9781da177e4SLinus Torvalds 		}
9791da177e4SLinus Torvalds 	}
9802ebbc012SAl Viro 	argp->end = (__be32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len);
9812ebbc012SAl Viro 	argp->p = (__be32*)  (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2));
9823cc03b16SNeilBrown 	argp->rqstp->rq_vec[v].iov_len = len;
9831da177e4SLinus Torvalds 	write->wr_vlen = v+1;
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 	DECODE_TAIL;
9861da177e4SLinus Torvalds }
9871da177e4SLinus Torvalds 
988b37ad28bSAl Viro static __be32
9891da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
9901da177e4SLinus Torvalds {
9911da177e4SLinus Torvalds 	DECODE_HEAD;
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	READ_BUF(12);
9941da177e4SLinus Torvalds 	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
9951da177e4SLinus Torvalds 	READ32(rlockowner->rl_owner.len);
9961da177e4SLinus Torvalds 	READ_BUF(rlockowner->rl_owner.len);
9971da177e4SLinus Torvalds 	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
9981da177e4SLinus Torvalds 
99960adfc50SAndy Adamson 	if (argp->minorversion && !zero_clientid(&rlockowner->rl_clientid))
100060adfc50SAndy Adamson 		return nfserr_inval;
10011da177e4SLinus Torvalds 	DECODE_TAIL;
10021da177e4SLinus Torvalds }
10031da177e4SLinus Torvalds 
1004b37ad28bSAl Viro static __be32
10052db134ebSAndy Adamson nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
10060733d213SAndy Adamson 			 struct nfsd4_exchange_id *exid)
10072db134ebSAndy Adamson {
10080733d213SAndy Adamson 	int dummy;
10090733d213SAndy Adamson 	DECODE_HEAD;
10100733d213SAndy Adamson 
10110733d213SAndy Adamson 	READ_BUF(NFS4_VERIFIER_SIZE);
10120733d213SAndy Adamson 	COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE);
10130733d213SAndy Adamson 
10140733d213SAndy Adamson 	READ_BUF(4);
10150733d213SAndy Adamson 	READ32(exid->clname.len);
10160733d213SAndy Adamson 
10170733d213SAndy Adamson 	READ_BUF(exid->clname.len);
10180733d213SAndy Adamson 	SAVEMEM(exid->clname.data, exid->clname.len);
10190733d213SAndy Adamson 
10200733d213SAndy Adamson 	READ_BUF(4);
10210733d213SAndy Adamson 	READ32(exid->flags);
10220733d213SAndy Adamson 
10230733d213SAndy Adamson 	/* Ignore state_protect4_a */
10240733d213SAndy Adamson 	READ_BUF(4);
10250733d213SAndy Adamson 	READ32(exid->spa_how);
10260733d213SAndy Adamson 	switch (exid->spa_how) {
10270733d213SAndy Adamson 	case SP4_NONE:
10280733d213SAndy Adamson 		break;
10290733d213SAndy Adamson 	case SP4_MACH_CRED:
10300733d213SAndy Adamson 		/* spo_must_enforce */
10310733d213SAndy Adamson 		READ_BUF(4);
10320733d213SAndy Adamson 		READ32(dummy);
10330733d213SAndy Adamson 		READ_BUF(dummy * 4);
10340733d213SAndy Adamson 		p += dummy;
10350733d213SAndy Adamson 
10360733d213SAndy Adamson 		/* spo_must_allow */
10370733d213SAndy Adamson 		READ_BUF(4);
10380733d213SAndy Adamson 		READ32(dummy);
10390733d213SAndy Adamson 		READ_BUF(dummy * 4);
10400733d213SAndy Adamson 		p += dummy;
10410733d213SAndy Adamson 		break;
10420733d213SAndy Adamson 	case SP4_SSV:
10430733d213SAndy Adamson 		/* ssp_ops */
10440733d213SAndy Adamson 		READ_BUF(4);
10450733d213SAndy Adamson 		READ32(dummy);
10460733d213SAndy Adamson 		READ_BUF(dummy * 4);
10470733d213SAndy Adamson 		p += dummy;
10480733d213SAndy Adamson 
10490733d213SAndy Adamson 		READ_BUF(4);
10500733d213SAndy Adamson 		READ32(dummy);
10510733d213SAndy Adamson 		READ_BUF(dummy * 4);
10520733d213SAndy Adamson 		p += dummy;
10530733d213SAndy Adamson 
10540733d213SAndy Adamson 		/* ssp_hash_algs<> */
10550733d213SAndy Adamson 		READ_BUF(4);
10560733d213SAndy Adamson 		READ32(dummy);
10570733d213SAndy Adamson 		READ_BUF(dummy);
10580733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
10590733d213SAndy Adamson 
10600733d213SAndy Adamson 		/* ssp_encr_algs<> */
10610733d213SAndy Adamson 		READ_BUF(4);
10620733d213SAndy Adamson 		READ32(dummy);
10630733d213SAndy Adamson 		READ_BUF(dummy);
10640733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
10650733d213SAndy Adamson 
10660733d213SAndy Adamson 		/* ssp_window and ssp_num_gss_handles */
10670733d213SAndy Adamson 		READ_BUF(8);
10680733d213SAndy Adamson 		READ32(dummy);
10690733d213SAndy Adamson 		READ32(dummy);
10700733d213SAndy Adamson 		break;
10710733d213SAndy Adamson 	default:
10720733d213SAndy Adamson 		goto xdr_error;
10730733d213SAndy Adamson 	}
10740733d213SAndy Adamson 
10750733d213SAndy Adamson 	/* Ignore Implementation ID */
10760733d213SAndy Adamson 	READ_BUF(4);    /* nfs_impl_id4 array length */
10770733d213SAndy Adamson 	READ32(dummy);
10780733d213SAndy Adamson 
10790733d213SAndy Adamson 	if (dummy > 1)
10800733d213SAndy Adamson 		goto xdr_error;
10810733d213SAndy Adamson 
10820733d213SAndy Adamson 	if (dummy == 1) {
10830733d213SAndy Adamson 		/* nii_domain */
10840733d213SAndy Adamson 		READ_BUF(4);
10850733d213SAndy Adamson 		READ32(dummy);
10860733d213SAndy Adamson 		READ_BUF(dummy);
10870733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
10880733d213SAndy Adamson 
10890733d213SAndy Adamson 		/* nii_name */
10900733d213SAndy Adamson 		READ_BUF(4);
10910733d213SAndy Adamson 		READ32(dummy);
10920733d213SAndy Adamson 		READ_BUF(dummy);
10930733d213SAndy Adamson 		p += XDR_QUADLEN(dummy);
10940733d213SAndy Adamson 
10950733d213SAndy Adamson 		/* nii_date */
10960733d213SAndy Adamson 		READ_BUF(12);
10970733d213SAndy Adamson 		p += 3;
10980733d213SAndy Adamson 	}
10990733d213SAndy Adamson 	DECODE_TAIL;
11002db134ebSAndy Adamson }
11012db134ebSAndy Adamson 
11022db134ebSAndy Adamson static __be32
11032db134ebSAndy Adamson nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
11042db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
11052db134ebSAndy Adamson {
1106ec6b5d7bSAndy Adamson 	DECODE_HEAD;
1107ec6b5d7bSAndy Adamson 
1108ec6b5d7bSAndy Adamson 	u32 dummy;
1109ec6b5d7bSAndy Adamson 	char *machine_name;
1110ec6b5d7bSAndy Adamson 	int i;
1111ec6b5d7bSAndy Adamson 	int nr_secflavs;
1112ec6b5d7bSAndy Adamson 
1113ec6b5d7bSAndy Adamson 	READ_BUF(16);
1114ec6b5d7bSAndy Adamson 	COPYMEM(&sess->clientid, 8);
1115ec6b5d7bSAndy Adamson 	READ32(sess->seqid);
1116ec6b5d7bSAndy Adamson 	READ32(sess->flags);
1117ec6b5d7bSAndy Adamson 
1118ec6b5d7bSAndy Adamson 	/* Fore channel attrs */
1119ec6b5d7bSAndy Adamson 	READ_BUF(28);
1120ec6b5d7bSAndy Adamson 	READ32(dummy); /* headerpadsz is always 0 */
1121ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxreq_sz);
1122ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxresp_sz);
1123ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxresp_cached);
1124ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxops);
1125ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.maxreqs);
1126ec6b5d7bSAndy Adamson 	READ32(sess->fore_channel.nr_rdma_attrs);
1127ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs == 1) {
1128ec6b5d7bSAndy Adamson 		READ_BUF(4);
1129ec6b5d7bSAndy Adamson 		READ32(sess->fore_channel.rdma_attrs);
1130ec6b5d7bSAndy Adamson 	} else if (sess->fore_channel.nr_rdma_attrs > 1) {
1131ec6b5d7bSAndy Adamson 		dprintk("Too many fore channel attr bitmaps!\n");
1132ec6b5d7bSAndy Adamson 		goto xdr_error;
1133ec6b5d7bSAndy Adamson 	}
1134ec6b5d7bSAndy Adamson 
1135ec6b5d7bSAndy Adamson 	/* Back channel attrs */
1136ec6b5d7bSAndy Adamson 	READ_BUF(28);
1137ec6b5d7bSAndy Adamson 	READ32(dummy); /* headerpadsz is always 0 */
1138ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxreq_sz);
1139ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxresp_sz);
1140ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxresp_cached);
1141ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxops);
1142ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.maxreqs);
1143ec6b5d7bSAndy Adamson 	READ32(sess->back_channel.nr_rdma_attrs);
1144ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs == 1) {
1145ec6b5d7bSAndy Adamson 		READ_BUF(4);
1146ec6b5d7bSAndy Adamson 		READ32(sess->back_channel.rdma_attrs);
1147ec6b5d7bSAndy Adamson 	} else if (sess->back_channel.nr_rdma_attrs > 1) {
1148ec6b5d7bSAndy Adamson 		dprintk("Too many back channel attr bitmaps!\n");
1149ec6b5d7bSAndy Adamson 		goto xdr_error;
1150ec6b5d7bSAndy Adamson 	}
1151ec6b5d7bSAndy Adamson 
1152ec6b5d7bSAndy Adamson 	READ_BUF(8);
1153ec6b5d7bSAndy Adamson 	READ32(sess->callback_prog);
1154ec6b5d7bSAndy Adamson 
1155ec6b5d7bSAndy Adamson 	/* callback_sec_params4 */
1156ec6b5d7bSAndy Adamson 	READ32(nr_secflavs);
1157ec6b5d7bSAndy Adamson 	for (i = 0; i < nr_secflavs; ++i) {
1158ec6b5d7bSAndy Adamson 		READ_BUF(4);
1159ec6b5d7bSAndy Adamson 		READ32(dummy);
1160ec6b5d7bSAndy Adamson 		switch (dummy) {
1161ec6b5d7bSAndy Adamson 		case RPC_AUTH_NULL:
1162ec6b5d7bSAndy Adamson 			/* Nothing to read */
1163ec6b5d7bSAndy Adamson 			break;
1164ec6b5d7bSAndy Adamson 		case RPC_AUTH_UNIX:
1165ec6b5d7bSAndy Adamson 			READ_BUF(8);
1166ec6b5d7bSAndy Adamson 			/* stamp */
1167ec6b5d7bSAndy Adamson 			READ32(dummy);
1168ec6b5d7bSAndy Adamson 
1169ec6b5d7bSAndy Adamson 			/* machine name */
1170ec6b5d7bSAndy Adamson 			READ32(dummy);
1171ec6b5d7bSAndy Adamson 			READ_BUF(dummy);
1172ec6b5d7bSAndy Adamson 			SAVEMEM(machine_name, dummy);
1173ec6b5d7bSAndy Adamson 
1174ec6b5d7bSAndy Adamson 			/* uid, gid */
1175ec6b5d7bSAndy Adamson 			READ_BUF(8);
1176ec6b5d7bSAndy Adamson 			READ32(sess->uid);
1177ec6b5d7bSAndy Adamson 			READ32(sess->gid);
1178ec6b5d7bSAndy Adamson 
1179ec6b5d7bSAndy Adamson 			/* more gids */
1180ec6b5d7bSAndy Adamson 			READ_BUF(4);
1181ec6b5d7bSAndy Adamson 			READ32(dummy);
1182ec6b5d7bSAndy Adamson 			READ_BUF(dummy * 4);
1183ec6b5d7bSAndy Adamson 			for (i = 0; i < dummy; ++i)
1184ec6b5d7bSAndy Adamson 				READ32(dummy);
1185ec6b5d7bSAndy Adamson 			break;
1186ec6b5d7bSAndy Adamson 		case RPC_AUTH_GSS:
1187ec6b5d7bSAndy Adamson 			dprintk("RPC_AUTH_GSS callback secflavor "
1188ec6b5d7bSAndy Adamson 				"not supported!\n");
1189ec6b5d7bSAndy Adamson 			READ_BUF(8);
1190ec6b5d7bSAndy Adamson 			/* gcbp_service */
1191ec6b5d7bSAndy Adamson 			READ32(dummy);
1192ec6b5d7bSAndy Adamson 			/* gcbp_handle_from_server */
1193ec6b5d7bSAndy Adamson 			READ32(dummy);
1194ec6b5d7bSAndy Adamson 			READ_BUF(dummy);
1195ec6b5d7bSAndy Adamson 			p += XDR_QUADLEN(dummy);
1196ec6b5d7bSAndy Adamson 			/* gcbp_handle_from_client */
1197ec6b5d7bSAndy Adamson 			READ_BUF(4);
1198ec6b5d7bSAndy Adamson 			READ32(dummy);
1199ec6b5d7bSAndy Adamson 			READ_BUF(dummy);
1200ec6b5d7bSAndy Adamson 			p += XDR_QUADLEN(dummy);
1201ec6b5d7bSAndy Adamson 			break;
1202ec6b5d7bSAndy Adamson 		default:
1203ec6b5d7bSAndy Adamson 			dprintk("Illegal callback secflavor\n");
1204ec6b5d7bSAndy Adamson 			return nfserr_inval;
1205ec6b5d7bSAndy Adamson 		}
1206ec6b5d7bSAndy Adamson 	}
1207ec6b5d7bSAndy Adamson 	DECODE_TAIL;
12082db134ebSAndy Adamson }
12092db134ebSAndy Adamson 
12102db134ebSAndy Adamson static __be32
12112db134ebSAndy Adamson nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
12122db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
12132db134ebSAndy Adamson {
1214e10e0cfcSBenny Halevy 	DECODE_HEAD;
1215e10e0cfcSBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN);
1216e10e0cfcSBenny Halevy 	COPYMEM(destroy_session->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1217e10e0cfcSBenny Halevy 
1218e10e0cfcSBenny Halevy 	DECODE_TAIL;
12192db134ebSAndy Adamson }
12202db134ebSAndy Adamson 
12212db134ebSAndy Adamson static __be32
12222db134ebSAndy Adamson nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
12232db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
12242db134ebSAndy Adamson {
1225b85d4c01SBenny Halevy 	DECODE_HEAD;
1226b85d4c01SBenny Halevy 
1227b85d4c01SBenny Halevy 	READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
1228b85d4c01SBenny Halevy 	COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
1229b85d4c01SBenny Halevy 	READ32(seq->seqid);
1230b85d4c01SBenny Halevy 	READ32(seq->slotid);
1231b85d4c01SBenny Halevy 	READ32(seq->maxslots);
1232b85d4c01SBenny Halevy 	READ32(seq->cachethis);
1233b85d4c01SBenny Halevy 
1234b85d4c01SBenny Halevy 	DECODE_TAIL;
12352db134ebSAndy Adamson }
12362db134ebSAndy Adamson 
12374dc6ec00SJ. Bruce Fields static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
12384dc6ec00SJ. Bruce Fields {
12394dc6ec00SJ. Bruce Fields 	DECODE_HEAD;
12404dc6ec00SJ. Bruce Fields 
12414dc6ec00SJ. Bruce Fields 	READ_BUF(4);
12424dc6ec00SJ. Bruce Fields 	READ32(rc->rca_one_fs);
12434dc6ec00SJ. Bruce Fields 
12444dc6ec00SJ. Bruce Fields 	DECODE_TAIL;
12454dc6ec00SJ. Bruce Fields }
12464dc6ec00SJ. Bruce Fields 
12472db134ebSAndy Adamson static __be32
1248347e0ad9SBenny Halevy nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
1249347e0ad9SBenny Halevy {
1250347e0ad9SBenny Halevy 	return nfs_ok;
1251347e0ad9SBenny Halevy }
1252347e0ad9SBenny Halevy 
12533c375c6fSBenny Halevy static __be32
12543c375c6fSBenny Halevy nfsd4_decode_notsupp(struct nfsd4_compoundargs *argp, void *p)
12553c375c6fSBenny Halevy {
12561e685ec2SBenny Halevy 	return nfserr_notsupp;
12573c375c6fSBenny Halevy }
12583c375c6fSBenny Halevy 
1259347e0ad9SBenny Halevy typedef __be32(*nfsd4_dec)(struct nfsd4_compoundargs *argp, void *);
1260347e0ad9SBenny Halevy 
1261347e0ad9SBenny Halevy static nfsd4_dec nfsd4_dec_ops[] = {
1262ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
1263ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
1264ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
1265ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
1266ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
1267ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
1268ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
1269ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
1270ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
1271ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
1272ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
1273ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
1274ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
1275ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
1276ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1277ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
1278ad1060c8SJ. Bruce Fields 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
1279ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_open_confirm,
1280ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
1281ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
1282a1c8c4d1SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_noop,
1283ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
1284ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
1285ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
1286ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
1287ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
1288ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
1289ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_renew,
1290ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
1291ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
1292ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
1293ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
1294ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_setclientid,
1295ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_dec)nfsd4_decode_setclientid_confirm,
1296ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
1297ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
1298ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_release_lockowner,
1299347e0ad9SBenny Halevy };
1300347e0ad9SBenny Halevy 
13012db134ebSAndy Adamson static nfsd4_dec nfsd41_dec_ops[] = {
13029064caaeSRandy Dunlap 	[OP_ACCESS]		= (nfsd4_dec)nfsd4_decode_access,
13039064caaeSRandy Dunlap 	[OP_CLOSE]		= (nfsd4_dec)nfsd4_decode_close,
13049064caaeSRandy Dunlap 	[OP_COMMIT]		= (nfsd4_dec)nfsd4_decode_commit,
13059064caaeSRandy Dunlap 	[OP_CREATE]		= (nfsd4_dec)nfsd4_decode_create,
13069064caaeSRandy Dunlap 	[OP_DELEGPURGE]		= (nfsd4_dec)nfsd4_decode_notsupp,
13079064caaeSRandy Dunlap 	[OP_DELEGRETURN]	= (nfsd4_dec)nfsd4_decode_delegreturn,
13089064caaeSRandy Dunlap 	[OP_GETATTR]		= (nfsd4_dec)nfsd4_decode_getattr,
13099064caaeSRandy Dunlap 	[OP_GETFH]		= (nfsd4_dec)nfsd4_decode_noop,
13109064caaeSRandy Dunlap 	[OP_LINK]		= (nfsd4_dec)nfsd4_decode_link,
13119064caaeSRandy Dunlap 	[OP_LOCK]		= (nfsd4_dec)nfsd4_decode_lock,
13129064caaeSRandy Dunlap 	[OP_LOCKT]		= (nfsd4_dec)nfsd4_decode_lockt,
13139064caaeSRandy Dunlap 	[OP_LOCKU]		= (nfsd4_dec)nfsd4_decode_locku,
13149064caaeSRandy Dunlap 	[OP_LOOKUP]		= (nfsd4_dec)nfsd4_decode_lookup,
13159064caaeSRandy Dunlap 	[OP_LOOKUPP]		= (nfsd4_dec)nfsd4_decode_noop,
13169064caaeSRandy Dunlap 	[OP_NVERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
13179064caaeSRandy Dunlap 	[OP_OPEN]		= (nfsd4_dec)nfsd4_decode_open,
13189064caaeSRandy Dunlap 	[OP_OPENATTR]		= (nfsd4_dec)nfsd4_decode_notsupp,
13199064caaeSRandy Dunlap 	[OP_OPEN_CONFIRM]	= (nfsd4_dec)nfsd4_decode_notsupp,
13209064caaeSRandy Dunlap 	[OP_OPEN_DOWNGRADE]	= (nfsd4_dec)nfsd4_decode_open_downgrade,
13219064caaeSRandy Dunlap 	[OP_PUTFH]		= (nfsd4_dec)nfsd4_decode_putfh,
13229064caaeSRandy Dunlap 	[OP_PUTPUBFH]		= (nfsd4_dec)nfsd4_decode_notsupp,
13239064caaeSRandy Dunlap 	[OP_PUTROOTFH]		= (nfsd4_dec)nfsd4_decode_noop,
13249064caaeSRandy Dunlap 	[OP_READ]		= (nfsd4_dec)nfsd4_decode_read,
13259064caaeSRandy Dunlap 	[OP_READDIR]		= (nfsd4_dec)nfsd4_decode_readdir,
13269064caaeSRandy Dunlap 	[OP_READLINK]		= (nfsd4_dec)nfsd4_decode_noop,
13279064caaeSRandy Dunlap 	[OP_REMOVE]		= (nfsd4_dec)nfsd4_decode_remove,
13289064caaeSRandy Dunlap 	[OP_RENAME]		= (nfsd4_dec)nfsd4_decode_rename,
13299064caaeSRandy Dunlap 	[OP_RENEW]		= (nfsd4_dec)nfsd4_decode_notsupp,
13309064caaeSRandy Dunlap 	[OP_RESTOREFH]		= (nfsd4_dec)nfsd4_decode_noop,
13319064caaeSRandy Dunlap 	[OP_SAVEFH]		= (nfsd4_dec)nfsd4_decode_noop,
13329064caaeSRandy Dunlap 	[OP_SECINFO]		= (nfsd4_dec)nfsd4_decode_secinfo,
13339064caaeSRandy Dunlap 	[OP_SETATTR]		= (nfsd4_dec)nfsd4_decode_setattr,
13349064caaeSRandy Dunlap 	[OP_SETCLIENTID]	= (nfsd4_dec)nfsd4_decode_notsupp,
13359064caaeSRandy Dunlap 	[OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp,
13369064caaeSRandy Dunlap 	[OP_VERIFY]		= (nfsd4_dec)nfsd4_decode_verify,
13379064caaeSRandy Dunlap 	[OP_WRITE]		= (nfsd4_dec)nfsd4_decode_write,
13389064caaeSRandy Dunlap 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_dec)nfsd4_decode_notsupp,
13392db134ebSAndy Adamson 
13402db134ebSAndy Adamson 	/* new operations for NFSv4.1 */
13419064caaeSRandy Dunlap 	[OP_BACKCHANNEL_CTL]	= (nfsd4_dec)nfsd4_decode_notsupp,
13429064caaeSRandy Dunlap 	[OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_notsupp,
13439064caaeSRandy Dunlap 	[OP_EXCHANGE_ID]	= (nfsd4_dec)nfsd4_decode_exchange_id,
13449064caaeSRandy Dunlap 	[OP_CREATE_SESSION]	= (nfsd4_dec)nfsd4_decode_create_session,
13459064caaeSRandy Dunlap 	[OP_DESTROY_SESSION]	= (nfsd4_dec)nfsd4_decode_destroy_session,
13469064caaeSRandy Dunlap 	[OP_FREE_STATEID]	= (nfsd4_dec)nfsd4_decode_notsupp,
13479064caaeSRandy Dunlap 	[OP_GET_DIR_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
13489064caaeSRandy Dunlap 	[OP_GETDEVICEINFO]	= (nfsd4_dec)nfsd4_decode_notsupp,
13499064caaeSRandy Dunlap 	[OP_GETDEVICELIST]	= (nfsd4_dec)nfsd4_decode_notsupp,
13509064caaeSRandy Dunlap 	[OP_LAYOUTCOMMIT]	= (nfsd4_dec)nfsd4_decode_notsupp,
13519064caaeSRandy Dunlap 	[OP_LAYOUTGET]		= (nfsd4_dec)nfsd4_decode_notsupp,
13529064caaeSRandy Dunlap 	[OP_LAYOUTRETURN]	= (nfsd4_dec)nfsd4_decode_notsupp,
13539064caaeSRandy Dunlap 	[OP_SECINFO_NO_NAME]	= (nfsd4_dec)nfsd4_decode_notsupp,
13549064caaeSRandy Dunlap 	[OP_SEQUENCE]		= (nfsd4_dec)nfsd4_decode_sequence,
13559064caaeSRandy Dunlap 	[OP_SET_SSV]		= (nfsd4_dec)nfsd4_decode_notsupp,
13569064caaeSRandy Dunlap 	[OP_TEST_STATEID]	= (nfsd4_dec)nfsd4_decode_notsupp,
13579064caaeSRandy Dunlap 	[OP_WANT_DELEGATION]	= (nfsd4_dec)nfsd4_decode_notsupp,
13589064caaeSRandy Dunlap 	[OP_DESTROY_CLIENTID]	= (nfsd4_dec)nfsd4_decode_notsupp,
13594dc6ec00SJ. Bruce Fields 	[OP_RECLAIM_COMPLETE]	= (nfsd4_dec)nfsd4_decode_reclaim_complete,
13602db134ebSAndy Adamson };
13612db134ebSAndy Adamson 
1362f2feb96bSBenny Halevy struct nfsd4_minorversion_ops {
1363f2feb96bSBenny Halevy 	nfsd4_dec *decoders;
1364f2feb96bSBenny Halevy 	int nops;
1365f2feb96bSBenny Halevy };
1366f2feb96bSBenny Halevy 
1367f2feb96bSBenny Halevy static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
1368ad1060c8SJ. Bruce Fields 	[0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
13692db134ebSAndy Adamson 	[1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
1370f2feb96bSBenny Halevy };
1371f2feb96bSBenny Halevy 
1372347e0ad9SBenny Halevy static __be32
13731da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
13741da177e4SLinus Torvalds {
13751da177e4SLinus Torvalds 	DECODE_HEAD;
13761da177e4SLinus Torvalds 	struct nfsd4_op *op;
1377f2feb96bSBenny Halevy 	struct nfsd4_minorversion_ops *ops;
13781da177e4SLinus Torvalds 	int i;
13791da177e4SLinus Torvalds 
13801da177e4SLinus Torvalds 	/*
13811da177e4SLinus Torvalds 	 * XXX: According to spec, we should check the tag
13821da177e4SLinus Torvalds 	 * for UTF-8 compliance.  I'm postponing this for
13831da177e4SLinus Torvalds 	 * now because it seems that some clients do use
13841da177e4SLinus Torvalds 	 * binary tags.
13851da177e4SLinus Torvalds 	 */
13861da177e4SLinus Torvalds 	READ_BUF(4);
13871da177e4SLinus Torvalds 	READ32(argp->taglen);
13881da177e4SLinus Torvalds 	READ_BUF(argp->taglen + 8);
13891da177e4SLinus Torvalds 	SAVEMEM(argp->tag, argp->taglen);
13901da177e4SLinus Torvalds 	READ32(argp->minorversion);
13911da177e4SLinus Torvalds 	READ32(argp->opcnt);
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds 	if (argp->taglen > NFSD4_MAX_TAGLEN)
13941da177e4SLinus Torvalds 		goto xdr_error;
13951da177e4SLinus Torvalds 	if (argp->opcnt > 100)
13961da177e4SLinus Torvalds 		goto xdr_error;
13971da177e4SLinus Torvalds 
1398e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
13991da177e4SLinus Torvalds 		argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
14001da177e4SLinus Torvalds 		if (!argp->ops) {
14011da177e4SLinus Torvalds 			argp->ops = argp->iops;
1402817cb9d4SChuck Lever 			dprintk("nfsd: couldn't allocate room for COMPOUND\n");
14031da177e4SLinus Torvalds 			goto xdr_error;
14041da177e4SLinus Torvalds 		}
14051da177e4SLinus Torvalds 	}
14061da177e4SLinus Torvalds 
1407f2feb96bSBenny Halevy 	if (argp->minorversion >= ARRAY_SIZE(nfsd4_minorversion))
140830cff1ffSBenny Halevy 		argp->opcnt = 0;
140930cff1ffSBenny Halevy 
1410f2feb96bSBenny Halevy 	ops = &nfsd4_minorversion[argp->minorversion];
14111da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
14121da177e4SLinus Torvalds 		op = &argp->ops[i];
14131da177e4SLinus Torvalds 		op->replay = NULL;
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds 		/*
14161da177e4SLinus Torvalds 		 * We can't use READ_BUF() here because we need to handle
14171da177e4SLinus Torvalds 		 * a missing opcode as an OP_WRITE + 1. So we need to check
14181da177e4SLinus Torvalds 		 * to see if we're truly at the end of our buffer or if there
14191da177e4SLinus Torvalds 		 * is another page we need to flip to.
14201da177e4SLinus Torvalds 		 */
14211da177e4SLinus Torvalds 
14221da177e4SLinus Torvalds 		if (argp->p == argp->end) {
14231da177e4SLinus Torvalds 			if (argp->pagelen < 4) {
14241da177e4SLinus Torvalds 				/* There isn't an opcode still on the wire */
14251da177e4SLinus Torvalds 				op->opnum = OP_WRITE + 1;
14261da177e4SLinus Torvalds 				op->status = nfserr_bad_xdr;
14271da177e4SLinus Torvalds 				argp->opcnt = i+1;
14281da177e4SLinus Torvalds 				break;
14291da177e4SLinus Torvalds 			}
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds 			/*
14321da177e4SLinus Torvalds 			 * False alarm. We just hit a page boundary, but there
14331da177e4SLinus Torvalds 			 * is still data available.  Move pointer across page
14341da177e4SLinus Torvalds 			 * boundary.  *snip from READ_BUF*
14351da177e4SLinus Torvalds 			 */
14361da177e4SLinus Torvalds 			argp->p = page_address(argp->pagelist[0]);
14371da177e4SLinus Torvalds 			argp->pagelist++;
14381da177e4SLinus Torvalds 			if (argp->pagelen < PAGE_SIZE) {
14392bc3c117SNeil Brown 				argp->end = argp->p + (argp->pagelen>>2);
14401da177e4SLinus Torvalds 				argp->pagelen = 0;
14411da177e4SLinus Torvalds 			} else {
14422bc3c117SNeil Brown 				argp->end = argp->p + (PAGE_SIZE>>2);
14431da177e4SLinus Torvalds 				argp->pagelen -= PAGE_SIZE;
14441da177e4SLinus Torvalds 			}
14451da177e4SLinus Torvalds 		}
14461da177e4SLinus Torvalds 		op->opnum = ntohl(*argp->p++);
14471da177e4SLinus Torvalds 
1448de3cab79SRicardo Labiaga 		if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP)
1449f2feb96bSBenny Halevy 			op->status = ops->decoders[op->opnum](argp, &op->u);
1450347e0ad9SBenny Halevy 		else {
14511da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
14521da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
14531da177e4SLinus Torvalds 		}
14541da177e4SLinus Torvalds 
14551da177e4SLinus Torvalds 		if (op->status) {
14561da177e4SLinus Torvalds 			argp->opcnt = i+1;
14571da177e4SLinus Torvalds 			break;
14581da177e4SLinus Torvalds 		}
14591da177e4SLinus Torvalds 	}
14601da177e4SLinus Torvalds 
14611da177e4SLinus Torvalds 	DECODE_TAIL;
14621da177e4SLinus Torvalds }
14631da177e4SLinus Torvalds 
14641da177e4SLinus Torvalds #define WRITE32(n)               *p++ = htonl(n)
14651da177e4SLinus Torvalds #define WRITE64(n)               do {				\
14661da177e4SLinus Torvalds 	*p++ = htonl((u32)((n) >> 32));				\
14671da177e4SLinus Torvalds 	*p++ = htonl((u32)(n));					\
14681da177e4SLinus Torvalds } while (0)
14695108b276SHarvey Harrison #define WRITEMEM(ptr,nbytes)     do { if (nbytes > 0) {		\
14701da177e4SLinus Torvalds 	*(p + XDR_QUADLEN(nbytes) -1) = 0;                      \
14711da177e4SLinus Torvalds 	memcpy(p, ptr, nbytes);					\
14721da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);				\
14735108b276SHarvey Harrison }} while (0)
1474c654b8a9SJ. Bruce Fields 
1475c654b8a9SJ. Bruce Fields static void write32(__be32 **p, u32 n)
1476c654b8a9SJ. Bruce Fields {
1477c654b8a9SJ. Bruce Fields 	*(*p)++ = n;
1478c654b8a9SJ. Bruce Fields }
1479c654b8a9SJ. Bruce Fields 
1480c654b8a9SJ. Bruce Fields static void write64(__be32 **p, u64 n)
1481c654b8a9SJ. Bruce Fields {
1482c654b8a9SJ. Bruce Fields 	write32(p, (u32)(n >> 32));
1483c654b8a9SJ. Bruce Fields 	write32(p, (u32)n);
1484c654b8a9SJ. Bruce Fields }
1485c654b8a9SJ. Bruce Fields 
1486c654b8a9SJ. Bruce Fields static void write_change(__be32 **p, struct kstat *stat, struct inode *inode)
1487c654b8a9SJ. Bruce Fields {
1488c654b8a9SJ. Bruce Fields 	if (IS_I_VERSION(inode)) {
1489c654b8a9SJ. Bruce Fields 		write64(p, inode->i_version);
1490c654b8a9SJ. Bruce Fields 	} else {
1491c654b8a9SJ. Bruce Fields 		write32(p, stat->ctime.tv_sec);
1492c654b8a9SJ. Bruce Fields 		write32(p, stat->ctime.tv_nsec);
1493c654b8a9SJ. Bruce Fields 	}
1494c654b8a9SJ. Bruce Fields }
1495c654b8a9SJ. Bruce Fields 
1496c654b8a9SJ. Bruce Fields static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
1497c654b8a9SJ. Bruce Fields {
1498c654b8a9SJ. Bruce Fields 	write32(p, c->atomic);
1499c654b8a9SJ. Bruce Fields 	if (c->change_supported) {
1500c654b8a9SJ. Bruce Fields 		write64(p, c->before_change);
1501c654b8a9SJ. Bruce Fields 		write64(p, c->after_change);
1502c654b8a9SJ. Bruce Fields 	} else {
1503c654b8a9SJ. Bruce Fields 		write32(p, c->before_ctime_sec);
1504c654b8a9SJ. Bruce Fields 		write32(p, c->before_ctime_nsec);
1505c654b8a9SJ. Bruce Fields 		write32(p, c->after_ctime_sec);
1506c654b8a9SJ. Bruce Fields 		write32(p, c->after_ctime_nsec);
1507c654b8a9SJ. Bruce Fields 	}
1508c654b8a9SJ. Bruce Fields }
15091da177e4SLinus Torvalds 
15101da177e4SLinus Torvalds #define RESERVE_SPACE(nbytes)	do {				\
15111da177e4SLinus Torvalds 	p = resp->p;						\
15121da177e4SLinus Torvalds 	BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end);		\
15131da177e4SLinus Torvalds } while (0)
15141da177e4SLinus Torvalds #define ADJUST_ARGS()		resp->p = p
15151da177e4SLinus Torvalds 
15161da177e4SLinus Torvalds /*
15171da177e4SLinus Torvalds  * Header routine to setup seqid operation replay cache
15181da177e4SLinus Torvalds  */
15191da177e4SLinus Torvalds #define ENCODE_SEQID_OP_HEAD					\
15202ebbc012SAl Viro 	__be32 *save;						\
15211da177e4SLinus Torvalds 								\
15221da177e4SLinus Torvalds 	save = resp->p;
15231da177e4SLinus Torvalds 
15241da177e4SLinus Torvalds /*
15257fb64ceeSNeilBrown  * Routine for encoding the result of a "seqid-mutating" NFSv4 operation.  This
15267fb64ceeSNeilBrown  * is where sequence id's are incremented, and the replay cache is filled.
15277fb64ceeSNeilBrown  * Note that we increment sequence id's here, at the last moment, so we're sure
15287fb64ceeSNeilBrown  * we know whether the error to be returned is a sequence id mutating error.
15291da177e4SLinus Torvalds  */
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds #define ENCODE_SEQID_OP_TAIL(stateowner) do {			\
15321da177e4SLinus Torvalds 	if (seqid_mutating_err(nfserr) && stateowner) { 	\
15331da177e4SLinus Torvalds 		stateowner->so_seqid++;				\
15341da177e4SLinus Torvalds 		stateowner->so_replay.rp_status = nfserr;   	\
15351da177e4SLinus Torvalds 		stateowner->so_replay.rp_buflen = 		\
15361da177e4SLinus Torvalds 			  (((char *)(resp)->p - (char *)save)); \
15371da177e4SLinus Torvalds 		memcpy(stateowner->so_replay.rp_buf, save,      \
15381da177e4SLinus Torvalds  			stateowner->so_replay.rp_buflen); 	\
15391da177e4SLinus Torvalds 	} } while (0);
15401da177e4SLinus Torvalds 
154181c3f413SJ.Bruce Fields /* Encode as an array of strings the string given with components
15423ad2f3fbSDaniel Mack  * separated @sep.
154381c3f413SJ.Bruce Fields  */
1544b37ad28bSAl Viro static __be32 nfsd4_encode_components(char sep, char *components,
15452ebbc012SAl Viro 				   __be32 **pp, int *buflen)
154681c3f413SJ.Bruce Fields {
15472ebbc012SAl Viro 	__be32 *p = *pp;
15482ebbc012SAl Viro 	__be32 *countp = p;
154981c3f413SJ.Bruce Fields 	int strlen, count=0;
155081c3f413SJ.Bruce Fields 	char *str, *end;
155181c3f413SJ.Bruce Fields 
155281c3f413SJ.Bruce Fields 	dprintk("nfsd4_encode_components(%s)\n", components);
155381c3f413SJ.Bruce Fields 	if ((*buflen -= 4) < 0)
155481c3f413SJ.Bruce Fields 		return nfserr_resource;
155581c3f413SJ.Bruce Fields 	WRITE32(0); /* We will fill this in with @count later */
155681c3f413SJ.Bruce Fields 	end = str = components;
155781c3f413SJ.Bruce Fields 	while (*end) {
155881c3f413SJ.Bruce Fields 		for (; *end && (*end != sep); end++)
155981c3f413SJ.Bruce Fields 			; /* Point to end of component */
156081c3f413SJ.Bruce Fields 		strlen = end - str;
156181c3f413SJ.Bruce Fields 		if (strlen) {
156281c3f413SJ.Bruce Fields 			if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
156381c3f413SJ.Bruce Fields 				return nfserr_resource;
156481c3f413SJ.Bruce Fields 			WRITE32(strlen);
156581c3f413SJ.Bruce Fields 			WRITEMEM(str, strlen);
156681c3f413SJ.Bruce Fields 			count++;
156781c3f413SJ.Bruce Fields 		}
156881c3f413SJ.Bruce Fields 		else
156981c3f413SJ.Bruce Fields 			end++;
157081c3f413SJ.Bruce Fields 		str = end;
157181c3f413SJ.Bruce Fields 	}
157281c3f413SJ.Bruce Fields 	*pp = p;
157381c3f413SJ.Bruce Fields 	p = countp;
157481c3f413SJ.Bruce Fields 	WRITE32(count);
157581c3f413SJ.Bruce Fields 	return 0;
157681c3f413SJ.Bruce Fields }
157781c3f413SJ.Bruce Fields 
157881c3f413SJ.Bruce Fields /*
157981c3f413SJ.Bruce Fields  * encode a location element of a fs_locations structure
158081c3f413SJ.Bruce Fields  */
1581b37ad28bSAl Viro static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
15822ebbc012SAl Viro 				    __be32 **pp, int *buflen)
158381c3f413SJ.Bruce Fields {
1584b37ad28bSAl Viro 	__be32 status;
15852ebbc012SAl Viro 	__be32 *p = *pp;
158681c3f413SJ.Bruce Fields 
158781c3f413SJ.Bruce Fields 	status = nfsd4_encode_components(':', location->hosts, &p, buflen);
158881c3f413SJ.Bruce Fields 	if (status)
158981c3f413SJ.Bruce Fields 		return status;
159081c3f413SJ.Bruce Fields 	status = nfsd4_encode_components('/', location->path, &p, buflen);
159181c3f413SJ.Bruce Fields 	if (status)
159281c3f413SJ.Bruce Fields 		return status;
159381c3f413SJ.Bruce Fields 	*pp = p;
159481c3f413SJ.Bruce Fields 	return 0;
159581c3f413SJ.Bruce Fields }
159681c3f413SJ.Bruce Fields 
159781c3f413SJ.Bruce Fields /*
159881c3f413SJ.Bruce Fields  * Return the path to an export point in the pseudo filesystem namespace
159981c3f413SJ.Bruce Fields  * Returned string is safe to use as long as the caller holds a reference
160081c3f413SJ.Bruce Fields  * to @exp.
160181c3f413SJ.Bruce Fields  */
1602b37ad28bSAl Viro static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
160381c3f413SJ.Bruce Fields {
160481c3f413SJ.Bruce Fields 	struct svc_fh tmp_fh;
16052671a4bfSTrond Myklebust 	char *path = NULL, *rootpath;
16062671a4bfSTrond Myklebust 	size_t rootlen;
160781c3f413SJ.Bruce Fields 
160881c3f413SJ.Bruce Fields 	fh_init(&tmp_fh, NFS4_FHSIZE);
1609df547efbSJ. Bruce Fields 	*stat = exp_pseudoroot(rqstp, &tmp_fh);
1610cc45f017SAl Viro 	if (*stat)
1611cc45f017SAl Viro 		return NULL;
161254775491SJan Blunck 	rootpath = tmp_fh.fh_export->ex_pathname;
161381c3f413SJ.Bruce Fields 
161454775491SJan Blunck 	path = exp->ex_pathname;
161581c3f413SJ.Bruce Fields 
16162671a4bfSTrond Myklebust 	rootlen = strlen(rootpath);
16172671a4bfSTrond Myklebust 	if (strncmp(path, rootpath, rootlen)) {
1618817cb9d4SChuck Lever 		dprintk("nfsd: fs_locations failed;"
161981c3f413SJ.Bruce Fields 			"%s is not contained in %s\n", path, rootpath);
1620cc45f017SAl Viro 		*stat = nfserr_notsupp;
16212671a4bfSTrond Myklebust 		path = NULL;
16222671a4bfSTrond Myklebust 		goto out;
162381c3f413SJ.Bruce Fields 	}
16242671a4bfSTrond Myklebust 	path += rootlen;
16252671a4bfSTrond Myklebust out:
16262671a4bfSTrond Myklebust 	fh_put(&tmp_fh);
16272671a4bfSTrond Myklebust 	return path;
162881c3f413SJ.Bruce Fields }
162981c3f413SJ.Bruce Fields 
163081c3f413SJ.Bruce Fields /*
163181c3f413SJ.Bruce Fields  *  encode a fs_locations structure
163281c3f413SJ.Bruce Fields  */
1633b37ad28bSAl Viro static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
163481c3f413SJ.Bruce Fields 				     struct svc_export *exp,
16352ebbc012SAl Viro 				     __be32 **pp, int *buflen)
163681c3f413SJ.Bruce Fields {
1637b37ad28bSAl Viro 	__be32 status;
1638cc45f017SAl Viro 	int i;
16392ebbc012SAl Viro 	__be32 *p = *pp;
164081c3f413SJ.Bruce Fields 	struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
1641cc45f017SAl Viro 	char *root = nfsd4_path(rqstp, exp, &status);
164281c3f413SJ.Bruce Fields 
1643cc45f017SAl Viro 	if (status)
1644cc45f017SAl Viro 		return status;
164581c3f413SJ.Bruce Fields 	status = nfsd4_encode_components('/', root, &p, buflen);
164681c3f413SJ.Bruce Fields 	if (status)
164781c3f413SJ.Bruce Fields 		return status;
164881c3f413SJ.Bruce Fields 	if ((*buflen -= 4) < 0)
164981c3f413SJ.Bruce Fields 		return nfserr_resource;
165081c3f413SJ.Bruce Fields 	WRITE32(fslocs->locations_count);
165181c3f413SJ.Bruce Fields 	for (i=0; i<fslocs->locations_count; i++) {
165281c3f413SJ.Bruce Fields 		status = nfsd4_encode_fs_location4(&fslocs->locations[i],
165381c3f413SJ.Bruce Fields 						   &p, buflen);
165481c3f413SJ.Bruce Fields 		if (status)
165581c3f413SJ.Bruce Fields 			return status;
165681c3f413SJ.Bruce Fields 	}
165781c3f413SJ.Bruce Fields 	*pp = p;
165881c3f413SJ.Bruce Fields 	return 0;
165981c3f413SJ.Bruce Fields }
16601da177e4SLinus Torvalds 
16611da177e4SLinus Torvalds static u32 nfs4_ftypes[16] = {
16621da177e4SLinus Torvalds         NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
16631da177e4SLinus Torvalds         NF4DIR,  NF4BAD,  NF4BLK, NF4BAD,
16641da177e4SLinus Torvalds         NF4REG,  NF4BAD,  NF4LNK, NF4BAD,
16651da177e4SLinus Torvalds         NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
16661da177e4SLinus Torvalds };
16671da177e4SLinus Torvalds 
1668b37ad28bSAl Viro static __be32
16691da177e4SLinus Torvalds nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
16702ebbc012SAl Viro 			__be32 **p, int *buflen)
16711da177e4SLinus Torvalds {
16721da177e4SLinus Torvalds 	int status;
16731da177e4SLinus Torvalds 
16741da177e4SLinus Torvalds 	if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
16751da177e4SLinus Torvalds 		return nfserr_resource;
16761da177e4SLinus Torvalds 	if (whotype != NFS4_ACL_WHO_NAMED)
16771da177e4SLinus Torvalds 		status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
16781da177e4SLinus Torvalds 	else if (group)
16791da177e4SLinus Torvalds 		status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
16801da177e4SLinus Torvalds 	else
16811da177e4SLinus Torvalds 		status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
16821da177e4SLinus Torvalds 	if (status < 0)
16831da177e4SLinus Torvalds 		return nfserrno(status);
16841da177e4SLinus Torvalds 	*p = xdr_encode_opaque(*p, NULL, status);
16851da177e4SLinus Torvalds 	*buflen -= (XDR_QUADLEN(status) << 2) + 4;
16861da177e4SLinus Torvalds 	BUG_ON(*buflen < 0);
16871da177e4SLinus Torvalds 	return 0;
16881da177e4SLinus Torvalds }
16891da177e4SLinus Torvalds 
1690b37ad28bSAl Viro static inline __be32
16912ebbc012SAl Viro nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen)
16921da177e4SLinus Torvalds {
16931da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
16941da177e4SLinus Torvalds }
16951da177e4SLinus Torvalds 
1696b37ad28bSAl Viro static inline __be32
16972ebbc012SAl Viro nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen)
16981da177e4SLinus Torvalds {
16991da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
17001da177e4SLinus Torvalds }
17011da177e4SLinus Torvalds 
1702b37ad28bSAl Viro static inline __be32
17031da177e4SLinus Torvalds nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
17042ebbc012SAl Viro 		__be32 **p, int *buflen)
17051da177e4SLinus Torvalds {
17061da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
17071da177e4SLinus Torvalds }
17081da177e4SLinus Torvalds 
170942ca0993SJ.Bruce Fields #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
171042ca0993SJ.Bruce Fields 			      FATTR4_WORD0_RDATTR_ERROR)
171142ca0993SJ.Bruce Fields #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
171242ca0993SJ.Bruce Fields 
1713b37ad28bSAl Viro static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
171442ca0993SJ.Bruce Fields {
171542ca0993SJ.Bruce Fields 	/* As per referral draft:  */
171642ca0993SJ.Bruce Fields 	if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
171742ca0993SJ.Bruce Fields 	    *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
171842ca0993SJ.Bruce Fields 		if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
171942ca0993SJ.Bruce Fields 	            *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
172042ca0993SJ.Bruce Fields 			*rdattr_err = NFSERR_MOVED;
172142ca0993SJ.Bruce Fields 		else
172242ca0993SJ.Bruce Fields 			return nfserr_moved;
172342ca0993SJ.Bruce Fields 	}
172442ca0993SJ.Bruce Fields 	*bmval0 &= WORD0_ABSENT_FS_ATTRS;
172542ca0993SJ.Bruce Fields 	*bmval1 &= WORD1_ABSENT_FS_ATTRS;
172642ca0993SJ.Bruce Fields 	return 0;
172742ca0993SJ.Bruce Fields }
17281da177e4SLinus Torvalds 
17291da177e4SLinus Torvalds /*
17301da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
17311da177e4SLinus Torvalds  * ourselves.
17321da177e4SLinus Torvalds  *
17331da177e4SLinus Torvalds  * @countp is the buffer size in _words_; upon successful return this becomes
17341da177e4SLinus Torvalds  * replaced with the number of words written.
17351da177e4SLinus Torvalds  */
1736b37ad28bSAl Viro __be32
17371da177e4SLinus Torvalds nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
17382ebbc012SAl Viro 		struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval,
1739406a7ea9SFrank Filz 		struct svc_rqst *rqstp, int ignore_crossmnt)
17401da177e4SLinus Torvalds {
17411da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
17421da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
17437e705706SAndy Adamson 	u32 bmval2 = bmval[2];
17441da177e4SLinus Torvalds 	struct kstat stat;
17451da177e4SLinus Torvalds 	struct svc_fh tempfh;
17461da177e4SLinus Torvalds 	struct kstatfs statfs;
17471da177e4SLinus Torvalds 	int buflen = *countp << 2;
17482ebbc012SAl Viro 	__be32 *attrlenp;
17491da177e4SLinus Torvalds 	u32 dummy;
17501da177e4SLinus Torvalds 	u64 dummy64;
175142ca0993SJ.Bruce Fields 	u32 rdattr_err = 0;
17522ebbc012SAl Viro 	__be32 *p = buffer;
1753b37ad28bSAl Viro 	__be32 status;
1754b8dd7b9aSAl Viro 	int err;
17551da177e4SLinus Torvalds 	int aclsupport = 0;
17561da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
17577e705706SAndy Adamson 	struct nfsd4_compoundres *resp = rqstp->rq_resp;
17587e705706SAndy Adamson 	u32 minorversion = resp->cstate.minorversion;
1759*ebabe9a9SChristoph Hellwig 	struct path path = {
1760*ebabe9a9SChristoph Hellwig 		.mnt	= exp->ex_path.mnt,
1761*ebabe9a9SChristoph Hellwig 		.dentry	= dentry,
1762*ebabe9a9SChristoph Hellwig 	};
17631da177e4SLinus Torvalds 
17641da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
17657e705706SAndy Adamson 	BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion));
17667e705706SAndy Adamson 	BUG_ON(bmval1 & ~nfsd_suppattrs1(minorversion));
17677e705706SAndy Adamson 	BUG_ON(bmval2 & ~nfsd_suppattrs2(minorversion));
17681da177e4SLinus Torvalds 
176942ca0993SJ.Bruce Fields 	if (exp->ex_fslocs.migrated) {
17707e705706SAndy Adamson 		BUG_ON(bmval[2]);
177142ca0993SJ.Bruce Fields 		status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err);
177242ca0993SJ.Bruce Fields 		if (status)
177342ca0993SJ.Bruce Fields 			goto out;
177442ca0993SJ.Bruce Fields 	}
177542ca0993SJ.Bruce Fields 
177654775491SJan Blunck 	err = vfs_getattr(exp->ex_path.mnt, dentry, &stat);
1777b8dd7b9aSAl Viro 	if (err)
17781da177e4SLinus Torvalds 		goto out_nfserr;
1779a16e92edSJ. Bruce Fields 	if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL |
1780a16e92edSJ. Bruce Fields 			FATTR4_WORD0_MAXNAME)) ||
17811da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
17821da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
1783*ebabe9a9SChristoph Hellwig 		err = vfs_statfs(&path, &statfs);
1784b8dd7b9aSAl Viro 		if (err)
17851da177e4SLinus Torvalds 			goto out_nfserr;
17861da177e4SLinus Torvalds 	}
17871da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
17881da177e4SLinus Torvalds 		fh_init(&tempfh, NFS4_FHSIZE);
17891da177e4SLinus Torvalds 		status = fh_compose(&tempfh, exp, dentry, NULL);
17901da177e4SLinus Torvalds 		if (status)
17911da177e4SLinus Torvalds 			goto out;
17921da177e4SLinus Torvalds 		fhp = &tempfh;
17931da177e4SLinus Torvalds 	}
17941da177e4SLinus Torvalds 	if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
17951da177e4SLinus Torvalds 			| FATTR4_WORD0_SUPPORTED_ATTRS)) {
1796b8dd7b9aSAl Viro 		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
1797b8dd7b9aSAl Viro 		aclsupport = (err == 0);
17981da177e4SLinus Torvalds 		if (bmval0 & FATTR4_WORD0_ACL) {
1799b8dd7b9aSAl Viro 			if (err == -EOPNOTSUPP)
18001da177e4SLinus Torvalds 				bmval0 &= ~FATTR4_WORD0_ACL;
1801b8dd7b9aSAl Viro 			else if (err == -EINVAL) {
18021da177e4SLinus Torvalds 				status = nfserr_attrnotsupp;
18031da177e4SLinus Torvalds 				goto out;
1804b8dd7b9aSAl Viro 			} else if (err != 0)
18051da177e4SLinus Torvalds 				goto out_nfserr;
18061da177e4SLinus Torvalds 		}
18071da177e4SLinus Torvalds 	}
18081da177e4SLinus Torvalds 	if ((buflen -= 16) < 0)
18091da177e4SLinus Torvalds 		goto out_resource;
18101da177e4SLinus Torvalds 
18117e705706SAndy Adamson 	if (unlikely(bmval2)) {
18127e705706SAndy Adamson 		WRITE32(3);
18137e705706SAndy Adamson 		WRITE32(bmval0);
18147e705706SAndy Adamson 		WRITE32(bmval1);
18157e705706SAndy Adamson 		WRITE32(bmval2);
18167e705706SAndy Adamson 	} else if (likely(bmval1)) {
18171da177e4SLinus Torvalds 		WRITE32(2);
18181da177e4SLinus Torvalds 		WRITE32(bmval0);
18191da177e4SLinus Torvalds 		WRITE32(bmval1);
18207e705706SAndy Adamson 	} else {
18217e705706SAndy Adamson 		WRITE32(1);
18227e705706SAndy Adamson 		WRITE32(bmval0);
18237e705706SAndy Adamson 	}
18241da177e4SLinus Torvalds 	attrlenp = p++;                /* to be backfilled later */
18251da177e4SLinus Torvalds 
18261da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
18277e705706SAndy Adamson 		u32 word0 = nfsd_suppattrs0(minorversion);
18287e705706SAndy Adamson 		u32 word1 = nfsd_suppattrs1(minorversion);
18297e705706SAndy Adamson 		u32 word2 = nfsd_suppattrs2(minorversion);
18307e705706SAndy Adamson 
18311da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
18321da177e4SLinus Torvalds 			goto out_resource;
183342ca0993SJ.Bruce Fields 		if (!aclsupport)
183442ca0993SJ.Bruce Fields 			word0 &= ~FATTR4_WORD0_ACL;
18357e705706SAndy Adamson 		if (!word2) {
18361da177e4SLinus Torvalds 			WRITE32(2);
183742ca0993SJ.Bruce Fields 			WRITE32(word0);
18387e705706SAndy Adamson 			WRITE32(word1);
18397e705706SAndy Adamson 		} else {
18407e705706SAndy Adamson 			WRITE32(3);
18417e705706SAndy Adamson 			WRITE32(word0);
18427e705706SAndy Adamson 			WRITE32(word1);
18437e705706SAndy Adamson 			WRITE32(word2);
18447e705706SAndy Adamson 		}
18451da177e4SLinus Torvalds 	}
18461da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
18471da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18481da177e4SLinus Torvalds 			goto out_resource;
18491da177e4SLinus Torvalds 		dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12];
18501da177e4SLinus Torvalds 		if (dummy == NF4BAD)
18511da177e4SLinus Torvalds 			goto out_serverfault;
18521da177e4SLinus Torvalds 		WRITE32(dummy);
18531da177e4SLinus Torvalds 	}
18541da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
18551da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18561da177e4SLinus Torvalds 			goto out_resource;
185749640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
1858e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT);
185949640001SNeilBrown 		else
1860e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
18611da177e4SLinus Torvalds 	}
18621da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
18631da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
18641da177e4SLinus Torvalds 			goto out_resource;
1865c654b8a9SJ. Bruce Fields 		write_change(&p, &stat, dentry->d_inode);
18661da177e4SLinus Torvalds 	}
18671da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
18681da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
18691da177e4SLinus Torvalds 			goto out_resource;
18701da177e4SLinus Torvalds 		WRITE64(stat.size);
18711da177e4SLinus Torvalds 	}
18721da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
18731da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18741da177e4SLinus Torvalds 			goto out_resource;
18751da177e4SLinus Torvalds 		WRITE32(1);
18761da177e4SLinus Torvalds 	}
18771da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
18781da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18791da177e4SLinus Torvalds 			goto out_resource;
18801da177e4SLinus Torvalds 		WRITE32(1);
18811da177e4SLinus Torvalds 	}
18821da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
18831da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
18841da177e4SLinus Torvalds 			goto out_resource;
18851da177e4SLinus Torvalds 		WRITE32(0);
18861da177e4SLinus Torvalds 	}
18871da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
18881da177e4SLinus Torvalds 		if ((buflen -= 16) < 0)
18891da177e4SLinus Torvalds 			goto out_resource;
189042ca0993SJ.Bruce Fields 		if (exp->ex_fslocs.migrated) {
189142ca0993SJ.Bruce Fields 			WRITE64(NFS4_REFERRAL_FSID_MAJOR);
189242ca0993SJ.Bruce Fields 			WRITE64(NFS4_REFERRAL_FSID_MINOR);
1893af6a4e28SNeilBrown 		} else switch(fsid_source(fhp)) {
1894af6a4e28SNeilBrown 		case FSIDSOURCE_FSID:
18951da177e4SLinus Torvalds 			WRITE64((u64)exp->ex_fsid);
18961da177e4SLinus Torvalds 			WRITE64((u64)0);
1897af6a4e28SNeilBrown 			break;
1898af6a4e28SNeilBrown 		case FSIDSOURCE_DEV:
18991da177e4SLinus Torvalds 			WRITE32(0);
19001da177e4SLinus Torvalds 			WRITE32(MAJOR(stat.dev));
19011da177e4SLinus Torvalds 			WRITE32(0);
19021da177e4SLinus Torvalds 			WRITE32(MINOR(stat.dev));
1903af6a4e28SNeilBrown 			break;
1904af6a4e28SNeilBrown 		case FSIDSOURCE_UUID:
1905af6a4e28SNeilBrown 			WRITEMEM(exp->ex_uuid, 16);
1906af6a4e28SNeilBrown 			break;
19071da177e4SLinus Torvalds 		}
19081da177e4SLinus Torvalds 	}
19091da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
19101da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19111da177e4SLinus Torvalds 			goto out_resource;
19121da177e4SLinus Torvalds 		WRITE32(0);
19131da177e4SLinus Torvalds 	}
19141da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
19151da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19161da177e4SLinus Torvalds 			goto out_resource;
1917cf07d2eaSJ. Bruce Fields 		WRITE32(nfsd4_lease);
19181da177e4SLinus Torvalds 	}
19191da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
19201da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19211da177e4SLinus Torvalds 			goto out_resource;
192242ca0993SJ.Bruce Fields 		WRITE32(rdattr_err);
19231da177e4SLinus Torvalds 	}
19241da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
19251da177e4SLinus Torvalds 		struct nfs4_ace *ace;
19261da177e4SLinus Torvalds 
19271da177e4SLinus Torvalds 		if (acl == NULL) {
19281da177e4SLinus Torvalds 			if ((buflen -= 4) < 0)
19291da177e4SLinus Torvalds 				goto out_resource;
19301da177e4SLinus Torvalds 
19311da177e4SLinus Torvalds 			WRITE32(0);
19321da177e4SLinus Torvalds 			goto out_acl;
19331da177e4SLinus Torvalds 		}
19341da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19351da177e4SLinus Torvalds 			goto out_resource;
19361da177e4SLinus Torvalds 		WRITE32(acl->naces);
19371da177e4SLinus Torvalds 
193828e05dd8SJ. Bruce Fields 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
19391da177e4SLinus Torvalds 			if ((buflen -= 4*3) < 0)
19401da177e4SLinus Torvalds 				goto out_resource;
19411da177e4SLinus Torvalds 			WRITE32(ace->type);
19421da177e4SLinus Torvalds 			WRITE32(ace->flag);
19431da177e4SLinus Torvalds 			WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
19441da177e4SLinus Torvalds 			status = nfsd4_encode_aclname(rqstp, ace->whotype,
19451da177e4SLinus Torvalds 				ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
19461da177e4SLinus Torvalds 				&p, &buflen);
19471da177e4SLinus Torvalds 			if (status == nfserr_resource)
19481da177e4SLinus Torvalds 				goto out_resource;
19491da177e4SLinus Torvalds 			if (status)
19501da177e4SLinus Torvalds 				goto out;
19511da177e4SLinus Torvalds 		}
19521da177e4SLinus Torvalds 	}
19531da177e4SLinus Torvalds out_acl:
19541da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
19551da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19561da177e4SLinus Torvalds 			goto out_resource;
19571da177e4SLinus Torvalds 		WRITE32(aclsupport ?
19581da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
19591da177e4SLinus Torvalds 	}
19601da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
19611da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19621da177e4SLinus Torvalds 			goto out_resource;
19631da177e4SLinus Torvalds 		WRITE32(1);
19641da177e4SLinus Torvalds 	}
19651da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
19661da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19671da177e4SLinus Torvalds 			goto out_resource;
19681da177e4SLinus Torvalds 		WRITE32(1);
19691da177e4SLinus Torvalds 	}
19701da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
19711da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19721da177e4SLinus Torvalds 			goto out_resource;
19731da177e4SLinus Torvalds 		WRITE32(1);
19741da177e4SLinus Torvalds 	}
19751da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
19761da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
19771da177e4SLinus Torvalds 			goto out_resource;
19781da177e4SLinus Torvalds 		WRITE32(1);
19791da177e4SLinus Torvalds 	}
19801da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
19811da177e4SLinus Torvalds 		buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4;
19821da177e4SLinus Torvalds 		if (buflen < 0)
19831da177e4SLinus Torvalds 			goto out_resource;
19841da177e4SLinus Torvalds 		WRITE32(fhp->fh_handle.fh_size);
19851da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
19861da177e4SLinus Torvalds 	}
19871da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
19881da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19891da177e4SLinus Torvalds 			goto out_resource;
199040ee5dc6SPeter Staubach 		WRITE64(stat.ino);
19911da177e4SLinus Torvalds 	}
19921da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
19931da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19941da177e4SLinus Torvalds 			goto out_resource;
19951da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
19961da177e4SLinus Torvalds 	}
19971da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
19981da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
19991da177e4SLinus Torvalds 			goto out_resource;
20001da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
20011da177e4SLinus Torvalds 	}
20021da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
20031da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20041da177e4SLinus Torvalds 			goto out_resource;
20051da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_files);
20061da177e4SLinus Torvalds 	}
200781c3f413SJ.Bruce Fields 	if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
200881c3f413SJ.Bruce Fields 		status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
200981c3f413SJ.Bruce Fields 		if (status == nfserr_resource)
201081c3f413SJ.Bruce Fields 			goto out_resource;
201181c3f413SJ.Bruce Fields 		if (status)
201281c3f413SJ.Bruce Fields 			goto out;
201381c3f413SJ.Bruce Fields 	}
20141da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
20151da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20161da177e4SLinus Torvalds 			goto out_resource;
20171da177e4SLinus Torvalds 		WRITE32(1);
20181da177e4SLinus Torvalds 	}
20191da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
20201da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20211da177e4SLinus Torvalds 			goto out_resource;
20221da177e4SLinus Torvalds 		WRITE64(~(u64)0);
20231da177e4SLinus Torvalds 	}
20241da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
20251da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20261da177e4SLinus Torvalds 			goto out_resource;
20271da177e4SLinus Torvalds 		WRITE32(255);
20281da177e4SLinus Torvalds 	}
20291da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
20301da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20311da177e4SLinus Torvalds 			goto out_resource;
2032a16e92edSJ. Bruce Fields 		WRITE32(statfs.f_namelen);
20331da177e4SLinus Torvalds 	}
20341da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
20351da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20361da177e4SLinus Torvalds 			goto out_resource;
20377adae489SGreg Banks 		WRITE64((u64) svc_max_payload(rqstp));
20381da177e4SLinus Torvalds 	}
20391da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
20401da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20411da177e4SLinus Torvalds 			goto out_resource;
20427adae489SGreg Banks 		WRITE64((u64) svc_max_payload(rqstp));
20431da177e4SLinus Torvalds 	}
20441da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
20451da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20461da177e4SLinus Torvalds 			goto out_resource;
20471da177e4SLinus Torvalds 		WRITE32(stat.mode & S_IALLUGO);
20481da177e4SLinus Torvalds 	}
20491da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
20501da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20511da177e4SLinus Torvalds 			goto out_resource;
20521da177e4SLinus Torvalds 		WRITE32(1);
20531da177e4SLinus Torvalds 	}
20541da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
20551da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
20561da177e4SLinus Torvalds 			goto out_resource;
20571da177e4SLinus Torvalds 		WRITE32(stat.nlink);
20581da177e4SLinus Torvalds 	}
20591da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
20601da177e4SLinus Torvalds 		status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
20611da177e4SLinus Torvalds 		if (status == nfserr_resource)
20621da177e4SLinus Torvalds 			goto out_resource;
20631da177e4SLinus Torvalds 		if (status)
20641da177e4SLinus Torvalds 			goto out;
20651da177e4SLinus Torvalds 	}
20661da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
20671da177e4SLinus Torvalds 		status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
20681da177e4SLinus Torvalds 		if (status == nfserr_resource)
20691da177e4SLinus Torvalds 			goto out_resource;
20701da177e4SLinus Torvalds 		if (status)
20711da177e4SLinus Torvalds 			goto out;
20721da177e4SLinus Torvalds 	}
20731da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
20741da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20751da177e4SLinus Torvalds 			goto out_resource;
20761da177e4SLinus Torvalds 		WRITE32((u32) MAJOR(stat.rdev));
20771da177e4SLinus Torvalds 		WRITE32((u32) MINOR(stat.rdev));
20781da177e4SLinus Torvalds 	}
20791da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
20801da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20811da177e4SLinus Torvalds 			goto out_resource;
20821da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
20831da177e4SLinus Torvalds 		WRITE64(dummy64);
20841da177e4SLinus Torvalds 	}
20851da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
20861da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20871da177e4SLinus Torvalds 			goto out_resource;
20881da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
20891da177e4SLinus Torvalds 		WRITE64(dummy64);
20901da177e4SLinus Torvalds 	}
20911da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
20921da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20931da177e4SLinus Torvalds 			goto out_resource;
20941da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
20951da177e4SLinus Torvalds 		WRITE64(dummy64);
20961da177e4SLinus Torvalds 	}
20971da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
20981da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
20991da177e4SLinus Torvalds 			goto out_resource;
21001da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
21011da177e4SLinus Torvalds 		WRITE64(dummy64);
21021da177e4SLinus Torvalds 	}
21031da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
21041da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
21051da177e4SLinus Torvalds 			goto out_resource;
21061da177e4SLinus Torvalds 		WRITE32(0);
21071da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_sec);
21081da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_nsec);
21091da177e4SLinus Torvalds 	}
21101da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
21111da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
21121da177e4SLinus Torvalds 			goto out_resource;
21131da177e4SLinus Torvalds 		WRITE32(0);
21141da177e4SLinus Torvalds 		WRITE32(1);
21151da177e4SLinus Torvalds 		WRITE32(0);
21161da177e4SLinus Torvalds 	}
21171da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
21181da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
21191da177e4SLinus Torvalds 			goto out_resource;
21201da177e4SLinus Torvalds 		WRITE32(0);
21211da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_sec);
21221da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_nsec);
21231da177e4SLinus Torvalds 	}
21241da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
21251da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
21261da177e4SLinus Torvalds 			goto out_resource;
21271da177e4SLinus Torvalds 		WRITE32(0);
21281da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_sec);
21291da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_nsec);
21301da177e4SLinus Torvalds 	}
21311da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
21321da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
21331da177e4SLinus Torvalds                 	goto out_resource;
2134406a7ea9SFrank Filz 		/*
2135406a7ea9SFrank Filz 		 * Get parent's attributes if not ignoring crossmount
2136406a7ea9SFrank Filz 		 * and this is the root of a cross-mounted filesystem.
2137406a7ea9SFrank Filz 		 */
2138406a7ea9SFrank Filz 		if (ignore_crossmnt == 0 &&
2139462d6057SAl Viro 		    dentry == exp->ex_path.mnt->mnt_root) {
2140462d6057SAl Viro 			struct path path = exp->ex_path;
2141462d6057SAl Viro 			path_get(&path);
2142462d6057SAl Viro 			while (follow_up(&path)) {
2143462d6057SAl Viro 				if (path.dentry != path.mnt->mnt_root)
2144462d6057SAl Viro 					break;
2145462d6057SAl Viro 			}
2146462d6057SAl Viro 			err = vfs_getattr(path.mnt, path.dentry, &stat);
2147462d6057SAl Viro 			path_put(&path);
214840ee5dc6SPeter Staubach 			if (err)
214940ee5dc6SPeter Staubach 				goto out_nfserr;
215040ee5dc6SPeter Staubach 		}
215140ee5dc6SPeter Staubach 		WRITE64(stat.ino);
21521da177e4SLinus Torvalds 	}
21538c18f205SBenny Halevy 	if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
21548c18f205SBenny Halevy 		WRITE32(3);
21558c18f205SBenny Halevy 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
21568c18f205SBenny Halevy 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
21578c18f205SBenny Halevy 		WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD2);
21588c18f205SBenny Halevy 	}
21597e705706SAndy Adamson 
21601da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
21611da177e4SLinus Torvalds 	*countp = p - buffer;
21621da177e4SLinus Torvalds 	status = nfs_ok;
21631da177e4SLinus Torvalds 
21641da177e4SLinus Torvalds out:
216528e05dd8SJ. Bruce Fields 	kfree(acl);
21661da177e4SLinus Torvalds 	if (fhp == &tempfh)
21671da177e4SLinus Torvalds 		fh_put(&tempfh);
21681da177e4SLinus Torvalds 	return status;
21691da177e4SLinus Torvalds out_nfserr:
2170b8dd7b9aSAl Viro 	status = nfserrno(err);
21711da177e4SLinus Torvalds 	goto out;
21721da177e4SLinus Torvalds out_resource:
21731da177e4SLinus Torvalds 	*countp = 0;
21741da177e4SLinus Torvalds 	status = nfserr_resource;
21751da177e4SLinus Torvalds 	goto out;
21761da177e4SLinus Torvalds out_serverfault:
21771da177e4SLinus Torvalds 	status = nfserr_serverfault;
21781da177e4SLinus Torvalds 	goto out;
21791da177e4SLinus Torvalds }
21801da177e4SLinus Torvalds 
2181c0ce6ec8SJ. Bruce Fields static inline int attributes_need_mount(u32 *bmval)
2182c0ce6ec8SJ. Bruce Fields {
2183c0ce6ec8SJ. Bruce Fields 	if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
2184c0ce6ec8SJ. Bruce Fields 		return 1;
2185c0ce6ec8SJ. Bruce Fields 	if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
2186c0ce6ec8SJ. Bruce Fields 		return 1;
2187c0ce6ec8SJ. Bruce Fields 	return 0;
2188c0ce6ec8SJ. Bruce Fields }
2189c0ce6ec8SJ. Bruce Fields 
2190b37ad28bSAl Viro static __be32
21911da177e4SLinus Torvalds nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
21922ebbc012SAl Viro 		const char *name, int namlen, __be32 *p, int *buflen)
21931da177e4SLinus Torvalds {
21941da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
21951da177e4SLinus Torvalds 	struct dentry *dentry;
2196b37ad28bSAl Viro 	__be32 nfserr;
2197406a7ea9SFrank Filz 	int ignore_crossmnt = 0;
21981da177e4SLinus Torvalds 
21991da177e4SLinus Torvalds 	dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
22001da177e4SLinus Torvalds 	if (IS_ERR(dentry))
22011da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
2202b2c0cea6SJ. Bruce Fields 	if (!dentry->d_inode) {
2203b2c0cea6SJ. Bruce Fields 		/*
2204b2c0cea6SJ. Bruce Fields 		 * nfsd_buffered_readdir drops the i_mutex between
2205b2c0cea6SJ. Bruce Fields 		 * readdir and calling this callback, leaving a window
2206b2c0cea6SJ. Bruce Fields 		 * where this directory entry could have gone away.
2207b2c0cea6SJ. Bruce Fields 		 */
2208b2c0cea6SJ. Bruce Fields 		dput(dentry);
2209b2c0cea6SJ. Bruce Fields 		return nfserr_noent;
2210b2c0cea6SJ. Bruce Fields 	}
22111da177e4SLinus Torvalds 
22121da177e4SLinus Torvalds 	exp_get(exp);
2213406a7ea9SFrank Filz 	/*
2214406a7ea9SFrank Filz 	 * In the case of a mountpoint, the client may be asking for
2215406a7ea9SFrank Filz 	 * attributes that are only properties of the underlying filesystem
2216406a7ea9SFrank Filz 	 * as opposed to the cross-mounted file system. In such a case,
2217406a7ea9SFrank Filz 	 * we will not follow the cross mount and will fill the attribtutes
2218406a7ea9SFrank Filz 	 * directly from the mountpoint dentry.
2219406a7ea9SFrank Filz 	 */
22203227fa41SJ. Bruce Fields 	if (nfsd_mountpoint(dentry, exp)) {
2221021d3a72SJ.Bruce Fields 		int err;
2222021d3a72SJ.Bruce Fields 
22233227fa41SJ. Bruce Fields 		if (!(exp->ex_flags & NFSEXP_V4ROOT)
22243227fa41SJ. Bruce Fields 				&& !attributes_need_mount(cd->rd_bmval)) {
22253227fa41SJ. Bruce Fields 			ignore_crossmnt = 1;
22263227fa41SJ. Bruce Fields 			goto out_encode;
22273227fa41SJ. Bruce Fields 		}
2228dcb488a3SAndy Adamson 		/*
2229dcb488a3SAndy Adamson 		 * Why the heck aren't we just using nfsd_lookup??
2230dcb488a3SAndy Adamson 		 * Different "."/".." handling?  Something else?
2231dcb488a3SAndy Adamson 		 * At least, add a comment here to explain....
2232dcb488a3SAndy Adamson 		 */
2233021d3a72SJ.Bruce Fields 		err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
2234021d3a72SJ.Bruce Fields 		if (err) {
2235021d3a72SJ.Bruce Fields 			nfserr = nfserrno(err);
22361da177e4SLinus Torvalds 			goto out_put;
22371da177e4SLinus Torvalds 		}
2238dcb488a3SAndy Adamson 		nfserr = check_nfsd_access(exp, cd->rd_rqstp);
2239dcb488a3SAndy Adamson 		if (nfserr)
2240dcb488a3SAndy Adamson 			goto out_put;
22411da177e4SLinus Torvalds 
22421da177e4SLinus Torvalds 	}
22433227fa41SJ. Bruce Fields out_encode:
22441da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
2245406a7ea9SFrank Filz 					cd->rd_rqstp, ignore_crossmnt);
22461da177e4SLinus Torvalds out_put:
22471da177e4SLinus Torvalds 	dput(dentry);
22481da177e4SLinus Torvalds 	exp_put(exp);
22491da177e4SLinus Torvalds 	return nfserr;
22501da177e4SLinus Torvalds }
22511da177e4SLinus Torvalds 
22522ebbc012SAl Viro static __be32 *
2253b37ad28bSAl Viro nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr)
22541da177e4SLinus Torvalds {
22552ebbc012SAl Viro 	__be32 *attrlenp;
22561da177e4SLinus Torvalds 
22571da177e4SLinus Torvalds 	if (buflen < 6)
22581da177e4SLinus Torvalds 		return NULL;
22591da177e4SLinus Torvalds 	*p++ = htonl(2);
22601da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
22611da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
22621da177e4SLinus Torvalds 
22631da177e4SLinus Torvalds 	attrlenp = p++;
22641da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
22651da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
22661da177e4SLinus Torvalds 	return p;
22671da177e4SLinus Torvalds }
22681da177e4SLinus Torvalds 
22691da177e4SLinus Torvalds static int
2270a0ad13efSNeilBrown nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2271a0ad13efSNeilBrown 		    loff_t offset, u64 ino, unsigned int d_type)
22721da177e4SLinus Torvalds {
2273a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
22741da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
22751da177e4SLinus Torvalds 	int buflen;
22762ebbc012SAl Viro 	__be32 *p = cd->buffer;
2277b2c0cea6SJ. Bruce Fields 	__be32 *cookiep;
2278b37ad28bSAl Viro 	__be32 nfserr = nfserr_toosmall;
22791da177e4SLinus Torvalds 
22801da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
22811da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
22821da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
22831da177e4SLinus Torvalds 		return 0;
22841da177e4SLinus Torvalds 	}
22851da177e4SLinus Torvalds 
22861da177e4SLinus Torvalds 	if (cd->offset)
22871da177e4SLinus Torvalds 		xdr_encode_hyper(cd->offset, (u64) offset);
22881da177e4SLinus Torvalds 
22891da177e4SLinus Torvalds 	buflen = cd->buflen - 4 - XDR_QUADLEN(namlen);
22901da177e4SLinus Torvalds 	if (buflen < 0)
22911da177e4SLinus Torvalds 		goto fail;
22921da177e4SLinus Torvalds 
22931da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
2294b2c0cea6SJ. Bruce Fields 	cookiep = p;
22951da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
22961da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
22971da177e4SLinus Torvalds 
22981da177e4SLinus Torvalds 	nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen);
22991da177e4SLinus Torvalds 	switch (nfserr) {
23001da177e4SLinus Torvalds 	case nfs_ok:
23011da177e4SLinus Torvalds 		p += buflen;
23021da177e4SLinus Torvalds 		break;
23031da177e4SLinus Torvalds 	case nfserr_resource:
23041da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
23051da177e4SLinus Torvalds 		goto fail;
23061da177e4SLinus Torvalds 	case nfserr_dropit:
23071da177e4SLinus Torvalds 		goto fail;
2308b2c0cea6SJ. Bruce Fields 	case nfserr_noent:
2309b2c0cea6SJ. Bruce Fields 		goto skip_entry;
23101da177e4SLinus Torvalds 	default:
23111da177e4SLinus Torvalds 		/*
23121da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
23131da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
23141da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
23151da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
23161da177e4SLinus Torvalds 		 * entire READDIR operation(!)
23171da177e4SLinus Torvalds 		 */
23181da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
23191da177e4SLinus Torvalds 			goto fail;
23201da177e4SLinus Torvalds 		p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
232134081efcSFred Isaman 		if (p == NULL) {
232234081efcSFred Isaman 			nfserr = nfserr_toosmall;
23231da177e4SLinus Torvalds 			goto fail;
23241da177e4SLinus Torvalds 		}
232534081efcSFred Isaman 	}
23261da177e4SLinus Torvalds 	cd->buflen -= (p - cd->buffer);
23271da177e4SLinus Torvalds 	cd->buffer = p;
2328b2c0cea6SJ. Bruce Fields 	cd->offset = cookiep;
2329b2c0cea6SJ. Bruce Fields skip_entry:
23301da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
23311da177e4SLinus Torvalds 	return 0;
23321da177e4SLinus Torvalds fail:
23331da177e4SLinus Torvalds 	cd->common.err = nfserr;
23341da177e4SLinus Torvalds 	return -EINVAL;
23351da177e4SLinus Torvalds }
23361da177e4SLinus Torvalds 
2337e2f282b9SBenny Halevy static void
2338e2f282b9SBenny Halevy nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
2339e2f282b9SBenny Halevy {
2340bc749ca4SJ. Bruce Fields 	__be32 *p;
2341e2f282b9SBenny Halevy 
2342e2f282b9SBenny Halevy 	RESERVE_SPACE(sizeof(stateid_t));
2343e2f282b9SBenny Halevy 	WRITE32(sid->si_generation);
2344e2f282b9SBenny Halevy 	WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
2345e2f282b9SBenny Halevy 	ADJUST_ARGS();
2346e2f282b9SBenny Halevy }
2347e2f282b9SBenny Halevy 
2348695e12f8SBenny Halevy static __be32
2349b37ad28bSAl Viro nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
23501da177e4SLinus Torvalds {
2351bc749ca4SJ. Bruce Fields 	__be32 *p;
23521da177e4SLinus Torvalds 
23531da177e4SLinus Torvalds 	if (!nfserr) {
23541da177e4SLinus Torvalds 		RESERVE_SPACE(8);
23551da177e4SLinus Torvalds 		WRITE32(access->ac_supported);
23561da177e4SLinus Torvalds 		WRITE32(access->ac_resp_access);
23571da177e4SLinus Torvalds 		ADJUST_ARGS();
23581da177e4SLinus Torvalds 	}
2359695e12f8SBenny Halevy 	return nfserr;
23601da177e4SLinus Torvalds }
23611da177e4SLinus Torvalds 
2362695e12f8SBenny Halevy static __be32
2363b37ad28bSAl Viro nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
23641da177e4SLinus Torvalds {
23651da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
23661da177e4SLinus Torvalds 
2367e2f282b9SBenny Halevy 	if (!nfserr)
2368e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &close->cl_stateid);
2369e2f282b9SBenny Halevy 
23701da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
2371695e12f8SBenny Halevy 	return nfserr;
23721da177e4SLinus Torvalds }
23731da177e4SLinus Torvalds 
23741da177e4SLinus Torvalds 
2375695e12f8SBenny Halevy static __be32
2376b37ad28bSAl Viro nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
23771da177e4SLinus Torvalds {
2378bc749ca4SJ. Bruce Fields 	__be32 *p;
23791da177e4SLinus Torvalds 
23801da177e4SLinus Torvalds 	if (!nfserr) {
23811da177e4SLinus Torvalds 		RESERVE_SPACE(8);
23821da177e4SLinus Torvalds 		WRITEMEM(commit->co_verf.data, 8);
23831da177e4SLinus Torvalds 		ADJUST_ARGS();
23841da177e4SLinus Torvalds 	}
2385695e12f8SBenny Halevy 	return nfserr;
23861da177e4SLinus Torvalds }
23871da177e4SLinus Torvalds 
2388695e12f8SBenny Halevy static __be32
2389b37ad28bSAl Viro nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
23901da177e4SLinus Torvalds {
2391bc749ca4SJ. Bruce Fields 	__be32 *p;
23921da177e4SLinus Torvalds 
23931da177e4SLinus Torvalds 	if (!nfserr) {
23941da177e4SLinus Torvalds 		RESERVE_SPACE(32);
2395c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &create->cr_cinfo);
23961da177e4SLinus Torvalds 		WRITE32(2);
23971da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[0]);
23981da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[1]);
23991da177e4SLinus Torvalds 		ADJUST_ARGS();
24001da177e4SLinus Torvalds 	}
2401695e12f8SBenny Halevy 	return nfserr;
24021da177e4SLinus Torvalds }
24031da177e4SLinus Torvalds 
2404b37ad28bSAl Viro static __be32
2405b37ad28bSAl Viro nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
24061da177e4SLinus Torvalds {
24071da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
24081da177e4SLinus Torvalds 	int buflen;
24091da177e4SLinus Torvalds 
24101da177e4SLinus Torvalds 	if (nfserr)
24111da177e4SLinus Torvalds 		return nfserr;
24121da177e4SLinus Torvalds 
24131da177e4SLinus Torvalds 	buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2);
24141da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
24151da177e4SLinus Torvalds 				    resp->p, &buflen, getattr->ga_bmval,
2416406a7ea9SFrank Filz 				    resp->rqstp, 0);
24171da177e4SLinus Torvalds 	if (!nfserr)
24181da177e4SLinus Torvalds 		resp->p += buflen;
24191da177e4SLinus Torvalds 	return nfserr;
24201da177e4SLinus Torvalds }
24211da177e4SLinus Torvalds 
2422695e12f8SBenny Halevy static __be32
2423695e12f8SBenny Halevy nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
24241da177e4SLinus Torvalds {
2425695e12f8SBenny Halevy 	struct svc_fh *fhp = *fhpp;
24261da177e4SLinus Torvalds 	unsigned int len;
2427bc749ca4SJ. Bruce Fields 	__be32 *p;
24281da177e4SLinus Torvalds 
24291da177e4SLinus Torvalds 	if (!nfserr) {
24301da177e4SLinus Torvalds 		len = fhp->fh_handle.fh_size;
24311da177e4SLinus Torvalds 		RESERVE_SPACE(len + 4);
24321da177e4SLinus Torvalds 		WRITE32(len);
24331da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, len);
24341da177e4SLinus Torvalds 		ADJUST_ARGS();
24351da177e4SLinus Torvalds 	}
2436695e12f8SBenny Halevy 	return nfserr;
24371da177e4SLinus Torvalds }
24381da177e4SLinus Torvalds 
24391da177e4SLinus Torvalds /*
24401da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
24411da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
24421da177e4SLinus Torvalds */
24431da177e4SLinus Torvalds static void
24441da177e4SLinus Torvalds nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
24451da177e4SLinus Torvalds {
2446bc749ca4SJ. Bruce Fields 	__be32 *p;
24471da177e4SLinus Torvalds 
24481da177e4SLinus Torvalds 	RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
24491da177e4SLinus Torvalds 	WRITE64(ld->ld_start);
24501da177e4SLinus Torvalds 	WRITE64(ld->ld_length);
24511da177e4SLinus Torvalds 	WRITE32(ld->ld_type);
24521da177e4SLinus Torvalds 	if (ld->ld_sop) {
24531da177e4SLinus Torvalds 		WRITEMEM(&ld->ld_clientid, 8);
24541da177e4SLinus Torvalds 		WRITE32(ld->ld_sop->so_owner.len);
24551da177e4SLinus Torvalds 		WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
24561da177e4SLinus Torvalds 		kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
24571da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
24581da177e4SLinus Torvalds 		WRITE64((u64)0); /* clientid */
24591da177e4SLinus Torvalds 		WRITE32(0); /* length of owner name */
24601da177e4SLinus Torvalds 	}
24611da177e4SLinus Torvalds 	ADJUST_ARGS();
24621da177e4SLinus Torvalds }
24631da177e4SLinus Torvalds 
2464695e12f8SBenny Halevy static __be32
2465b37ad28bSAl Viro nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
24661da177e4SLinus Torvalds {
24671da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
24681da177e4SLinus Torvalds 
2469e2f282b9SBenny Halevy 	if (!nfserr)
2470e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &lock->lk_resp_stateid);
2471e2f282b9SBenny Halevy 	else if (nfserr == nfserr_denied)
24721da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lock->lk_denied);
24731da177e4SLinus Torvalds 
24743a65588aSJ. Bruce Fields 	ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
2475695e12f8SBenny Halevy 	return nfserr;
24761da177e4SLinus Torvalds }
24771da177e4SLinus Torvalds 
2478695e12f8SBenny Halevy static __be32
2479b37ad28bSAl Viro nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
24801da177e4SLinus Torvalds {
24811da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
24821da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lockt->lt_denied);
2483695e12f8SBenny Halevy 	return nfserr;
24841da177e4SLinus Torvalds }
24851da177e4SLinus Torvalds 
2486695e12f8SBenny Halevy static __be32
2487b37ad28bSAl Viro nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
24881da177e4SLinus Torvalds {
24891da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
24901da177e4SLinus Torvalds 
2491e2f282b9SBenny Halevy 	if (!nfserr)
2492e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &locku->lu_stateid);
24931da177e4SLinus Torvalds 
24941da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
2495695e12f8SBenny Halevy 	return nfserr;
24961da177e4SLinus Torvalds }
24971da177e4SLinus Torvalds 
24981da177e4SLinus Torvalds 
2499695e12f8SBenny Halevy static __be32
2500b37ad28bSAl Viro nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
25011da177e4SLinus Torvalds {
2502bc749ca4SJ. Bruce Fields 	__be32 *p;
25031da177e4SLinus Torvalds 
25041da177e4SLinus Torvalds 	if (!nfserr) {
25051da177e4SLinus Torvalds 		RESERVE_SPACE(20);
2506c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &link->li_cinfo);
25071da177e4SLinus Torvalds 		ADJUST_ARGS();
25081da177e4SLinus Torvalds 	}
2509695e12f8SBenny Halevy 	return nfserr;
25101da177e4SLinus Torvalds }
25111da177e4SLinus Torvalds 
25121da177e4SLinus Torvalds 
2513695e12f8SBenny Halevy static __be32
2514b37ad28bSAl Viro nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
25151da177e4SLinus Torvalds {
2516bc749ca4SJ. Bruce Fields 	__be32 *p;
25171da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
25181da177e4SLinus Torvalds 
25191da177e4SLinus Torvalds 	if (nfserr)
25201da177e4SLinus Torvalds 		goto out;
25211da177e4SLinus Torvalds 
2522e2f282b9SBenny Halevy 	nfsd4_encode_stateid(resp, &open->op_stateid);
2523e2f282b9SBenny Halevy 	RESERVE_SPACE(40);
2524c654b8a9SJ. Bruce Fields 	write_cinfo(&p, &open->op_cinfo);
25251da177e4SLinus Torvalds 	WRITE32(open->op_rflags);
25261da177e4SLinus Torvalds 	WRITE32(2);
25271da177e4SLinus Torvalds 	WRITE32(open->op_bmval[0]);
25281da177e4SLinus Torvalds 	WRITE32(open->op_bmval[1]);
25291da177e4SLinus Torvalds 	WRITE32(open->op_delegate_type);
25301da177e4SLinus Torvalds 	ADJUST_ARGS();
25311da177e4SLinus Torvalds 
25321da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
25331da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
25341da177e4SLinus Torvalds 		break;
25351da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
2536e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
2537e2f282b9SBenny Halevy 		RESERVE_SPACE(20);
25387b190fecSNeilBrown 		WRITE32(open->op_recall);
25391da177e4SLinus Torvalds 
25401da177e4SLinus Torvalds 		/*
25411da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
25421da177e4SLinus Torvalds 		 */
25431da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
25441da177e4SLinus Torvalds 		WRITE32(0);
25451da177e4SLinus Torvalds 		WRITE32(0);
25461da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
25471da177e4SLinus Torvalds 		ADJUST_ARGS();
25481da177e4SLinus Torvalds 		break;
25491da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
2550e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
2551e2f282b9SBenny Halevy 		RESERVE_SPACE(32);
25521da177e4SLinus Torvalds 		WRITE32(0);
25531da177e4SLinus Torvalds 
25541da177e4SLinus Torvalds 		/*
25551da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
25561da177e4SLinus Torvalds 		 */
25571da177e4SLinus Torvalds 		WRITE32(NFS4_LIMIT_SIZE);
25581da177e4SLinus Torvalds 		WRITE32(~(u32)0);
25591da177e4SLinus Torvalds 		WRITE32(~(u32)0);
25601da177e4SLinus Torvalds 
25611da177e4SLinus Torvalds 		/*
25621da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
25631da177e4SLinus Torvalds 		 */
25641da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
25651da177e4SLinus Torvalds 		WRITE32(0);
25661da177e4SLinus Torvalds 		WRITE32(0);
25671da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
25681da177e4SLinus Torvalds 		ADJUST_ARGS();
25691da177e4SLinus Torvalds 		break;
25701da177e4SLinus Torvalds 	default:
25711da177e4SLinus Torvalds 		BUG();
25721da177e4SLinus Torvalds 	}
25731da177e4SLinus Torvalds 	/* XXX save filehandle here */
25741da177e4SLinus Torvalds out:
25751da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(open->op_stateowner);
2576695e12f8SBenny Halevy 	return nfserr;
25771da177e4SLinus Torvalds }
25781da177e4SLinus Torvalds 
2579695e12f8SBenny Halevy static __be32
2580b37ad28bSAl Viro nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
25811da177e4SLinus Torvalds {
25821da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
25831da177e4SLinus Torvalds 
2584e2f282b9SBenny Halevy 	if (!nfserr)
2585e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
25861da177e4SLinus Torvalds 
25871da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
2588695e12f8SBenny Halevy 	return nfserr;
25891da177e4SLinus Torvalds }
25901da177e4SLinus Torvalds 
2591695e12f8SBenny Halevy static __be32
2592b37ad28bSAl Viro nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
25931da177e4SLinus Torvalds {
25941da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
25951da177e4SLinus Torvalds 
2596e2f282b9SBenny Halevy 	if (!nfserr)
2597e2f282b9SBenny Halevy 		nfsd4_encode_stateid(resp, &od->od_stateid);
25981da177e4SLinus Torvalds 
25991da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(od->od_stateowner);
2600695e12f8SBenny Halevy 	return nfserr;
26011da177e4SLinus Torvalds }
26021da177e4SLinus Torvalds 
2603b37ad28bSAl Viro static __be32
2604b37ad28bSAl Viro nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
260544524359SNeilBrown 		  struct nfsd4_read *read)
26061da177e4SLinus Torvalds {
26071da177e4SLinus Torvalds 	u32 eof;
26081da177e4SLinus Torvalds 	int v, pn;
26091da177e4SLinus Torvalds 	unsigned long maxcount;
26101da177e4SLinus Torvalds 	long len;
2611bc749ca4SJ. Bruce Fields 	__be32 *p;
26121da177e4SLinus Torvalds 
26131da177e4SLinus Torvalds 	if (nfserr)
26141da177e4SLinus Torvalds 		return nfserr;
26151da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
26161da177e4SLinus Torvalds 		return nfserr_resource;
26171da177e4SLinus Torvalds 
26181da177e4SLinus Torvalds 	RESERVE_SPACE(8); /* eof flag and byte count */
26191da177e4SLinus Torvalds 
26207adae489SGreg Banks 	maxcount = svc_max_payload(resp->rqstp);
26211da177e4SLinus Torvalds 	if (maxcount > read->rd_length)
26221da177e4SLinus Torvalds 		maxcount = read->rd_length;
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds 	len = maxcount;
26251da177e4SLinus Torvalds 	v = 0;
26261da177e4SLinus Torvalds 	while (len > 0) {
262744524359SNeilBrown 		pn = resp->rqstp->rq_resused++;
26283cc03b16SNeilBrown 		resp->rqstp->rq_vec[v].iov_base =
262944524359SNeilBrown 			page_address(resp->rqstp->rq_respages[pn]);
26303cc03b16SNeilBrown 		resp->rqstp->rq_vec[v].iov_len =
263144524359SNeilBrown 			len < PAGE_SIZE ? len : PAGE_SIZE;
26321da177e4SLinus Torvalds 		v++;
26331da177e4SLinus Torvalds 		len -= PAGE_SIZE;
26341da177e4SLinus Torvalds 	}
26351da177e4SLinus Torvalds 	read->rd_vlen = v;
26361da177e4SLinus Torvalds 
26371da177e4SLinus Torvalds 	nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp,
26383cc03b16SNeilBrown 			read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
26391da177e4SLinus Torvalds 			&maxcount);
26401da177e4SLinus Torvalds 
26411da177e4SLinus Torvalds 	if (nfserr == nfserr_symlink)
26421da177e4SLinus Torvalds 		nfserr = nfserr_inval;
26431da177e4SLinus Torvalds 	if (nfserr)
26441da177e4SLinus Torvalds 		return nfserr;
264544524359SNeilBrown 	eof = (read->rd_offset + maxcount >=
264644524359SNeilBrown 	       read->rd_fhp->fh_dentry->d_inode->i_size);
26471da177e4SLinus Torvalds 
26481da177e4SLinus Torvalds 	WRITE32(eof);
26491da177e4SLinus Torvalds 	WRITE32(maxcount);
26501da177e4SLinus Torvalds 	ADJUST_ARGS();
26516ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
26526ed6deccSNeilBrown 					- (char*)resp->xbuf->head[0].iov_base;
26531da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
26541da177e4SLinus Torvalds 
26556ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
26566ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
26571da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
26581da177e4SLinus Torvalds 	if (maxcount&3) {
26596ed6deccSNeilBrown 		RESERVE_SPACE(4);
26606ed6deccSNeilBrown 		WRITE32(0);
26611da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
26621da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
26636ed6deccSNeilBrown 		ADJUST_ARGS();
26641da177e4SLinus Torvalds 	}
26651da177e4SLinus Torvalds 	return 0;
26661da177e4SLinus Torvalds }
26671da177e4SLinus Torvalds 
2668b37ad28bSAl Viro static __be32
2669b37ad28bSAl Viro nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
26701da177e4SLinus Torvalds {
26711da177e4SLinus Torvalds 	int maxcount;
26721da177e4SLinus Torvalds 	char *page;
2673bc749ca4SJ. Bruce Fields 	__be32 *p;
26741da177e4SLinus Torvalds 
26751da177e4SLinus Torvalds 	if (nfserr)
26761da177e4SLinus Torvalds 		return nfserr;
26771da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
26781da177e4SLinus Torvalds 		return nfserr_resource;
26791da177e4SLinus Torvalds 
268044524359SNeilBrown 	page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
26811da177e4SLinus Torvalds 
26821da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
26831da177e4SLinus Torvalds 	RESERVE_SPACE(4);
26841da177e4SLinus Torvalds 
26851da177e4SLinus Torvalds 	/*
26861da177e4SLinus Torvalds 	 * XXX: By default, the ->readlink() VFS op will truncate symlinks
26871da177e4SLinus Torvalds 	 * if they would overflow the buffer.  Is this kosher in NFSv4?  If
26881da177e4SLinus Torvalds 	 * not, one easy fix is: if ->readlink() precisely fills the buffer,
26891da177e4SLinus Torvalds 	 * assume that truncation occurred, and return NFS4ERR_RESOURCE.
26901da177e4SLinus Torvalds 	 */
26911da177e4SLinus Torvalds 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
26921da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
26931da177e4SLinus Torvalds 		return nfserr_inval;
26941da177e4SLinus Torvalds 	if (nfserr)
26951da177e4SLinus Torvalds 		return nfserr;
26961da177e4SLinus Torvalds 
26971da177e4SLinus Torvalds 	WRITE32(maxcount);
26981da177e4SLinus Torvalds 	ADJUST_ARGS();
26996ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
27006ed6deccSNeilBrown 				- (char*)resp->xbuf->head[0].iov_base;
27011da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
27026ed6deccSNeilBrown 
27036ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
27046ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
27056ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_len = 0;
27061da177e4SLinus Torvalds 	if (maxcount&3) {
27076ed6deccSNeilBrown 		RESERVE_SPACE(4);
27086ed6deccSNeilBrown 		WRITE32(0);
27091da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
27101da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
27116ed6deccSNeilBrown 		ADJUST_ARGS();
27121da177e4SLinus Torvalds 	}
27131da177e4SLinus Torvalds 	return 0;
27141da177e4SLinus Torvalds }
27151da177e4SLinus Torvalds 
2716b37ad28bSAl Viro static __be32
2717b37ad28bSAl Viro nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
27181da177e4SLinus Torvalds {
27191da177e4SLinus Torvalds 	int maxcount;
27201da177e4SLinus Torvalds 	loff_t offset;
27212ebbc012SAl Viro 	__be32 *page, *savep, *tailbase;
2722bc749ca4SJ. Bruce Fields 	__be32 *p;
27231da177e4SLinus Torvalds 
27241da177e4SLinus Torvalds 	if (nfserr)
27251da177e4SLinus Torvalds 		return nfserr;
27261da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
27271da177e4SLinus Torvalds 		return nfserr_resource;
27281da177e4SLinus Torvalds 
27291da177e4SLinus Torvalds 	RESERVE_SPACE(8);  /* verifier */
27301da177e4SLinus Torvalds 	savep = p;
27311da177e4SLinus Torvalds 
27321da177e4SLinus Torvalds 	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
27331da177e4SLinus Torvalds 	WRITE32(0);
27341da177e4SLinus Torvalds 	WRITE32(0);
27351da177e4SLinus Torvalds 	ADJUST_ARGS();
27361da177e4SLinus Torvalds 	resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
2737bb6e8a9fSNeilBrown 	tailbase = p;
27381da177e4SLinus Torvalds 
27391da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
27401da177e4SLinus Torvalds 	if (maxcount > readdir->rd_maxcount)
27411da177e4SLinus Torvalds 		maxcount = readdir->rd_maxcount;
27421da177e4SLinus Torvalds 
27431da177e4SLinus Torvalds 	/*
27441da177e4SLinus Torvalds 	 * Convert from bytes to words, account for the two words already
27451da177e4SLinus Torvalds 	 * written, make sure to leave two words at the end for the next
27461da177e4SLinus Torvalds 	 * pointer and eof field.
27471da177e4SLinus Torvalds 	 */
27481da177e4SLinus Torvalds 	maxcount = (maxcount >> 2) - 4;
27491da177e4SLinus Torvalds 	if (maxcount < 0) {
27501da177e4SLinus Torvalds 		nfserr =  nfserr_toosmall;
27511da177e4SLinus Torvalds 		goto err_no_verf;
27521da177e4SLinus Torvalds 	}
27531da177e4SLinus Torvalds 
275444524359SNeilBrown 	page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
27551da177e4SLinus Torvalds 	readdir->common.err = 0;
27561da177e4SLinus Torvalds 	readdir->buflen = maxcount;
27571da177e4SLinus Torvalds 	readdir->buffer = page;
27581da177e4SLinus Torvalds 	readdir->offset = NULL;
27591da177e4SLinus Torvalds 
27601da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
27611da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
27621da177e4SLinus Torvalds 			      &offset,
27631da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
27641da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
27651da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
27661da177e4SLinus Torvalds 	    readdir->buffer == page)
27671da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
27681da177e4SLinus Torvalds 	if (nfserr == nfserr_symlink)
27691da177e4SLinus Torvalds 		nfserr = nfserr_notdir;
27701da177e4SLinus Torvalds 	if (nfserr)
27711da177e4SLinus Torvalds 		goto err_no_verf;
27721da177e4SLinus Torvalds 
27731da177e4SLinus Torvalds 	if (readdir->offset)
27741da177e4SLinus Torvalds 		xdr_encode_hyper(readdir->offset, offset);
27751da177e4SLinus Torvalds 
27761da177e4SLinus Torvalds 	p = readdir->buffer;
27771da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
27781da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
277944524359SNeilBrown 	resp->xbuf->page_len = ((char*)p) - (char*)page_address(
278044524359SNeilBrown 		resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
27811da177e4SLinus Torvalds 
2782bb6e8a9fSNeilBrown 	/* Use rest of head for padding and remaining ops: */
2783bb6e8a9fSNeilBrown 	resp->xbuf->tail[0].iov_base = tailbase;
27841da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
27851da177e4SLinus Torvalds 	resp->p = resp->xbuf->tail[0].iov_base;
2786bb6e8a9fSNeilBrown 	resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
27871da177e4SLinus Torvalds 
27881da177e4SLinus Torvalds 	return 0;
27891da177e4SLinus Torvalds err_no_verf:
27901da177e4SLinus Torvalds 	p = savep;
27911da177e4SLinus Torvalds 	ADJUST_ARGS();
27921da177e4SLinus Torvalds 	return nfserr;
27931da177e4SLinus Torvalds }
27941da177e4SLinus Torvalds 
2795695e12f8SBenny Halevy static __be32
2796b37ad28bSAl Viro nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
27971da177e4SLinus Torvalds {
2798bc749ca4SJ. Bruce Fields 	__be32 *p;
27991da177e4SLinus Torvalds 
28001da177e4SLinus Torvalds 	if (!nfserr) {
28011da177e4SLinus Torvalds 		RESERVE_SPACE(20);
2802c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &remove->rm_cinfo);
28031da177e4SLinus Torvalds 		ADJUST_ARGS();
28041da177e4SLinus Torvalds 	}
2805695e12f8SBenny Halevy 	return nfserr;
28061da177e4SLinus Torvalds }
28071da177e4SLinus Torvalds 
2808695e12f8SBenny Halevy static __be32
2809b37ad28bSAl Viro nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
28101da177e4SLinus Torvalds {
2811bc749ca4SJ. Bruce Fields 	__be32 *p;
28121da177e4SLinus Torvalds 
28131da177e4SLinus Torvalds 	if (!nfserr) {
28141da177e4SLinus Torvalds 		RESERVE_SPACE(40);
2815c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &rename->rn_sinfo);
2816c654b8a9SJ. Bruce Fields 		write_cinfo(&p, &rename->rn_tinfo);
28171da177e4SLinus Torvalds 		ADJUST_ARGS();
28181da177e4SLinus Torvalds 	}
2819695e12f8SBenny Halevy 	return nfserr;
28201da177e4SLinus Torvalds }
28211da177e4SLinus Torvalds 
2822695e12f8SBenny Halevy static __be32
2823ca5c8cdeSAl Viro nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
2824dcb488a3SAndy Adamson 		     struct nfsd4_secinfo *secinfo)
2825dcb488a3SAndy Adamson {
2826dcb488a3SAndy Adamson 	int i = 0;
2827dcb488a3SAndy Adamson 	struct svc_export *exp = secinfo->si_exp;
28284796f457SJ. Bruce Fields 	u32 nflavs;
28294796f457SJ. Bruce Fields 	struct exp_flavor_info *flavs;
28304796f457SJ. Bruce Fields 	struct exp_flavor_info def_flavs[2];
2831bc749ca4SJ. Bruce Fields 	__be32 *p;
2832dcb488a3SAndy Adamson 
2833dcb488a3SAndy Adamson 	if (nfserr)
2834dcb488a3SAndy Adamson 		goto out;
28354796f457SJ. Bruce Fields 	if (exp->ex_nflavors) {
28364796f457SJ. Bruce Fields 		flavs = exp->ex_flavors;
28374796f457SJ. Bruce Fields 		nflavs = exp->ex_nflavors;
28384796f457SJ. Bruce Fields 	} else { /* Handling of some defaults in absence of real secinfo: */
28394796f457SJ. Bruce Fields 		flavs = def_flavs;
28404796f457SJ. Bruce Fields 		if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
28414796f457SJ. Bruce Fields 			nflavs = 2;
28424796f457SJ. Bruce Fields 			flavs[0].pseudoflavor = RPC_AUTH_UNIX;
28434796f457SJ. Bruce Fields 			flavs[1].pseudoflavor = RPC_AUTH_NULL;
28444796f457SJ. Bruce Fields 		} else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
28454796f457SJ. Bruce Fields 			nflavs = 1;
28464796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
28474796f457SJ. Bruce Fields 					= svcauth_gss_flavor(exp->ex_client);
28484796f457SJ. Bruce Fields 		} else {
28494796f457SJ. Bruce Fields 			nflavs = 1;
28504796f457SJ. Bruce Fields 			flavs[0].pseudoflavor
28514796f457SJ. Bruce Fields 					= exp->ex_client->flavour->flavour;
28524796f457SJ. Bruce Fields 		}
28534796f457SJ. Bruce Fields 	}
28544796f457SJ. Bruce Fields 
2855dcb488a3SAndy Adamson 	RESERVE_SPACE(4);
28564796f457SJ. Bruce Fields 	WRITE32(nflavs);
2857dcb488a3SAndy Adamson 	ADJUST_ARGS();
28584796f457SJ. Bruce Fields 	for (i = 0; i < nflavs; i++) {
28594796f457SJ. Bruce Fields 		u32 flav = flavs[i].pseudoflavor;
2860dcb488a3SAndy Adamson 		struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
2861dcb488a3SAndy Adamson 
2862dcb488a3SAndy Adamson 		if (gm) {
2863dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2864dcb488a3SAndy Adamson 			WRITE32(RPC_AUTH_GSS);
2865dcb488a3SAndy Adamson 			ADJUST_ARGS();
2866dcb488a3SAndy Adamson 			RESERVE_SPACE(4 + gm->gm_oid.len);
2867dcb488a3SAndy Adamson 			WRITE32(gm->gm_oid.len);
2868dcb488a3SAndy Adamson 			WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
2869dcb488a3SAndy Adamson 			ADJUST_ARGS();
2870dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2871dcb488a3SAndy Adamson 			WRITE32(0); /* qop */
2872dcb488a3SAndy Adamson 			ADJUST_ARGS();
2873dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2874dcb488a3SAndy Adamson 			WRITE32(gss_pseudoflavor_to_service(gm, flav));
2875dcb488a3SAndy Adamson 			ADJUST_ARGS();
2876dcb488a3SAndy Adamson 			gss_mech_put(gm);
2877dcb488a3SAndy Adamson 		} else {
2878dcb488a3SAndy Adamson 			RESERVE_SPACE(4);
2879dcb488a3SAndy Adamson 			WRITE32(flav);
2880dcb488a3SAndy Adamson 			ADJUST_ARGS();
2881dcb488a3SAndy Adamson 		}
2882dcb488a3SAndy Adamson 	}
2883dcb488a3SAndy Adamson out:
2884dcb488a3SAndy Adamson 	if (exp)
2885dcb488a3SAndy Adamson 		exp_put(exp);
2886695e12f8SBenny Halevy 	return nfserr;
2887dcb488a3SAndy Adamson }
2888dcb488a3SAndy Adamson 
28891da177e4SLinus Torvalds /*
28901da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
28911da177e4SLinus Torvalds  * regardless of the error status.
28921da177e4SLinus Torvalds  */
2893695e12f8SBenny Halevy static __be32
2894b37ad28bSAl Viro nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
28951da177e4SLinus Torvalds {
2896bc749ca4SJ. Bruce Fields 	__be32 *p;
28971da177e4SLinus Torvalds 
28981da177e4SLinus Torvalds 	RESERVE_SPACE(12);
28991da177e4SLinus Torvalds 	if (nfserr) {
29001da177e4SLinus Torvalds 		WRITE32(2);
29011da177e4SLinus Torvalds 		WRITE32(0);
29021da177e4SLinus Torvalds 		WRITE32(0);
29031da177e4SLinus Torvalds 	}
29041da177e4SLinus Torvalds 	else {
29051da177e4SLinus Torvalds 		WRITE32(2);
29061da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[0]);
29071da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[1]);
29081da177e4SLinus Torvalds 	}
29091da177e4SLinus Torvalds 	ADJUST_ARGS();
2910695e12f8SBenny Halevy 	return nfserr;
29111da177e4SLinus Torvalds }
29121da177e4SLinus Torvalds 
2913695e12f8SBenny Halevy static __be32
2914b37ad28bSAl Viro nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
29151da177e4SLinus Torvalds {
2916bc749ca4SJ. Bruce Fields 	__be32 *p;
29171da177e4SLinus Torvalds 
29181da177e4SLinus Torvalds 	if (!nfserr) {
29191da177e4SLinus Torvalds 		RESERVE_SPACE(8 + sizeof(nfs4_verifier));
29201da177e4SLinus Torvalds 		WRITEMEM(&scd->se_clientid, 8);
29211da177e4SLinus Torvalds 		WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier));
29221da177e4SLinus Torvalds 		ADJUST_ARGS();
29231da177e4SLinus Torvalds 	}
29241da177e4SLinus Torvalds 	else if (nfserr == nfserr_clid_inuse) {
29251da177e4SLinus Torvalds 		RESERVE_SPACE(8);
29261da177e4SLinus Torvalds 		WRITE32(0);
29271da177e4SLinus Torvalds 		WRITE32(0);
29281da177e4SLinus Torvalds 		ADJUST_ARGS();
29291da177e4SLinus Torvalds 	}
2930695e12f8SBenny Halevy 	return nfserr;
29311da177e4SLinus Torvalds }
29321da177e4SLinus Torvalds 
2933695e12f8SBenny Halevy static __be32
2934b37ad28bSAl Viro nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
29351da177e4SLinus Torvalds {
2936bc749ca4SJ. Bruce Fields 	__be32 *p;
29371da177e4SLinus Torvalds 
29381da177e4SLinus Torvalds 	if (!nfserr) {
29391da177e4SLinus Torvalds 		RESERVE_SPACE(16);
29401da177e4SLinus Torvalds 		WRITE32(write->wr_bytes_written);
29411da177e4SLinus Torvalds 		WRITE32(write->wr_how_written);
29421da177e4SLinus Torvalds 		WRITEMEM(write->wr_verifier.data, 8);
29431da177e4SLinus Torvalds 		ADJUST_ARGS();
29441da177e4SLinus Torvalds 	}
2945695e12f8SBenny Halevy 	return nfserr;
29461da177e4SLinus Torvalds }
29471da177e4SLinus Torvalds 
2948695e12f8SBenny Halevy static __be32
29492db134ebSAndy Adamson nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
29502db134ebSAndy Adamson 			 struct nfsd4_exchange_id *exid)
29512db134ebSAndy Adamson {
2952bc749ca4SJ. Bruce Fields 	__be32 *p;
29530733d213SAndy Adamson 	char *major_id;
29540733d213SAndy Adamson 	char *server_scope;
29550733d213SAndy Adamson 	int major_id_sz;
29560733d213SAndy Adamson 	int server_scope_sz;
29570733d213SAndy Adamson 	uint64_t minor_id = 0;
29580733d213SAndy Adamson 
29590733d213SAndy Adamson 	if (nfserr)
29602db134ebSAndy Adamson 		return nfserr;
29610733d213SAndy Adamson 
29620733d213SAndy Adamson 	major_id = utsname()->nodename;
29630733d213SAndy Adamson 	major_id_sz = strlen(major_id);
29640733d213SAndy Adamson 	server_scope = utsname()->nodename;
29650733d213SAndy Adamson 	server_scope_sz = strlen(server_scope);
29660733d213SAndy Adamson 
29670733d213SAndy Adamson 	RESERVE_SPACE(
29680733d213SAndy Adamson 		8 /* eir_clientid */ +
29690733d213SAndy Adamson 		4 /* eir_sequenceid */ +
29700733d213SAndy Adamson 		4 /* eir_flags */ +
29710733d213SAndy Adamson 		4 /* spr_how (SP4_NONE) */ +
29720733d213SAndy Adamson 		8 /* so_minor_id */ +
29730733d213SAndy Adamson 		4 /* so_major_id.len */ +
29740733d213SAndy Adamson 		(XDR_QUADLEN(major_id_sz) * 4) +
29750733d213SAndy Adamson 		4 /* eir_server_scope.len */ +
29760733d213SAndy Adamson 		(XDR_QUADLEN(server_scope_sz) * 4) +
29770733d213SAndy Adamson 		4 /* eir_server_impl_id.count (0) */);
29780733d213SAndy Adamson 
29790733d213SAndy Adamson 	WRITEMEM(&exid->clientid, 8);
29800733d213SAndy Adamson 	WRITE32(exid->seqid);
29810733d213SAndy Adamson 	WRITE32(exid->flags);
29820733d213SAndy Adamson 
29830733d213SAndy Adamson 	/* state_protect4_r. Currently only support SP4_NONE */
29840733d213SAndy Adamson 	BUG_ON(exid->spa_how != SP4_NONE);
29850733d213SAndy Adamson 	WRITE32(exid->spa_how);
29860733d213SAndy Adamson 
29870733d213SAndy Adamson 	/* The server_owner struct */
29880733d213SAndy Adamson 	WRITE64(minor_id);      /* Minor id */
29890733d213SAndy Adamson 	/* major id */
29900733d213SAndy Adamson 	WRITE32(major_id_sz);
29910733d213SAndy Adamson 	WRITEMEM(major_id, major_id_sz);
29920733d213SAndy Adamson 
29930733d213SAndy Adamson 	/* Server scope */
29940733d213SAndy Adamson 	WRITE32(server_scope_sz);
29950733d213SAndy Adamson 	WRITEMEM(server_scope, server_scope_sz);
29960733d213SAndy Adamson 
29970733d213SAndy Adamson 	/* Implementation id */
29980733d213SAndy Adamson 	WRITE32(0);	/* zero length nfs_impl_id4 array */
29990733d213SAndy Adamson 	ADJUST_ARGS();
30000733d213SAndy Adamson 	return 0;
30012db134ebSAndy Adamson }
30022db134ebSAndy Adamson 
30032db134ebSAndy Adamson static __be32
30042db134ebSAndy Adamson nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
30052db134ebSAndy Adamson 			    struct nfsd4_create_session *sess)
30062db134ebSAndy Adamson {
3007bc749ca4SJ. Bruce Fields 	__be32 *p;
3008ec6b5d7bSAndy Adamson 
3009ec6b5d7bSAndy Adamson 	if (nfserr)
30102db134ebSAndy Adamson 		return nfserr;
3011ec6b5d7bSAndy Adamson 
3012ec6b5d7bSAndy Adamson 	RESERVE_SPACE(24);
3013ec6b5d7bSAndy Adamson 	WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN);
3014ec6b5d7bSAndy Adamson 	WRITE32(sess->seqid);
3015ec6b5d7bSAndy Adamson 	WRITE32(sess->flags);
3016ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
3017ec6b5d7bSAndy Adamson 
3018ec6b5d7bSAndy Adamson 	RESERVE_SPACE(28);
3019ec6b5d7bSAndy Adamson 	WRITE32(0); /* headerpadsz */
3020ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxreq_sz);
3021ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxresp_sz);
3022ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxresp_cached);
3023ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxops);
3024ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.maxreqs);
3025ec6b5d7bSAndy Adamson 	WRITE32(sess->fore_channel.nr_rdma_attrs);
3026ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
3027ec6b5d7bSAndy Adamson 
3028ec6b5d7bSAndy Adamson 	if (sess->fore_channel.nr_rdma_attrs) {
3029ec6b5d7bSAndy Adamson 		RESERVE_SPACE(4);
3030ec6b5d7bSAndy Adamson 		WRITE32(sess->fore_channel.rdma_attrs);
3031ec6b5d7bSAndy Adamson 		ADJUST_ARGS();
3032ec6b5d7bSAndy Adamson 	}
3033ec6b5d7bSAndy Adamson 
3034ec6b5d7bSAndy Adamson 	RESERVE_SPACE(28);
3035ec6b5d7bSAndy Adamson 	WRITE32(0); /* headerpadsz */
3036ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxreq_sz);
3037ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxresp_sz);
3038ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxresp_cached);
3039ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxops);
3040ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.maxreqs);
3041ec6b5d7bSAndy Adamson 	WRITE32(sess->back_channel.nr_rdma_attrs);
3042ec6b5d7bSAndy Adamson 	ADJUST_ARGS();
3043ec6b5d7bSAndy Adamson 
3044ec6b5d7bSAndy Adamson 	if (sess->back_channel.nr_rdma_attrs) {
3045ec6b5d7bSAndy Adamson 		RESERVE_SPACE(4);
3046ec6b5d7bSAndy Adamson 		WRITE32(sess->back_channel.rdma_attrs);
3047ec6b5d7bSAndy Adamson 		ADJUST_ARGS();
3048ec6b5d7bSAndy Adamson 	}
3049ec6b5d7bSAndy Adamson 	return 0;
30502db134ebSAndy Adamson }
30512db134ebSAndy Adamson 
30522db134ebSAndy Adamson static __be32
30532db134ebSAndy Adamson nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
30542db134ebSAndy Adamson 			     struct nfsd4_destroy_session *destroy_session)
30552db134ebSAndy Adamson {
30562db134ebSAndy Adamson 	return nfserr;
30572db134ebSAndy Adamson }
30582db134ebSAndy Adamson 
3059bf864a31SAndy Adamson __be32
30602db134ebSAndy Adamson nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
30612db134ebSAndy Adamson 		      struct nfsd4_sequence *seq)
30622db134ebSAndy Adamson {
3063bc749ca4SJ. Bruce Fields 	__be32 *p;
3064b85d4c01SBenny Halevy 
3065b85d4c01SBenny Halevy 	if (nfserr)
30662db134ebSAndy Adamson 		return nfserr;
3067b85d4c01SBenny Halevy 
3068b85d4c01SBenny Halevy 	RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 20);
3069b85d4c01SBenny Halevy 	WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
3070b85d4c01SBenny Halevy 	WRITE32(seq->seqid);
3071b85d4c01SBenny Halevy 	WRITE32(seq->slotid);
3072b85d4c01SBenny Halevy 	WRITE32(seq->maxslots);
3073b85d4c01SBenny Halevy 	/*
3074b85d4c01SBenny Halevy 	 * FIXME: for now:
3075b85d4c01SBenny Halevy 	 *   target_maxslots = maxslots
3076b85d4c01SBenny Halevy 	 *   status_flags = 0
3077b85d4c01SBenny Halevy 	 */
3078b85d4c01SBenny Halevy 	WRITE32(seq->maxslots);
3079b85d4c01SBenny Halevy 	WRITE32(0);
3080b85d4c01SBenny Halevy 
3081b85d4c01SBenny Halevy 	ADJUST_ARGS();
3082557ce264SAndy Adamson 	resp->cstate.datap = p; /* DRC cache data pointer */
3083b85d4c01SBenny Halevy 	return 0;
30842db134ebSAndy Adamson }
30852db134ebSAndy Adamson 
30862db134ebSAndy Adamson static __be32
3087695e12f8SBenny Halevy nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
3088695e12f8SBenny Halevy {
3089695e12f8SBenny Halevy 	return nfserr;
3090695e12f8SBenny Halevy }
3091695e12f8SBenny Halevy 
3092695e12f8SBenny Halevy typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
3093695e12f8SBenny Halevy 
30942db134ebSAndy Adamson /*
30952db134ebSAndy Adamson  * Note: nfsd4_enc_ops vector is shared for v4.0 and v4.1
30962db134ebSAndy Adamson  * since we don't need to filter out obsolete ops as this is
30972db134ebSAndy Adamson  * done in the decoding phase.
30982db134ebSAndy Adamson  */
3099695e12f8SBenny Halevy static nfsd4_enc nfsd4_enc_ops[] = {
3100ad1060c8SJ. Bruce Fields 	[OP_ACCESS]		= (nfsd4_enc)nfsd4_encode_access,
3101ad1060c8SJ. Bruce Fields 	[OP_CLOSE]		= (nfsd4_enc)nfsd4_encode_close,
3102ad1060c8SJ. Bruce Fields 	[OP_COMMIT]		= (nfsd4_enc)nfsd4_encode_commit,
3103ad1060c8SJ. Bruce Fields 	[OP_CREATE]		= (nfsd4_enc)nfsd4_encode_create,
3104ad1060c8SJ. Bruce Fields 	[OP_DELEGPURGE]		= (nfsd4_enc)nfsd4_encode_noop,
3105ad1060c8SJ. Bruce Fields 	[OP_DELEGRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
3106ad1060c8SJ. Bruce Fields 	[OP_GETATTR]		= (nfsd4_enc)nfsd4_encode_getattr,
3107ad1060c8SJ. Bruce Fields 	[OP_GETFH]		= (nfsd4_enc)nfsd4_encode_getfh,
3108ad1060c8SJ. Bruce Fields 	[OP_LINK]		= (nfsd4_enc)nfsd4_encode_link,
3109ad1060c8SJ. Bruce Fields 	[OP_LOCK]		= (nfsd4_enc)nfsd4_encode_lock,
3110ad1060c8SJ. Bruce Fields 	[OP_LOCKT]		= (nfsd4_enc)nfsd4_encode_lockt,
3111ad1060c8SJ. Bruce Fields 	[OP_LOCKU]		= (nfsd4_enc)nfsd4_encode_locku,
3112ad1060c8SJ. Bruce Fields 	[OP_LOOKUP]		= (nfsd4_enc)nfsd4_encode_noop,
3113ad1060c8SJ. Bruce Fields 	[OP_LOOKUPP]		= (nfsd4_enc)nfsd4_encode_noop,
3114ad1060c8SJ. Bruce Fields 	[OP_NVERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
3115ad1060c8SJ. Bruce Fields 	[OP_OPEN]		= (nfsd4_enc)nfsd4_encode_open,
311684f09f46SBenny Halevy 	[OP_OPENATTR]		= (nfsd4_enc)nfsd4_encode_noop,
3117ad1060c8SJ. Bruce Fields 	[OP_OPEN_CONFIRM]	= (nfsd4_enc)nfsd4_encode_open_confirm,
3118ad1060c8SJ. Bruce Fields 	[OP_OPEN_DOWNGRADE]	= (nfsd4_enc)nfsd4_encode_open_downgrade,
3119ad1060c8SJ. Bruce Fields 	[OP_PUTFH]		= (nfsd4_enc)nfsd4_encode_noop,
3120ad1060c8SJ. Bruce Fields 	[OP_PUTPUBFH]		= (nfsd4_enc)nfsd4_encode_noop,
3121ad1060c8SJ. Bruce Fields 	[OP_PUTROOTFH]		= (nfsd4_enc)nfsd4_encode_noop,
3122ad1060c8SJ. Bruce Fields 	[OP_READ]		= (nfsd4_enc)nfsd4_encode_read,
3123ad1060c8SJ. Bruce Fields 	[OP_READDIR]		= (nfsd4_enc)nfsd4_encode_readdir,
3124ad1060c8SJ. Bruce Fields 	[OP_READLINK]		= (nfsd4_enc)nfsd4_encode_readlink,
3125ad1060c8SJ. Bruce Fields 	[OP_REMOVE]		= (nfsd4_enc)nfsd4_encode_remove,
3126ad1060c8SJ. Bruce Fields 	[OP_RENAME]		= (nfsd4_enc)nfsd4_encode_rename,
3127ad1060c8SJ. Bruce Fields 	[OP_RENEW]		= (nfsd4_enc)nfsd4_encode_noop,
3128ad1060c8SJ. Bruce Fields 	[OP_RESTOREFH]		= (nfsd4_enc)nfsd4_encode_noop,
3129ad1060c8SJ. Bruce Fields 	[OP_SAVEFH]		= (nfsd4_enc)nfsd4_encode_noop,
3130ad1060c8SJ. Bruce Fields 	[OP_SECINFO]		= (nfsd4_enc)nfsd4_encode_secinfo,
3131ad1060c8SJ. Bruce Fields 	[OP_SETATTR]		= (nfsd4_enc)nfsd4_encode_setattr,
3132ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID]	= (nfsd4_enc)nfsd4_encode_setclientid,
3133ad1060c8SJ. Bruce Fields 	[OP_SETCLIENTID_CONFIRM] = (nfsd4_enc)nfsd4_encode_noop,
3134ad1060c8SJ. Bruce Fields 	[OP_VERIFY]		= (nfsd4_enc)nfsd4_encode_noop,
3135ad1060c8SJ. Bruce Fields 	[OP_WRITE]		= (nfsd4_enc)nfsd4_encode_write,
3136ad1060c8SJ. Bruce Fields 	[OP_RELEASE_LOCKOWNER]	= (nfsd4_enc)nfsd4_encode_noop,
31372db134ebSAndy Adamson 
31382db134ebSAndy Adamson 	/* NFSv4.1 operations */
31392db134ebSAndy Adamson 	[OP_BACKCHANNEL_CTL]	= (nfsd4_enc)nfsd4_encode_noop,
31402db134ebSAndy Adamson 	[OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_noop,
31412db134ebSAndy Adamson 	[OP_EXCHANGE_ID]	= (nfsd4_enc)nfsd4_encode_exchange_id,
31422db134ebSAndy Adamson 	[OP_CREATE_SESSION]	= (nfsd4_enc)nfsd4_encode_create_session,
31432db134ebSAndy Adamson 	[OP_DESTROY_SESSION]	= (nfsd4_enc)nfsd4_encode_destroy_session,
31442db134ebSAndy Adamson 	[OP_FREE_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
31452db134ebSAndy Adamson 	[OP_GET_DIR_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
31462db134ebSAndy Adamson 	[OP_GETDEVICEINFO]	= (nfsd4_enc)nfsd4_encode_noop,
31472db134ebSAndy Adamson 	[OP_GETDEVICELIST]	= (nfsd4_enc)nfsd4_encode_noop,
31482db134ebSAndy Adamson 	[OP_LAYOUTCOMMIT]	= (nfsd4_enc)nfsd4_encode_noop,
31492db134ebSAndy Adamson 	[OP_LAYOUTGET]		= (nfsd4_enc)nfsd4_encode_noop,
31502db134ebSAndy Adamson 	[OP_LAYOUTRETURN]	= (nfsd4_enc)nfsd4_encode_noop,
31512db134ebSAndy Adamson 	[OP_SECINFO_NO_NAME]	= (nfsd4_enc)nfsd4_encode_noop,
31522db134ebSAndy Adamson 	[OP_SEQUENCE]		= (nfsd4_enc)nfsd4_encode_sequence,
31532db134ebSAndy Adamson 	[OP_SET_SSV]		= (nfsd4_enc)nfsd4_encode_noop,
31542db134ebSAndy Adamson 	[OP_TEST_STATEID]	= (nfsd4_enc)nfsd4_encode_noop,
31552db134ebSAndy Adamson 	[OP_WANT_DELEGATION]	= (nfsd4_enc)nfsd4_encode_noop,
31562db134ebSAndy Adamson 	[OP_DESTROY_CLIENTID]	= (nfsd4_enc)nfsd4_encode_noop,
31572db134ebSAndy Adamson 	[OP_RECLAIM_COMPLETE]	= (nfsd4_enc)nfsd4_encode_noop,
3158695e12f8SBenny Halevy };
3159695e12f8SBenny Halevy 
3160496c262cSAndy Adamson /*
3161496c262cSAndy Adamson  * Calculate the total amount of memory that the compound response has taken
3162496c262cSAndy Adamson  * after encoding the current operation.
3163496c262cSAndy Adamson  *
3164496c262cSAndy Adamson  * pad: add on 8 bytes for the next operation's op_code and status so that
3165496c262cSAndy Adamson  * there is room to cache a failure on the next operation.
3166496c262cSAndy Adamson  *
3167496c262cSAndy Adamson  * Compare this length to the session se_fmaxresp_cached.
3168496c262cSAndy Adamson  *
3169496c262cSAndy Adamson  * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
3170496c262cSAndy Adamson  * will be at least a page and will therefore hold the xdr_buf head.
3171496c262cSAndy Adamson  */
3172496c262cSAndy Adamson static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
3173496c262cSAndy Adamson {
3174496c262cSAndy Adamson 	int status = 0;
3175496c262cSAndy Adamson 	struct xdr_buf *xb = &resp->rqstp->rq_res;
3176496c262cSAndy Adamson 	struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
3177496c262cSAndy Adamson 	struct nfsd4_session *session = NULL;
3178496c262cSAndy Adamson 	struct nfsd4_slot *slot = resp->cstate.slot;
3179496c262cSAndy Adamson 	u32 length, tlen = 0, pad = 8;
3180496c262cSAndy Adamson 
3181496c262cSAndy Adamson 	if (!nfsd4_has_session(&resp->cstate))
3182496c262cSAndy Adamson 		return status;
3183496c262cSAndy Adamson 
3184496c262cSAndy Adamson 	session = resp->cstate.session;
3185557ce264SAndy Adamson 	if (session == NULL || slot->sl_cachethis == 0)
3186496c262cSAndy Adamson 		return status;
3187496c262cSAndy Adamson 
3188496c262cSAndy Adamson 	if (resp->opcnt >= args->opcnt)
3189496c262cSAndy Adamson 		pad = 0; /* this is the last operation */
3190496c262cSAndy Adamson 
3191496c262cSAndy Adamson 	if (xb->page_len == 0) {
3192496c262cSAndy Adamson 		length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
3193496c262cSAndy Adamson 	} else {
3194496c262cSAndy Adamson 		if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0)
3195496c262cSAndy Adamson 			tlen = (char *)resp->p - (char *)xb->tail[0].iov_base;
3196496c262cSAndy Adamson 
3197496c262cSAndy Adamson 		length = xb->head[0].iov_len + xb->page_len + tlen + pad;
3198496c262cSAndy Adamson 	}
3199496c262cSAndy Adamson 	dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
3200496c262cSAndy Adamson 		length, xb->page_len, tlen, pad);
3201496c262cSAndy Adamson 
32026c18ba9fSAlexandros Batsakis 	if (length <= session->se_fchannel.maxresp_cached)
3203496c262cSAndy Adamson 		return status;
3204496c262cSAndy Adamson 	else
3205496c262cSAndy Adamson 		return nfserr_rep_too_big_to_cache;
3206496c262cSAndy Adamson }
3207496c262cSAndy Adamson 
32081da177e4SLinus Torvalds void
32091da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
32101da177e4SLinus Torvalds {
32112ebbc012SAl Viro 	__be32 *statp;
3212bc749ca4SJ. Bruce Fields 	__be32 *p;
32131da177e4SLinus Torvalds 
32141da177e4SLinus Torvalds 	RESERVE_SPACE(8);
32151da177e4SLinus Torvalds 	WRITE32(op->opnum);
32161da177e4SLinus Torvalds 	statp = p++;	/* to be backfilled at the end */
32171da177e4SLinus Torvalds 	ADJUST_ARGS();
32181da177e4SLinus Torvalds 
3219695e12f8SBenny Halevy 	if (op->opnum == OP_ILLEGAL)
3220695e12f8SBenny Halevy 		goto status;
3221695e12f8SBenny Halevy 	BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
3222695e12f8SBenny Halevy 	       !nfsd4_enc_ops[op->opnum]);
3223695e12f8SBenny Halevy 	op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
3224496c262cSAndy Adamson 	/* nfsd4_check_drc_limit guarantees enough room for error status */
3225496c262cSAndy Adamson 	if (!op->status && nfsd4_check_drc_limit(resp))
3226496c262cSAndy Adamson 		op->status = nfserr_rep_too_big_to_cache;
3227695e12f8SBenny Halevy status:
32281da177e4SLinus Torvalds 	/*
32291da177e4SLinus Torvalds 	 * Note: We write the status directly, instead of using WRITE32(),
32301da177e4SLinus Torvalds 	 * since it is already in network byte order.
32311da177e4SLinus Torvalds 	 */
32321da177e4SLinus Torvalds 	*statp = op->status;
32331da177e4SLinus Torvalds }
32341da177e4SLinus Torvalds 
32351da177e4SLinus Torvalds /*
32361da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
32371da177e4SLinus Torvalds  *
32381da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
32391da177e4SLinus Torvalds  * previously sent already encoded operation.
32401da177e4SLinus Torvalds  *
32411da177e4SLinus Torvalds  * called with nfs4_lock_state() held
32421da177e4SLinus Torvalds  */
32431da177e4SLinus Torvalds void
32441da177e4SLinus Torvalds nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
32451da177e4SLinus Torvalds {
3246bc749ca4SJ. Bruce Fields 	__be32 *p;
32471da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
32481da177e4SLinus Torvalds 
32491da177e4SLinus Torvalds 	BUG_ON(!rp);
32501da177e4SLinus Torvalds 
32511da177e4SLinus Torvalds 	RESERVE_SPACE(8);
32521da177e4SLinus Torvalds 	WRITE32(op->opnum);
32531da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
32541da177e4SLinus Torvalds 	ADJUST_ARGS();
32551da177e4SLinus Torvalds 
32561da177e4SLinus Torvalds 	RESERVE_SPACE(rp->rp_buflen);
32571da177e4SLinus Torvalds 	WRITEMEM(rp->rp_buf, rp->rp_buflen);
32581da177e4SLinus Torvalds 	ADJUST_ARGS();
32591da177e4SLinus Torvalds }
32601da177e4SLinus Torvalds 
32611da177e4SLinus Torvalds int
32622ebbc012SAl Viro nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
32631da177e4SLinus Torvalds {
32641da177e4SLinus Torvalds         return xdr_ressize_check(rqstp, p);
32651da177e4SLinus Torvalds }
32661da177e4SLinus Torvalds 
32671da177e4SLinus Torvalds void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
32681da177e4SLinus Torvalds {
32691da177e4SLinus Torvalds 	if (args->ops != args->iops) {
32701da177e4SLinus Torvalds 		kfree(args->ops);
32711da177e4SLinus Torvalds 		args->ops = args->iops;
32721da177e4SLinus Torvalds 	}
32731da177e4SLinus Torvalds 	kfree(args->tmpp);
32741da177e4SLinus Torvalds 	args->tmpp = NULL;
32751da177e4SLinus Torvalds 	while (args->to_free) {
32761da177e4SLinus Torvalds 		struct tmpbuf *tb = args->to_free;
32771da177e4SLinus Torvalds 		args->to_free = tb->next;
32781da177e4SLinus Torvalds 		tb->release(tb->buf);
32791da177e4SLinus Torvalds 		kfree(tb);
32801da177e4SLinus Torvalds 	}
32811da177e4SLinus Torvalds }
32821da177e4SLinus Torvalds 
32831da177e4SLinus Torvalds int
32842ebbc012SAl Viro nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
32851da177e4SLinus Torvalds {
3286b37ad28bSAl Viro 	__be32 status;
32871da177e4SLinus Torvalds 
32881da177e4SLinus Torvalds 	args->p = p;
32891da177e4SLinus Torvalds 	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
32901da177e4SLinus Torvalds 	args->pagelist = rqstp->rq_arg.pages;
32911da177e4SLinus Torvalds 	args->pagelen = rqstp->rq_arg.page_len;
32921da177e4SLinus Torvalds 	args->tmpp = NULL;
32931da177e4SLinus Torvalds 	args->to_free = NULL;
32941da177e4SLinus Torvalds 	args->ops = args->iops;
32951da177e4SLinus Torvalds 	args->rqstp = rqstp;
32961da177e4SLinus Torvalds 
32971da177e4SLinus Torvalds 	status = nfsd4_decode_compound(args);
32981da177e4SLinus Torvalds 	if (status) {
32991da177e4SLinus Torvalds 		nfsd4_release_compoundargs(args);
33001da177e4SLinus Torvalds 	}
33011da177e4SLinus Torvalds 	return !status;
33021da177e4SLinus Torvalds }
33031da177e4SLinus Torvalds 
33041da177e4SLinus Torvalds int
33052ebbc012SAl Viro nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp)
33061da177e4SLinus Torvalds {
33071da177e4SLinus Torvalds 	/*
33081da177e4SLinus Torvalds 	 * All that remains is to write the tag and operation count...
33091da177e4SLinus Torvalds 	 */
3310557ce264SAndy Adamson 	struct nfsd4_compound_state *cs = &resp->cstate;
33111da177e4SLinus Torvalds 	struct kvec *iov;
33121da177e4SLinus Torvalds 	p = resp->tagp;
33131da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
33141da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
33151da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
33161da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
33171da177e4SLinus Torvalds 
33181da177e4SLinus Torvalds 	if (rqstp->rq_res.page_len)
33191da177e4SLinus Torvalds 		iov = &rqstp->rq_res.tail[0];
33201da177e4SLinus Torvalds 	else
33211da177e4SLinus Torvalds 		iov = &rqstp->rq_res.head[0];
33221da177e4SLinus Torvalds 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
33231da177e4SLinus Torvalds 	BUG_ON(iov->iov_len > PAGE_SIZE);
332426c0c75eSJ. Bruce Fields 	if (nfsd4_has_session(cs)) {
332526c0c75eSJ. Bruce Fields 		if (cs->status != nfserr_replay_cache) {
3326da3846a2SAndy Adamson 			nfsd4_store_cache_entry(resp);
3327da3846a2SAndy Adamson 			dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
3328dbd65a7eSBenny Halevy 			cs->slot->sl_inuse = false;
332926c0c75eSJ. Bruce Fields 		}
3330d7682988SBenny Halevy 		/* Renew the clientid on success and on replay */
3331d7682988SBenny Halevy 		release_session_client(cs->session);
3332da3846a2SAndy Adamson 	}
33331da177e4SLinus Torvalds 	return 1;
33341da177e4SLinus Torvalds }
33351da177e4SLinus Torvalds 
33361da177e4SLinus Torvalds /*
33371da177e4SLinus Torvalds  * Local variables:
33381da177e4SLinus Torvalds  *  c-basic-offset: 8
33391da177e4SLinus Torvalds  * End:
33401da177e4SLinus Torvalds  */
3341