nfs4xdr.c 113 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 *  Server-side XDR for NFSv4
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Kendrick Smith <kmsmith@umich.edu>
 *  Andy Adamson   <andros@umich.edu>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

36
#include <linux/file.h>
37
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
38
#include <linux/namei.h>
39
#include <linux/statfs.h>
Andy Adamson's avatar
Andy Adamson committed
40
#include <linux/utsname.h>
41
#include <linux/pagemap.h>
42
#include <linux/sunrpc/svcauth_gss.h>
43

44 45
#include "idmap.h"
#include "acl.h"
46
#include "xdr4.h"
47
#include "vfs.h"
48
#include "state.h"
49
#include "cache.h"
50
#include "netns.h"
51
#include "pnfs.h"
52

53 54 55 56 57
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#include <linux/security.h>
#endif


Linus Torvalds's avatar
Linus Torvalds committed
58 59
#define NFSDDBG_FACILITY		NFSDDBG_XDR

60 61 62 63 64 65 66 67
/*
 * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
 * directory in order to indicate to the client that a filesystem boundary is present
 * We use a fixed fsid for a referral
 */
#define NFS4_REFERRAL_FSID_MAJOR	0x8000000ULL
#define NFS4_REFERRAL_FSID_MINOR	0x8000000ULL

68
static __be32
69
check_filename(char *str, int len)
Linus Torvalds's avatar
Linus Torvalds committed
70 71 72 73 74 75
{
	int i;

	if (len == 0)
		return nfserr_inval;
	if (isdotent(str, len))
76
		return nfserr_badname;
Linus Torvalds's avatar
Linus Torvalds committed
77 78
	for (i = 0; i < len; i++)
		if (str[i] == '/')
79
			return nfserr_badname;
Linus Torvalds's avatar
Linus Torvalds committed
80 81 82 83
	return 0;
}

#define DECODE_HEAD				\
84
	__be32 *p;				\
85
	__be32 status
Linus Torvalds's avatar
Linus Torvalds committed
86 87 88 89 90
#define DECODE_TAIL				\
	status = 0;				\
out:						\
	return status;				\
xdr_error:					\
91 92
	dprintk("NFSD: xdr error (%s:%d)\n",	\
			__FILE__, __LINE__);	\
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96 97 98 99 100 101 102 103
	status = nfserr_bad_xdr;		\
	goto out

#define READMEM(x,nbytes) do {			\
	x = (char *)p;				\
	p += XDR_QUADLEN(nbytes);		\
} while (0)
#define SAVEMEM(x,nbytes) do {			\
	if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
 		savemem(argp, p, nbytes) :	\
 		(char *)p)) {			\
104 105
		dprintk("NFSD: xdr error (%s:%d)\n", \
				__FILE__, __LINE__); \
Linus Torvalds's avatar
Linus Torvalds committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
		goto xdr_error;			\
		}				\
	p += XDR_QUADLEN(nbytes);		\
} while (0)
#define COPYMEM(x,nbytes) do {			\
	memcpy((x), p, nbytes);			\
	p += XDR_QUADLEN(nbytes);		\
} while (0)

/* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */
#define READ_BUF(nbytes)  do {			\
	if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) {	\
		p = argp->p;			\
		argp->p += XDR_QUADLEN(nbytes);	\
	} else if (!(p = read_buf(argp, nbytes))) { \
121 122
		dprintk("NFSD: xdr error (%s:%d)\n", \
				__FILE__, __LINE__); \
Linus Torvalds's avatar
Linus Torvalds committed
123 124 125 126
		goto xdr_error;			\
	}					\
} while (0)

127 128 129
static void next_decode_page(struct nfsd4_compoundargs *argp)
{
	argp->p = page_address(argp->pagelist[0]);
130
	argp->pagelist++;
131 132 133 134 135 136 137 138 139
	if (argp->pagelen < PAGE_SIZE) {
		argp->end = argp->p + (argp->pagelen>>2);
		argp->pagelen = 0;
	} else {
		argp->end = argp->p + (PAGE_SIZE>>2);
		argp->pagelen -= PAGE_SIZE;
	}
}

140
static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
Linus Torvalds's avatar
Linus Torvalds committed
141 142 143 144
{
	/* We want more bytes than seem to be available.
	 * Maybe we need a new page, maybe we have just run out
	 */
145
	unsigned int avail = (char *)argp->end - (char *)argp->p;
146
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed
147 148 149 150 151 152 153 154
	if (avail + argp->pagelen < nbytes)
		return NULL;
	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
		return NULL;
	/* ok, we can do it with the current plus the next page */
	if (nbytes <= sizeof(argp->tmp))
		p = argp->tmp;
	else {
Jesper Juhl's avatar
Jesper Juhl committed
155
		kfree(argp->tmpp);
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158 159 160
		p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
		if (!p)
			return NULL;
		
	}
161 162 163 164 165
	/*
	 * The following memcpy is safe because read_buf is always
	 * called with nbytes > avail, and the two cases above both
	 * guarantee p points to at least nbytes bytes.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
166
	memcpy(p, argp->p, avail);
167
	next_decode_page(argp);
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172
	memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
	argp->p += XDR_QUADLEN(nbytes - avail);
	return p;
}

Andy Adamson's avatar
Andy Adamson committed
173 174 175 176 177
static int zero_clientid(clientid_t *clid)
{
	return (clid->cl_boot == 0) && (clid->cl_id == 0);
}

178
/**
179
 * svcxdr_tmpalloc - allocate memory to be freed after compound processing
180 181
 * @argp: NFSv4 compound argument structure
 * @p: pointer to be freed (with kfree())
182 183 184 185
 *
 * Marks @p to be freed when processing the compound operation
 * described in @argp finishes.
 */
186 187
static void *
svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)
Linus Torvalds's avatar
Linus Torvalds committed
188
{
189
	struct svcxdr_tmpbuf *tb;
Linus Torvalds's avatar
Linus Torvalds committed
190

191
	tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
192
	if (!tb)
193
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
194 195
	tb->next = argp->to_free;
	argp->to_free = tb;
196
	return tb->buf;
Linus Torvalds's avatar
Linus Torvalds committed
197 198
}

