xref: /linux/fs/nfsd/nfs4xdr.c (revision 6ed6decccf544970664757464cfb67e081775e6a)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  fs/nfs/nfs4xdr.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Server-side XDR for NFSv4
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  Copyright (c) 2002 The Regents of the University of Michigan.
71da177e4SLinus Torvalds  *  All rights reserved.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  Kendrick Smith <kmsmith@umich.edu>
101da177e4SLinus Torvalds  *  Andy Adamson   <andros@umich.edu>
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *  Redistribution and use in source and binary forms, with or without
131da177e4SLinus Torvalds  *  modification, are permitted provided that the following conditions
141da177e4SLinus Torvalds  *  are met:
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *  1. Redistributions of source code must retain the above copyright
171da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer.
181da177e4SLinus Torvalds  *  2. Redistributions in binary form must reproduce the above copyright
191da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer in the
201da177e4SLinus Torvalds  *     documentation and/or other materials provided with the distribution.
211da177e4SLinus Torvalds  *  3. Neither the name of the University nor the names of its
221da177e4SLinus Torvalds  *     contributors may be used to endorse or promote products derived
231da177e4SLinus Torvalds  *     from this software without specific prior written permission.
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
261da177e4SLinus Torvalds  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
271da177e4SLinus Torvalds  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
281da177e4SLinus Torvalds  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
291da177e4SLinus Torvalds  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
301da177e4SLinus Torvalds  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
311da177e4SLinus Torvalds  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
321da177e4SLinus Torvalds  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
331da177e4SLinus Torvalds  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
341da177e4SLinus Torvalds  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
351da177e4SLinus Torvalds  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
361da177e4SLinus Torvalds  *
371da177e4SLinus Torvalds  * TODO: Neil Brown made the following observation:  We currently
381da177e4SLinus Torvalds  * initially reserve NFSD_BUFSIZE space on the transmit queue and
391da177e4SLinus Torvalds  * never release any of that until the request is complete.
401da177e4SLinus Torvalds  * It would be good to calculate a new maximum response size while
411da177e4SLinus Torvalds  * decoding the COMPOUND, and call svc_reserve with this number
421da177e4SLinus Torvalds  * at the end of nfs4svc_decode_compoundargs.
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #include <linux/param.h>
461da177e4SLinus Torvalds #include <linux/smp.h>
471da177e4SLinus Torvalds #include <linux/smp_lock.h>
481da177e4SLinus Torvalds #include <linux/fs.h>
491da177e4SLinus Torvalds #include <linux/namei.h>
501da177e4SLinus Torvalds #include <linux/vfs.h>
511da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h>
521da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
531da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
541da177e4SLinus Torvalds #include <linux/nfsd/nfsd.h>
551da177e4SLinus Torvalds #include <linux/nfsd/state.h>
561da177e4SLinus Torvalds #include <linux/nfsd/xdr4.h>
571da177e4SLinus Torvalds #include <linux/nfsd_idmap.h>
581da177e4SLinus Torvalds #include <linux/nfs4.h>
591da177e4SLinus Torvalds #include <linux/nfs4_acl.h>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds static int
641da177e4SLinus Torvalds check_filename(char *str, int len, int err)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds 	int i;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	if (len == 0)
691da177e4SLinus Torvalds 		return nfserr_inval;
701da177e4SLinus Torvalds 	if (isdotent(str, len))
711da177e4SLinus Torvalds 		return err;
721da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
731da177e4SLinus Torvalds 		if (str[i] == '/')
741da177e4SLinus Torvalds 			return err;
751da177e4SLinus Torvalds 	return 0;
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds /*
791da177e4SLinus Torvalds  * START OF "GENERIC" DECODE ROUTINES.
801da177e4SLinus Torvalds  *   These may look a little ugly since they are imported from a "generic"
811da177e4SLinus Torvalds  * set of XDR encode/decode routines which are intended to be shared by
821da177e4SLinus Torvalds  * all of our NFSv4 implementations (OpenBSD, MacOS X...).
831da177e4SLinus Torvalds  *
841da177e4SLinus Torvalds  * If the pain of reading these is too great, it should be a straightforward
851da177e4SLinus Torvalds  * task to translate them into Linux-specific versions which are more
861da177e4SLinus Torvalds  * consistent with the style used in NFSv2/v3...
871da177e4SLinus Torvalds  */
881da177e4SLinus Torvalds #define DECODE_HEAD				\
891da177e4SLinus Torvalds 	u32 *p;					\
901da177e4SLinus Torvalds 	int status
911da177e4SLinus Torvalds #define DECODE_TAIL				\
921da177e4SLinus Torvalds 	status = 0;				\
931da177e4SLinus Torvalds out:						\
941da177e4SLinus Torvalds 	return status;				\
951da177e4SLinus Torvalds xdr_error:					\
961da177e4SLinus Torvalds 	printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__);	\
971da177e4SLinus Torvalds 	status = nfserr_bad_xdr;		\
981da177e4SLinus Torvalds 	goto out
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds #define READ32(x)         (x) = ntohl(*p++)
1011da177e4SLinus Torvalds #define READ64(x)         do {			\
1021da177e4SLinus Torvalds 	(x) = (u64)ntohl(*p++) << 32;		\
1031da177e4SLinus Torvalds 	(x) |= ntohl(*p++);			\
1041da177e4SLinus Torvalds } while (0)
1051da177e4SLinus Torvalds #define READTIME(x)       do {			\
1061da177e4SLinus Torvalds 	p++;					\
1071da177e4SLinus Torvalds 	(x) = ntohl(*p++);			\
1081da177e4SLinus Torvalds 	p++;					\
1091da177e4SLinus Torvalds } while (0)
1101da177e4SLinus Torvalds #define READMEM(x,nbytes) do {			\
1111da177e4SLinus Torvalds 	x = (char *)p;				\
1121da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1131da177e4SLinus Torvalds } while (0)
1141da177e4SLinus Torvalds #define SAVEMEM(x,nbytes) do {			\
1151da177e4SLinus Torvalds 	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
1161da177e4SLinus Torvalds  		savemem(argp, p, nbytes) :	\
1171da177e4SLinus Torvalds  		(char *)p)) {			\
1181da177e4SLinus Torvalds 		printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \
1191da177e4SLinus Torvalds 		goto xdr_error;			\
1201da177e4SLinus Torvalds 		}				\
1211da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1221da177e4SLinus Torvalds } while (0)
1231da177e4SLinus Torvalds #define COPYMEM(x,nbytes) do {			\
1241da177e4SLinus Torvalds 	memcpy((x), p, nbytes);			\
1251da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);		\
1261da177e4SLinus Torvalds } while (0)
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
1291da177e4SLinus Torvalds #define READ_BUF(nbytes)  do {			\
1301da177e4SLinus Torvalds 	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
1311da177e4SLinus Torvalds 		p = argp->p;			\
1321da177e4SLinus Torvalds 		argp->p += XDR_QUADLEN(nbytes);	\
1331da177e4SLinus Torvalds 	} else if (!(p = read_buf(argp, nbytes))) { \
1341da177e4SLinus Torvalds 		printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \
1351da177e4SLinus Torvalds 		goto xdr_error;			\
1361da177e4SLinus Torvalds 	}					\
1371da177e4SLinus Torvalds } while (0)
1381da177e4SLinus Torvalds 
139fd39ca9aSNeilBrown static u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	/* We want more bytes than seem to be available.
1421da177e4SLinus Torvalds 	 * Maybe we need a new page, maybe we have just run out
1431da177e4SLinus Torvalds 	 */
1441da177e4SLinus Torvalds 	int avail = (char*)argp->end - (char*)argp->p;
1451da177e4SLinus Torvalds 	u32 *p;
1461da177e4SLinus Torvalds 	if (avail + argp->pagelen < nbytes)
1471da177e4SLinus Torvalds 		return NULL;
1481da177e4SLinus Torvalds 	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
1491da177e4SLinus Torvalds 		return NULL;
1501da177e4SLinus Torvalds 	/* ok, we can do it with the current plus the next page */
1511da177e4SLinus Torvalds 	if (nbytes <= sizeof(argp->tmp))
1521da177e4SLinus Torvalds 		p = argp->tmp;
1531da177e4SLinus Torvalds 	else {
1541da177e4SLinus Torvalds 		kfree(argp->tmpp);
1551da177e4SLinus Torvalds 		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
1561da177e4SLinus Torvalds 		if (!p)
1571da177e4SLinus Torvalds 			return NULL;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 	memcpy(p, argp->p, avail);
1611da177e4SLinus Torvalds 	/* step to next page */
1621da177e4SLinus Torvalds 	argp->p = page_address(argp->pagelist[0]);
1631da177e4SLinus Torvalds 	argp->pagelist++;
1641da177e4SLinus Torvalds 	if (argp->pagelen < PAGE_SIZE) {
1651da177e4SLinus Torvalds 		argp->end = p + (argp->pagelen>>2);
1661da177e4SLinus Torvalds 		argp->pagelen = 0;
1671da177e4SLinus Torvalds 	} else {
1681da177e4SLinus Torvalds 		argp->end = p + (PAGE_SIZE>>2);
1691da177e4SLinus Torvalds 		argp->pagelen -= PAGE_SIZE;
1701da177e4SLinus Torvalds 	}
1711da177e4SLinus Torvalds 	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
1721da177e4SLinus Torvalds 	argp->p += XDR_QUADLEN(nbytes - avail);
1731da177e4SLinus Torvalds 	return p;
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds static int
1771da177e4SLinus Torvalds defer_free(struct nfsd4_compoundargs *argp,
1781da177e4SLinus Torvalds 		void (*release)(const void *), void *p)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	struct tmpbuf *tb;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	tb = kmalloc(sizeof(*tb), GFP_KERNEL);
1831da177e4SLinus Torvalds 	if (!tb)
1841da177e4SLinus Torvalds 		return -ENOMEM;
1851da177e4SLinus Torvalds 	tb->buf = p;
1861da177e4SLinus Torvalds 	tb->release = release;
1871da177e4SLinus Torvalds 	tb->next = argp->to_free;
1881da177e4SLinus Torvalds 	argp->to_free = tb;
1891da177e4SLinus Torvalds 	return 0;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
192fd39ca9aSNeilBrown static char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
1931da177e4SLinus Torvalds {
1941da177e4SLinus Torvalds 	void *new = NULL;
1951da177e4SLinus Torvalds 	if (p == argp->tmp) {
1961da177e4SLinus Torvalds 		new = kmalloc(nbytes, GFP_KERNEL);
1971da177e4SLinus Torvalds 		if (!new) return NULL;
1981da177e4SLinus Torvalds 		p = new;
1991da177e4SLinus Torvalds 		memcpy(p, argp->tmp, nbytes);
2001da177e4SLinus Torvalds 	} else {
2011da177e4SLinus Torvalds 		if (p != argp->tmpp)
2021da177e4SLinus Torvalds 			BUG();
2031da177e4SLinus Torvalds 		argp->tmpp = NULL;
2041da177e4SLinus Torvalds 	}
2051da177e4SLinus Torvalds 	if (defer_free(argp, kfree, p)) {
2061da177e4SLinus Torvalds 		kfree(new);
2071da177e4SLinus Torvalds 		return NULL;
2081da177e4SLinus Torvalds 	} else
2091da177e4SLinus Torvalds 		return (char *)p;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds static int
2141da177e4SLinus Torvalds nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	u32 bmlen;
2171da177e4SLinus Torvalds 	DECODE_HEAD;
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 	bmval[0] = 0;
2201da177e4SLinus Torvalds 	bmval[1] = 0;
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	READ_BUF(4);
2231da177e4SLinus Torvalds 	READ32(bmlen);
2241da177e4SLinus Torvalds 	if (bmlen > 1000)
2251da177e4SLinus Torvalds 		goto xdr_error;
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	READ_BUF(bmlen << 2);
2281da177e4SLinus Torvalds 	if (bmlen > 0)
2291da177e4SLinus Torvalds 		READ32(bmval[0]);
2301da177e4SLinus Torvalds 	if (bmlen > 1)
2311da177e4SLinus Torvalds 		READ32(bmval[1]);
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	DECODE_TAIL;
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds static int
2371da177e4SLinus Torvalds nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,
2381da177e4SLinus Torvalds     struct nfs4_acl **acl)
2391da177e4SLinus Torvalds {
2401da177e4SLinus Torvalds 	int expected_len, len = 0;
2411da177e4SLinus Torvalds 	u32 dummy32;
2421da177e4SLinus Torvalds 	char *buf;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	DECODE_HEAD;
2451da177e4SLinus Torvalds 	iattr->ia_valid = 0;
2461da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
2471da177e4SLinus Torvalds 		return status;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	/*
2501da177e4SLinus Torvalds 	 * According to spec, unsupported attributes return ERR_NOTSUPP;
2511da177e4SLinus Torvalds 	 * read-only attributes return ERR_INVAL.
2521da177e4SLinus Torvalds 	 */
2531da177e4SLinus Torvalds 	if ((bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) || (bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1))
2541da177e4SLinus Torvalds 		return nfserr_attrnotsupp;
2551da177e4SLinus Torvalds 	if ((bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0) || (bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1))
2561da177e4SLinus Torvalds 		return nfserr_inval;
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	READ_BUF(4);
2591da177e4SLinus Torvalds 	READ32(expected_len);
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_SIZE) {
2621da177e4SLinus Torvalds 		READ_BUF(8);
2631da177e4SLinus Torvalds 		len += 8;
2641da177e4SLinus Torvalds 		READ64(iattr->ia_size);
2651da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_SIZE;
2661da177e4SLinus Torvalds 	}
2671da177e4SLinus Torvalds 	if (bmval[0] & FATTR4_WORD0_ACL) {
2681da177e4SLinus Torvalds 		int nace, i;
2691da177e4SLinus Torvalds 		struct nfs4_ace ace;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 		READ_BUF(4); len += 4;
2721da177e4SLinus Torvalds 		READ32(nace);
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 		*acl = nfs4_acl_new();
2751da177e4SLinus Torvalds 		if (*acl == NULL) {
2761da177e4SLinus Torvalds 			status = -ENOMEM;
2771da177e4SLinus Torvalds 			goto out_nfserr;
2781da177e4SLinus Torvalds 		}
2791da177e4SLinus Torvalds 		defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 		for (i = 0; i < nace; i++) {
2821da177e4SLinus Torvalds 			READ_BUF(16); len += 16;
2831da177e4SLinus Torvalds 			READ32(ace.type);
2841da177e4SLinus Torvalds 			READ32(ace.flag);
2851da177e4SLinus Torvalds 			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);
2901da177e4SLinus Torvalds 			ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
2911da177e4SLinus Torvalds 			status = 0;
2921da177e4SLinus Torvalds 			if (ace.whotype != NFS4_ACL_WHO_NAMED)
2931da177e4SLinus Torvalds 				ace.who = 0;
2941da177e4SLinus Torvalds 			else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP)
2951da177e4SLinus Torvalds 				status = nfsd_map_name_to_gid(argp->rqstp,
2961da177e4SLinus Torvalds 						buf, dummy32, &ace.who);
2971da177e4SLinus Torvalds 			else
2981da177e4SLinus Torvalds 				status = nfsd_map_name_to_uid(argp->rqstp,
2991da177e4SLinus Torvalds 						buf, dummy32, &ace.who);
3001da177e4SLinus Torvalds 			if (status)
3011da177e4SLinus Torvalds 				goto out_nfserr;
302b905b7b0SNeilBrown 			status = nfs4_acl_add_ace(*acl, ace.type, ace.flag,
303b905b7b0SNeilBrown 				 ace.access_mask, ace.whotype, ace.who);
304b905b7b0SNeilBrown 			if (status)
3051da177e4SLinus Torvalds 				goto out_nfserr;
3061da177e4SLinus Torvalds 		}
3071da177e4SLinus Torvalds 	} else
3081da177e4SLinus Torvalds 		*acl = NULL;
3091da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_MODE) {
3101da177e4SLinus Torvalds 		READ_BUF(4);
3111da177e4SLinus Torvalds 		len += 4;
3121da177e4SLinus Torvalds 		READ32(iattr->ia_mode);
3131da177e4SLinus Torvalds 		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
3141da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_MODE;
3151da177e4SLinus Torvalds 	}
3161da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER) {
3171da177e4SLinus Torvalds 		READ_BUF(4);
3181da177e4SLinus Torvalds 		len += 4;
3191da177e4SLinus Torvalds 		READ32(dummy32);
3201da177e4SLinus Torvalds 		READ_BUF(dummy32);
3211da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3221da177e4SLinus Torvalds 		READMEM(buf, dummy32);
3231da177e4SLinus Torvalds 		if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
3241da177e4SLinus Torvalds 			goto out_nfserr;
3251da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_UID;
3261da177e4SLinus Torvalds 	}
3271da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
3281da177e4SLinus Torvalds 		READ_BUF(4);
3291da177e4SLinus Torvalds 		len += 4;
3301da177e4SLinus Torvalds 		READ32(dummy32);
3311da177e4SLinus Torvalds 		READ_BUF(dummy32);
3321da177e4SLinus Torvalds 		len += (XDR_QUADLEN(dummy32) << 2);
3331da177e4SLinus Torvalds 		READMEM(buf, dummy32);
3341da177e4SLinus Torvalds 		if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
3351da177e4SLinus Torvalds 			goto out_nfserr;
3361da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_GID;
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
3391da177e4SLinus Torvalds 		READ_BUF(4);
3401da177e4SLinus Torvalds 		len += 4;
3411da177e4SLinus Torvalds 		READ32(dummy32);
3421da177e4SLinus Torvalds 		switch (dummy32) {
3431da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3441da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3451da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3461da177e4SLinus Torvalds 			READ_BUF(12);
3471da177e4SLinus Torvalds 			len += 12;
3481da177e4SLinus Torvalds 			READ32(dummy32);
3491da177e4SLinus Torvalds 			if (dummy32)
3501da177e4SLinus Torvalds 				return nfserr_inval;
3511da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_sec);
3521da177e4SLinus Torvalds 			READ32(iattr->ia_atime.tv_nsec);
3531da177e4SLinus Torvalds 			if (iattr->ia_atime.tv_nsec >= (u32)1000000000)
3541da177e4SLinus Torvalds 				return nfserr_inval;
3551da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
3561da177e4SLinus Torvalds 			break;
3571da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3581da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_ATIME;
3591da177e4SLinus Torvalds 			break;
3601da177e4SLinus Torvalds 		default:
3611da177e4SLinus Torvalds 			goto xdr_error;
3621da177e4SLinus Torvalds 		}
3631da177e4SLinus Torvalds 	}
3641da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_METADATA) {
3651da177e4SLinus Torvalds 		/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3661da177e4SLinus Torvalds 		   all 32 bits of 'nseconds'. */
3671da177e4SLinus Torvalds 		READ_BUF(12);
3681da177e4SLinus Torvalds 		len += 12;
3691da177e4SLinus Torvalds 		READ32(dummy32);
3701da177e4SLinus Torvalds 		if (dummy32)
3711da177e4SLinus Torvalds 			return nfserr_inval;
3721da177e4SLinus Torvalds 		READ32(iattr->ia_ctime.tv_sec);
3731da177e4SLinus Torvalds 		READ32(iattr->ia_ctime.tv_nsec);
3741da177e4SLinus Torvalds 		if (iattr->ia_ctime.tv_nsec >= (u32)1000000000)
3751da177e4SLinus Torvalds 			return nfserr_inval;
3761da177e4SLinus Torvalds 		iattr->ia_valid |= ATTR_CTIME;
3771da177e4SLinus Torvalds 	}
3781da177e4SLinus Torvalds 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
3791da177e4SLinus Torvalds 		READ_BUF(4);
3801da177e4SLinus Torvalds 		len += 4;
3811da177e4SLinus Torvalds 		READ32(dummy32);
3821da177e4SLinus Torvalds 		switch (dummy32) {
3831da177e4SLinus Torvalds 		case NFS4_SET_TO_CLIENT_TIME:
3841da177e4SLinus Torvalds 			/* We require the high 32 bits of 'seconds' to be 0, and we ignore
3851da177e4SLinus Torvalds 			   all 32 bits of 'nseconds'. */
3861da177e4SLinus Torvalds 			READ_BUF(12);
3871da177e4SLinus Torvalds 			len += 12;
3881da177e4SLinus Torvalds 			READ32(dummy32);
3891da177e4SLinus Torvalds 			if (dummy32)
3901da177e4SLinus Torvalds 				return nfserr_inval;
3911da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_sec);
3921da177e4SLinus Torvalds 			READ32(iattr->ia_mtime.tv_nsec);
3931da177e4SLinus Torvalds 			if (iattr->ia_mtime.tv_nsec >= (u32)1000000000)
3941da177e4SLinus Torvalds 				return nfserr_inval;
3951da177e4SLinus Torvalds 			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
3961da177e4SLinus Torvalds 			break;
3971da177e4SLinus Torvalds 		case NFS4_SET_TO_SERVER_TIME:
3981da177e4SLinus Torvalds 			iattr->ia_valid |= ATTR_MTIME;
3991da177e4SLinus Torvalds 			break;
4001da177e4SLinus Torvalds 		default:
4011da177e4SLinus Torvalds 			goto xdr_error;
4021da177e4SLinus Torvalds 		}
4031da177e4SLinus Torvalds 	}
4041da177e4SLinus Torvalds 	if (len != expected_len)
4051da177e4SLinus Torvalds 		goto xdr_error;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	DECODE_TAIL;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds out_nfserr:
4101da177e4SLinus Torvalds 	status = nfserrno(status);
4111da177e4SLinus Torvalds 	goto out;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds static int
4151da177e4SLinus Torvalds nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
4161da177e4SLinus Torvalds {
4171da177e4SLinus Torvalds 	DECODE_HEAD;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	READ_BUF(4);
4201da177e4SLinus Torvalds 	READ32(access->ac_req_access);
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	DECODE_TAIL;
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds static int
4261da177e4SLinus Torvalds nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	DECODE_HEAD;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	close->cl_stateowner = NULL;
4311da177e4SLinus Torvalds 	READ_BUF(4 + sizeof(stateid_t));
4321da177e4SLinus Torvalds 	READ32(close->cl_seqid);
4331da177e4SLinus Torvalds 	READ32(close->cl_stateid.si_generation);
4341da177e4SLinus Torvalds 	COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	DECODE_TAIL;
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds static int
4411da177e4SLinus Torvalds nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
4421da177e4SLinus Torvalds {
4431da177e4SLinus Torvalds 	DECODE_HEAD;
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	READ_BUF(12);
4461da177e4SLinus Torvalds 	READ64(commit->co_offset);
4471da177e4SLinus Torvalds 	READ32(commit->co_count);
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	DECODE_TAIL;
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds static int
4531da177e4SLinus Torvalds nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
4541da177e4SLinus Torvalds {
4551da177e4SLinus Torvalds 	DECODE_HEAD;
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	READ_BUF(4);
4581da177e4SLinus Torvalds 	READ32(create->cr_type);
4591da177e4SLinus Torvalds 	switch (create->cr_type) {
4601da177e4SLinus Torvalds 	case NF4LNK:
4611da177e4SLinus Torvalds 		READ_BUF(4);
4621da177e4SLinus Torvalds 		READ32(create->cr_linklen);
4631da177e4SLinus Torvalds 		READ_BUF(create->cr_linklen);
4641da177e4SLinus Torvalds 		SAVEMEM(create->cr_linkname, create->cr_linklen);
4651da177e4SLinus Torvalds 		break;
4661da177e4SLinus Torvalds 	case NF4BLK:
4671da177e4SLinus Torvalds 	case NF4CHR:
4681da177e4SLinus Torvalds 		READ_BUF(8);
4691da177e4SLinus Torvalds 		READ32(create->cr_specdata1);
4701da177e4SLinus Torvalds 		READ32(create->cr_specdata2);
4711da177e4SLinus Torvalds 		break;
4721da177e4SLinus Torvalds 	case NF4SOCK:
4731da177e4SLinus Torvalds 	case NF4FIFO:
4741da177e4SLinus Torvalds 	case NF4DIR:
4751da177e4SLinus Torvalds 	default:
4761da177e4SLinus Torvalds 		break;
4771da177e4SLinus Torvalds 	}
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	READ_BUF(4);
4801da177e4SLinus Torvalds 	READ32(create->cr_namelen);
4811da177e4SLinus Torvalds 	READ_BUF(create->cr_namelen);
4821da177e4SLinus Torvalds 	SAVEMEM(create->cr_name, create->cr_namelen);
4831da177e4SLinus Torvalds 	if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
4841da177e4SLinus Torvalds 		return status;
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 	if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl)))
4871da177e4SLinus Torvalds 		goto out;
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	DECODE_TAIL;
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds static inline int
4931da177e4SLinus Torvalds nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
4941da177e4SLinus Torvalds {
4951da177e4SLinus Torvalds 	DECODE_HEAD;
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	READ_BUF(sizeof(stateid_t));
4981da177e4SLinus Torvalds 	READ32(dr->dr_stateid.si_generation);
4991da177e4SLinus Torvalds 	COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t));
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 	DECODE_TAIL;
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds static inline int
5051da177e4SLinus Torvalds nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
5061da177e4SLinus Torvalds {
5071da177e4SLinus Torvalds 	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
5081da177e4SLinus Torvalds }
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds static int
5111da177e4SLinus Torvalds nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
5121da177e4SLinus Torvalds {
5131da177e4SLinus Torvalds 	DECODE_HEAD;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds 	READ_BUF(4);
5161da177e4SLinus Torvalds 	READ32(link->li_namelen);
5171da177e4SLinus Torvalds 	READ_BUF(link->li_namelen);
5181da177e4SLinus Torvalds 	SAVEMEM(link->li_name, link->li_namelen);
5191da177e4SLinus Torvalds 	if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval)))
5201da177e4SLinus Torvalds 		return status;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	DECODE_TAIL;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds static int
5261da177e4SLinus Torvalds nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
5271da177e4SLinus Torvalds {
5281da177e4SLinus Torvalds 	DECODE_HEAD;
5291da177e4SLinus Torvalds 
5303a65588aSJ. Bruce Fields 	lock->lk_replay_owner = NULL;
5311da177e4SLinus Torvalds 	/*
5321da177e4SLinus Torvalds 	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
5331da177e4SLinus Torvalds 	*/
5341da177e4SLinus Torvalds 	READ_BUF(28);
5351da177e4SLinus Torvalds 	READ32(lock->lk_type);
5361da177e4SLinus Torvalds 	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
5371da177e4SLinus Torvalds 		goto xdr_error;
5381da177e4SLinus Torvalds 	READ32(lock->lk_reclaim);
5391da177e4SLinus Torvalds 	READ64(lock->lk_offset);
5401da177e4SLinus Torvalds 	READ64(lock->lk_length);
5411da177e4SLinus Torvalds 	READ32(lock->lk_is_new);
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	if (lock->lk_is_new) {
5441da177e4SLinus Torvalds 		READ_BUF(36);
5451da177e4SLinus Torvalds 		READ32(lock->lk_new_open_seqid);
5461da177e4SLinus Torvalds 		READ32(lock->lk_new_open_stateid.si_generation);
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t));
5491da177e4SLinus Torvalds 		READ32(lock->lk_new_lock_seqid);
5501da177e4SLinus Torvalds 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
5511da177e4SLinus Torvalds 		READ32(lock->lk_new_owner.len);
5521da177e4SLinus Torvalds 		READ_BUF(lock->lk_new_owner.len);
5531da177e4SLinus Torvalds 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
5541da177e4SLinus Torvalds 	} else {
5551da177e4SLinus Torvalds 		READ_BUF(20);
5561da177e4SLinus Torvalds 		READ32(lock->lk_old_lock_stateid.si_generation);
5571da177e4SLinus Torvalds 		COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t));
5581da177e4SLinus Torvalds 		READ32(lock->lk_old_lock_seqid);
5591da177e4SLinus Torvalds 	}
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	DECODE_TAIL;
5621da177e4SLinus Torvalds }
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds static int
5651da177e4SLinus Torvalds nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
5661da177e4SLinus Torvalds {
5671da177e4SLinus Torvalds 	DECODE_HEAD;
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	READ_BUF(32);
5701da177e4SLinus Torvalds 	READ32(lockt->lt_type);
5711da177e4SLinus Torvalds 	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
5721da177e4SLinus Torvalds 		goto xdr_error;
5731da177e4SLinus Torvalds 	READ64(lockt->lt_offset);
5741da177e4SLinus Torvalds 	READ64(lockt->lt_length);
5751da177e4SLinus Torvalds 	COPYMEM(&lockt->lt_clientid, 8);
5761da177e4SLinus Torvalds 	READ32(lockt->lt_owner.len);
5771da177e4SLinus Torvalds 	READ_BUF(lockt->lt_owner.len);
5781da177e4SLinus Torvalds 	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 	DECODE_TAIL;
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds static int
5841da177e4SLinus Torvalds nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
5851da177e4SLinus Torvalds {
5861da177e4SLinus Torvalds 	DECODE_HEAD;
5871da177e4SLinus Torvalds 
5881da177e4SLinus Torvalds 	locku->lu_stateowner = NULL;
5891da177e4SLinus Torvalds 	READ_BUF(24 + sizeof(stateid_t));
5901da177e4SLinus Torvalds 	READ32(locku->lu_type);
5911da177e4SLinus Torvalds 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
5921da177e4SLinus Torvalds 		goto xdr_error;
5931da177e4SLinus Torvalds 	READ32(locku->lu_seqid);
5941da177e4SLinus Torvalds 	READ32(locku->lu_stateid.si_generation);
5951da177e4SLinus Torvalds 	COPYMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t));
5961da177e4SLinus Torvalds 	READ64(locku->lu_offset);
5971da177e4SLinus Torvalds 	READ64(locku->lu_length);
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 	DECODE_TAIL;
6001da177e4SLinus Torvalds }
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds static int
6031da177e4SLinus Torvalds nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
6041da177e4SLinus Torvalds {
6051da177e4SLinus Torvalds 	DECODE_HEAD;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 	READ_BUF(4);
6081da177e4SLinus Torvalds 	READ32(lookup->lo_len);
6091da177e4SLinus Torvalds 	READ_BUF(lookup->lo_len);
6101da177e4SLinus Torvalds 	SAVEMEM(lookup->lo_name, lookup->lo_len);
6111da177e4SLinus Torvalds 	if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent)))
6121da177e4SLinus Torvalds 		return status;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	DECODE_TAIL;
6151da177e4SLinus Torvalds }
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds static int
6181da177e4SLinus Torvalds nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
6191da177e4SLinus Torvalds {
6201da177e4SLinus Torvalds 	DECODE_HEAD;
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	memset(open->op_bmval, 0, sizeof(open->op_bmval));
6231da177e4SLinus Torvalds 	open->op_iattr.ia_valid = 0;
6241da177e4SLinus Torvalds 	open->op_stateowner = NULL;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	/* seqid, share_access, share_deny, clientid, ownerlen */
6271da177e4SLinus Torvalds 	READ_BUF(16 + sizeof(clientid_t));
6281da177e4SLinus Torvalds 	READ32(open->op_seqid);
6291da177e4SLinus Torvalds 	READ32(open->op_share_access);
6301da177e4SLinus Torvalds 	READ32(open->op_share_deny);
6311da177e4SLinus Torvalds 	COPYMEM(&open->op_clientid, sizeof(clientid_t));
6321da177e4SLinus Torvalds 	READ32(open->op_owner.len);
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	/* owner, open_flag */
6351da177e4SLinus Torvalds 	READ_BUF(open->op_owner.len + 4);
6361da177e4SLinus Torvalds 	SAVEMEM(open->op_owner.data, open->op_owner.len);
6371da177e4SLinus Torvalds 	READ32(open->op_create);
6381da177e4SLinus Torvalds 	switch (open->op_create) {
6391da177e4SLinus Torvalds 	case NFS4_OPEN_NOCREATE:
6401da177e4SLinus Torvalds 		break;
6411da177e4SLinus Torvalds 	case NFS4_OPEN_CREATE:
6421da177e4SLinus Torvalds 		READ_BUF(4);
6431da177e4SLinus Torvalds 		READ32(open->op_createmode);
6441da177e4SLinus Torvalds 		switch (open->op_createmode) {
6451da177e4SLinus Torvalds 		case NFS4_CREATE_UNCHECKED:
6461da177e4SLinus Torvalds 		case NFS4_CREATE_GUARDED:
6471da177e4SLinus Torvalds 			if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl)))
6481da177e4SLinus Torvalds 				goto out;
6491da177e4SLinus Torvalds 			break;
6501da177e4SLinus Torvalds 		case NFS4_CREATE_EXCLUSIVE:
6511da177e4SLinus Torvalds 			READ_BUF(8);
6521da177e4SLinus Torvalds 			COPYMEM(open->op_verf.data, 8);
6531da177e4SLinus Torvalds 			break;
6541da177e4SLinus Torvalds 		default:
6551da177e4SLinus Torvalds 			goto xdr_error;
6561da177e4SLinus Torvalds 		}
6571da177e4SLinus Torvalds 		break;
6581da177e4SLinus Torvalds 	default:
6591da177e4SLinus Torvalds 		goto xdr_error;
6601da177e4SLinus Torvalds 	}
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	/* open_claim */
6631da177e4SLinus Torvalds 	READ_BUF(4);
6641da177e4SLinus Torvalds 	READ32(open->op_claim_type);
6651da177e4SLinus Torvalds 	switch (open->op_claim_type) {
6661da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_NULL:
6671da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
6681da177e4SLinus Torvalds 		READ_BUF(4);
6691da177e4SLinus Torvalds 		READ32(open->op_fname.len);
6701da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
6711da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
6721da177e4SLinus Torvalds 		if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
6731da177e4SLinus Torvalds 			return status;
6741da177e4SLinus Torvalds 		break;
6751da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_PREVIOUS:
6761da177e4SLinus Torvalds 		READ_BUF(4);
6771da177e4SLinus Torvalds 		READ32(open->op_delegate_type);
6781da177e4SLinus Torvalds 		break;
6791da177e4SLinus Torvalds 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
6801da177e4SLinus Torvalds 		READ_BUF(sizeof(stateid_t) + 4);
6811da177e4SLinus Torvalds 		COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t));
6821da177e4SLinus Torvalds 		READ32(open->op_fname.len);
6831da177e4SLinus Torvalds 		READ_BUF(open->op_fname.len);
6841da177e4SLinus Torvalds 		SAVEMEM(open->op_fname.data, open->op_fname.len);
6851da177e4SLinus Torvalds 		if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
6861da177e4SLinus Torvalds 			return status;
6871da177e4SLinus Torvalds 		break;
6881da177e4SLinus Torvalds 	default:
6891da177e4SLinus Torvalds 		goto xdr_error;
6901da177e4SLinus Torvalds 	}
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds 	DECODE_TAIL;
6931da177e4SLinus Torvalds }
6941da177e4SLinus Torvalds 
6951da177e4SLinus Torvalds static int
6961da177e4SLinus Torvalds nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
6971da177e4SLinus Torvalds {
6981da177e4SLinus Torvalds 	DECODE_HEAD;
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	open_conf->oc_stateowner = NULL;
7011da177e4SLinus Torvalds 	READ_BUF(4 + sizeof(stateid_t));
7021da177e4SLinus Torvalds 	READ32(open_conf->oc_req_stateid.si_generation);
7031da177e4SLinus Torvalds 	COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t));
7041da177e4SLinus Torvalds 	READ32(open_conf->oc_seqid);
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 	DECODE_TAIL;
7071da177e4SLinus Torvalds }
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds static int
7101da177e4SLinus Torvalds nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
7111da177e4SLinus Torvalds {
7121da177e4SLinus Torvalds 	DECODE_HEAD;
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds 	open_down->od_stateowner = NULL;
7151da177e4SLinus Torvalds 	READ_BUF(12 + sizeof(stateid_t));
7161da177e4SLinus Torvalds 	READ32(open_down->od_stateid.si_generation);
7171da177e4SLinus Torvalds 	COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t));
7181da177e4SLinus Torvalds 	READ32(open_down->od_seqid);
7191da177e4SLinus Torvalds 	READ32(open_down->od_share_access);
7201da177e4SLinus Torvalds 	READ32(open_down->od_share_deny);
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds 	DECODE_TAIL;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds static int
7261da177e4SLinus Torvalds nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
7271da177e4SLinus Torvalds {
7281da177e4SLinus Torvalds 	DECODE_HEAD;
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 	READ_BUF(4);
7311da177e4SLinus Torvalds 	READ32(putfh->pf_fhlen);
7321da177e4SLinus Torvalds 	if (putfh->pf_fhlen > NFS4_FHSIZE)
7331da177e4SLinus Torvalds 		goto xdr_error;
7341da177e4SLinus Torvalds 	READ_BUF(putfh->pf_fhlen);
7351da177e4SLinus Torvalds 	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	DECODE_TAIL;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds static int
7411da177e4SLinus Torvalds nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds 	DECODE_HEAD;
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	READ_BUF(sizeof(stateid_t) + 12);
7461da177e4SLinus Torvalds 	READ32(read->rd_stateid.si_generation);
7471da177e4SLinus Torvalds 	COPYMEM(&read->rd_stateid.si_opaque, sizeof(stateid_opaque_t));
7481da177e4SLinus Torvalds 	READ64(read->rd_offset);
7491da177e4SLinus Torvalds 	READ32(read->rd_length);
7501da177e4SLinus Torvalds 
7511da177e4SLinus Torvalds 	DECODE_TAIL;
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds static int
7551da177e4SLinus Torvalds nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
7561da177e4SLinus Torvalds {
7571da177e4SLinus Torvalds 	DECODE_HEAD;
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	READ_BUF(24);
7601da177e4SLinus Torvalds 	READ64(readdir->rd_cookie);
7611da177e4SLinus Torvalds 	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
7621da177e4SLinus Torvalds 	READ32(readdir->rd_dircount);    /* just in case you needed a useless field... */
7631da177e4SLinus Torvalds 	READ32(readdir->rd_maxcount);
7641da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
7651da177e4SLinus Torvalds 		goto out;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	DECODE_TAIL;
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds static int
7711da177e4SLinus Torvalds nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
7721da177e4SLinus Torvalds {
7731da177e4SLinus Torvalds 	DECODE_HEAD;
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	READ_BUF(4);
7761da177e4SLinus Torvalds 	READ32(remove->rm_namelen);
7771da177e4SLinus Torvalds 	READ_BUF(remove->rm_namelen);
7781da177e4SLinus Torvalds 	SAVEMEM(remove->rm_name, remove->rm_namelen);
7791da177e4SLinus Torvalds 	if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent)))
7801da177e4SLinus Torvalds 		return status;
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	DECODE_TAIL;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds static int
7861da177e4SLinus Torvalds nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
7871da177e4SLinus Torvalds {
7881da177e4SLinus Torvalds 	DECODE_HEAD;
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 	READ_BUF(4);
7911da177e4SLinus Torvalds 	READ32(rename->rn_snamelen);
7921da177e4SLinus Torvalds 	READ_BUF(rename->rn_snamelen + 4);
7931da177e4SLinus Torvalds 	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
7941da177e4SLinus Torvalds 	READ32(rename->rn_tnamelen);
7951da177e4SLinus Torvalds 	READ_BUF(rename->rn_tnamelen);
7961da177e4SLinus Torvalds 	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
7971da177e4SLinus Torvalds 	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent)))
7981da177e4SLinus Torvalds 		return status;
7991da177e4SLinus Torvalds 	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval)))
8001da177e4SLinus Torvalds 		return status;
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 	DECODE_TAIL;
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds static int
8061da177e4SLinus Torvalds nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
8071da177e4SLinus Torvalds {
8081da177e4SLinus Torvalds 	DECODE_HEAD;
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 	READ_BUF(sizeof(clientid_t));
8111da177e4SLinus Torvalds 	COPYMEM(clientid, sizeof(clientid_t));
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds 	DECODE_TAIL;
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds static int
8171da177e4SLinus Torvalds nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
8181da177e4SLinus Torvalds {
8191da177e4SLinus Torvalds 	DECODE_HEAD;
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds 	READ_BUF(sizeof(stateid_t));
8221da177e4SLinus Torvalds 	READ32(setattr->sa_stateid.si_generation);
8231da177e4SLinus Torvalds 	COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t));
8241da177e4SLinus Torvalds 	if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl)))
8251da177e4SLinus Torvalds 		goto out;
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 	DECODE_TAIL;
8281da177e4SLinus Torvalds }
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds static int
8311da177e4SLinus Torvalds nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
8321da177e4SLinus Torvalds {
8331da177e4SLinus Torvalds 	DECODE_HEAD;
8341da177e4SLinus Torvalds 
8351da177e4SLinus Torvalds 	READ_BUF(12);
8361da177e4SLinus Torvalds 	COPYMEM(setclientid->se_verf.data, 8);
8371da177e4SLinus Torvalds 	READ32(setclientid->se_namelen);
8381da177e4SLinus Torvalds 
8391da177e4SLinus Torvalds 	READ_BUF(setclientid->se_namelen + 8);
8401da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_name, setclientid->se_namelen);
8411da177e4SLinus Torvalds 	READ32(setclientid->se_callback_prog);
8421da177e4SLinus Torvalds 	READ32(setclientid->se_callback_netid_len);
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_netid_len + 4);
8451da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
8461da177e4SLinus Torvalds 	READ32(setclientid->se_callback_addr_len);
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 	READ_BUF(setclientid->se_callback_addr_len + 4);
8491da177e4SLinus Torvalds 	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
8501da177e4SLinus Torvalds 	READ32(setclientid->se_callback_ident);
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 	DECODE_TAIL;
8531da177e4SLinus Torvalds }
8541da177e4SLinus Torvalds 
8551da177e4SLinus Torvalds static int
8561da177e4SLinus Torvalds nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
8571da177e4SLinus Torvalds {
8581da177e4SLinus Torvalds 	DECODE_HEAD;
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 	READ_BUF(8 + sizeof(nfs4_verifier));
8611da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_clientid, 8);
8621da177e4SLinus Torvalds 	COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier));
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 	DECODE_TAIL;
8651da177e4SLinus Torvalds }
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds /* Also used for NVERIFY */
8681da177e4SLinus Torvalds static int
8691da177e4SLinus Torvalds nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
8701da177e4SLinus Torvalds {
8711da177e4SLinus Torvalds #if 0
8721da177e4SLinus Torvalds 	struct nfsd4_compoundargs save = {
8731da177e4SLinus Torvalds 		.p = argp->p,
8741da177e4SLinus Torvalds 		.end = argp->end,
8751da177e4SLinus Torvalds 		.rqstp = argp->rqstp,
8761da177e4SLinus Torvalds 	};
8771da177e4SLinus Torvalds 	u32             ve_bmval[2];
8781da177e4SLinus Torvalds 	struct iattr    ve_iattr;           /* request */
8791da177e4SLinus Torvalds 	struct nfs4_acl *ve_acl;            /* request */
8801da177e4SLinus Torvalds #endif
8811da177e4SLinus Torvalds 	DECODE_HEAD;
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
8841da177e4SLinus Torvalds 		goto out;
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 	/* For convenience's sake, we compare raw xdr'd attributes in
8871da177e4SLinus Torvalds 	 * nfsd4_proc_verify; however we still decode here just to return
8881da177e4SLinus Torvalds 	 * correct error in case of bad xdr. */
8891da177e4SLinus Torvalds #if 0
8901da177e4SLinus Torvalds 	status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl);
8911da177e4SLinus Torvalds 	if (status == nfserr_inval) {
8921da177e4SLinus Torvalds 		status = nfserrno(status);
8931da177e4SLinus Torvalds 		goto out;
8941da177e4SLinus Torvalds 	}
8951da177e4SLinus Torvalds #endif
8961da177e4SLinus Torvalds 	READ_BUF(4);
8971da177e4SLinus Torvalds 	READ32(verify->ve_attrlen);
8981da177e4SLinus Torvalds 	READ_BUF(verify->ve_attrlen);
8991da177e4SLinus Torvalds 	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	DECODE_TAIL;
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds static int
9051da177e4SLinus Torvalds nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
9061da177e4SLinus Torvalds {
9071da177e4SLinus Torvalds 	int avail;
9081da177e4SLinus Torvalds 	int v;
9091da177e4SLinus Torvalds 	int len;
9101da177e4SLinus Torvalds 	DECODE_HEAD;
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	READ_BUF(sizeof(stateid_opaque_t) + 20);
9131da177e4SLinus Torvalds 	READ32(write->wr_stateid.si_generation);
9141da177e4SLinus Torvalds 	COPYMEM(&write->wr_stateid.si_opaque, sizeof(stateid_opaque_t));
9151da177e4SLinus Torvalds 	READ64(write->wr_offset);
9161da177e4SLinus Torvalds 	READ32(write->wr_stable_how);
9171da177e4SLinus Torvalds 	if (write->wr_stable_how > 2)
9181da177e4SLinus Torvalds 		goto xdr_error;
9191da177e4SLinus Torvalds 	READ32(write->wr_buflen);
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	/* Sorry .. no magic macros for this.. *
9221da177e4SLinus Torvalds 	 * READ_BUF(write->wr_buflen);
9231da177e4SLinus Torvalds 	 * SAVEMEM(write->wr_buf, write->wr_buflen);
9241da177e4SLinus Torvalds 	 */
9251da177e4SLinus Torvalds 	avail = (char*)argp->end - (char*)argp->p;
9261da177e4SLinus Torvalds 	if (avail + argp->pagelen < write->wr_buflen) {
9271da177e4SLinus Torvalds 		printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__);
9281da177e4SLinus Torvalds 		goto xdr_error;
9291da177e4SLinus Torvalds 	}
9301da177e4SLinus Torvalds 	write->wr_vec[0].iov_base = p;
9311da177e4SLinus Torvalds 	write->wr_vec[0].iov_len = avail;
9321da177e4SLinus Torvalds 	v = 0;
9331da177e4SLinus Torvalds 	len = write->wr_buflen;
9341da177e4SLinus Torvalds 	while (len > write->wr_vec[v].iov_len) {
9351da177e4SLinus Torvalds 		len -= write->wr_vec[v].iov_len;
9361da177e4SLinus Torvalds 		v++;
9371da177e4SLinus Torvalds 		write->wr_vec[v].iov_base = page_address(argp->pagelist[0]);
9381da177e4SLinus Torvalds 		argp->pagelist++;
9391da177e4SLinus Torvalds 		if (argp->pagelen >= PAGE_SIZE) {
9401da177e4SLinus Torvalds 			write->wr_vec[v].iov_len = PAGE_SIZE;
9411da177e4SLinus Torvalds 			argp->pagelen -= PAGE_SIZE;
9421da177e4SLinus Torvalds 		} else {
9431da177e4SLinus Torvalds 			write->wr_vec[v].iov_len = argp->pagelen;
9441da177e4SLinus Torvalds 			argp->pagelen -= len;
9451da177e4SLinus Torvalds 		}
9461da177e4SLinus Torvalds 	}
9471da177e4SLinus Torvalds 	argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len);
9481da177e4SLinus Torvalds 	argp->p = (u32*)  (write->wr_vec[v].iov_base + (XDR_QUADLEN(len) << 2));
9491da177e4SLinus Torvalds 	write->wr_vec[v].iov_len = len;
9501da177e4SLinus Torvalds 	write->wr_vlen = v+1;
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	DECODE_TAIL;
9531da177e4SLinus Torvalds }
9541da177e4SLinus Torvalds 
9551da177e4SLinus Torvalds static int
9561da177e4SLinus Torvalds nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
9571da177e4SLinus Torvalds {
9581da177e4SLinus Torvalds 	DECODE_HEAD;
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 	READ_BUF(12);
9611da177e4SLinus Torvalds 	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
9621da177e4SLinus Torvalds 	READ32(rlockowner->rl_owner.len);
9631da177e4SLinus Torvalds 	READ_BUF(rlockowner->rl_owner.len);
9641da177e4SLinus Torvalds 	READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 	DECODE_TAIL;
9671da177e4SLinus Torvalds }
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds static int
9701da177e4SLinus Torvalds nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
9711da177e4SLinus Torvalds {
9721da177e4SLinus Torvalds 	DECODE_HEAD;
9731da177e4SLinus Torvalds 	struct nfsd4_op *op;
9741da177e4SLinus Torvalds 	int i;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 	/*
9771da177e4SLinus Torvalds 	 * XXX: According to spec, we should check the tag
9781da177e4SLinus Torvalds 	 * for UTF-8 compliance.  I'm postponing this for
9791da177e4SLinus Torvalds 	 * now because it seems that some clients do use
9801da177e4SLinus Torvalds 	 * binary tags.
9811da177e4SLinus Torvalds 	 */
9821da177e4SLinus Torvalds 	READ_BUF(4);
9831da177e4SLinus Torvalds 	READ32(argp->taglen);
9841da177e4SLinus Torvalds 	READ_BUF(argp->taglen + 8);
9851da177e4SLinus Torvalds 	SAVEMEM(argp->tag, argp->taglen);
9861da177e4SLinus Torvalds 	READ32(argp->minorversion);
9871da177e4SLinus Torvalds 	READ32(argp->opcnt);
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 	if (argp->taglen > NFSD4_MAX_TAGLEN)
9901da177e4SLinus Torvalds 		goto xdr_error;
9911da177e4SLinus Torvalds 	if (argp->opcnt > 100)
9921da177e4SLinus Torvalds 		goto xdr_error;
9931da177e4SLinus Torvalds 
994e8c96f8cSTobias Klauser 	if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
9951da177e4SLinus Torvalds 		argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
9961da177e4SLinus Torvalds 		if (!argp->ops) {
9971da177e4SLinus Torvalds 			argp->ops = argp->iops;
9981da177e4SLinus Torvalds 			printk(KERN_INFO "nfsd: couldn't allocate room for COMPOUND\n");
9991da177e4SLinus Torvalds 			goto xdr_error;
10001da177e4SLinus Torvalds 		}
10011da177e4SLinus Torvalds 	}
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	for (i = 0; i < argp->opcnt; i++) {
10041da177e4SLinus Torvalds 		op = &argp->ops[i];
10051da177e4SLinus Torvalds 		op->replay = NULL;
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 		/*
10081da177e4SLinus Torvalds 		 * We can't use READ_BUF() here because we need to handle
10091da177e4SLinus Torvalds 		 * a missing opcode as an OP_WRITE + 1. So we need to check
10101da177e4SLinus Torvalds 		 * to see if we're truly at the end of our buffer or if there
10111da177e4SLinus Torvalds 		 * is another page we need to flip to.
10121da177e4SLinus Torvalds 		 */
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 		if (argp->p == argp->end) {
10151da177e4SLinus Torvalds 			if (argp->pagelen < 4) {
10161da177e4SLinus Torvalds 				/* There isn't an opcode still on the wire */
10171da177e4SLinus Torvalds 				op->opnum = OP_WRITE + 1;
10181da177e4SLinus Torvalds 				op->status = nfserr_bad_xdr;
10191da177e4SLinus Torvalds 				argp->opcnt = i+1;
10201da177e4SLinus Torvalds 				break;
10211da177e4SLinus Torvalds 			}
10221da177e4SLinus Torvalds 
10231da177e4SLinus Torvalds 			/*
10241da177e4SLinus Torvalds 			 * False alarm. We just hit a page boundary, but there
10251da177e4SLinus Torvalds 			 * is still data available.  Move pointer across page
10261da177e4SLinus Torvalds 			 * boundary.  *snip from READ_BUF*
10271da177e4SLinus Torvalds 			 */
10281da177e4SLinus Torvalds 			argp->p = page_address(argp->pagelist[0]);
10291da177e4SLinus Torvalds 			argp->pagelist++;
10301da177e4SLinus Torvalds 			if (argp->pagelen < PAGE_SIZE) {
10311da177e4SLinus Torvalds 				argp->end = p + (argp->pagelen>>2);
10321da177e4SLinus Torvalds 				argp->pagelen = 0;
10331da177e4SLinus Torvalds 			} else {
10341da177e4SLinus Torvalds 				argp->end = p + (PAGE_SIZE>>2);
10351da177e4SLinus Torvalds 				argp->pagelen -= PAGE_SIZE;
10361da177e4SLinus Torvalds 			}
10371da177e4SLinus Torvalds 		}
10381da177e4SLinus Torvalds 		op->opnum = ntohl(*argp->p++);
10391da177e4SLinus Torvalds 
10401da177e4SLinus Torvalds 		switch (op->opnum) {
10411da177e4SLinus Torvalds 		case 2: /* Reserved operation */
10421da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
10431da177e4SLinus Torvalds 			if (argp->minorversion == 0)
10441da177e4SLinus Torvalds 				op->status = nfserr_op_illegal;
10451da177e4SLinus Torvalds 			else
10461da177e4SLinus Torvalds 				op->status = nfserr_minor_vers_mismatch;
10471da177e4SLinus Torvalds 			break;
10481da177e4SLinus Torvalds 		case OP_ACCESS:
10491da177e4SLinus Torvalds 			op->status = nfsd4_decode_access(argp, &op->u.access);
10501da177e4SLinus Torvalds 			break;
10511da177e4SLinus Torvalds 		case OP_CLOSE:
10521da177e4SLinus Torvalds 			op->status = nfsd4_decode_close(argp, &op->u.close);
10531da177e4SLinus Torvalds 			break;
10541da177e4SLinus Torvalds 		case OP_COMMIT:
10551da177e4SLinus Torvalds 			op->status = nfsd4_decode_commit(argp, &op->u.commit);
10561da177e4SLinus Torvalds 			break;
10571da177e4SLinus Torvalds 		case OP_CREATE:
10581da177e4SLinus Torvalds 			op->status = nfsd4_decode_create(argp, &op->u.create);
10591da177e4SLinus Torvalds 			break;
10601da177e4SLinus Torvalds 		case OP_DELEGRETURN:
10611da177e4SLinus Torvalds 			op->status = nfsd4_decode_delegreturn(argp, &op->u.delegreturn);
10621da177e4SLinus Torvalds 			break;
10631da177e4SLinus Torvalds 		case OP_GETATTR:
10641da177e4SLinus Torvalds 			op->status = nfsd4_decode_getattr(argp, &op->u.getattr);
10651da177e4SLinus Torvalds 			break;
10661da177e4SLinus Torvalds 		case OP_GETFH:
10671da177e4SLinus Torvalds 			op->status = nfs_ok;
10681da177e4SLinus Torvalds 			break;
10691da177e4SLinus Torvalds 		case OP_LINK:
10701da177e4SLinus Torvalds 			op->status = nfsd4_decode_link(argp, &op->u.link);
10711da177e4SLinus Torvalds 			break;
10721da177e4SLinus Torvalds 		case OP_LOCK:
10731da177e4SLinus Torvalds 			op->status = nfsd4_decode_lock(argp, &op->u.lock);
10741da177e4SLinus Torvalds 			break;
10751da177e4SLinus Torvalds 		case OP_LOCKT:
10761da177e4SLinus Torvalds 			op->status = nfsd4_decode_lockt(argp, &op->u.lockt);
10771da177e4SLinus Torvalds 			break;
10781da177e4SLinus Torvalds 		case OP_LOCKU:
10791da177e4SLinus Torvalds 			op->status = nfsd4_decode_locku(argp, &op->u.locku);
10801da177e4SLinus Torvalds 			break;
10811da177e4SLinus Torvalds 		case OP_LOOKUP:
10821da177e4SLinus Torvalds 			op->status = nfsd4_decode_lookup(argp, &op->u.lookup);
10831da177e4SLinus Torvalds 			break;
10841da177e4SLinus Torvalds 		case OP_LOOKUPP:
10851da177e4SLinus Torvalds 			op->status = nfs_ok;
10861da177e4SLinus Torvalds 			break;
10871da177e4SLinus Torvalds 		case OP_NVERIFY:
10881da177e4SLinus Torvalds 			op->status = nfsd4_decode_verify(argp, &op->u.nverify);
10891da177e4SLinus Torvalds 			break;
10901da177e4SLinus Torvalds 		case OP_OPEN:
10911da177e4SLinus Torvalds 			op->status = nfsd4_decode_open(argp, &op->u.open);
10921da177e4SLinus Torvalds 			break;
10931da177e4SLinus Torvalds 		case OP_OPEN_CONFIRM:
10941da177e4SLinus Torvalds 			op->status = nfsd4_decode_open_confirm(argp, &op->u.open_confirm);
10951da177e4SLinus Torvalds 			break;
10961da177e4SLinus Torvalds 		case OP_OPEN_DOWNGRADE:
10971da177e4SLinus Torvalds 			op->status = nfsd4_decode_open_downgrade(argp, &op->u.open_downgrade);
10981da177e4SLinus Torvalds 			break;
10991da177e4SLinus Torvalds 		case OP_PUTFH:
11001da177e4SLinus Torvalds 			op->status = nfsd4_decode_putfh(argp, &op->u.putfh);
11011da177e4SLinus Torvalds 			break;
11021da177e4SLinus Torvalds 		case OP_PUTROOTFH:
11031da177e4SLinus Torvalds 			op->status = nfs_ok;
11041da177e4SLinus Torvalds 			break;
11051da177e4SLinus Torvalds 		case OP_READ:
11061da177e4SLinus Torvalds 			op->status = nfsd4_decode_read(argp, &op->u.read);
11071da177e4SLinus Torvalds 			break;
11081da177e4SLinus Torvalds 		case OP_READDIR:
11091da177e4SLinus Torvalds 			op->status = nfsd4_decode_readdir(argp, &op->u.readdir);
11101da177e4SLinus Torvalds 			break;
11111da177e4SLinus Torvalds 		case OP_READLINK:
11121da177e4SLinus Torvalds 			op->status = nfs_ok;
11131da177e4SLinus Torvalds 			break;
11141da177e4SLinus Torvalds 		case OP_REMOVE:
11151da177e4SLinus Torvalds 			op->status = nfsd4_decode_remove(argp, &op->u.remove);
11161da177e4SLinus Torvalds 			break;
11171da177e4SLinus Torvalds 		case OP_RENAME:
11181da177e4SLinus Torvalds 			op->status = nfsd4_decode_rename(argp, &op->u.rename);
11191da177e4SLinus Torvalds 			break;
11201da177e4SLinus Torvalds 		case OP_RESTOREFH:
11211da177e4SLinus Torvalds 			op->status = nfs_ok;
11221da177e4SLinus Torvalds 			break;
11231da177e4SLinus Torvalds 		case OP_RENEW:
11241da177e4SLinus Torvalds 			op->status = nfsd4_decode_renew(argp, &op->u.renew);
11251da177e4SLinus Torvalds 			break;
11261da177e4SLinus Torvalds 		case OP_SAVEFH:
11271da177e4SLinus Torvalds 			op->status = nfs_ok;
11281da177e4SLinus Torvalds 			break;
11291da177e4SLinus Torvalds 		case OP_SETATTR:
11301da177e4SLinus Torvalds 			op->status = nfsd4_decode_setattr(argp, &op->u.setattr);
11311da177e4SLinus Torvalds 			break;
11321da177e4SLinus Torvalds 		case OP_SETCLIENTID:
11331da177e4SLinus Torvalds 			op->status = nfsd4_decode_setclientid(argp, &op->u.setclientid);
11341da177e4SLinus Torvalds 			break;
11351da177e4SLinus Torvalds 		case OP_SETCLIENTID_CONFIRM:
11361da177e4SLinus Torvalds 			op->status = nfsd4_decode_setclientid_confirm(argp, &op->u.setclientid_confirm);
11371da177e4SLinus Torvalds 			break;
11381da177e4SLinus Torvalds 		case OP_VERIFY:
11391da177e4SLinus Torvalds 			op->status = nfsd4_decode_verify(argp, &op->u.verify);
11401da177e4SLinus Torvalds 			break;
11411da177e4SLinus Torvalds 		case OP_WRITE:
11421da177e4SLinus Torvalds 			op->status = nfsd4_decode_write(argp, &op->u.write);
11431da177e4SLinus Torvalds 			break;
11441da177e4SLinus Torvalds 		case OP_RELEASE_LOCKOWNER:
11451da177e4SLinus Torvalds 			op->status = nfsd4_decode_release_lockowner(argp, &op->u.release_lockowner);
11461da177e4SLinus Torvalds 			break;
11471da177e4SLinus Torvalds 		default:
11481da177e4SLinus Torvalds 			op->opnum = OP_ILLEGAL;
11491da177e4SLinus Torvalds 			op->status = nfserr_op_illegal;
11501da177e4SLinus Torvalds 			break;
11511da177e4SLinus Torvalds 		}
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 		if (op->status) {
11541da177e4SLinus Torvalds 			argp->opcnt = i+1;
11551da177e4SLinus Torvalds 			break;
11561da177e4SLinus Torvalds 		}
11571da177e4SLinus Torvalds 	}
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds 	DECODE_TAIL;
11601da177e4SLinus Torvalds }
11611da177e4SLinus Torvalds /*
11621da177e4SLinus Torvalds  * END OF "GENERIC" DECODE ROUTINES.
11631da177e4SLinus Torvalds  */
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds /*
11661da177e4SLinus Torvalds  * START OF "GENERIC" ENCODE ROUTINES.
11671da177e4SLinus Torvalds  *   These may look a little ugly since they are imported from a "generic"
11681da177e4SLinus Torvalds  * set of XDR encode/decode routines which are intended to be shared by
11691da177e4SLinus Torvalds  * all of our NFSv4 implementations (OpenBSD, MacOS X...).
11701da177e4SLinus Torvalds  *
11711da177e4SLinus Torvalds  * If the pain of reading these is too great, it should be a straightforward
11721da177e4SLinus Torvalds  * task to translate them into Linux-specific versions which are more
11731da177e4SLinus Torvalds  * consistent with the style used in NFSv2/v3...
11741da177e4SLinus Torvalds  */
11751da177e4SLinus Torvalds #define ENCODE_HEAD              u32 *p
11761da177e4SLinus Torvalds 
11771da177e4SLinus Torvalds #define WRITE32(n)               *p++ = htonl(n)
11781da177e4SLinus Torvalds #define WRITE64(n)               do {				\
11791da177e4SLinus Torvalds 	*p++ = htonl((u32)((n) >> 32));				\
11801da177e4SLinus Torvalds 	*p++ = htonl((u32)(n));					\
11811da177e4SLinus Torvalds } while (0)
11821da177e4SLinus Torvalds #define WRITEMEM(ptr,nbytes)     do {				\
11831da177e4SLinus Torvalds 	*(p + XDR_QUADLEN(nbytes) -1) = 0;                      \
11841da177e4SLinus Torvalds 	memcpy(p, ptr, nbytes);					\
11851da177e4SLinus Torvalds 	p += XDR_QUADLEN(nbytes);				\
11861da177e4SLinus Torvalds } while (0)
11871da177e4SLinus Torvalds #define WRITECINFO(c)		do {				\
11881da177e4SLinus Torvalds 	*p++ = htonl(c.atomic);					\
11891da177e4SLinus Torvalds 	*p++ = htonl(c.before_ctime_sec);				\
11901da177e4SLinus Torvalds 	*p++ = htonl(c.before_ctime_nsec);				\
11911da177e4SLinus Torvalds 	*p++ = htonl(c.after_ctime_sec);				\
11921da177e4SLinus Torvalds 	*p++ = htonl(c.after_ctime_nsec);				\
11931da177e4SLinus Torvalds } while (0)
11941da177e4SLinus Torvalds 
11951da177e4SLinus Torvalds #define RESERVE_SPACE(nbytes)	do {				\
11961da177e4SLinus Torvalds 	p = resp->p;						\
11971da177e4SLinus Torvalds 	BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end);		\
11981da177e4SLinus Torvalds } while (0)
11991da177e4SLinus Torvalds #define ADJUST_ARGS()		resp->p = p
12001da177e4SLinus Torvalds 
12011da177e4SLinus Torvalds /*
12021da177e4SLinus Torvalds  * Header routine to setup seqid operation replay cache
12031da177e4SLinus Torvalds  */
12041da177e4SLinus Torvalds #define ENCODE_SEQID_OP_HEAD					\
12051da177e4SLinus Torvalds 	u32 *p;							\
12061da177e4SLinus Torvalds 	u32 *save;						\
12071da177e4SLinus Torvalds 								\
12081da177e4SLinus Torvalds 	save = resp->p;
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds /*
12117fb64ceeSNeilBrown  * Routine for encoding the result of a "seqid-mutating" NFSv4 operation.  This
12127fb64ceeSNeilBrown  * is where sequence id's are incremented, and the replay cache is filled.
12137fb64ceeSNeilBrown  * Note that we increment sequence id's here, at the last moment, so we're sure
12147fb64ceeSNeilBrown  * we know whether the error to be returned is a sequence id mutating error.
12151da177e4SLinus Torvalds  */
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds #define ENCODE_SEQID_OP_TAIL(stateowner) do {			\
12181da177e4SLinus Torvalds 	if (seqid_mutating_err(nfserr) && stateowner) { 	\
12191da177e4SLinus Torvalds 		stateowner->so_seqid++;				\
12201da177e4SLinus Torvalds 		stateowner->so_replay.rp_status = nfserr;   	\
12211da177e4SLinus Torvalds 		stateowner->so_replay.rp_buflen = 		\
12221da177e4SLinus Torvalds 			  (((char *)(resp)->p - (char *)save)); \
12231da177e4SLinus Torvalds 		memcpy(stateowner->so_replay.rp_buf, save,      \
12241da177e4SLinus Torvalds  			stateowner->so_replay.rp_buflen); 	\
12251da177e4SLinus Torvalds 	} } while (0);
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds static u32 nfs4_ftypes[16] = {
12291da177e4SLinus Torvalds         NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
12301da177e4SLinus Torvalds         NF4DIR,  NF4BAD,  NF4BLK, NF4BAD,
12311da177e4SLinus Torvalds         NF4REG,  NF4BAD,  NF4LNK, NF4BAD,
12321da177e4SLinus Torvalds         NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
12331da177e4SLinus Torvalds };
12341da177e4SLinus Torvalds 
12351da177e4SLinus Torvalds static int
12361da177e4SLinus Torvalds nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
12371da177e4SLinus Torvalds 			u32 **p, int *buflen)
12381da177e4SLinus Torvalds {
12391da177e4SLinus Torvalds 	int status;
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds 	if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
12421da177e4SLinus Torvalds 		return nfserr_resource;
12431da177e4SLinus Torvalds 	if (whotype != NFS4_ACL_WHO_NAMED)
12441da177e4SLinus Torvalds 		status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
12451da177e4SLinus Torvalds 	else if (group)
12461da177e4SLinus Torvalds 		status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
12471da177e4SLinus Torvalds 	else
12481da177e4SLinus Torvalds 		status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
12491da177e4SLinus Torvalds 	if (status < 0)
12501da177e4SLinus Torvalds 		return nfserrno(status);
12511da177e4SLinus Torvalds 	*p = xdr_encode_opaque(*p, NULL, status);
12521da177e4SLinus Torvalds 	*buflen -= (XDR_QUADLEN(status) << 2) + 4;
12531da177e4SLinus Torvalds 	BUG_ON(*buflen < 0);
12541da177e4SLinus Torvalds 	return 0;
12551da177e4SLinus Torvalds }
12561da177e4SLinus Torvalds 
12571da177e4SLinus Torvalds static inline int
12581da177e4SLinus Torvalds nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen)
12591da177e4SLinus Torvalds {
12601da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
12611da177e4SLinus Torvalds }
12621da177e4SLinus Torvalds 
12631da177e4SLinus Torvalds static inline int
12641da177e4SLinus Torvalds nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen)
12651da177e4SLinus Torvalds {
12661da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds static inline int
12701da177e4SLinus Torvalds nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
12711da177e4SLinus Torvalds 		u32 **p, int *buflen)
12721da177e4SLinus Torvalds {
12731da177e4SLinus Torvalds 	return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
12741da177e4SLinus Torvalds }
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 
12771da177e4SLinus Torvalds /*
12781da177e4SLinus Torvalds  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
12791da177e4SLinus Torvalds  * ourselves.
12801da177e4SLinus Torvalds  *
12811da177e4SLinus Torvalds  * @countp is the buffer size in _words_; upon successful return this becomes
12821da177e4SLinus Torvalds  * replaced with the number of words written.
12831da177e4SLinus Torvalds  */
12841da177e4SLinus Torvalds int
12851da177e4SLinus Torvalds nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
12861da177e4SLinus Torvalds 		struct dentry *dentry, u32 *buffer, int *countp, u32 *bmval,
12871da177e4SLinus Torvalds 		struct svc_rqst *rqstp)
12881da177e4SLinus Torvalds {
12891da177e4SLinus Torvalds 	u32 bmval0 = bmval[0];
12901da177e4SLinus Torvalds 	u32 bmval1 = bmval[1];
12911da177e4SLinus Torvalds 	struct kstat stat;
12921da177e4SLinus Torvalds 	struct svc_fh tempfh;
12931da177e4SLinus Torvalds 	struct kstatfs statfs;
12941da177e4SLinus Torvalds 	int buflen = *countp << 2;
12951da177e4SLinus Torvalds 	u32 *attrlenp;
12961da177e4SLinus Torvalds 	u32 dummy;
12971da177e4SLinus Torvalds 	u64 dummy64;
12981da177e4SLinus Torvalds 	u32 *p = buffer;
12991da177e4SLinus Torvalds 	int status;
13001da177e4SLinus Torvalds 	int aclsupport = 0;
13011da177e4SLinus Torvalds 	struct nfs4_acl *acl = NULL;
13021da177e4SLinus Torvalds 
13031da177e4SLinus Torvalds 	BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
13041da177e4SLinus Torvalds 	BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
13051da177e4SLinus Torvalds 	BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 	status = vfs_getattr(exp->ex_mnt, dentry, &stat);
13081da177e4SLinus Torvalds 	if (status)
13091da177e4SLinus Torvalds 		goto out_nfserr;
13101da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL)) ||
13111da177e4SLinus Torvalds 	    (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
13121da177e4SLinus Torvalds 		       FATTR4_WORD1_SPACE_TOTAL))) {
13131da177e4SLinus Torvalds 		status = vfs_statfs(dentry->d_inode->i_sb, &statfs);
13141da177e4SLinus Torvalds 		if (status)
13151da177e4SLinus Torvalds 			goto out_nfserr;
13161da177e4SLinus Torvalds 	}
13171da177e4SLinus Torvalds 	if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
13181da177e4SLinus Torvalds 		fh_init(&tempfh, NFS4_FHSIZE);
13191da177e4SLinus Torvalds 		status = fh_compose(&tempfh, exp, dentry, NULL);
13201da177e4SLinus Torvalds 		if (status)
13211da177e4SLinus Torvalds 			goto out;
13221da177e4SLinus Torvalds 		fhp = &tempfh;
13231da177e4SLinus Torvalds 	}
13241da177e4SLinus Torvalds 	if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
13251da177e4SLinus Torvalds 			| FATTR4_WORD0_SUPPORTED_ATTRS)) {
13261da177e4SLinus Torvalds 		status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
13271da177e4SLinus Torvalds 		aclsupport = (status == 0);
13281da177e4SLinus Torvalds 		if (bmval0 & FATTR4_WORD0_ACL) {
13291da177e4SLinus Torvalds 			if (status == -EOPNOTSUPP)
13301da177e4SLinus Torvalds 				bmval0 &= ~FATTR4_WORD0_ACL;
13311da177e4SLinus Torvalds 			else if (status == -EINVAL) {
13321da177e4SLinus Torvalds 				status = nfserr_attrnotsupp;
13331da177e4SLinus Torvalds 				goto out;
13341da177e4SLinus Torvalds 			} else if (status != 0)
13351da177e4SLinus Torvalds 				goto out_nfserr;
13361da177e4SLinus Torvalds 		}
13371da177e4SLinus Torvalds 	}
13381da177e4SLinus Torvalds 	if ((buflen -= 16) < 0)
13391da177e4SLinus Torvalds 		goto out_resource;
13401da177e4SLinus Torvalds 
13411da177e4SLinus Torvalds 	WRITE32(2);
13421da177e4SLinus Torvalds 	WRITE32(bmval0);
13431da177e4SLinus Torvalds 	WRITE32(bmval1);
13441da177e4SLinus Torvalds 	attrlenp = p++;                /* to be backfilled later */
13451da177e4SLinus Torvalds 
13461da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
13471da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
13481da177e4SLinus Torvalds 			goto out_resource;
13491da177e4SLinus Torvalds 		WRITE32(2);
13501da177e4SLinus Torvalds 		WRITE32(aclsupport ?
13511da177e4SLinus Torvalds 			NFSD_SUPPORTED_ATTRS_WORD0 :
13521da177e4SLinus Torvalds 			NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
13531da177e4SLinus Torvalds 		WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
13541da177e4SLinus Torvalds 	}
13551da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_TYPE) {
13561da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
13571da177e4SLinus Torvalds 			goto out_resource;
13581da177e4SLinus Torvalds 		dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12];
13591da177e4SLinus Torvalds 		if (dummy == NF4BAD)
13601da177e4SLinus Torvalds 			goto out_serverfault;
13611da177e4SLinus Torvalds 		WRITE32(dummy);
13621da177e4SLinus Torvalds 	}
13631da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
13641da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
13651da177e4SLinus Torvalds 			goto out_resource;
136649640001SNeilBrown 		if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
1367e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT);
136849640001SNeilBrown 		else
1369e34ac862SNeilBrown 			WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
13701da177e4SLinus Torvalds 	}
13711da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHANGE) {
13721da177e4SLinus Torvalds 		/*
13731da177e4SLinus Torvalds 		 * Note: This _must_ be consistent with the scheme for writing
13741da177e4SLinus Torvalds 		 * change_info, so any changes made here must be reflected there
13751da177e4SLinus Torvalds 		 * as well.  (See xdr4.h:set_change_info() and the WRITECINFO()
13761da177e4SLinus Torvalds 		 * macro above.)
13771da177e4SLinus Torvalds 		 */
13781da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
13791da177e4SLinus Torvalds 			goto out_resource;
13801da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_sec);
13811da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_nsec);
13821da177e4SLinus Torvalds 	}
13831da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SIZE) {
13841da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
13851da177e4SLinus Torvalds 			goto out_resource;
13861da177e4SLinus Torvalds 		WRITE64(stat.size);
13871da177e4SLinus Torvalds 	}
13881da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
13891da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
13901da177e4SLinus Torvalds 			goto out_resource;
13911da177e4SLinus Torvalds 		WRITE32(1);
13921da177e4SLinus Torvalds 	}
13931da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
13941da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
13951da177e4SLinus Torvalds 			goto out_resource;
13961da177e4SLinus Torvalds 		WRITE32(1);
13971da177e4SLinus Torvalds 	}
13981da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
13991da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14001da177e4SLinus Torvalds 			goto out_resource;
14011da177e4SLinus Torvalds 		WRITE32(0);
14021da177e4SLinus Torvalds 	}
14031da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FSID) {
14041da177e4SLinus Torvalds 		if ((buflen -= 16) < 0)
14051da177e4SLinus Torvalds 			goto out_resource;
14061da177e4SLinus Torvalds 		if (is_fsid(fhp, rqstp->rq_reffh)) {
14071da177e4SLinus Torvalds 			WRITE64((u64)exp->ex_fsid);
14081da177e4SLinus Torvalds 			WRITE64((u64)0);
14091da177e4SLinus Torvalds 		} else {
14101da177e4SLinus Torvalds 			WRITE32(0);
14111da177e4SLinus Torvalds 			WRITE32(MAJOR(stat.dev));
14121da177e4SLinus Torvalds 			WRITE32(0);
14131da177e4SLinus Torvalds 			WRITE32(MINOR(stat.dev));
14141da177e4SLinus Torvalds 		}
14151da177e4SLinus Torvalds 	}
14161da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
14171da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14181da177e4SLinus Torvalds 			goto out_resource;
14191da177e4SLinus Torvalds 		WRITE32(0);
14201da177e4SLinus Torvalds 	}
14211da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
14221da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14231da177e4SLinus Torvalds 			goto out_resource;
14241da177e4SLinus Torvalds 		WRITE32(NFSD_LEASE_TIME);
14251da177e4SLinus Torvalds 	}
14261da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
14271da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14281da177e4SLinus Torvalds 			goto out_resource;
14291da177e4SLinus Torvalds 		WRITE32(0);
14301da177e4SLinus Torvalds 	}
14311da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACL) {
14321da177e4SLinus Torvalds 		struct nfs4_ace *ace;
14331da177e4SLinus Torvalds 		struct list_head *h;
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds 		if (acl == NULL) {
14361da177e4SLinus Torvalds 			if ((buflen -= 4) < 0)
14371da177e4SLinus Torvalds 				goto out_resource;
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds 			WRITE32(0);
14401da177e4SLinus Torvalds 			goto out_acl;
14411da177e4SLinus Torvalds 		}
14421da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14431da177e4SLinus Torvalds 			goto out_resource;
14441da177e4SLinus Torvalds 		WRITE32(acl->naces);
14451da177e4SLinus Torvalds 
14461da177e4SLinus Torvalds 		list_for_each(h, &acl->ace_head) {
14471da177e4SLinus Torvalds 			ace = list_entry(h, struct nfs4_ace, l_ace);
14481da177e4SLinus Torvalds 
14491da177e4SLinus Torvalds 			if ((buflen -= 4*3) < 0)
14501da177e4SLinus Torvalds 				goto out_resource;
14511da177e4SLinus Torvalds 			WRITE32(ace->type);
14521da177e4SLinus Torvalds 			WRITE32(ace->flag);
14531da177e4SLinus Torvalds 			WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
14541da177e4SLinus Torvalds 			status = nfsd4_encode_aclname(rqstp, ace->whotype,
14551da177e4SLinus Torvalds 				ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
14561da177e4SLinus Torvalds 				&p, &buflen);
14571da177e4SLinus Torvalds 			if (status == nfserr_resource)
14581da177e4SLinus Torvalds 				goto out_resource;
14591da177e4SLinus Torvalds 			if (status)
14601da177e4SLinus Torvalds 				goto out;
14611da177e4SLinus Torvalds 		}
14621da177e4SLinus Torvalds 	}
14631da177e4SLinus Torvalds out_acl:
14641da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
14651da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14661da177e4SLinus Torvalds 			goto out_resource;
14671da177e4SLinus Torvalds 		WRITE32(aclsupport ?
14681da177e4SLinus Torvalds 			ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
14691da177e4SLinus Torvalds 	}
14701da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CANSETTIME) {
14711da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14721da177e4SLinus Torvalds 			goto out_resource;
14731da177e4SLinus Torvalds 		WRITE32(1);
14741da177e4SLinus Torvalds 	}
14751da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
14761da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14771da177e4SLinus Torvalds 			goto out_resource;
14781da177e4SLinus Torvalds 		WRITE32(1);
14791da177e4SLinus Torvalds 	}
14801da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
14811da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14821da177e4SLinus Torvalds 			goto out_resource;
14831da177e4SLinus Torvalds 		WRITE32(1);
14841da177e4SLinus Torvalds 	}
14851da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
14861da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
14871da177e4SLinus Torvalds 			goto out_resource;
14881da177e4SLinus Torvalds 		WRITE32(1);
14891da177e4SLinus Torvalds 	}
14901da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
14911da177e4SLinus Torvalds 		buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4;
14921da177e4SLinus Torvalds 		if (buflen < 0)
14931da177e4SLinus Torvalds 			goto out_resource;
14941da177e4SLinus Torvalds 		WRITE32(fhp->fh_handle.fh_size);
14951da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
14961da177e4SLinus Torvalds 	}
14971da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILEID) {
14981da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
14991da177e4SLinus Torvalds 			goto out_resource;
15001da177e4SLinus Torvalds 		WRITE64((u64) stat.ino);
15011da177e4SLinus Torvalds 	}
15021da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
15031da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15041da177e4SLinus Torvalds 			goto out_resource;
15051da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
15061da177e4SLinus Torvalds 	}
15071da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_FREE) {
15081da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15091da177e4SLinus Torvalds 			goto out_resource;
15101da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_ffree);
15111da177e4SLinus Torvalds 	}
15121da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
15131da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15141da177e4SLinus Torvalds 			goto out_resource;
15151da177e4SLinus Torvalds 		WRITE64((u64) statfs.f_files);
15161da177e4SLinus Torvalds 	}
15171da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
15181da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
15191da177e4SLinus Torvalds 			goto out_resource;
15201da177e4SLinus Torvalds 		WRITE32(1);
15211da177e4SLinus Torvalds 	}
15221da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
15231da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15241da177e4SLinus Torvalds 			goto out_resource;
15251da177e4SLinus Torvalds 		WRITE64(~(u64)0);
15261da177e4SLinus Torvalds 	}
15271da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXLINK) {
15281da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
15291da177e4SLinus Torvalds 			goto out_resource;
15301da177e4SLinus Torvalds 		WRITE32(255);
15311da177e4SLinus Torvalds 	}
15321da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXNAME) {
15331da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
15341da177e4SLinus Torvalds 			goto out_resource;
15351da177e4SLinus Torvalds 		WRITE32(~(u32) 0);
15361da177e4SLinus Torvalds 	}
15371da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXREAD) {
15381da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15391da177e4SLinus Torvalds 			goto out_resource;
15401da177e4SLinus Torvalds 		WRITE64((u64) NFSSVC_MAXBLKSIZE);
15411da177e4SLinus Torvalds 	}
15421da177e4SLinus Torvalds 	if (bmval0 & FATTR4_WORD0_MAXWRITE) {
15431da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15441da177e4SLinus Torvalds 			goto out_resource;
15451da177e4SLinus Torvalds 		WRITE64((u64) NFSSVC_MAXBLKSIZE);
15461da177e4SLinus Torvalds 	}
15471da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MODE) {
15481da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
15491da177e4SLinus Torvalds 			goto out_resource;
15501da177e4SLinus Torvalds 		WRITE32(stat.mode & S_IALLUGO);
15511da177e4SLinus Torvalds 	}
15521da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
15531da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
15541da177e4SLinus Torvalds 			goto out_resource;
15551da177e4SLinus Torvalds 		WRITE32(1);
15561da177e4SLinus Torvalds 	}
15571da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_NUMLINKS) {
15581da177e4SLinus Torvalds 		if ((buflen -= 4) < 0)
15591da177e4SLinus Torvalds 			goto out_resource;
15601da177e4SLinus Torvalds 		WRITE32(stat.nlink);
15611da177e4SLinus Torvalds 	}
15621da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER) {
15631da177e4SLinus Torvalds 		status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
15641da177e4SLinus Torvalds 		if (status == nfserr_resource)
15651da177e4SLinus Torvalds 			goto out_resource;
15661da177e4SLinus Torvalds 		if (status)
15671da177e4SLinus Torvalds 			goto out;
15681da177e4SLinus Torvalds 	}
15691da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
15701da177e4SLinus Torvalds 		status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
15711da177e4SLinus Torvalds 		if (status == nfserr_resource)
15721da177e4SLinus Torvalds 			goto out_resource;
15731da177e4SLinus Torvalds 		if (status)
15741da177e4SLinus Torvalds 			goto out;
15751da177e4SLinus Torvalds 	}
15761da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_RAWDEV) {
15771da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15781da177e4SLinus Torvalds 			goto out_resource;
15791da177e4SLinus Torvalds 		WRITE32((u32) MAJOR(stat.rdev));
15801da177e4SLinus Torvalds 		WRITE32((u32) MINOR(stat.rdev));
15811da177e4SLinus Torvalds 	}
15821da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
15831da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15841da177e4SLinus Torvalds 			goto out_resource;
15851da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
15861da177e4SLinus Torvalds 		WRITE64(dummy64);
15871da177e4SLinus Torvalds 	}
15881da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
15891da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15901da177e4SLinus Torvalds 			goto out_resource;
15911da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
15921da177e4SLinus Torvalds 		WRITE64(dummy64);
15931da177e4SLinus Torvalds 	}
15941da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
15951da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
15961da177e4SLinus Torvalds 			goto out_resource;
15971da177e4SLinus Torvalds 		dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
15981da177e4SLinus Torvalds 		WRITE64(dummy64);
15991da177e4SLinus Torvalds 	}
16001da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_SPACE_USED) {
16011da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
16021da177e4SLinus Torvalds 			goto out_resource;
16031da177e4SLinus Torvalds 		dummy64 = (u64)stat.blocks << 9;
16041da177e4SLinus Torvalds 		WRITE64(dummy64);
16051da177e4SLinus Torvalds 	}
16061da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
16071da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
16081da177e4SLinus Torvalds 			goto out_resource;
16091da177e4SLinus Torvalds 		WRITE32(0);
16101da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_sec);
16111da177e4SLinus Torvalds 		WRITE32(stat.atime.tv_nsec);
16121da177e4SLinus Torvalds 	}
16131da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
16141da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
16151da177e4SLinus Torvalds 			goto out_resource;
16161da177e4SLinus Torvalds 		WRITE32(0);
16171da177e4SLinus Torvalds 		WRITE32(1);
16181da177e4SLinus Torvalds 		WRITE32(0);
16191da177e4SLinus Torvalds 	}
16201da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
16211da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
16221da177e4SLinus Torvalds 			goto out_resource;
16231da177e4SLinus Torvalds 		WRITE32(0);
16241da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_sec);
16251da177e4SLinus Torvalds 		WRITE32(stat.ctime.tv_nsec);
16261da177e4SLinus Torvalds 	}
16271da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
16281da177e4SLinus Torvalds 		if ((buflen -= 12) < 0)
16291da177e4SLinus Torvalds 			goto out_resource;
16301da177e4SLinus Torvalds 		WRITE32(0);
16311da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_sec);
16321da177e4SLinus Torvalds 		WRITE32(stat.mtime.tv_nsec);
16331da177e4SLinus Torvalds 	}
16341da177e4SLinus Torvalds 	if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
16351da177e4SLinus Torvalds 		struct dentry *mnt_pnt, *mnt_root;
16361da177e4SLinus Torvalds 
16371da177e4SLinus Torvalds 		if ((buflen -= 8) < 0)
16381da177e4SLinus Torvalds                 	goto out_resource;
16391da177e4SLinus Torvalds 		mnt_root = exp->ex_mnt->mnt_root;
16401da177e4SLinus Torvalds 		if (mnt_root->d_inode == dentry->d_inode) {
16411da177e4SLinus Torvalds 			mnt_pnt = exp->ex_mnt->mnt_mountpoint;
16421da177e4SLinus Torvalds 			WRITE64((u64) mnt_pnt->d_inode->i_ino);
16431da177e4SLinus Torvalds 		} else
16441da177e4SLinus Torvalds                 	WRITE64((u64) stat.ino);
16451da177e4SLinus Torvalds 	}
16461da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
16471da177e4SLinus Torvalds 	*countp = p - buffer;
16481da177e4SLinus Torvalds 	status = nfs_ok;
16491da177e4SLinus Torvalds 
16501da177e4SLinus Torvalds out:
16511da177e4SLinus Torvalds 	nfs4_acl_free(acl);
16521da177e4SLinus Torvalds 	if (fhp == &tempfh)
16531da177e4SLinus Torvalds 		fh_put(&tempfh);
16541da177e4SLinus Torvalds 	return status;
16551da177e4SLinus Torvalds out_nfserr:
16561da177e4SLinus Torvalds 	status = nfserrno(status);
16571da177e4SLinus Torvalds 	goto out;
16581da177e4SLinus Torvalds out_resource:
16591da177e4SLinus Torvalds 	*countp = 0;
16601da177e4SLinus Torvalds 	status = nfserr_resource;
16611da177e4SLinus Torvalds 	goto out;
16621da177e4SLinus Torvalds out_serverfault:
16631da177e4SLinus Torvalds 	status = nfserr_serverfault;
16641da177e4SLinus Torvalds 	goto out;
16651da177e4SLinus Torvalds }
16661da177e4SLinus Torvalds 
16671da177e4SLinus Torvalds static int
16681da177e4SLinus Torvalds nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
16691da177e4SLinus Torvalds 		const char *name, int namlen, u32 *p, int *buflen)
16701da177e4SLinus Torvalds {
16711da177e4SLinus Torvalds 	struct svc_export *exp = cd->rd_fhp->fh_export;
16721da177e4SLinus Torvalds 	struct dentry *dentry;
16731da177e4SLinus Torvalds 	int nfserr;
16741da177e4SLinus Torvalds 
16751da177e4SLinus Torvalds 	dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
16761da177e4SLinus Torvalds 	if (IS_ERR(dentry))
16771da177e4SLinus Torvalds 		return nfserrno(PTR_ERR(dentry));
16781da177e4SLinus Torvalds 
16791da177e4SLinus Torvalds 	exp_get(exp);
16801da177e4SLinus Torvalds 	if (d_mountpoint(dentry)) {
16811da177e4SLinus Torvalds 		if (nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp)) {
16821da177e4SLinus Torvalds 		/*
16831da177e4SLinus Torvalds 		 * -EAGAIN is the only error returned from
16841da177e4SLinus Torvalds 		 * nfsd_cross_mnt() and it indicates that an
16851da177e4SLinus Torvalds 		 * up-call has  been initiated to fill in the export
16861da177e4SLinus Torvalds 		 * options on exp.  When the answer comes back,
16871da177e4SLinus Torvalds 		 * this call will be retried.
16881da177e4SLinus Torvalds 		 */
16891da177e4SLinus Torvalds 			nfserr = nfserr_dropit;
16901da177e4SLinus Torvalds 			goto out_put;
16911da177e4SLinus Torvalds 		}
16921da177e4SLinus Torvalds 
16931da177e4SLinus Torvalds 	}
16941da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
16951da177e4SLinus Torvalds 					cd->rd_rqstp);
16961da177e4SLinus Torvalds out_put:
16971da177e4SLinus Torvalds 	dput(dentry);
16981da177e4SLinus Torvalds 	exp_put(exp);
16991da177e4SLinus Torvalds 	return nfserr;
17001da177e4SLinus Torvalds }
17011da177e4SLinus Torvalds 
17021da177e4SLinus Torvalds static u32 *
17031da177e4SLinus Torvalds nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr)
17041da177e4SLinus Torvalds {
17051da177e4SLinus Torvalds 	u32 *attrlenp;
17061da177e4SLinus Torvalds 
17071da177e4SLinus Torvalds 	if (buflen < 6)
17081da177e4SLinus Torvalds 		return NULL;
17091da177e4SLinus Torvalds 	*p++ = htonl(2);
17101da177e4SLinus Torvalds 	*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
17111da177e4SLinus Torvalds 	*p++ = htonl(0);			 /* bmval1 */
17121da177e4SLinus Torvalds 
17131da177e4SLinus Torvalds 	attrlenp = p++;
17141da177e4SLinus Torvalds 	*p++ = nfserr;       /* no htonl */
17151da177e4SLinus Torvalds 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
17161da177e4SLinus Torvalds 	return p;
17171da177e4SLinus Torvalds }
17181da177e4SLinus Torvalds 
17191da177e4SLinus Torvalds static int
17201da177e4SLinus Torvalds nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
17211da177e4SLinus Torvalds 		    loff_t offset, ino_t ino, unsigned int d_type)
17221da177e4SLinus Torvalds {
17231da177e4SLinus Torvalds 	struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
17241da177e4SLinus Torvalds 	int buflen;
17251da177e4SLinus Torvalds 	u32 *p = cd->buffer;
17261da177e4SLinus Torvalds 	int nfserr = nfserr_toosmall;
17271da177e4SLinus Torvalds 
17281da177e4SLinus Torvalds 	/* In nfsv4, "." and ".." never make it onto the wire.. */
17291da177e4SLinus Torvalds 	if (name && isdotent(name, namlen)) {
17301da177e4SLinus Torvalds 		cd->common.err = nfs_ok;
17311da177e4SLinus Torvalds 		return 0;
17321da177e4SLinus Torvalds 	}
17331da177e4SLinus Torvalds 
17341da177e4SLinus Torvalds 	if (cd->offset)
17351da177e4SLinus Torvalds 		xdr_encode_hyper(cd->offset, (u64) offset);
17361da177e4SLinus Torvalds 
17371da177e4SLinus Torvalds 	buflen = cd->buflen - 4 - XDR_QUADLEN(namlen);
17381da177e4SLinus Torvalds 	if (buflen < 0)
17391da177e4SLinus Torvalds 		goto fail;
17401da177e4SLinus Torvalds 
17411da177e4SLinus Torvalds 	*p++ = xdr_one;                             /* mark entry present */
17421da177e4SLinus Torvalds 	cd->offset = p;                             /* remember pointer */
17431da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
17441da177e4SLinus Torvalds 	p = xdr_encode_array(p, name, namlen);      /* name length & name */
17451da177e4SLinus Torvalds 
17461da177e4SLinus Torvalds 	nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen);
17471da177e4SLinus Torvalds 	switch (nfserr) {
17481da177e4SLinus Torvalds 	case nfs_ok:
17491da177e4SLinus Torvalds 		p += buflen;
17501da177e4SLinus Torvalds 		break;
17511da177e4SLinus Torvalds 	case nfserr_resource:
17521da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
17531da177e4SLinus Torvalds 		goto fail;
17541da177e4SLinus Torvalds 	case nfserr_dropit:
17551da177e4SLinus Torvalds 		goto fail;
17561da177e4SLinus Torvalds 	default:
17571da177e4SLinus Torvalds 		/*
17581da177e4SLinus Torvalds 		 * If the client requested the RDATTR_ERROR attribute,
17591da177e4SLinus Torvalds 		 * we stuff the error code into this attribute
17601da177e4SLinus Torvalds 		 * and continue.  If this attribute was not requested,
17611da177e4SLinus Torvalds 		 * then in accordance with the spec, we fail the
17621da177e4SLinus Torvalds 		 * entire READDIR operation(!)
17631da177e4SLinus Torvalds 		 */
17641da177e4SLinus Torvalds 		if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
17651da177e4SLinus Torvalds 			goto fail;
17661da177e4SLinus Torvalds 		p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
176734081efcSFred Isaman 		if (p == NULL) {
176834081efcSFred Isaman 			nfserr = nfserr_toosmall;
17691da177e4SLinus Torvalds 			goto fail;
17701da177e4SLinus Torvalds 		}
177134081efcSFred Isaman 	}
17721da177e4SLinus Torvalds 	cd->buflen -= (p - cd->buffer);
17731da177e4SLinus Torvalds 	cd->buffer = p;
17741da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
17751da177e4SLinus Torvalds 	return 0;
17761da177e4SLinus Torvalds fail:
17771da177e4SLinus Torvalds 	cd->common.err = nfserr;
17781da177e4SLinus Torvalds 	return -EINVAL;
17791da177e4SLinus Torvalds }
17801da177e4SLinus Torvalds 
17811da177e4SLinus Torvalds static void
17821da177e4SLinus Torvalds nfsd4_encode_access(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_access *access)
17831da177e4SLinus Torvalds {
17841da177e4SLinus Torvalds 	ENCODE_HEAD;
17851da177e4SLinus Torvalds 
17861da177e4SLinus Torvalds 	if (!nfserr) {
17871da177e4SLinus Torvalds 		RESERVE_SPACE(8);
17881da177e4SLinus Torvalds 		WRITE32(access->ac_supported);
17891da177e4SLinus Torvalds 		WRITE32(access->ac_resp_access);
17901da177e4SLinus Torvalds 		ADJUST_ARGS();
17911da177e4SLinus Torvalds 	}
17921da177e4SLinus Torvalds }
17931da177e4SLinus Torvalds 
17941da177e4SLinus Torvalds static void
17951da177e4SLinus Torvalds nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_close *close)
17961da177e4SLinus Torvalds {
17971da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
17981da177e4SLinus Torvalds 
17991da177e4SLinus Torvalds 	if (!nfserr) {
18001da177e4SLinus Torvalds 		RESERVE_SPACE(sizeof(stateid_t));
18011da177e4SLinus Torvalds 		WRITE32(close->cl_stateid.si_generation);
18021da177e4SLinus Torvalds 		WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
18031da177e4SLinus Torvalds 		ADJUST_ARGS();
18041da177e4SLinus Torvalds 	}
18051da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
18061da177e4SLinus Torvalds }
18071da177e4SLinus Torvalds 
18081da177e4SLinus Torvalds 
18091da177e4SLinus Torvalds static void
18101da177e4SLinus Torvalds nfsd4_encode_commit(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_commit *commit)
18111da177e4SLinus Torvalds {
18121da177e4SLinus Torvalds 	ENCODE_HEAD;
18131da177e4SLinus Torvalds 
18141da177e4SLinus Torvalds 	if (!nfserr) {
18151da177e4SLinus Torvalds 		RESERVE_SPACE(8);
18161da177e4SLinus Torvalds 		WRITEMEM(commit->co_verf.data, 8);
18171da177e4SLinus Torvalds 		ADJUST_ARGS();
18181da177e4SLinus Torvalds 	}
18191da177e4SLinus Torvalds }
18201da177e4SLinus Torvalds 
18211da177e4SLinus Torvalds static void
18221da177e4SLinus Torvalds nfsd4_encode_create(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_create *create)
18231da177e4SLinus Torvalds {
18241da177e4SLinus Torvalds 	ENCODE_HEAD;
18251da177e4SLinus Torvalds 
18261da177e4SLinus Torvalds 	if (!nfserr) {
18271da177e4SLinus Torvalds 		RESERVE_SPACE(32);
18281da177e4SLinus Torvalds 		WRITECINFO(create->cr_cinfo);
18291da177e4SLinus Torvalds 		WRITE32(2);
18301da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[0]);
18311da177e4SLinus Torvalds 		WRITE32(create->cr_bmval[1]);
18321da177e4SLinus Torvalds 		ADJUST_ARGS();
18331da177e4SLinus Torvalds 	}
18341da177e4SLinus Torvalds }
18351da177e4SLinus Torvalds 
18361da177e4SLinus Torvalds static int
18371da177e4SLinus Torvalds nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_getattr *getattr)
18381da177e4SLinus Torvalds {
18391da177e4SLinus Torvalds 	struct svc_fh *fhp = getattr->ga_fhp;
18401da177e4SLinus Torvalds 	int buflen;
18411da177e4SLinus Torvalds 
18421da177e4SLinus Torvalds 	if (nfserr)
18431da177e4SLinus Torvalds 		return nfserr;
18441da177e4SLinus Torvalds 
18451da177e4SLinus Torvalds 	buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2);
18461da177e4SLinus Torvalds 	nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
18471da177e4SLinus Torvalds 				    resp->p, &buflen, getattr->ga_bmval,
18481da177e4SLinus Torvalds 				    resp->rqstp);
18491da177e4SLinus Torvalds 
18501da177e4SLinus Torvalds 	if (!nfserr)
18511da177e4SLinus Torvalds 		resp->p += buflen;
18521da177e4SLinus Torvalds 	return nfserr;
18531da177e4SLinus Torvalds }
18541da177e4SLinus Torvalds 
18551da177e4SLinus Torvalds static void
18561da177e4SLinus Torvalds nfsd4_encode_getfh(struct nfsd4_compoundres *resp, int nfserr, struct svc_fh *fhp)
18571da177e4SLinus Torvalds {
18581da177e4SLinus Torvalds 	unsigned int len;
18591da177e4SLinus Torvalds 	ENCODE_HEAD;
18601da177e4SLinus Torvalds 
18611da177e4SLinus Torvalds 	if (!nfserr) {
18621da177e4SLinus Torvalds 		len = fhp->fh_handle.fh_size;
18631da177e4SLinus Torvalds 		RESERVE_SPACE(len + 4);
18641da177e4SLinus Torvalds 		WRITE32(len);
18651da177e4SLinus Torvalds 		WRITEMEM(&fhp->fh_handle.fh_base, len);
18661da177e4SLinus Torvalds 		ADJUST_ARGS();
18671da177e4SLinus Torvalds 	}
18681da177e4SLinus Torvalds }
18691da177e4SLinus Torvalds 
18701da177e4SLinus Torvalds /*
18711da177e4SLinus Torvalds * Including all fields other than the name, a LOCK4denied structure requires
18721da177e4SLinus Torvalds *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
18731da177e4SLinus Torvalds */
18741da177e4SLinus Torvalds static void
18751da177e4SLinus Torvalds nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
18761da177e4SLinus Torvalds {
18771da177e4SLinus Torvalds 	ENCODE_HEAD;
18781da177e4SLinus Torvalds 
18791da177e4SLinus Torvalds 	RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
18801da177e4SLinus Torvalds 	WRITE64(ld->ld_start);
18811da177e4SLinus Torvalds 	WRITE64(ld->ld_length);
18821da177e4SLinus Torvalds 	WRITE32(ld->ld_type);
18831da177e4SLinus Torvalds 	if (ld->ld_sop) {
18841da177e4SLinus Torvalds 		WRITEMEM(&ld->ld_clientid, 8);
18851da177e4SLinus Torvalds 		WRITE32(ld->ld_sop->so_owner.len);
18861da177e4SLinus Torvalds 		WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
18871da177e4SLinus Torvalds 		kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
18881da177e4SLinus Torvalds 	}  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
18891da177e4SLinus Torvalds 		WRITE64((u64)0); /* clientid */
18901da177e4SLinus Torvalds 		WRITE32(0); /* length of owner name */
18911da177e4SLinus Torvalds 	}
18921da177e4SLinus Torvalds 	ADJUST_ARGS();
18931da177e4SLinus Torvalds }
18941da177e4SLinus Torvalds 
18951da177e4SLinus Torvalds static void
18961da177e4SLinus Torvalds nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock)
18971da177e4SLinus Torvalds {
18981da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
18991da177e4SLinus Torvalds 
19001da177e4SLinus Torvalds 	if (!nfserr) {
19011da177e4SLinus Torvalds 		RESERVE_SPACE(4 + sizeof(stateid_t));
19021da177e4SLinus Torvalds 		WRITE32(lock->lk_resp_stateid.si_generation);
19031da177e4SLinus Torvalds 		WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
19041da177e4SLinus Torvalds 		ADJUST_ARGS();
19051da177e4SLinus Torvalds 	} else if (nfserr == nfserr_denied)
19061da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lock->lk_denied);
19071da177e4SLinus Torvalds 
19083a65588aSJ. Bruce Fields 	ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
19091da177e4SLinus Torvalds }
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds static void
19121da177e4SLinus Torvalds nfsd4_encode_lockt(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lockt *lockt)
19131da177e4SLinus Torvalds {
19141da177e4SLinus Torvalds 	if (nfserr == nfserr_denied)
19151da177e4SLinus Torvalds 		nfsd4_encode_lock_denied(resp, &lockt->lt_denied);
19161da177e4SLinus Torvalds }
19171da177e4SLinus Torvalds 
19181da177e4SLinus Torvalds static void
19191da177e4SLinus Torvalds nfsd4_encode_locku(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_locku *locku)
19201da177e4SLinus Torvalds {
19211da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
19221da177e4SLinus Torvalds 
19231da177e4SLinus Torvalds 	if (!nfserr) {
19241da177e4SLinus Torvalds 		RESERVE_SPACE(sizeof(stateid_t));
19251da177e4SLinus Torvalds 		WRITE32(locku->lu_stateid.si_generation);
19261da177e4SLinus Torvalds 		WRITEMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t));
19271da177e4SLinus Torvalds 		ADJUST_ARGS();
19281da177e4SLinus Torvalds 	}
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
19311da177e4SLinus Torvalds }
19321da177e4SLinus Torvalds 
19331da177e4SLinus Torvalds 
19341da177e4SLinus Torvalds static void
19351da177e4SLinus Torvalds nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link *link)
19361da177e4SLinus Torvalds {
19371da177e4SLinus Torvalds 	ENCODE_HEAD;
19381da177e4SLinus Torvalds 
19391da177e4SLinus Torvalds 	if (!nfserr) {
19401da177e4SLinus Torvalds 		RESERVE_SPACE(20);
19411da177e4SLinus Torvalds 		WRITECINFO(link->li_cinfo);
19421da177e4SLinus Torvalds 		ADJUST_ARGS();
19431da177e4SLinus Torvalds 	}
19441da177e4SLinus Torvalds }
19451da177e4SLinus Torvalds 
19461da177e4SLinus Torvalds 
19471da177e4SLinus Torvalds static void
19481da177e4SLinus Torvalds nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open *open)
19491da177e4SLinus Torvalds {
19501da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds 	if (nfserr)
19531da177e4SLinus Torvalds 		goto out;
19541da177e4SLinus Torvalds 
19551da177e4SLinus Torvalds 	RESERVE_SPACE(36 + sizeof(stateid_t));
19561da177e4SLinus Torvalds 	WRITE32(open->op_stateid.si_generation);
19571da177e4SLinus Torvalds 	WRITEMEM(&open->op_stateid.si_opaque, sizeof(stateid_opaque_t));
19581da177e4SLinus Torvalds 	WRITECINFO(open->op_cinfo);
19591da177e4SLinus Torvalds 	WRITE32(open->op_rflags);
19601da177e4SLinus Torvalds 	WRITE32(2);
19611da177e4SLinus Torvalds 	WRITE32(open->op_bmval[0]);
19621da177e4SLinus Torvalds 	WRITE32(open->op_bmval[1]);
19631da177e4SLinus Torvalds 	WRITE32(open->op_delegate_type);
19641da177e4SLinus Torvalds 	ADJUST_ARGS();
19651da177e4SLinus Torvalds 
19661da177e4SLinus Torvalds 	switch (open->op_delegate_type) {
19671da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_NONE:
19681da177e4SLinus Torvalds 		break;
19691da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_READ:
19701da177e4SLinus Torvalds 		RESERVE_SPACE(20 + sizeof(stateid_t));
19711da177e4SLinus Torvalds 		WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
19727b190fecSNeilBrown 		WRITE32(open->op_recall);
19731da177e4SLinus Torvalds 
19741da177e4SLinus Torvalds 		/*
19751da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
19761da177e4SLinus Torvalds 		 */
19771da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
19781da177e4SLinus Torvalds 		WRITE32(0);
19791da177e4SLinus Torvalds 		WRITE32(0);
19801da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
19811da177e4SLinus Torvalds 		ADJUST_ARGS();
19821da177e4SLinus Torvalds 		break;
19831da177e4SLinus Torvalds 	case NFS4_OPEN_DELEGATE_WRITE:
19841da177e4SLinus Torvalds 		RESERVE_SPACE(32 + sizeof(stateid_t));
19851da177e4SLinus Torvalds 		WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
19861da177e4SLinus Torvalds 		WRITE32(0);
19871da177e4SLinus Torvalds 
19881da177e4SLinus Torvalds 		/*
19891da177e4SLinus Torvalds 		 * TODO: space_limit's in delegations
19901da177e4SLinus Torvalds 		 */
19911da177e4SLinus Torvalds 		WRITE32(NFS4_LIMIT_SIZE);
19921da177e4SLinus Torvalds 		WRITE32(~(u32)0);
19931da177e4SLinus Torvalds 		WRITE32(~(u32)0);
19941da177e4SLinus Torvalds 
19951da177e4SLinus Torvalds 		/*
19961da177e4SLinus Torvalds 		 * TODO: ACE's in delegations
19971da177e4SLinus Torvalds 		 */
19981da177e4SLinus Torvalds 		WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
19991da177e4SLinus Torvalds 		WRITE32(0);
20001da177e4SLinus Torvalds 		WRITE32(0);
20011da177e4SLinus Torvalds 		WRITE32(0);   /* XXX: is NULL principal ok? */
20021da177e4SLinus Torvalds 		ADJUST_ARGS();
20031da177e4SLinus Torvalds 		break;
20041da177e4SLinus Torvalds 	default:
20051da177e4SLinus Torvalds 		BUG();
20061da177e4SLinus Torvalds 	}
20071da177e4SLinus Torvalds 	/* XXX save filehandle here */
20081da177e4SLinus Torvalds out:
20091da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(open->op_stateowner);
20101da177e4SLinus Torvalds }
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds static void
20131da177e4SLinus Torvalds nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_confirm *oc)
20141da177e4SLinus Torvalds {
20151da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
20161da177e4SLinus Torvalds 
20171da177e4SLinus Torvalds 	if (!nfserr) {
20181da177e4SLinus Torvalds 		RESERVE_SPACE(sizeof(stateid_t));
20191da177e4SLinus Torvalds 		WRITE32(oc->oc_resp_stateid.si_generation);
20201da177e4SLinus Torvalds 		WRITEMEM(&oc->oc_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
20211da177e4SLinus Torvalds 		ADJUST_ARGS();
20221da177e4SLinus Torvalds 	}
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
20251da177e4SLinus Torvalds }
20261da177e4SLinus Torvalds 
20271da177e4SLinus Torvalds static void
20281da177e4SLinus Torvalds nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_downgrade *od)
20291da177e4SLinus Torvalds {
20301da177e4SLinus Torvalds 	ENCODE_SEQID_OP_HEAD;
20311da177e4SLinus Torvalds 
20321da177e4SLinus Torvalds 	if (!nfserr) {
20331da177e4SLinus Torvalds 		RESERVE_SPACE(sizeof(stateid_t));
20341da177e4SLinus Torvalds 		WRITE32(od->od_stateid.si_generation);
20351da177e4SLinus Torvalds 		WRITEMEM(&od->od_stateid.si_opaque, sizeof(stateid_opaque_t));
20361da177e4SLinus Torvalds 		ADJUST_ARGS();
20371da177e4SLinus Torvalds 	}
20381da177e4SLinus Torvalds 
20391da177e4SLinus Torvalds 	ENCODE_SEQID_OP_TAIL(od->od_stateowner);
20401da177e4SLinus Torvalds }
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds static int
20431da177e4SLinus Torvalds nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read)
20441da177e4SLinus Torvalds {
20451da177e4SLinus Torvalds 	u32 eof;
20461da177e4SLinus Torvalds 	int v, pn;
20471da177e4SLinus Torvalds 	unsigned long maxcount;
20481da177e4SLinus Torvalds 	long len;
20491da177e4SLinus Torvalds 	ENCODE_HEAD;
20501da177e4SLinus Torvalds 
20511da177e4SLinus Torvalds 	if (nfserr)
20521da177e4SLinus Torvalds 		return nfserr;
20531da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
20541da177e4SLinus Torvalds 		return nfserr_resource;
20551da177e4SLinus Torvalds 
20561da177e4SLinus Torvalds 	RESERVE_SPACE(8); /* eof flag and byte count */
20571da177e4SLinus Torvalds 
20581da177e4SLinus Torvalds 	maxcount = NFSSVC_MAXBLKSIZE;
20591da177e4SLinus Torvalds 	if (maxcount > read->rd_length)
20601da177e4SLinus Torvalds 		maxcount = read->rd_length;
20611da177e4SLinus Torvalds 
20621da177e4SLinus Torvalds 	len = maxcount;
20631da177e4SLinus Torvalds 	v = 0;
20641da177e4SLinus Torvalds 	while (len > 0) {
20651da177e4SLinus Torvalds 		pn = resp->rqstp->rq_resused;
20661da177e4SLinus Torvalds 		svc_take_page(resp->rqstp);
20671da177e4SLinus Torvalds 		read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]);
20681da177e4SLinus Torvalds 		read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE;
20691da177e4SLinus Torvalds 		v++;
20701da177e4SLinus Torvalds 		len -= PAGE_SIZE;
20711da177e4SLinus Torvalds 	}
20721da177e4SLinus Torvalds 	read->rd_vlen = v;
20731da177e4SLinus Torvalds 
20741da177e4SLinus Torvalds 	nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp,
20751da177e4SLinus Torvalds 			read->rd_offset, read->rd_iov, read->rd_vlen,
20761da177e4SLinus Torvalds 			&maxcount);
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds 	if (nfserr == nfserr_symlink)
20791da177e4SLinus Torvalds 		nfserr = nfserr_inval;
20801da177e4SLinus Torvalds 	if (nfserr)
20811da177e4SLinus Torvalds 		return nfserr;
20821da177e4SLinus Torvalds 	eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size);
20831da177e4SLinus Torvalds 
20841da177e4SLinus Torvalds 	WRITE32(eof);
20851da177e4SLinus Torvalds 	WRITE32(maxcount);
20861da177e4SLinus Torvalds 	ADJUST_ARGS();
2087*6ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
2088*6ed6deccSNeilBrown 					- (char*)resp->xbuf->head[0].iov_base;
20891da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
20901da177e4SLinus Torvalds 
2091*6ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
2092*6ed6deccSNeilBrown 	resp->rqstp->rq_restailpage = 0;
2093*6ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
20941da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
20951da177e4SLinus Torvalds 	if (maxcount&3) {
2096*6ed6deccSNeilBrown 		RESERVE_SPACE(4);
2097*6ed6deccSNeilBrown 		WRITE32(0);
20981da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
20991da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
2100*6ed6deccSNeilBrown 		ADJUST_ARGS();
21011da177e4SLinus Torvalds 	}
21021da177e4SLinus Torvalds 	return 0;
21031da177e4SLinus Torvalds }
21041da177e4SLinus Torvalds 
21051da177e4SLinus Torvalds static int
21061da177e4SLinus Torvalds nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink)
21071da177e4SLinus Torvalds {
21081da177e4SLinus Torvalds 	int maxcount;
21091da177e4SLinus Torvalds 	char *page;
21101da177e4SLinus Torvalds 	ENCODE_HEAD;
21111da177e4SLinus Torvalds 
21121da177e4SLinus Torvalds 	if (nfserr)
21131da177e4SLinus Torvalds 		return nfserr;
21141da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
21151da177e4SLinus Torvalds 		return nfserr_resource;
21161da177e4SLinus Torvalds 
21171da177e4SLinus Torvalds 	svc_take_page(resp->rqstp);
21181da177e4SLinus Torvalds 	page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
21191da177e4SLinus Torvalds 
21201da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
21211da177e4SLinus Torvalds 	RESERVE_SPACE(4);
21221da177e4SLinus Torvalds 
21231da177e4SLinus Torvalds 	/*
21241da177e4SLinus Torvalds 	 * XXX: By default, the ->readlink() VFS op will truncate symlinks
21251da177e4SLinus Torvalds 	 * if they would overflow the buffer.  Is this kosher in NFSv4?  If
21261da177e4SLinus Torvalds 	 * not, one easy fix is: if ->readlink() precisely fills the buffer,
21271da177e4SLinus Torvalds 	 * assume that truncation occurred, and return NFS4ERR_RESOURCE.
21281da177e4SLinus Torvalds 	 */
21291da177e4SLinus Torvalds 	nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
21301da177e4SLinus Torvalds 	if (nfserr == nfserr_isdir)
21311da177e4SLinus Torvalds 		return nfserr_inval;
21321da177e4SLinus Torvalds 	if (nfserr)
21331da177e4SLinus Torvalds 		return nfserr;
21341da177e4SLinus Torvalds 
21351da177e4SLinus Torvalds 	WRITE32(maxcount);
21361da177e4SLinus Torvalds 	ADJUST_ARGS();
2137*6ed6deccSNeilBrown 	resp->xbuf->head[0].iov_len = (char*)p
2138*6ed6deccSNeilBrown 				- (char*)resp->xbuf->head[0].iov_base;
21391da177e4SLinus Torvalds 	resp->xbuf->page_len = maxcount;
2140*6ed6deccSNeilBrown 
2141*6ed6deccSNeilBrown 	/* Use rest of head for padding and remaining ops: */
2142*6ed6deccSNeilBrown 	resp->rqstp->rq_restailpage = 0;
2143*6ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_base = p;
2144*6ed6deccSNeilBrown 	resp->xbuf->tail[0].iov_len = 0;
21451da177e4SLinus Torvalds 	if (maxcount&3) {
2146*6ed6deccSNeilBrown 		RESERVE_SPACE(4);
2147*6ed6deccSNeilBrown 		WRITE32(0);
21481da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_base += maxcount&3;
21491da177e4SLinus Torvalds 		resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
2150*6ed6deccSNeilBrown 		ADJUST_ARGS();
21511da177e4SLinus Torvalds 	}
21521da177e4SLinus Torvalds 	return 0;
21531da177e4SLinus Torvalds }
21541da177e4SLinus Torvalds 
21551da177e4SLinus Torvalds static int
21561da177e4SLinus Torvalds nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readdir *readdir)
21571da177e4SLinus Torvalds {
21581da177e4SLinus Torvalds 	int maxcount;
21591da177e4SLinus Torvalds 	loff_t offset;
21601da177e4SLinus Torvalds 	u32 *page, *savep;
21611da177e4SLinus Torvalds 	ENCODE_HEAD;
21621da177e4SLinus Torvalds 
21631da177e4SLinus Torvalds 	if (nfserr)
21641da177e4SLinus Torvalds 		return nfserr;
21651da177e4SLinus Torvalds 	if (resp->xbuf->page_len)
21661da177e4SLinus Torvalds 		return nfserr_resource;
21671da177e4SLinus Torvalds 
21681da177e4SLinus Torvalds 	RESERVE_SPACE(8);  /* verifier */
21691da177e4SLinus Torvalds 	savep = p;
21701da177e4SLinus Torvalds 
21711da177e4SLinus Torvalds 	/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
21721da177e4SLinus Torvalds 	WRITE32(0);
21731da177e4SLinus Torvalds 	WRITE32(0);
21741da177e4SLinus Torvalds 	ADJUST_ARGS();
21751da177e4SLinus Torvalds 	resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 	maxcount = PAGE_SIZE;
21781da177e4SLinus Torvalds 	if (maxcount > readdir->rd_maxcount)
21791da177e4SLinus Torvalds 		maxcount = readdir->rd_maxcount;
21801da177e4SLinus Torvalds 
21811da177e4SLinus Torvalds 	/*
21821da177e4SLinus Torvalds 	 * Convert from bytes to words, account for the two words already
21831da177e4SLinus Torvalds 	 * written, make sure to leave two words at the end for the next
21841da177e4SLinus Torvalds 	 * pointer and eof field.
21851da177e4SLinus Torvalds 	 */
21861da177e4SLinus Torvalds 	maxcount = (maxcount >> 2) - 4;
21871da177e4SLinus Torvalds 	if (maxcount < 0) {
21881da177e4SLinus Torvalds 		nfserr =  nfserr_toosmall;
21891da177e4SLinus Torvalds 		goto err_no_verf;
21901da177e4SLinus Torvalds 	}
21911da177e4SLinus Torvalds 
21921da177e4SLinus Torvalds 	svc_take_page(resp->rqstp);
21931da177e4SLinus Torvalds 	page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
21941da177e4SLinus Torvalds 	readdir->common.err = 0;
21951da177e4SLinus Torvalds 	readdir->buflen = maxcount;
21961da177e4SLinus Torvalds 	readdir->buffer = page;
21971da177e4SLinus Torvalds 	readdir->offset = NULL;
21981da177e4SLinus Torvalds 
21991da177e4SLinus Torvalds 	offset = readdir->rd_cookie;
22001da177e4SLinus Torvalds 	nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
22011da177e4SLinus Torvalds 			      &offset,
22021da177e4SLinus Torvalds 			      &readdir->common, nfsd4_encode_dirent);
22031da177e4SLinus Torvalds 	if (nfserr == nfs_ok &&
22041da177e4SLinus Torvalds 	    readdir->common.err == nfserr_toosmall &&
22051da177e4SLinus Torvalds 	    readdir->buffer == page)
22061da177e4SLinus Torvalds 		nfserr = nfserr_toosmall;
22071da177e4SLinus Torvalds 	if (nfserr == nfserr_symlink)
22081da177e4SLinus Torvalds 		nfserr = nfserr_notdir;
22091da177e4SLinus Torvalds 	if (nfserr)
22101da177e4SLinus Torvalds 		goto err_no_verf;
22111da177e4SLinus Torvalds 
22121da177e4SLinus Torvalds 	if (readdir->offset)
22131da177e4SLinus Torvalds 		xdr_encode_hyper(readdir->offset, offset);
22141da177e4SLinus Torvalds 
22151da177e4SLinus Torvalds 	p = readdir->buffer;
22161da177e4SLinus Torvalds 	*p++ = 0;	/* no more entries */
22171da177e4SLinus Torvalds 	*p++ = htonl(readdir->common.err == nfserr_eof);
22181da177e4SLinus Torvalds 	resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
22191da177e4SLinus Torvalds 
22201da177e4SLinus Torvalds 	/* allocate a page for the tail */
22211da177e4SLinus Torvalds 	svc_take_page(resp->rqstp);
22221da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_base =
22231da177e4SLinus Torvalds 		page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
22241da177e4SLinus Torvalds 	resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
22251da177e4SLinus Torvalds 	resp->xbuf->tail[0].iov_len = 0;
22261da177e4SLinus Torvalds 	resp->p = resp->xbuf->tail[0].iov_base;
22271da177e4SLinus Torvalds 	resp->end = resp->p + PAGE_SIZE/4;
22281da177e4SLinus Torvalds 
22291da177e4SLinus Torvalds 	return 0;
22301da177e4SLinus Torvalds err_no_verf:
22311da177e4SLinus Torvalds 	p = savep;
22321da177e4SLinus Torvalds 	ADJUST_ARGS();
22331da177e4SLinus Torvalds 	return nfserr;
22341da177e4SLinus Torvalds }
22351da177e4SLinus Torvalds 
22361da177e4SLinus Torvalds static void
22371da177e4SLinus Torvalds nfsd4_encode_remove(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_remove *remove)
22381da177e4SLinus Torvalds {
22391da177e4SLinus Torvalds 	ENCODE_HEAD;
22401da177e4SLinus Torvalds 
22411da177e4SLinus Torvalds 	if (!nfserr) {
22421da177e4SLinus Torvalds 		RESERVE_SPACE(20);
22431da177e4SLinus Torvalds 		WRITECINFO(remove->rm_cinfo);
22441da177e4SLinus Torvalds 		ADJUST_ARGS();
22451da177e4SLinus Torvalds 	}
22461da177e4SLinus Torvalds }
22471da177e4SLinus Torvalds 
22481da177e4SLinus Torvalds static void
22491da177e4SLinus Torvalds nfsd4_encode_rename(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_rename *rename)
22501da177e4SLinus Torvalds {
22511da177e4SLinus Torvalds 	ENCODE_HEAD;
22521da177e4SLinus Torvalds 
22531da177e4SLinus Torvalds 	if (!nfserr) {
22541da177e4SLinus Torvalds 		RESERVE_SPACE(40);
22551da177e4SLinus Torvalds 		WRITECINFO(rename->rn_sinfo);
22561da177e4SLinus Torvalds 		WRITECINFO(rename->rn_tinfo);
22571da177e4SLinus Torvalds 		ADJUST_ARGS();
22581da177e4SLinus Torvalds 	}
22591da177e4SLinus Torvalds }
22601da177e4SLinus Torvalds 
22611da177e4SLinus Torvalds /*
22621da177e4SLinus Torvalds  * The SETATTR encode routine is special -- it always encodes a bitmap,
22631da177e4SLinus Torvalds  * regardless of the error status.
22641da177e4SLinus Torvalds  */
22651da177e4SLinus Torvalds static void
22661da177e4SLinus Torvalds nfsd4_encode_setattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setattr *setattr)
22671da177e4SLinus Torvalds {
22681da177e4SLinus Torvalds 	ENCODE_HEAD;
22691da177e4SLinus Torvalds 
22701da177e4SLinus Torvalds 	RESERVE_SPACE(12);
22711da177e4SLinus Torvalds 	if (nfserr) {
22721da177e4SLinus Torvalds 		WRITE32(2);
22731da177e4SLinus Torvalds 		WRITE32(0);
22741da177e4SLinus Torvalds 		WRITE32(0);
22751da177e4SLinus Torvalds 	}
22761da177e4SLinus Torvalds 	else {
22771da177e4SLinus Torvalds 		WRITE32(2);
22781da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[0]);
22791da177e4SLinus Torvalds 		WRITE32(setattr->sa_bmval[1]);
22801da177e4SLinus Torvalds 	}
22811da177e4SLinus Torvalds 	ADJUST_ARGS();
22821da177e4SLinus Torvalds }
22831da177e4SLinus Torvalds 
22841da177e4SLinus Torvalds static void
22851da177e4SLinus Torvalds nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setclientid *scd)
22861da177e4SLinus Torvalds {
22871da177e4SLinus Torvalds 	ENCODE_HEAD;
22881da177e4SLinus Torvalds 
22891da177e4SLinus Torvalds 	if (!nfserr) {
22901da177e4SLinus Torvalds 		RESERVE_SPACE(8 + sizeof(nfs4_verifier));
22911da177e4SLinus Torvalds 		WRITEMEM(&scd->se_clientid, 8);
22921da177e4SLinus Torvalds 		WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier));
22931da177e4SLinus Torvalds 		ADJUST_ARGS();
22941da177e4SLinus Torvalds 	}
22951da177e4SLinus Torvalds 	else if (nfserr == nfserr_clid_inuse) {
22961da177e4SLinus Torvalds 		RESERVE_SPACE(8);
22971da177e4SLinus Torvalds 		WRITE32(0);
22981da177e4SLinus Torvalds 		WRITE32(0);
22991da177e4SLinus Torvalds 		ADJUST_ARGS();
23001da177e4SLinus Torvalds 	}
23011da177e4SLinus Torvalds }
23021da177e4SLinus Torvalds 
23031da177e4SLinus Torvalds static void
23041da177e4SLinus Torvalds nfsd4_encode_write(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_write *write)
23051da177e4SLinus Torvalds {
23061da177e4SLinus Torvalds 	ENCODE_HEAD;
23071da177e4SLinus Torvalds 
23081da177e4SLinus Torvalds 	if (!nfserr) {
23091da177e4SLinus Torvalds 		RESERVE_SPACE(16);
23101da177e4SLinus Torvalds 		WRITE32(write->wr_bytes_written);
23111da177e4SLinus Torvalds 		WRITE32(write->wr_how_written);
23121da177e4SLinus Torvalds 		WRITEMEM(write->wr_verifier.data, 8);
23131da177e4SLinus Torvalds 		ADJUST_ARGS();
23141da177e4SLinus Torvalds 	}
23151da177e4SLinus Torvalds }
23161da177e4SLinus Torvalds 
23171da177e4SLinus Torvalds void
23181da177e4SLinus Torvalds nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
23191da177e4SLinus Torvalds {
23201da177e4SLinus Torvalds 	u32 *statp;
23211da177e4SLinus Torvalds 	ENCODE_HEAD;
23221da177e4SLinus Torvalds 
23231da177e4SLinus Torvalds 	RESERVE_SPACE(8);
23241da177e4SLinus Torvalds 	WRITE32(op->opnum);
23251da177e4SLinus Torvalds 	statp = p++;	/* to be backfilled at the end */
23261da177e4SLinus Torvalds 	ADJUST_ARGS();
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 	switch (op->opnum) {
23291da177e4SLinus Torvalds 	case OP_ACCESS:
23301da177e4SLinus Torvalds 		nfsd4_encode_access(resp, op->status, &op->u.access);
23311da177e4SLinus Torvalds 		break;
23321da177e4SLinus Torvalds 	case OP_CLOSE:
23331da177e4SLinus Torvalds 		nfsd4_encode_close(resp, op->status, &op->u.close);
23341da177e4SLinus Torvalds 		break;
23351da177e4SLinus Torvalds 	case OP_COMMIT:
23361da177e4SLinus Torvalds 		nfsd4_encode_commit(resp, op->status, &op->u.commit);
23371da177e4SLinus Torvalds 		break;
23381da177e4SLinus Torvalds 	case OP_CREATE:
23391da177e4SLinus Torvalds 		nfsd4_encode_create(resp, op->status, &op->u.create);
23401da177e4SLinus Torvalds 		break;
23411da177e4SLinus Torvalds 	case OP_DELEGRETURN:
23421da177e4SLinus Torvalds 		break;
23431da177e4SLinus Torvalds 	case OP_GETATTR:
23441da177e4SLinus Torvalds 		op->status = nfsd4_encode_getattr(resp, op->status, &op->u.getattr);
23451da177e4SLinus Torvalds 		break;
23461da177e4SLinus Torvalds 	case OP_GETFH:
23471da177e4SLinus Torvalds 		nfsd4_encode_getfh(resp, op->status, op->u.getfh);
23481da177e4SLinus Torvalds 		break;
23491da177e4SLinus Torvalds 	case OP_LINK:
23501da177e4SLinus Torvalds 		nfsd4_encode_link(resp, op->status, &op->u.link);
23511da177e4SLinus Torvalds 		break;
23521da177e4SLinus Torvalds 	case OP_LOCK:
23531da177e4SLinus Torvalds 		nfsd4_encode_lock(resp, op->status, &op->u.lock);
23541da177e4SLinus Torvalds 		break;
23551da177e4SLinus Torvalds 	case OP_LOCKT:
23561da177e4SLinus Torvalds 		nfsd4_encode_lockt(resp, op->status, &op->u.lockt);
23571da177e4SLinus Torvalds 		break;
23581da177e4SLinus Torvalds 	case OP_LOCKU:
23591da177e4SLinus Torvalds 		nfsd4_encode_locku(resp, op->status, &op->u.locku);
23601da177e4SLinus Torvalds 		break;
23611da177e4SLinus Torvalds 	case OP_LOOKUP:
23621da177e4SLinus Torvalds 		break;
23631da177e4SLinus Torvalds 	case OP_LOOKUPP:
23641da177e4SLinus Torvalds 		break;
23651da177e4SLinus Torvalds 	case OP_NVERIFY:
23661da177e4SLinus Torvalds 		break;
23671da177e4SLinus Torvalds 	case OP_OPEN:
23681da177e4SLinus Torvalds 		nfsd4_encode_open(resp, op->status, &op->u.open);
23691da177e4SLinus Torvalds 		break;
23701da177e4SLinus Torvalds 	case OP_OPEN_CONFIRM:
23711da177e4SLinus Torvalds 		nfsd4_encode_open_confirm(resp, op->status, &op->u.open_confirm);
23721da177e4SLinus Torvalds 		break;
23731da177e4SLinus Torvalds 	case OP_OPEN_DOWNGRADE:
23741da177e4SLinus Torvalds 		nfsd4_encode_open_downgrade(resp, op->status, &op->u.open_downgrade);
23751da177e4SLinus Torvalds 		break;
23761da177e4SLinus Torvalds 	case OP_PUTFH:
23771da177e4SLinus Torvalds 		break;
23781da177e4SLinus Torvalds 	case OP_PUTROOTFH:
23791da177e4SLinus Torvalds 		break;
23801da177e4SLinus Torvalds 	case OP_READ:
23811da177e4SLinus Torvalds 		op->status = nfsd4_encode_read(resp, op->status, &op->u.read);
23821da177e4SLinus Torvalds 		break;
23831da177e4SLinus Torvalds 	case OP_READDIR:
23841da177e4SLinus Torvalds 		op->status = nfsd4_encode_readdir(resp, op->status, &op->u.readdir);
23851da177e4SLinus Torvalds 		break;
23861da177e4SLinus Torvalds 	case OP_READLINK:
23871da177e4SLinus Torvalds 		op->status = nfsd4_encode_readlink(resp, op->status, &op->u.readlink);
23881da177e4SLinus Torvalds 		break;
23891da177e4SLinus Torvalds 	case OP_REMOVE:
23901da177e4SLinus Torvalds 		nfsd4_encode_remove(resp, op->status, &op->u.remove);
23911da177e4SLinus Torvalds 		break;
23921da177e4SLinus Torvalds 	case OP_RENAME:
23931da177e4SLinus Torvalds 		nfsd4_encode_rename(resp, op->status, &op->u.rename);
23941da177e4SLinus Torvalds 		break;
23951da177e4SLinus Torvalds 	case OP_RENEW:
23961da177e4SLinus Torvalds 		break;
23971da177e4SLinus Torvalds 	case OP_RESTOREFH:
23981da177e4SLinus Torvalds 		break;
23991da177e4SLinus Torvalds 	case OP_SAVEFH:
24001da177e4SLinus Torvalds 		break;
24011da177e4SLinus Torvalds 	case OP_SETATTR:
24021da177e4SLinus Torvalds 		nfsd4_encode_setattr(resp, op->status, &op->u.setattr);
24031da177e4SLinus Torvalds 		break;
24041da177e4SLinus Torvalds 	case OP_SETCLIENTID:
24051da177e4SLinus Torvalds 		nfsd4_encode_setclientid(resp, op->status, &op->u.setclientid);
24061da177e4SLinus Torvalds 		break;
24071da177e4SLinus Torvalds 	case OP_SETCLIENTID_CONFIRM:
24081da177e4SLinus Torvalds 		break;
24091da177e4SLinus Torvalds 	case OP_VERIFY:
24101da177e4SLinus Torvalds 		break;
24111da177e4SLinus Torvalds 	case OP_WRITE:
24121da177e4SLinus Torvalds 		nfsd4_encode_write(resp, op->status, &op->u.write);
24131da177e4SLinus Torvalds 		break;
24141da177e4SLinus Torvalds 	case OP_RELEASE_LOCKOWNER:
24151da177e4SLinus Torvalds 		break;
24161da177e4SLinus Torvalds 	default:
24171da177e4SLinus Torvalds 		break;
24181da177e4SLinus Torvalds 	}
24191da177e4SLinus Torvalds 
24201da177e4SLinus Torvalds 	/*
24211da177e4SLinus Torvalds 	 * Note: We write the status directly, instead of using WRITE32(),
24221da177e4SLinus Torvalds 	 * since it is already in network byte order.
24231da177e4SLinus Torvalds 	 */
24241da177e4SLinus Torvalds 	*statp = op->status;
24251da177e4SLinus Torvalds }
24261da177e4SLinus Torvalds 
24271da177e4SLinus Torvalds /*
24281da177e4SLinus Torvalds  * Encode the reply stored in the stateowner reply cache
24291da177e4SLinus Torvalds  *
24301da177e4SLinus Torvalds  * XDR note: do not encode rp->rp_buflen: the buffer contains the
24311da177e4SLinus Torvalds  * previously sent already encoded operation.
24321da177e4SLinus Torvalds  *
24331da177e4SLinus Torvalds  * called with nfs4_lock_state() held
24341da177e4SLinus Torvalds  */
24351da177e4SLinus Torvalds void
24361da177e4SLinus Torvalds nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
24371da177e4SLinus Torvalds {
24381da177e4SLinus Torvalds 	ENCODE_HEAD;
24391da177e4SLinus Torvalds 	struct nfs4_replay *rp = op->replay;
24401da177e4SLinus Torvalds 
24411da177e4SLinus Torvalds 	BUG_ON(!rp);
24421da177e4SLinus Torvalds 
24431da177e4SLinus Torvalds 	RESERVE_SPACE(8);
24441da177e4SLinus Torvalds 	WRITE32(op->opnum);
24451da177e4SLinus Torvalds 	*p++ = rp->rp_status;  /* already xdr'ed */
24461da177e4SLinus Torvalds 	ADJUST_ARGS();
24471da177e4SLinus Torvalds 
24481da177e4SLinus Torvalds 	RESERVE_SPACE(rp->rp_buflen);
24491da177e4SLinus Torvalds 	WRITEMEM(rp->rp_buf, rp->rp_buflen);
24501da177e4SLinus Torvalds 	ADJUST_ARGS();
24511da177e4SLinus Torvalds }
24521da177e4SLinus Torvalds 
24531da177e4SLinus Torvalds /*
24541da177e4SLinus Torvalds  * END OF "GENERIC" ENCODE ROUTINES.
24551da177e4SLinus Torvalds  */
24561da177e4SLinus Torvalds 
24571da177e4SLinus Torvalds int
24581da177e4SLinus Torvalds nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
24591da177e4SLinus Torvalds {
24601da177e4SLinus Torvalds         return xdr_ressize_check(rqstp, p);
24611da177e4SLinus Torvalds }
24621da177e4SLinus Torvalds 
24631da177e4SLinus Torvalds void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
24641da177e4SLinus Torvalds {
24651da177e4SLinus Torvalds 	if (args->ops != args->iops) {
24661da177e4SLinus Torvalds 		kfree(args->ops);
24671da177e4SLinus Torvalds 		args->ops = args->iops;
24681da177e4SLinus Torvalds 	}
24691da177e4SLinus Torvalds 	kfree(args->tmpp);
24701da177e4SLinus Torvalds 	args->tmpp = NULL;
24711da177e4SLinus Torvalds 	while (args->to_free) {
24721da177e4SLinus Torvalds 		struct tmpbuf *tb = args->to_free;
24731da177e4SLinus Torvalds 		args->to_free = tb->next;
24741da177e4SLinus Torvalds 		tb->release(tb->buf);
24751da177e4SLinus Torvalds 		kfree(tb);
24761da177e4SLinus Torvalds 	}
24771da177e4SLinus Torvalds }
24781da177e4SLinus Torvalds 
24791da177e4SLinus Torvalds int
24801da177e4SLinus Torvalds nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args)
24811da177e4SLinus Torvalds {
24821da177e4SLinus Torvalds 	int status;
24831da177e4SLinus Torvalds 
24841da177e4SLinus Torvalds 	args->p = p;
24851da177e4SLinus Torvalds 	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
24861da177e4SLinus Torvalds 	args->pagelist = rqstp->rq_arg.pages;
24871da177e4SLinus Torvalds 	args->pagelen = rqstp->rq_arg.page_len;
24881da177e4SLinus Torvalds 	args->tmpp = NULL;
24891da177e4SLinus Torvalds 	args->to_free = NULL;
24901da177e4SLinus Torvalds 	args->ops = args->iops;
24911da177e4SLinus Torvalds 	args->rqstp = rqstp;
24921da177e4SLinus Torvalds 
24931da177e4SLinus Torvalds 	status = nfsd4_decode_compound(args);
24941da177e4SLinus Torvalds 	if (status) {
24951da177e4SLinus Torvalds 		nfsd4_release_compoundargs(args);
24961da177e4SLinus Torvalds 	}
24971da177e4SLinus Torvalds 	return !status;
24981da177e4SLinus Torvalds }
24991da177e4SLinus Torvalds 
25001da177e4SLinus Torvalds int
25011da177e4SLinus Torvalds nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundres *resp)
25021da177e4SLinus Torvalds {
25031da177e4SLinus Torvalds 	/*
25041da177e4SLinus Torvalds 	 * All that remains is to write the tag and operation count...
25051da177e4SLinus Torvalds 	 */
25061da177e4SLinus Torvalds 	struct kvec *iov;
25071da177e4SLinus Torvalds 	p = resp->tagp;
25081da177e4SLinus Torvalds 	*p++ = htonl(resp->taglen);
25091da177e4SLinus Torvalds 	memcpy(p, resp->tag, resp->taglen);
25101da177e4SLinus Torvalds 	p += XDR_QUADLEN(resp->taglen);
25111da177e4SLinus Torvalds 	*p++ = htonl(resp->opcnt);
25121da177e4SLinus Torvalds 
25131da177e4SLinus Torvalds 	if (rqstp->rq_res.page_len)
25141da177e4SLinus Torvalds 		iov = &rqstp->rq_res.tail[0];
25151da177e4SLinus Torvalds 	else
25161da177e4SLinus Torvalds 		iov = &rqstp->rq_res.head[0];
25171da177e4SLinus Torvalds 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
25181da177e4SLinus Torvalds 	BUG_ON(iov->iov_len > PAGE_SIZE);
25191da177e4SLinus Torvalds 	return 1;
25201da177e4SLinus Torvalds }
25211da177e4SLinus Torvalds 
25221da177e4SLinus Torvalds /*
25231da177e4SLinus Torvalds  * Local variables:
25241da177e4SLinus Torvalds  *  c-basic-offset: 8
25251da177e4SLinus Torvalds  * End:
25261da177e4SLinus Torvalds  */
2527