199 200 201 202 203 204 205 206 207 208
/*
 * For xdr strings that need to be passed to other kernel api's
 * as null-terminated strings.
 *
 * Note null-terminating in place usually isn't safe since the
 * buffer might end on a page boundary.
 */
static char *
svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
{
209
	char *p = svcxdr_tmpalloc(argp, len + 1);
210 211 212 213 214 215

	if (!p)
		return NULL;
	memcpy(p, buf, len);
	p[len] = '\0';
	return p;
Linus Torvalds's avatar
Linus Torvalds committed
216 217
}

218 219 220 221 222 223 224 225 226 227
/**
 * savemem - duplicate a chunk of memory for later processing
 * @argp: NFSv4 compound argument structure to be freed with
 * @p: pointer to be duplicated
 * @nbytes: length to be duplicated
 *
 * Returns a pointer to a copy of @nbytes bytes of memory at @p
 * that are preserved until processing of the NFSv4 compound
 * operation described by @argp finishes.
 */
228
static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
Linus Torvalds's avatar
Linus Torvalds committed
229
{
230 231 232 233
	void *ret;

	ret = svcxdr_tmpalloc(argp, nbytes);
	if (!ret)
Linus Torvalds's avatar
Linus Torvalds committed
234
		return NULL;
235 236
	memcpy(ret, p, nbytes);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
237 238
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
/*
 * We require the high 32 bits of 'seconds' to be 0, and
 * we ignore all 32 bits of 'nseconds'.
 */
static __be32
nfsd4_decode_time(struct nfsd4_compoundargs *argp, struct timespec *tv)
{
	DECODE_HEAD;
	u64 sec;

	READ_BUF(12);
	p = xdr_decode_hyper(p, &sec);
	tv->tv_sec = sec;
	tv->tv_nsec = be32_to_cpup(p++);
	if (tv->tv_nsec >= (u32)1000000000)
		return nfserr_inval;

	DECODE_TAIL;
}

259
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
260 261 262 263 264 265 266
nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
{
	u32 bmlen;
	DECODE_HEAD;

	bmval[0] = 0;
	bmval[1] = 0;
267
	bmval[2] = 0;
Linus Torvalds's avatar
Linus Torvalds committed
268 269

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
270
	bmlen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
271 272 273 274 275
	if (bmlen > 1000)
		goto xdr_error;

	READ_BUF(bmlen << 2);
	if (bmlen > 0)
J. Bruce Fields's avatar
J. Bruce Fields committed
276
		bmval[0] = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
277
	if (bmlen > 1)
J. Bruce Fields's avatar
J. Bruce Fields committed
278
		bmval[1] = be32_to_cpup(p++);
279
	if (bmlen > 2)
J. Bruce Fields's avatar
J. Bruce Fields committed
280
		bmval[2] = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
281 282 283 284

	DECODE_TAIL;
}

285
static __be32
286
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
287 288
		   struct iattr *iattr, struct nfs4_acl **acl,
		   struct xdr_netobj *label)
Linus Torvalds's avatar
Linus Torvalds committed
289 290 291 292 293 294 295 296 297 298 299
{
	int expected_len, len = 0;
	u32 dummy32;
	char *buf;

	DECODE_HEAD;
	iattr->ia_valid = 0;
	if ((status = nfsd4_decode_bitmap(argp, bmval)))
		return status;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
300
	expected_len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
301 302 303 304

	if (bmval[0] & FATTR4_WORD0_SIZE) {
		READ_BUF(8);
		len += 8;
J. Bruce Fields's avatar
J. Bruce Fields committed
305
		p = xdr_decode_hyper(p, &iattr->ia_size);
Linus Torvalds's avatar
Linus Torvalds committed
306 307 308
		iattr->ia_valid |= ATTR_SIZE;
	}
	if (bmval[0] & FATTR4_WORD0_ACL) {
309
		u32 nace;
310
		struct nfs4_ace *ace;
Linus Torvalds's avatar
Linus Torvalds committed
311 312

		READ_BUF(4); len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
313
		nace = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
314

315
		if (nace > NFS4_ACL_MAX)
316
			return nfserr_fbig;
317

318
		*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
319 320 321
		if (*acl == NULL)
			return nfserr_jukebox;

322 323
		(*acl)->naces = nace;
		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
Linus Torvalds's avatar
Linus Torvalds committed
324
			READ_BUF(16); len += 16;
J. Bruce Fields's avatar
J. Bruce Fields committed
325 326 327 328
			ace->type = be32_to_cpup(p++);
			ace->flag = be32_to_cpup(p++);
			ace->access_mask = be32_to_cpup(p++);
			dummy32 = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
329 330 331
			READ_BUF(dummy32);
			len += XDR_QUADLEN(dummy32) << 2;
			READMEM(buf, dummy32);
332
			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
333
			status = nfs_ok;
334
			if (ace->whotype != NFS4_ACL_WHO_NAMED)
335
				;
336
			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
337
				status = nfsd_map_name_to_gid(argp->rqstp,
338
						buf, dummy32, &ace->who_gid);
Linus Torvalds's avatar
Linus Torvalds committed
339
			else
340
				status = nfsd_map_name_to_uid(argp->rqstp,
341
						buf, dummy32, &ace->who_uid);
342 343
			if (status)
				return status;
Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347 348 349
		}
	} else
		*acl = NULL;
	if (bmval[1] & FATTR4_WORD1_MODE) {
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
350
		iattr->ia_mode = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
351 352 353 354 355 356
		iattr->ia_mode &= (S_IFMT | S_IALLUGO);
		iattr->ia_valid |= ATTR_MODE;
	}
	if (bmval[1] & FATTR4_WORD1_OWNER) {
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
357
		dummy32 = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
358 359 360
		READ_BUF(dummy32);
		len += (XDR_QUADLEN(dummy32) << 2);
		READMEM(buf, dummy32);
361 362
		if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
			return status;
Linus Torvalds's avatar
Linus Torvalds committed
363 364 365 366 367
		iattr->ia_valid |= ATTR_UID;
	}
	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
368
		dummy32 = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
369 370 371
		READ_BUF(dummy32);
		len += (XDR_QUADLEN(dummy32) << 2);
		READMEM(buf, dummy32);
372 373
		if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
			return status;
Linus Torvalds's avatar
Linus Torvalds committed
374 375 376 377 378
		iattr->ia_valid |= ATTR_GID;
	}
	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
379
		dummy32 = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
380 381 382
		switch (dummy32) {
		case NFS4_SET_TO_CLIENT_TIME:
			len += 12;
383 384 385
			status = nfsd4_decode_time(argp, &iattr->ia_atime);
			if (status)
				return status;
Linus Torvalds's avatar
Linus Torvalds committed
386 387 388 389 390 391 392 393 394 395 396 397
			iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
			break;
		case NFS4_SET_TO_SERVER_TIME:
			iattr->ia_valid |= ATTR_ATIME;
			break;
		default:
			goto xdr_error;
		}
	}
	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
398
		dummy32 = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
399 400 401
		switch (dummy32) {
		case NFS4_SET_TO_CLIENT_TIME:
			len += 12;
402 403 404
			status = nfsd4_decode_time(argp, &iattr->ia_mtime);
			if (status)
				return status;
Linus Torvalds's avatar
Linus Torvalds committed
405 406 407 408 409 410 411 412 413
			iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
			break;
		case NFS4_SET_TO_SERVER_TIME:
			iattr->ia_valid |= ATTR_MTIME;
			break;
		default:
			goto xdr_error;
		}
	}
414 415 416 417 418 419

	label->len = 0;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
	if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
420
		dummy32 = be32_to_cpup(p++); /* lfs: we don't use it */
421 422
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
423
		dummy32 = be32_to_cpup(p++); /* pi: we don't use it either */
424 425
		READ_BUF(4);
		len += 4;
J. Bruce Fields's avatar
J. Bruce Fields committed
426
		dummy32 = be32_to_cpup(p++);
427
		READ_BUF(dummy32);
428
		if (dummy32 > NFS4_MAXLABELLEN)
429 430 431
			return nfserr_badlabel;
		len += (XDR_QUADLEN(dummy32) << 2);
		READMEM(buf, dummy32);
432 433
		label->len = dummy32;
		label->data = svcxdr_dupstr(argp, buf, dummy32);
434 435 436 437 438
		if (!label->data)
			return nfserr_jukebox;
	}
#endif

439 440 441 442 443
	if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
	    || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
	    || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
		READ_BUF(expected_len - len);
	else if (len != expected_len)
Linus Torvalds's avatar
Linus Torvalds committed
444 445 446 447 448
		goto xdr_error;

	DECODE_TAIL;
}

449 450 451 452 453 454
static __be32
nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
{
	DECODE_HEAD;

	READ_BUF(sizeof(stateid_t));
J. Bruce Fields's avatar
J. Bruce Fields committed
455
	sid->si_generation = be32_to_cpup(p++);
456 457 458 459 460
	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));

	DECODE_TAIL;
}

461
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
462 463 464 465 466
nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
467
	access->ac_req_access = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
468 469 470 471

	DECODE_TAIL;
}

472 473 474
static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
{
	DECODE_HEAD;
475
	u32 dummy, uid, gid;
476 477 478 479 480 481
	char *machine_name;
	int i;
	int nr_secflavs;

	/* callback_sec_params4 */
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
482
	nr_secflavs = be32_to_cpup(p++);
483 484 485 486 487
	if (nr_secflavs)
		cbs->flavor = (u32)(-1);
	else
		/* Is this legal? Be generous, take it to mean AUTH_NONE: */
		cbs->flavor = 0;
488 489
	for (i = 0; i < nr_secflavs; ++i) {
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
490
		dummy = be32_to_cpup(p++);
491 492 493
		switch (dummy) {
		case RPC_AUTH_NULL:
			/* Nothing to read */
494 495
			if (cbs->flavor == (u32)(-1))
				cbs->flavor = RPC_AUTH_NULL;
496 497 498 499
			break;
		case RPC_AUTH_UNIX:
			READ_BUF(8);
			/* stamp */
J. Bruce Fields's avatar
J. Bruce Fields committed
500
			dummy = be32_to_cpup(p++);
501 502

			/* machine name */
J. Bruce Fields's avatar
J. Bruce Fields committed
503
			dummy = be32_to_cpup(p++);
504 505 506 507 508
			READ_BUF(dummy);
			SAVEMEM(machine_name, dummy);

			/* uid, gid */
			READ_BUF(8);
J. Bruce Fields's avatar
J. Bruce Fields committed
509 510
			uid = be32_to_cpup(p++);
			gid = be32_to_cpup(p++);
511 512 513

			/* more gids */
			READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
514
			dummy = be32_to_cpup(p++);
515
			READ_BUF(dummy * 4);
516
			if (cbs->flavor == (u32)(-1)) {
517 518 519 520 521 522 523 524 525 526
				kuid_t kuid = make_kuid(&init_user_ns, uid);
				kgid_t kgid = make_kgid(&init_user_ns, gid);
				if (uid_valid(kuid) && gid_valid(kgid)) {
					cbs->uid = kuid;
					cbs->gid = kgid;
					cbs->flavor = RPC_AUTH_UNIX;
				} else {
					dprintk("RPC_AUTH_UNIX with invalid"
						"uid or gid ignoring!\n");
				}
527
			}
528 529 530 531 532 533
			break;
		case RPC_AUTH_GSS:
			dprintk("RPC_AUTH_GSS callback secflavor "
				"not supported!\n");
			READ_BUF(8);
			/* gcbp_service */
J. Bruce Fields's avatar
J. Bruce Fields committed
534
			dummy = be32_to_cpup(p++);
535
			/* gcbp_handle_from_server */
J. Bruce Fields's avatar
J. Bruce Fields committed
536
			dummy = be32_to_cpup(p++);
537 538 539 540
			READ_BUF(dummy);
			p += XDR_QUADLEN(dummy);
			/* gcbp_handle_from_client */
			READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
541
			dummy = be32_to_cpup(p++);
542 543 544 545 546 547 548 549 550 551
			READ_BUF(dummy);
			break;
		default:
			dprintk("Illegal callback secflavor\n");
			return nfserr_inval;
		}
	}
	DECODE_TAIL;
}

552 553 554 555 556
static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
557
	bc->bc_cb_program = be32_to_cpup(p++);
558 559 560 561 562
	nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);

	DECODE_TAIL;
}

563 564 565 566 567 568
static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts)
{
	DECODE_HEAD;

	READ_BUF(NFS4_MAX_SESSIONID_LEN + 8);
	COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
J. Bruce Fields's avatar
J. Bruce Fields committed
569
	bcts->dir = be32_to_cpup(p++);
570 571
	/* XXX: skipping ctsa_use_conn_in_rdma_mode.  Perhaps Tom Tucker
	 * could help us figure out we should be using it. */
572 573 574
	DECODE_TAIL;
}

575
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
576 577 578 579
nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
{
	DECODE_HEAD;

580
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
581
	close->cl_seqid = be32_to_cpup(p++);
582
	return nfsd4_decode_stateid(argp, &close->cl_stateid);
Linus Torvalds's avatar
Linus Torvalds committed
583 584 585 586 587

	DECODE_TAIL;
}


588
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
589 590 591 592 593
nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
{
	DECODE_HEAD;

	READ_BUF(12);
J. Bruce Fields's avatar
J. Bruce Fields committed
594
	p = xdr_decode_hyper(p, &commit->co_offset);
J. Bruce Fields's avatar
J. Bruce Fields committed
595
	commit->co_count = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
596 597 598 599

	DECODE_TAIL;
}

600
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
601 602 603 604 605
nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
606
	create->cr_type = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
607 608 609
	switch (create->cr_type) {
	case NF4LNK:
		READ_BUF(4);
610 611
		create->cr_datalen = be32_to_cpup(p++);
		READ_BUF(create->cr_datalen);
612
		create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen);
613
		if (!create->cr_data)
614
			return nfserr_jukebox;
Linus Torvalds's avatar
Linus Torvalds committed
615 616 617 618
		break;
	case NF4BLK:
	case NF4CHR:
		READ_BUF(8);
J. Bruce Fields's avatar
J. Bruce Fields committed
619 620
		create->cr_specdata1 = be32_to_cpup(p++);
		create->cr_specdata2 = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
621 622 623 624 625 626 627 628 629
		break;
	case NF4SOCK:
	case NF4FIFO:
	case NF4DIR:
	default:
		break;
	}

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
630
	create->cr_namelen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
631 632
	READ_BUF(create->cr_namelen);
	SAVEMEM(create->cr_name, create->cr_namelen);
633
	if ((status = check_filename(create->cr_name, create->cr_namelen)))
Linus Torvalds's avatar
Linus Torvalds committed
634 635
		return status;

636
	status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
637
				    &create->cr_acl, &create->cr_label);
638
	if (status)
Linus Torvalds's avatar
Linus Torvalds committed
639 640 641 642 643
		goto out;

	DECODE_TAIL;
}

644
static inline __be32
Linus Torvalds's avatar
Linus Torvalds committed
645 646
nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
{
647
	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
Linus Torvalds's avatar
Linus Torvalds committed
648 649
}

650
static inline __be32
Linus Torvalds's avatar
Linus Torvalds committed
651 652 653 654 655
nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
{
	return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
}

656
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
657 658 659 660 661
nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
662
	link->li_namelen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
663 664
	READ_BUF(link->li_namelen);
	SAVEMEM(link->li_name, link->li_namelen);
665
	if ((status = check_filename(link->li_name, link->li_namelen)))
Linus Torvalds's avatar
Linus Torvalds committed
666 667 668 669 670
		return status;

	DECODE_TAIL;
}

671
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
672 673 674 675 676 677 678 679
nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
{
	DECODE_HEAD;

	/*
	* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
	*/
	READ_BUF(28);
J. Bruce Fields's avatar
J. Bruce Fields committed
680
	lock->lk_type = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
681 682
	if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
		goto xdr_error;
J. Bruce Fields's avatar
J. Bruce Fields committed
683
	lock->lk_reclaim = be32_to_cpup(p++);
J. Bruce Fields's avatar
J. Bruce Fields committed
684 685
	p = xdr_decode_hyper(p, &lock->lk_offset);
	p = xdr_decode_hyper(p, &lock->lk_length);
J. Bruce Fields's avatar
J. Bruce Fields committed
686
	lock->lk_is_new = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
687 688

	if (lock->lk_is_new) {
689
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
690
		lock->lk_new_open_seqid = be32_to_cpup(p++);
691 692 693 694
		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
		if (status)
			return status;
		READ_BUF(8 + sizeof(clientid_t));
J. Bruce Fields's avatar
J. Bruce Fields committed
695
		lock->lk_new_lock_seqid = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
696
		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
J. Bruce Fields's avatar
J. Bruce Fields committed
697
		lock->lk_new_owner.len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
698 699 700
		READ_BUF(lock->lk_new_owner.len);
		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
	} else {
701 702 703 704
		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
		if (status)
			return status;
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
705
		lock->lk_old_lock_seqid = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
706 707 708 709 710
	}

	DECODE_TAIL;
}

711
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
712 713 714 715 716
nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
{
	DECODE_HEAD;
		        
	READ_BUF(32);
J. Bruce Fields's avatar
J. Bruce Fields committed
717
	lockt->lt_type = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
718 719
	if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
		goto xdr_error;
J. Bruce Fields's avatar
J. Bruce Fields committed
720 721
	p = xdr_decode_hyper(p, &lockt->lt_offset);
	p = xdr_decode_hyper(p, &lockt->lt_length);
Linus Torvalds's avatar
Linus Torvalds committed
722
	COPYMEM(&lockt->lt_clientid, 8);
J. Bruce Fields's avatar
J. Bruce Fields committed
723
	lockt->lt_owner.len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
724 725 726 727 728 729
	READ_BUF(lockt->lt_owner.len);
	READMEM(lockt->lt_owner.data, lockt->lt_owner.len);

	DECODE_TAIL;
}

730
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
731 732 733 734
nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
{
	DECODE_HEAD;

735
	READ_BUF(8);
J. Bruce Fields's avatar
J. Bruce Fields committed
736
	locku->lu_type = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
737 738
	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
		goto xdr_error;
J. Bruce Fields's avatar
J. Bruce Fields committed
739
	locku->lu_seqid = be32_to_cpup(p++);
740 741 742 743
	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
	if (status)
		return status;
	READ_BUF(16);
J. Bruce Fields's avatar
J. Bruce Fields committed
744 745
	p = xdr_decode_hyper(p, &locku->lu_offset);
	p = xdr_decode_hyper(p, &locku->lu_length);
Linus Torvalds's avatar
Linus Torvalds committed
746 747 748 749

	DECODE_TAIL;
}

750
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
751 752 753 754 755
nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
756
	lookup->lo_len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
757 758
	READ_BUF(lookup->lo_len);
	SAVEMEM(lookup->lo_name, lookup->lo_len);
759
	if ((status = check_filename(lookup->lo_name, lookup->lo_len)))
Linus Torvalds's avatar
Linus Torvalds committed
760 761 762 763 764
		return status;

	DECODE_TAIL;
}

765
static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when)
766 767 768 769 770
{
	__be32 *p;
	u32 w;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
771
	w = be32_to_cpup(p++);
772 773 774 775 776
	*share_access = w & NFS4_SHARE_ACCESS_MASK;
	*deleg_want = w & NFS4_SHARE_WANT_MASK;
	if (deleg_when)
		*deleg_when = w & NFS4_SHARE_WHEN_MASK;

777 778 779 780 781 782 783 784
	switch (w & NFS4_SHARE_ACCESS_MASK) {
	case NFS4_SHARE_ACCESS_READ:
	case NFS4_SHARE_ACCESS_WRITE:
	case NFS4_SHARE_ACCESS_BOTH:
		break;
	default:
		return nfserr_bad_xdr;
	}
785
	w &= ~NFS4_SHARE_ACCESS_MASK;
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
	if (!w)
		return nfs_ok;
	if (!argp->minorversion)
		return nfserr_bad_xdr;
	switch (w & NFS4_SHARE_WANT_MASK) {
	case NFS4_SHARE_WANT_NO_PREFERENCE:
	case NFS4_SHARE_WANT_READ_DELEG:
	case NFS4_SHARE_WANT_WRITE_DELEG:
	case NFS4_SHARE_WANT_ANY_DELEG:
	case NFS4_SHARE_WANT_NO_DELEG:
	case NFS4_SHARE_WANT_CANCEL:
		break;
	default:
		return nfserr_bad_xdr;
	}
801
	w &= ~NFS4_SHARE_WANT_MASK;
802 803
	if (!w)
		return nfs_ok;
804 805 806

	if (!deleg_when)	/* open_downgrade */
		return nfserr_inval;
807 808 809
	switch (w) {
	case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
	case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
810 811
	case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
	      NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
812 813 814 815 816 817 818 819 820 821 822
		return nfs_ok;
	}
xdr_error:
	return nfserr_bad_xdr;
}

static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
{
	__be32 *p;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
823
	*x = be32_to_cpup(p++);
824
	/* Note: unlinke access bits, deny bits may be zero. */
825
	if (*x & ~NFS4_SHARE_DENY_BOTH)
826 827 828 829 830 831
		return nfserr_bad_xdr;
	return nfs_ok;
xdr_error:
	return nfserr_bad_xdr;
}

832 833 834 835 836
static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
{
	__be32 *p;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
837
	o->len = be32_to_cpup(p++);
838 839 840 841 842 843 844 845 846 847 848

	if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT)
		return nfserr_bad_xdr;

	READ_BUF(o->len);
	SAVEMEM(o->data, o->len);
	return nfs_ok;
xdr_error:
	return nfserr_bad_xdr;
}

849
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
850 851 852
nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
{
	DECODE_HEAD;
853
	u32 dummy;
Linus Torvalds's avatar
Linus Torvalds committed
854 855 856

	memset(open->op_bmval, 0, sizeof(open->op_bmval));
	open->op_iattr.ia_valid = 0;
857
	open->op_openowner = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
858

859
	open->op_xdr_error = 0;
Linus Torvalds's avatar
Linus Torvalds committed
860
	/* seqid, share_access, share_deny, clientid, ownerlen */
861
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
862
	open->op_seqid = be32_to_cpup(p++);
863 864 865
	/* decode, yet ignore deleg_when until supported */
	status = nfsd4_decode_share_access(argp, &open->op_share_access,
					   &open->op_deleg_want, &dummy);
866 867 868 869 870
	if (status)
		goto xdr_error;
	status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
	if (status)
		goto xdr_error;
871
	READ_BUF(sizeof(clientid_t));
Linus Torvalds's avatar
Linus Torvalds committed
872
	COPYMEM(&open->op_clientid, sizeof(clientid_t));
873 874 875 876
	status = nfsd4_decode_opaque(argp, &open->op_owner);
	if (status)
		goto xdr_error;
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
877
	open->op_create = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
878 879 880 881 882
	switch (open->op_create) {
	case NFS4_OPEN_NOCREATE:
		break;
	case NFS4_OPEN_CREATE:
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
883
		open->op_createmode = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
884 885 886
		switch (open->op_createmode) {
		case NFS4_CREATE_UNCHECKED:
		case NFS4_CREATE_GUARDED:
887
			status = nfsd4_decode_fattr(argp, open->op_bmval,
888
				&open->op_iattr, &open->op_acl, &open->op_label);
889
			if (status)
Linus Torvalds's avatar
Linus Torvalds committed
890 891 892
				goto out;
			break;
		case NFS4_CREATE_EXCLUSIVE:
893 894
			READ_BUF(NFS4_VERIFIER_SIZE);
			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
895
			break;
Benny Halevy's avatar
Benny Halevy committed
896 897 898
		case NFS4_CREATE_EXCLUSIVE4_1:
			if (argp->minorversion < 1)
				goto xdr_error;
899 900
			READ_BUF(NFS4_VERIFIER_SIZE);
			COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
Benny Halevy's avatar
Benny Halevy committed
901
			status = nfsd4_decode_fattr(argp, open->op_bmval,
902
				&open->op_iattr, &open->op_acl, &open->op_label);
Benny Halevy's avatar
Benny Halevy committed
903 904 905
			if (status)
				goto out;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
906 907 908 909 910 911 912 913 914 915
		default:
			goto xdr_error;
		}
		break;
	default:
		goto xdr_error;
	}

	/* open_claim */
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
916
	open->op_claim_type = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
917 918 919 920
	switch (open->op_claim_type) {
	case NFS4_OPEN_CLAIM_NULL:
	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
921
		open->op_fname.len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
922 923
		READ_BUF(open->op_fname.len);
		SAVEMEM(open->op_fname.data, open->op_fname.len);
924
		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
Linus Torvalds's avatar
Linus Torvalds committed
925 926 927 928
			return status;
		break;
	case NFS4_OPEN_CLAIM_PREVIOUS:
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
929
		open->op_delegate_type = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
930 931
		break;
	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
932 933 934 935
		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
		if (status)
			return status;
		READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
936
		open->op_fname.len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
937 938
		READ_BUF(open->op_fname.len);
		SAVEMEM(open->op_fname.data, open->op_fname.len);
939
		if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
Linus Torvalds's avatar
Linus Torvalds committed
940 941
			return status;
		break;
942 943 944 945 946 947 948 949 950 951 952 953 954
	case NFS4_OPEN_CLAIM_FH:
	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
		if (argp->minorversion < 1)
			goto xdr_error;
		/* void */
		break;
	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
		if (argp->minorversion < 1)
			goto xdr_error;
		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
		if (status)
			return status;
		break;
Linus Torvalds's avatar
Linus Torvalds committed
955 956 957 958 959 960 961
	default:
		goto xdr_error;
	}

	DECODE_TAIL;
}

962
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
963 964 965
nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
{
	DECODE_HEAD;
966 967 968 969

	if (argp->minorversion >= 1)
		return nfserr_notsupp;

970 971 972 973
	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
	if (status)
		return status;
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
974
	open_conf->oc_seqid = be32_to_cpup(p++);
975

Linus Torvalds's avatar
Linus Torvalds committed
976 977 978
	DECODE_TAIL;
}

979
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
980 981 982 983
nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
{
	DECODE_HEAD;
		    
984 985 986
	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
	if (status)
		return status;
987
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
988
	open_down->od_seqid = be32_to_cpup(p++);
989 990
	status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
					   &open_down->od_deleg_want, NULL);
991 992 993 994 995
	if (status)
		return status;
	status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
	if (status)
		return status;
Linus Torvalds's avatar
Linus Torvalds committed
996 997 998
	DECODE_TAIL;
}

999
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1000 1001 1002 1003 1004
nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1005
	putfh->pf_fhlen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1006 1007 1008 1009 1010 1011 1012 1013
	if (putfh->pf_fhlen > NFS4_FHSIZE)
		goto xdr_error;
	READ_BUF(putfh->pf_fhlen);
	SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);

	DECODE_TAIL;
}

1014 1015 1016 1017 1018 1019 1020 1021
static __be32
nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, void *p)
{
	if (argp->minorversion == 0)
		return nfs_ok;
	return nfserr_notsupp;
}

1022
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1023 1024 1025 1026
nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
{
	DECODE_HEAD;

1027 1028 1029 1030
	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
	if (status)
		return status;
	READ_BUF(12);
J. Bruce Fields's avatar
J. Bruce Fields committed
1031
	p = xdr_decode_hyper(p, &read->rd_offset);
J. Bruce Fields's avatar
J. Bruce Fields committed
1032
	read->rd_length = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1033 1034 1035 1036

	DECODE_TAIL;
}

1037
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1038 1039 1040 1041 1042
nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
{
	DECODE_HEAD;

	READ_BUF(24);
J. Bruce Fields's avatar
J. Bruce Fields committed
1043
	p = xdr_decode_hyper(p, &readdir->rd_cookie);
Linus Torvalds's avatar
Linus Torvalds committed
1044
	COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
J. Bruce Fields's avatar
J. Bruce Fields committed
1045 1046
	readdir->rd_dircount = be32_to_cpup(p++);
	readdir->rd_maxcount = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1047 1048 1049 1050 1051 1052
	if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
		goto out;

	DECODE_TAIL;
}

1053
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1054 1055 1056 1057 1058
nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1059
	remove->rm_namelen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1060 1061
	READ_BUF(remove->rm_namelen);
	SAVEMEM(remove->rm_name, remove->rm_namelen);
1062
	if ((status = check_filename(remove->rm_name, remove->rm_namelen)))
Linus Torvalds's avatar
Linus Torvalds committed
1063 1064 1065 1066 1067
		return status;

	DECODE_TAIL;
}

1068
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1069 1070 1071 1072 1073
nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1074
	rename->rn_snamelen = be32_to_cpup(p++);
1075
	READ_BUF(rename->rn_snamelen);
Linus Torvalds's avatar
Linus Torvalds committed
1076
	SAVEMEM(rename->rn_sname, rename->rn_snamelen);
1077
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1078
	rename->rn_tnamelen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1079 1080
	READ_BUF(rename->rn_tnamelen);
	SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
1081
	if ((status = check_filename(rename->rn_sname, rename->rn_snamelen)))
Linus Torvalds's avatar
Linus Torvalds committed
1082
		return status;
1083
	if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen)))
Linus Torvalds's avatar
Linus Torvalds committed
1084 1085 1086 1087 1088
		return status;

	DECODE_TAIL;
}

1089
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1090 1091 1092 1093
nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
{
	DECODE_HEAD;

1094 1095 1096
	if (argp->minorversion >= 1)
		return nfserr_notsupp;

Linus Torvalds's avatar
Linus Torvalds committed
1097 1098 1099 1100 1101 1102
	READ_BUF(sizeof(clientid_t));
	COPYMEM(clientid, sizeof(clientid_t));

	DECODE_TAIL;
}

1103 1104 1105 1106 1107 1108 1109
static __be32
nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
		     struct nfsd4_secinfo *secinfo)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1110
	secinfo->si_namelen = be32_to_cpup(p++);
1111 1112
	READ_BUF(secinfo->si_namelen);
	SAVEMEM(secinfo->si_name, secinfo->si_namelen);
1113
	status = check_filename(secinfo->si_name, secinfo->si_namelen);
1114 1115 1116 1117 1118
	if (status)
		return status;
	DECODE_TAIL;
}

1119 1120 1121 1122 1123 1124 1125
static __be32
nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
		     struct nfsd4_secinfo_no_name *sin)
{
	DECODE_HEAD;

	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1126
	sin->sin_style = be32_to_cpup(p++);
1127 1128 1129
	DECODE_TAIL;
}

1130
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1131 1132
nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
{
1133
	__be32 status;
Linus Torvalds's avatar
Linus Torvalds committed
1134

1135 1136 1137
	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
	if (status)
		return status;
1138
	return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
1139
				  &setattr->sa_acl, &setattr->sa_label);
Linus Torvalds's avatar
Linus Torvalds committed
1140 1141
}

1142
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1143 1144 1145 1146
nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
{
	DECODE_HEAD;

1147 1148 1149
	if (argp->minorversion >= 1)
		return nfserr_notsupp;

1150 1151
	READ_BUF(NFS4_VERIFIER_SIZE);
	COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
1152

1153 1154 1155 1156
	status = nfsd4_decode_opaque(argp, &setclientid->se_name);
	if (status)
		return nfserr_bad_xdr;
	READ_BUF(8);
J. Bruce Fields's avatar
J. Bruce Fields committed
1157 1158
	setclientid->se_callback_prog = be32_to_cpup(p++);
	setclientid->se_callback_netid_len = be32_to_cpup(p++);
1159
	READ_BUF(setclientid->se_callback_netid_len);
Linus Torvalds's avatar
Linus Torvalds committed
1160
	SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
1161
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1162
	setclientid->se_callback_addr_len = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1163

1164
	READ_BUF(setclientid->se_callback_addr_len);
Linus Torvalds's avatar
Linus Torvalds committed
1165
	SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
1166
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1167
	setclientid->se_callback_ident = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1168 1169 1170 1171

	DECODE_TAIL;
}

1172
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1173 1174 1175 1176
nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
{
	DECODE_HEAD;

1177 1178 1179
	if (argp->minorversion >= 1)
		return nfserr_notsupp;

1180
	READ_BUF(8 + NFS4_VERIFIER_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
1181
	COPYMEM(&scd_c->sc_clientid, 8);
1182
	COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
1183 1184 1185 1186 1187

	DECODE_TAIL;
}

/* Also used for NVERIFY */
1188
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1189 1190 1191 1192 1193 1194 1195 1196
nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
{
	DECODE_HEAD;

	if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval)))
		goto out;

	/* For convenience's sake, we compare raw xdr'd attributes in
1197 1198
	 * nfsd4_proc_verify */

Linus Torvalds's avatar
Linus Torvalds committed
1199
	READ_BUF(4);
J. Bruce Fields's avatar
J. Bruce Fields committed
1200
	verify->ve_attrlen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1201 1202 1203 1204 1205 1206
	READ_BUF(verify->ve_attrlen);
	SAVEMEM(verify->ve_attrval, verify->ve_attrlen);

	DECODE_TAIL;
}

1207
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1208 1209 1210 1211 1212 1213
nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
{
	int avail;
	int len;
	DECODE_HEAD;

1214 1215 1216 1217
	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
	if (status)
		return status;
	READ_BUF(16);
J. Bruce Fields's avatar
J. Bruce Fields committed
1218
	p = xdr_decode_hyper(p, &write->wr_offset);
J. Bruce Fields's avatar
J. Bruce Fields committed
1219
	write->wr_stable_how = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1220 1221
	if (write->wr_stable_how > 2)
		goto xdr_error;
J. Bruce Fields's avatar
J. Bruce Fields committed
1222
	write->wr_buflen = be32_to_cpup(p++);
Linus Torvalds's avatar
Linus Torvalds committed
1223 1224 1225 1226 1227 1228 1229

	/* Sorry .. no magic macros for this.. *
	 * READ_BUF(write->wr_buflen);
	 * SAVEMEM(write->wr_buf, write->wr_buflen);
	 */
	avail = (char*)argp->end - (char*)argp->p;
	if (avail + argp->pagelen < write->wr_buflen) {
1230 1231
		dprintk("NFSD: xdr error (%s:%d)\n",
				__FILE__, __LINE__);
Linus Torvalds's avatar
Linus Torvalds committed
1232 1233
		goto xdr_error;
	}
1234 1235 1236
	write->wr_head.iov_base = p;
	write->wr_head.iov_len = avail;
	write->wr_pagelist = argp->pagelist;
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249

	len = XDR_QUADLEN(write->wr_buflen) << 2;
	if (len >= avail) {
		int pages;

		len -= avail;

		pages = len >> PAGE_SHIFT;
		argp->pagelist += pages;
		argp->pagelen -= pages * PAGE_SIZE;
		len -= pages * PAGE_SIZE;

		argp->p = (__be32 *)page_address(argp->pagelist[0]);
1250
		argp->pagelist++;
1251
		argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
1252
	}
1253
	argp->p += XDR_QUADLEN(len);
Linus Torvalds's avatar
Linus Torvalds committed
1254 1255 1256 1257

	DECODE_TAIL;
}

1258
static __be32
Linus Torvalds's avatar
Linus Torvalds committed
1259 1260 1261 1262
nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
{
	DECODE_HEAD;

1263 1264 1265
	if (argp->minorversion >= 1)
		return nfserr_notsupp;

Linus Torvalds's avatar
Linus Torvalds committed
1266 1267
	READ_BUF(12);
	COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));