nfs4proc.c 37.8 KB
Newer Older
L
Linus Torvalds 已提交
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 36 37 38 39 40
/*
 *  fs/nfsd/nfs4proc.c
 *
 *  Server-side procedures 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.
 */

#include <linux/param.h>
#include <linux/major.h>
#include <linux/slab.h>
41
#include <linux/file.h>
L
Linus Torvalds 已提交
42 43 44 45 46 47 48 49

#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h>
#include <linux/nfs4.h>
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
#include <linux/nfs4_acl.h>
A
Andy Adamson 已提交
50
#include <linux/sunrpc/gss_api.h>
L
Linus Torvalds 已提交
51 52 53

#define NFSDDBG_FACILITY		NFSDDBG_PROC

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
static u32 nfsd_attrmask[] = {
	NFSD_WRITEABLE_ATTRS_WORD0,
	NFSD_WRITEABLE_ATTRS_WORD1,
	NFSD_WRITEABLE_ATTRS_WORD2
};

static u32 nfsd41_ex_attrmask[] = {
	NFSD_SUPPATTR_EXCLCREAT_WORD0,
	NFSD_SUPPATTR_EXCLCREAT_WORD1,
	NFSD_SUPPATTR_EXCLCREAT_WORD2
};

static __be32
check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		   u32 *bmval, u32 *writable)
{
	struct dentry *dentry = cstate->current_fh.fh_dentry;
	struct svc_export *exp = cstate->current_fh.fh_export;

	/*
	 * Check about attributes are supported by the NFSv4 server or not.
	 * According to spec, unsupported attributes return ERR_ATTRNOTSUPP.
	 */
	if ((bmval[0] & ~nfsd_suppattrs0(cstate->minorversion)) ||
	    (bmval[1] & ~nfsd_suppattrs1(cstate->minorversion)) ||
	    (bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
		return nfserr_attrnotsupp;

	/*
	 * Check FATTR4_WORD0_ACL & FATTR4_WORD0_FS_LOCATIONS can be supported
	 * in current environment or not.
	 */
	if (bmval[0] & FATTR4_WORD0_ACL) {
		if (!IS_POSIXACL(dentry->d_inode))
			return nfserr_attrnotsupp;
	}
	if (bmval[0] & FATTR4_WORD0_FS_LOCATIONS) {
		if (exp->ex_fslocs.locations == NULL)
			return nfserr_attrnotsupp;
	}

	/*
	 * According to spec, read-only attributes return ERR_INVAL.
	 */
	if (writable) {
		if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
		    (bmval[2] & ~writable[2]))
			return nfserr_inval;
	}

	return nfs_ok;
}

static __be32
nfsd4_check_open_attributes(struct svc_rqst *rqstp,
	struct nfsd4_compound_state *cstate, struct nfsd4_open *open)
{
	__be32 status = nfs_ok;

	if (open->op_create == NFS4_OPEN_CREATE) {
		if (open->op_createmode == NFS4_CREATE_UNCHECKED
		    || open->op_createmode == NFS4_CREATE_GUARDED)
			status = check_attr_support(rqstp, cstate,
					open->op_bmval, nfsd_attrmask);
		else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1)
			status = check_attr_support(rqstp, cstate,
					open->op_bmval, nfsd41_ex_attrmask);
	}

	return status;
}

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
static int
is_create_with_attrs(struct nfsd4_open *open)
{
	return open->op_create == NFS4_OPEN_CREATE
		&& (open->op_createmode == NFS4_CREATE_UNCHECKED
		    || open->op_createmode == NFS4_CREATE_GUARDED
		    || open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1);
}

/*
 * if error occurs when setting the acl, just clear the acl bit
 * in the returned attr bitmap.
 */
static void
do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
		struct nfs4_acl *acl, u32 *bmval)
{
	__be32 status;

	status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
	if (status)
		/*
		 * We should probably fail the whole open at this point,
		 * but we've already created the file, so it's too late;
		 * So this seems the least of evils:
		 */
		bmval[0] &= ~FATTR4_WORD0_ACL;
}

L
Linus Torvalds 已提交
155 156 157 158 159 160 161 162 163 164
static inline void
fh_dup2(struct svc_fh *dst, struct svc_fh *src)
{
	fh_put(dst);
	dget(src->fh_dentry);
	if (src->fh_export)
		cache_get(&src->fh_export->h);
	*dst = *src;
}

165
static __be32
166
do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open, int accmode)
L
Linus Torvalds 已提交
167
{
168
	__be32 status;
L
Linus Torvalds 已提交
169 170 171 172 173 174

	if (open->op_truncate &&
		!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
		return nfserr_inval;

	if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
M
Miklos Szeredi 已提交
175
		accmode |= NFSD_MAY_READ;
176
	if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
M
Miklos Szeredi 已提交
177
		accmode |= (NFSD_MAY_WRITE | NFSD_MAY_TRUNC);
178
	if (open->op_share_deny & NFS4_SHARE_DENY_WRITE)
M
Miklos Szeredi 已提交
179
		accmode |= NFSD_MAY_WRITE;
L
Linus Torvalds 已提交
180 181 182 183 184 185

	status = fh_verify(rqstp, current_fh, S_IFREG, accmode);

	return status;
}

186
static __be32
L
Linus Torvalds 已提交
187 188 189
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
	struct svc_fh resfh;
190
	__be32 status;
191
	int created = 0;
L
Linus Torvalds 已提交
192 193 194 195 196

	fh_init(&resfh, NFS4_FHSIZE);
	open->op_truncate = 0;

	if (open->op_create) {
B
Benny Halevy 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
		/* FIXME: check session persistence and pnfs flags.
		 * The nfsv4.1 spec requires the following semantics:
		 *
		 * Persistent   | pNFS   | Server REQUIRED | Client Allowed
		 * Reply Cache  | server |                 |
		 * -------------+--------+-----------------+--------------------
		 * no           | no     | EXCLUSIVE4_1    | EXCLUSIVE4_1
		 *              |        |                 | (SHOULD)
		 *              |        | and EXCLUSIVE4  | or EXCLUSIVE4
		 *              |        |                 | (SHOULD NOT)
		 * no           | yes    | EXCLUSIVE4_1    | EXCLUSIVE4_1
		 * yes          | no     | GUARDED4        | GUARDED4
		 * yes          | yes    | GUARDED4        | GUARDED4
		 */

L
Linus Torvalds 已提交
212 213 214 215 216 217 218
		/*
		 * Note: create modes (UNCHECKED,GUARDED...) are the same
		 * in NFSv4 as in v3.
		 */
		status = nfsd_create_v3(rqstp, current_fh, open->op_fname.data,
					open->op_fname.len, &open->op_iattr,
					&resfh, open->op_createmode,
219 220 221
					(u32 *)open->op_verf.data,
					&open->op_truncate, &created);

222 223 224 225
		/*
		 * Following rfc 3530 14.2.16, use the returned bitmask
		 * to indicate which attributes we used to store the
		 * verifier:
226 227
		 */
		if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0)
228
			open->op_bmval[1] = (FATTR4_WORD1_TIME_ACCESS |
229
						FATTR4_WORD1_TIME_MODIFY);
230
	} else {
L
Linus Torvalds 已提交
231 232 233 234
		status = nfsd_lookup(rqstp, current_fh,
				     open->op_fname.data, open->op_fname.len, &resfh);
		fh_unlock(current_fh);
	}
235 236
	if (status)
		goto out;
L
Linus Torvalds 已提交
237

238 239 240
	if (is_create_with_attrs(open) && open->op_acl != NULL)
		do_set_nfs4_acl(rqstp, &resfh, open->op_acl, open->op_bmval);

241
	set_change_info(&open->op_cinfo, current_fh);
J
J. Bruce Fields 已提交
242
	fh_dup2(current_fh, &resfh);
L
Linus Torvalds 已提交
243

244
	/* set reply cache */
245 246
	fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh,
			&resfh.fh_handle);
247
	if (!created)
M
Miklos Szeredi 已提交
248 249
		status = do_open_permission(rqstp, current_fh, open,
					    NFSD_MAY_NOP);
L
Linus Torvalds 已提交
250

251
out:
L
Linus Torvalds 已提交
252 253 254 255
	fh_put(&resfh);
	return status;
}

256
static __be32
L
Linus Torvalds 已提交
257 258
do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
259
	__be32 status;
L
Linus Torvalds 已提交
260 261 262 263 264 265 266 267 268 269 270 271

	/* Only reclaims from previously confirmed clients are valid */
	if ((status = nfs4_check_open_reclaim(&open->op_clientid)))
		return status;

	/* We don't know the target directory, and therefore can not
	* set the change info
	*/

	memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info));

	/* set replay cache */
272 273
	fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh,
			&current_fh->fh_handle);
L
Linus Torvalds 已提交
274 275 276 277

	open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
		(open->op_iattr.ia_size == 0);

M
Miklos Szeredi 已提交
278 279
	status = do_open_permission(rqstp, current_fh, open,
				    NFSD_MAY_OWNER_OVERRIDE);
L
Linus Torvalds 已提交
280 281 282 283

	return status;
}

A
Andy Adamson 已提交
284 285 286 287 288 289 290 291 292
static void
copy_clientid(clientid_t *clid, struct nfsd4_session *session)
{
	struct nfsd4_sessionid *sid =
			(struct nfsd4_sessionid *)session->se_sessionid.data;

	clid->cl_boot = sid->clientid.cl_boot;
	clid->cl_id = sid->clientid.cl_id;
}
L
Linus Torvalds 已提交
293

294
static __be32
295
nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
296
	   struct nfsd4_open *open)
L
Linus Torvalds 已提交
297
{
298
	__be32 status;
A
Andy Adamson 已提交
299 300
	struct nfsd4_compoundres *resp;

L
Linus Torvalds 已提交
301 302 303 304 305 306 307 308
	dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n",
		(int)open->op_fname.len, open->op_fname.data,
		open->op_stateowner);

	/* This check required by spec. */
	if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
		return nfserr_inval;

A
Andy Adamson 已提交
309 310 311
	if (nfsd4_has_session(cstate))
		copy_clientid(&open->op_clientid, cstate->session);

L
Linus Torvalds 已提交
312 313 314
	nfs4_lock_state();

	/* check seqid for replay. set nfs4_owner */
A
Andy Adamson 已提交
315 316
	resp = rqstp->rq_resp;
	status = nfsd4_process_open1(&resp->cstate, open);
A
Al Viro 已提交
317
	if (status == nfserr_replay_me) {
L
Linus Torvalds 已提交
318
		struct nfs4_replay *rp = &open->op_stateowner->so_replay;
319
		fh_put(&cstate->current_fh);
320 321
		fh_copy_shallow(&cstate->current_fh.fh_handle,
				&rp->rp_openfh);
M
Miklos Szeredi 已提交
322
		status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP);
L
Linus Torvalds 已提交
323 324 325 326
		if (status)
			dprintk("nfsd4_open: replay failed"
				" restoring previous filehandle\n");
		else
A
Al Viro 已提交
327
			status = nfserr_replay_me;
L
Linus Torvalds 已提交
328 329 330
	}
	if (status)
		goto out;
331

332 333 334 335
	status = nfsd4_check_open_attributes(rqstp, cstate, open);
	if (status)
		goto out;

336 337
	/* Openowner is now set, so sequence id will get bumped.  Now we need
	 * these checks before we do any creates: */
338
	status = nfserr_grace;
339
	if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
340 341
		goto out;
	status = nfserr_no_grace;
342
	if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
343
		goto out;
344

L
Linus Torvalds 已提交
345
	switch (open->op_claim_type) {
346
		case NFS4_OPEN_CLAIM_DELEGATE_CUR:
L
Linus Torvalds 已提交
347 348 349 350 351 352 353
		case NFS4_OPEN_CLAIM_NULL:
			/*
			 * (1) set CURRENT_FH to the file being opened,
			 * creating it if necessary, (2) set open->op_cinfo,
			 * (3) set open->op_truncate if the file is to be
			 * truncated after opening, (4) do permission checking.
			 */
354 355
			status = do_open_lookup(rqstp, &cstate->current_fh,
						open);
L
Linus Torvalds 已提交
356 357 358 359
			if (status)
				goto out;
			break;
		case NFS4_OPEN_CLAIM_PREVIOUS:
360
			open->op_stateowner->so_confirmed = 1;
L
Linus Torvalds 已提交
361 362 363 364 365 366
			/*
			 * The CURRENT_FH is already set to the file being
			 * opened.  (1) set open->op_cinfo, (2) set
			 * open->op_truncate if the file is to be truncated
			 * after opening, (3) do permission checking.
			*/
367 368
			status = do_open_fhandle(rqstp, &cstate->current_fh,
						 open);
L
Linus Torvalds 已提交
369 370 371 372
			if (status)
				goto out;
			break;
             	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
373
			open->op_stateowner->so_confirmed = 1;
374
			dprintk("NFSD: unsupported OPEN claim type %d\n",
L
Linus Torvalds 已提交
375 376 377 378
				open->op_claim_type);
			status = nfserr_notsupp;
			goto out;
		default:
379
			dprintk("NFSD: Invalid OPEN claim type %d\n",
L
Linus Torvalds 已提交
380 381 382 383 384 385 386 387 388
				open->op_claim_type);
			status = nfserr_inval;
			goto out;
	}
	/*
	 * nfsd4_process_open2() does the actual opening of the file.  If
	 * successful, it (1) truncates the file if open->op_truncate was
	 * set, (2) sets open->op_stateid, (3) sets open->op_delegation.
	 */
389
	status = nfsd4_process_open2(rqstp, &cstate->current_fh, open);
L
Linus Torvalds 已提交
390
out:
N
Neil Brown 已提交
391
	if (open->op_stateowner) {
L
Linus Torvalds 已提交
392
		nfs4_get_stateowner(open->op_stateowner);
393
		cstate->replay_owner = open->op_stateowner;
N
Neil Brown 已提交
394
	}
L
Linus Torvalds 已提交
395 396 397 398 399 400 401
	nfs4_unlock_state();
	return status;
}

/*
 * filehandle-manipulating ops.
 */
402
static __be32
403 404
nfsd4_getfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct svc_fh **getfh)
L
Linus Torvalds 已提交
405
{
406
	if (!cstate->current_fh.fh_dentry)
L
Linus Torvalds 已提交
407 408
		return nfserr_nofilehandle;

409
	*getfh = &cstate->current_fh;
L
Linus Torvalds 已提交
410 411 412
	return nfs_ok;
}

413
static __be32
414 415
nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct nfsd4_putfh *putfh)
L
Linus Torvalds 已提交
416
{
417 418 419 420
	fh_put(&cstate->current_fh);
	cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen;
	memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval,
	       putfh->pf_fhlen);
M
Miklos Szeredi 已提交
421
	return fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP);
L
Linus Torvalds 已提交
422 423
}

424
static __be32
425 426
nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		void *arg)
L
Linus Torvalds 已提交
427
{
428
	__be32 status;
L
Linus Torvalds 已提交
429

430
	fh_put(&cstate->current_fh);
431
	status = exp_pseudoroot(rqstp, &cstate->current_fh);
L
Linus Torvalds 已提交
432 433 434
	return status;
}

435
static __be32
436 437
nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		void *arg)
L
Linus Torvalds 已提交
438
{
439
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
440 441
		return nfserr_restorefh;

442
	fh_dup2(&cstate->current_fh, &cstate->save_fh);
L
Linus Torvalds 已提交
443 444 445
	return nfs_ok;
}

446
static __be32
447 448
nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     void *arg)
L
Linus Torvalds 已提交
449
{
450
	if (!cstate->current_fh.fh_dentry)
L
Linus Torvalds 已提交
451 452
		return nfserr_nofilehandle;

453
	fh_dup2(&cstate->save_fh, &cstate->current_fh);
L
Linus Torvalds 已提交
454 455 456 457 458 459
	return nfs_ok;
}

/*
 * misc nfsv4 ops
 */
460
static __be32
461 462
nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_access *access)
L
Linus Torvalds 已提交
463 464 465 466 467
{
	if (access->ac_req_access & ~NFS3_ACCESS_FULL)
		return nfserr_inval;

	access->ac_resp_access = access->ac_req_access;
468 469
	return nfsd_access(rqstp, &cstate->current_fh, &access->ac_resp_access,
			   &access->ac_supported);
L
Linus Torvalds 已提交
470 471
}

472
static __be32
473 474
nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_commit *commit)
L
Linus Torvalds 已提交
475
{
476
	__be32 status;
L
Linus Torvalds 已提交
477 478 479 480 481

	u32 *p = (u32 *)commit->co_verf.data;
	*p++ = nfssvc_boot.tv_sec;
	*p++ = nfssvc_boot.tv_usec;

482 483
	status = nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
			     commit->co_count);
L
Linus Torvalds 已提交
484 485 486 487 488
	if (status == nfserr_symlink)
		status = nfserr_inval;
	return status;
}

489
static __be32
490 491
nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_create *create)
L
Linus Torvalds 已提交
492 493
{
	struct svc_fh resfh;
494
	__be32 status;
L
Linus Torvalds 已提交
495 496 497 498
	dev_t rdev;

	fh_init(&resfh, NFS4_FHSIZE);

M
Miklos Szeredi 已提交
499 500
	status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR,
			   NFSD_MAY_CREATE);
L
Linus Torvalds 已提交
501 502 503 504 505
	if (status == nfserr_symlink)
		status = nfserr_notdir;
	if (status)
		return status;

506 507 508 509 510
	status = check_attr_support(rqstp, cstate, create->cr_bmval,
				    nfsd_attrmask);
	if (status)
		return status;

L
Linus Torvalds 已提交
511 512 513 514 515 516 517 518 519 520 521
	switch (create->cr_type) {
	case NF4LNK:
		/* ugh! we have to null-terminate the linktext, or
		 * vfs_symlink() will choke.  it is always safe to
		 * null-terminate by brute force, since at worst we
		 * will overwrite the first byte of the create namelen
		 * in the XDR buffer, which has already been extracted
		 * during XDR decode.
		 */
		create->cr_linkname[create->cr_linklen] = 0;

522 523 524 525
		status = nfsd_symlink(rqstp, &cstate->current_fh,
				      create->cr_name, create->cr_namelen,
				      create->cr_linkname, create->cr_linklen,
				      &resfh, &create->cr_iattr);
L
Linus Torvalds 已提交
526 527 528 529 530 531 532
		break;

	case NF4BLK:
		rdev = MKDEV(create->cr_specdata1, create->cr_specdata2);
		if (MAJOR(rdev) != create->cr_specdata1 ||
		    MINOR(rdev) != create->cr_specdata2)
			return nfserr_inval;
533 534 535
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFBLK, rdev, &resfh);
L
Linus Torvalds 已提交
536 537 538 539 540 541 542
		break;

	case NF4CHR:
		rdev = MKDEV(create->cr_specdata1, create->cr_specdata2);
		if (MAJOR(rdev) != create->cr_specdata1 ||
		    MINOR(rdev) != create->cr_specdata2)
			return nfserr_inval;
543 544 545
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr,S_IFCHR, rdev, &resfh);
L
Linus Torvalds 已提交
546 547 548
		break;

	case NF4SOCK:
549 550 551
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFSOCK, 0, &resfh);
L
Linus Torvalds 已提交
552 553 554
		break;

	case NF4FIFO:
555 556 557
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFIFO, 0, &resfh);
L
Linus Torvalds 已提交
558 559 560 561
		break;

	case NF4DIR:
		create->cr_iattr.ia_valid &= ~ATTR_SIZE;
562 563 564
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFDIR, 0, &resfh);
L
Linus Torvalds 已提交
565 566 567 568 569 570
		break;

	default:
		status = nfserr_badtype;
	}

571 572
	if (status)
		goto out;
L
Linus Torvalds 已提交
573

574 575 576 577 578 579 580 581
	if (create->cr_acl != NULL)
		do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
				create->cr_bmval);

	fh_unlock(&cstate->current_fh);
	set_change_info(&create->cr_cinfo, &cstate->current_fh);
	fh_dup2(&cstate->current_fh, &resfh);
out:
L
Linus Torvalds 已提交
582 583 584 585
	fh_put(&resfh);
	return status;
}

586
static __be32
587 588
nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_getattr *getattr)
L
Linus Torvalds 已提交
589
{
590
	__be32 status;
L
Linus Torvalds 已提交
591

M
Miklos Szeredi 已提交
592
	status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP);
L
Linus Torvalds 已提交
593 594 595 596 597 598
	if (status)
		return status;

	if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
		return nfserr_inval;

599 600 601
	getattr->ga_bmval[0] &= nfsd_suppattrs0(cstate->minorversion);
	getattr->ga_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
	getattr->ga_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
L
Linus Torvalds 已提交
602

603
	getattr->ga_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
604 605 606
	return nfs_ok;
}

607
static __be32
608 609
nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	   struct nfsd4_link *link)
L
Linus Torvalds 已提交
610
{
611
	__be32 status = nfserr_nofilehandle;
L
Linus Torvalds 已提交
612

613
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
614
		return status;
615 616
	status = nfsd_link(rqstp, &cstate->current_fh,
			   link->li_name, link->li_namelen, &cstate->save_fh);
L
Linus Torvalds 已提交
617
	if (!status)
618
		set_change_info(&link->li_cinfo, &cstate->current_fh);
L
Linus Torvalds 已提交
619 620 621
	return status;
}

622
static __be32
623 624
nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      void *arg)
L
Linus Torvalds 已提交
625 626
{
	struct svc_fh tmp_fh;
627
	__be32 ret;
L
Linus Torvalds 已提交
628 629

	fh_init(&tmp_fh, NFS4_FHSIZE);
630 631
	ret = exp_pseudoroot(rqstp, &tmp_fh);
	if (ret)
L
Linus Torvalds 已提交
632
		return ret;
633
	if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) {
L
Linus Torvalds 已提交
634 635 636 637
		fh_put(&tmp_fh);
		return nfserr_noent;
	}
	fh_put(&tmp_fh);
638 639
	return nfsd_lookup(rqstp, &cstate->current_fh,
			   "..", 2, &cstate->current_fh);
L
Linus Torvalds 已提交
640 641
}

642
static __be32
643 644
nfsd4_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_lookup *lookup)
L
Linus Torvalds 已提交
645
{
646 647 648
	return nfsd_lookup(rqstp, &cstate->current_fh,
			   lookup->lo_name, lookup->lo_len,
			   &cstate->current_fh);
L
Linus Torvalds 已提交
649 650
}

651
static __be32
652 653
nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	   struct nfsd4_read *read)
L
Linus Torvalds 已提交
654
{
655
	__be32 status;
L
Linus Torvalds 已提交
656 657 658

	/* no need to check permission - this will be done in nfsd_read() */

659
	read->rd_filp = NULL;
L
Linus Torvalds 已提交
660 661 662 663 664
	if (read->rd_offset >= OFFSET_MAX)
		return nfserr_inval;

	nfs4_lock_state();
	/* check stateid */
665 666
	if ((status = nfs4_preprocess_stateid_op(cstate, &read->rd_stateid,
						 RD_STATE, &read->rd_filp))) {
L
Linus Torvalds 已提交
667 668 669
		dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
		goto out;
	}
670 671
	if (read->rd_filp)
		get_file(read->rd_filp);
L
Linus Torvalds 已提交
672 673 674 675
	status = nfs_ok;
out:
	nfs4_unlock_state();
	read->rd_rqstp = rqstp;
676
	read->rd_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
677 678 679
	return status;
}

680
static __be32
681 682
nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_readdir *readdir)
L
Linus Torvalds 已提交
683 684 685 686 687 688 689 690 691
{
	u64 cookie = readdir->rd_cookie;
	static const nfs4_verifier zeroverf;

	/* no need to check permission - this will be done in nfsd_readdir() */

	if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
		return nfserr_inval;

692 693 694
	readdir->rd_bmval[0] &= nfsd_suppattrs0(cstate->minorversion);
	readdir->rd_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
	readdir->rd_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
L
Linus Torvalds 已提交
695 696 697 698 699 700

	if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) ||
	    (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE)))
		return nfserr_bad_cookie;

	readdir->rd_rqstp = rqstp;
701
	readdir->rd_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
702 703 704
	return nfs_ok;
}

705
static __be32
706 707
nfsd4_readlink(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	       struct nfsd4_readlink *readlink)
L
Linus Torvalds 已提交
708 709
{
	readlink->rl_rqstp = rqstp;
710
	readlink->rl_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
711 712 713
	return nfs_ok;
}

714
static __be32
715 716
nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_remove *remove)
L
Linus Torvalds 已提交
717
{
718
	__be32 status;
L
Linus Torvalds 已提交
719

720
	if (locks_in_grace())
721
		return nfserr_grace;
722 723
	status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
			     remove->rm_name, remove->rm_namelen);
L
Linus Torvalds 已提交
724 725 726
	if (status == nfserr_symlink)
		return nfserr_notdir;
	if (!status) {
727 728
		fh_unlock(&cstate->current_fh);
		set_change_info(&remove->rm_cinfo, &cstate->current_fh);
L
Linus Torvalds 已提交
729 730 731 732
	}
	return status;
}

733
static __be32
734 735
nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_rename *rename)
L
Linus Torvalds 已提交
736
{
737
	__be32 status = nfserr_nofilehandle;
L
Linus Torvalds 已提交
738

739
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
740
		return status;
741
	if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags
742 743
					& NFSEXP_NOSUBTREECHECK))
		return nfserr_grace;
744 745
	status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
			     rename->rn_snamelen, &cstate->current_fh,
L
Linus Torvalds 已提交
746 747 748 749 750 751 752
			     rename->rn_tname, rename->rn_tnamelen);

	/* the underlying filesystem returns different error's than required
	 * by NFSv4. both save_fh and current_fh have been verified.. */
	if (status == nfserr_isdir)
		status = nfserr_exist;
	else if ((status == nfserr_notdir) &&
753 754
                  (S_ISDIR(cstate->save_fh.fh_dentry->d_inode->i_mode) &&
                   S_ISDIR(cstate->current_fh.fh_dentry->d_inode->i_mode)))
L
Linus Torvalds 已提交
755 756 757 758 759
		status = nfserr_exist;
	else if (status == nfserr_symlink)
		status = nfserr_notdir;

	if (!status) {
760 761
		set_change_info(&rename->rn_sinfo, &cstate->current_fh);
		set_change_info(&rename->rn_tinfo, &cstate->save_fh);
L
Linus Torvalds 已提交
762 763 764 765
	}
	return status;
}

A
Andy Adamson 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
static __be32
nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_secinfo *secinfo)
{
	struct svc_fh resfh;
	struct svc_export *exp;
	struct dentry *dentry;
	__be32 err;

	fh_init(&resfh, NFS4_FHSIZE);
	err = nfsd_lookup_dentry(rqstp, &cstate->current_fh,
				    secinfo->si_name, secinfo->si_namelen,
				    &exp, &dentry);
	if (err)
		return err;
	if (dentry->d_inode == NULL) {
		exp_put(exp);
		err = nfserr_noent;
	} else
		secinfo->si_exp = exp;
	dput(dentry);
	return err;
}

790
static __be32
791 792
nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_setattr *setattr)
L
Linus Torvalds 已提交
793
{
794
	__be32 status = nfs_ok;
L
Linus Torvalds 已提交
795 796 797

	if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
		nfs4_lock_state();
798
		status = nfs4_preprocess_stateid_op(cstate,
799
			&setattr->sa_stateid, WR_STATE, NULL);
L
Linus Torvalds 已提交
800
		nfs4_unlock_state();
801
		if (status) {
802
			dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
803 804
			return status;
		}
L
Linus Torvalds 已提交
805
	}
806 807 808
	status = mnt_want_write(cstate->current_fh.fh_export->ex_path.mnt);
	if (status)
		return status;
L
Linus Torvalds 已提交
809
	status = nfs_ok;
810 811 812 813 814 815

	status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
				    nfsd_attrmask);
	if (status)
		goto out;

L
Linus Torvalds 已提交
816
	if (setattr->sa_acl != NULL)
817 818
		status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
					    setattr->sa_acl);
L
Linus Torvalds 已提交
819
	if (status)
820
		goto out;
821
	status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
L
Linus Torvalds 已提交
822
				0, (time_t)0);
823 824
out:
	mnt_drop_write(cstate->current_fh.fh_export->ex_path.mnt);
L
Linus Torvalds 已提交
825 826 827
	return status;
}

828
static __be32
829 830
nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct nfsd4_write *write)
L
Linus Torvalds 已提交
831 832 833 834
{
	stateid_t *stateid = &write->wr_stateid;
	struct file *filp = NULL;
	u32 *p;
835
	__be32 status = nfs_ok;
836
	unsigned long cnt;
L
Linus Torvalds 已提交
837 838 839 840 841 842 843

	/* no need to check permission - this will be done in nfsd_write() */

	if (write->wr_offset >= OFFSET_MAX)
		return nfserr_inval;

	nfs4_lock_state();
844
	status = nfs4_preprocess_stateid_op(cstate, stateid, WR_STATE, &filp);
845 846
	if (filp)
		get_file(filp);
L
Linus Torvalds 已提交
847 848
	nfs4_unlock_state();

849 850 851 852 853
	if (status) {
		dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
		return status;
	}

854
	cnt = write->wr_buflen;
L
Linus Torvalds 已提交
855 856 857 858 859
	write->wr_how_written = write->wr_stable_how;
	p = (u32 *)write->wr_verifier.data;
	*p++ = nfssvc_boot.tv_sec;
	*p++ = nfssvc_boot.tv_usec;

860 861
	status =  nfsd_write(rqstp, &cstate->current_fh, filp,
			     write->wr_offset, rqstp->rq_vec, write->wr_vlen,
862
			     &cnt, &write->wr_how_written);
863 864
	if (filp)
		fput(filp);
L
Linus Torvalds 已提交
865

866 867
	write->wr_bytes_written = cnt;

L
Linus Torvalds 已提交
868 869 870 871 872 873 874 875 876 877
	if (status == nfserr_symlink)
		status = nfserr_inval;
	return status;
}

/* This routine never returns NFS_OK!  If there are no other errors, it
 * will return NFSERR_SAME or NFSERR_NOT_SAME depending on whether the
 * attributes matched.  VERIFY is implemented by mapping NFSERR_SAME
 * to NFS_OK after the call; NVERIFY by mapping NFSERR_NOT_SAME to NFS_OK.
 */
878
static __be32
879
_nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
880
	     struct nfsd4_verify *verify)
L
Linus Torvalds 已提交
881
{
A
Al Viro 已提交
882
	__be32 *buf, *p;
L
Linus Torvalds 已提交
883
	int count;
884
	__be32 status;
L
Linus Torvalds 已提交
885

M
Miklos Szeredi 已提交
886
	status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP);
L
Linus Torvalds 已提交
887 888 889
	if (status)
		return status;

890 891 892 893
	status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL);
	if (status)
		return status;

L
Linus Torvalds 已提交
894 895 896 897 898 899 900 901 902 903 904 905 906 907
	if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
	    || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
		return nfserr_inval;
	if (verify->ve_attrlen & 3)
		return nfserr_inval;

	/* count in words:
	 *   bitmap_len(1) + bitmap(2) + attr_len(1) = 4
	 */
	count = 4 + (verify->ve_attrlen >> 2);
	buf = kmalloc(count << 2, GFP_KERNEL);
	if (!buf)
		return nfserr_resource;

908 909 910
	status = nfsd4_encode_fattr(&cstate->current_fh,
				    cstate->current_fh.fh_export,
				    cstate->current_fh.fh_dentry, buf,
L
Linus Torvalds 已提交
911
				    &count, verify->ve_bmval,
912
				    rqstp, 0);
L
Linus Torvalds 已提交
913 914 915 916 917 918 919

	/* this means that nfsd4_encode_fattr() ran out of space */
	if (status == nfserr_resource && count == 0)
		status = nfserr_not_same;
	if (status)
		goto out_kfree;

920 921
	/* skip bitmap */
	p = buf + 1 + ntohl(buf[0]);
L
Linus Torvalds 已提交
922 923 924 925 926 927 928 929 930 931 932
	status = nfserr_not_same;
	if (ntohl(*p++) != verify->ve_attrlen)
		goto out_kfree;
	if (!memcmp(p, verify->ve_attrval, verify->ve_attrlen))
		status = nfserr_same;

out_kfree:
	kfree(buf);
	return status;
}

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
static __be32
nfsd4_nverify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_verify *verify)
{
	__be32 status;

	status = _nfsd4_verify(rqstp, cstate, verify);
	return status == nfserr_not_same ? nfs_ok : status;
}

static __be32
nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_verify *verify)
{
	__be32 status;

	status = _nfsd4_verify(rqstp, cstate, verify);
	return status == nfserr_same ? nfs_ok : status;
}

L
Linus Torvalds 已提交
953 954 955
/*
 * NULL call.
 */
A
Al Viro 已提交
956
static __be32
L
Linus Torvalds 已提交
957 958 959 960 961
nfsd4_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
	return nfs_ok;
}

962 963 964 965 966 967
static inline void nfsd4_increment_op_stats(u32 opnum)
{
	if (opnum >= FIRST_NFS4_OP && opnum <= LAST_NFS4_OP)
		nfsdstats.nfs4_opcount[opnum]++;
}

968 969
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
			      void *);
970 971 972 973 974
enum nfsd4_op_flags {
	ALLOWED_WITHOUT_FH = 1 << 0,	/* No current filehandle required */
	ALLOWED_ON_ABSENT_FS = 2 << 0,	/* ops processed on absent fs */
	ALLOWED_AS_FIRST_OP = 3 << 0,	/* ops reqired first in compound */
};
975 976 977 978

struct nfsd4_operation {
	nfsd4op_func op_func;
	u32 op_flags;
B
Benny Halevy 已提交
979
	char *op_name;
980 981 982 983
};

static struct nfsd4_operation nfsd4_ops[];

A
Adrian Bunk 已提交
984
static const char *nfsd4_op_name(unsigned opnum);
B
Benny Halevy 已提交
985

986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
/*
 * This is a replay of a compound for which no cache entry pages
 * were used. Encode the sequence operation, and if cachethis is FALSE
 * encode the uncache rep error on the next operation.
 */
static __be32
nfsd4_enc_uncached_replay(struct nfsd4_compoundargs *args,
			 struct nfsd4_compoundres *resp)
{
	struct nfsd4_op *op;

	dprintk("--> %s resp->opcnt %d ce_cachethis %u \n", __func__,
		resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis);

	/* Encode the replayed sequence operation */
	BUG_ON(resp->opcnt != 1);
	op = &args->ops[resp->opcnt - 1];
	nfsd4_encode_operation(resp, op);

	/*return nfserr_retry_uncached_rep in next operation. */
	if (resp->cstate.slot->sl_cache_entry.ce_cachethis == 0) {
		op = &args->ops[resp->opcnt++];
		op->status = nfserr_retry_uncached_rep;
		nfsd4_encode_operation(resp, op);
	}
	return op->status;
}

1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
/*
 * Enforce NFSv4.1 COMPOUND ordering rules.
 *
 * TODO:
 * - enforce NFS4ERR_NOT_ONLY_OP,
 * - DESTROY_SESSION MUST be the final operation in the COMPOUND request.
 */
static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args)
{
	if (args->minorversion && args->opcnt > 0) {
		struct nfsd4_op *op = &args->ops[0];
		return (op->status == nfserr_op_illegal) ||
		       (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP);
	}
	return true;
}

L
Linus Torvalds 已提交
1031 1032 1033
/*
 * COMPOUND call.
 */
A
Al Viro 已提交
1034
static __be32
L
Linus Torvalds 已提交
1035 1036 1037 1038 1039
nfsd4_proc_compound(struct svc_rqst *rqstp,
		    struct nfsd4_compoundargs *args,
		    struct nfsd4_compoundres *resp)
{
	struct nfsd4_op	*op;
1040
	struct nfsd4_operation *opdesc;
1041
	struct nfsd4_compound_state *cstate = &resp->cstate;
1042
	int		slack_bytes;
1043
	__be32		status;
L
Linus Torvalds 已提交
1044 1045

	resp->xbuf = &rqstp->rq_res;
1046 1047
	resp->p = rqstp->rq_res.head[0].iov_base +
						rqstp->rq_res.head[0].iov_len;
L
Linus Torvalds 已提交
1048 1049 1050 1051 1052 1053 1054 1055
	resp->tagp = resp->p;
	/* reserve space for: taglen, tag, and opcnt */
	resp->p += 2 + XDR_QUADLEN(args->taglen);
	resp->end = rqstp->rq_res.head[0].iov_base + PAGE_SIZE;
	resp->taglen = args->taglen;
	resp->tag = args->tag;
	resp->opcnt = 0;
	resp->rqstp = rqstp;
A
Andy Adamson 已提交
1056
	resp->cstate.minorversion = args->minorversion;
1057 1058 1059
	resp->cstate.replay_owner = NULL;
	fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
	fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
1060 1061
	/* Use the deferral mechanism only for NFSv4.0 compounds */
	rqstp->rq_usedeferral = (args->minorversion == 0);
L
Linus Torvalds 已提交
1062 1063 1064 1065 1066

	/*
	 * According to RFC3010, this takes precedence over all other errors.
	 */
	status = nfserr_minor_vers_mismatch;
1067
	if (args->minorversion > nfsd_supported_minorversion)
L
Linus Torvalds 已提交
1068 1069
		goto out;

1070 1071 1072 1073 1074 1075
	if (!nfs41_op_ordering_ok(args)) {
		op = &args->ops[0];
		op->status = nfserr_sequence_pos;
		goto encode_op;
	}

L
Linus Torvalds 已提交
1076 1077 1078 1079
	status = nfs_ok;
	while (!status && resp->opcnt < args->opcnt) {
		op = &args->ops[resp->opcnt++];

B
Benny Halevy 已提交
1080 1081 1082
		dprintk("nfsv4 compound op #%d/%d: %d (%s)\n",
			resp->opcnt, args->opcnt, op->opnum,
			nfsd4_op_name(op->opnum));
L
Linus Torvalds 已提交
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
		/*
		 * The XDR decode routines may have pre-set op->status;
		 * for example, if there is a miscellaneous XDR error
		 * it will be set to nfserr_bad_xdr.
		 */
		if (op->status)
			goto encode_op;

		/* We must be able to encode a successful response to
		 * this operation, with enough room left over to encode a
		 * failed response to the next operation.  If we don't
		 * have enough room, fail with ERR_RESOURCE.
		 */
1096 1097 1098 1099
		slack_bytes = (char *)resp->end - (char *)resp->p;
		if (slack_bytes < COMPOUND_SLACK_SPACE
				+ COMPOUND_ERR_SLACK_SPACE) {
			BUG_ON(slack_bytes < COMPOUND_ERR_SLACK_SPACE);
L
Linus Torvalds 已提交
1100 1101 1102 1103
			op->status = nfserr_resource;
			goto encode_op;
		}

1104 1105
		opdesc = &nfsd4_ops[op->opnum];

1106
		if (!cstate->current_fh.fh_dentry) {
1107
			if (!(opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
1108 1109 1110
				op->status = nfserr_nofilehandle;
				goto encode_op;
			}
1111 1112
		} else if (cstate->current_fh.fh_export->ex_fslocs.migrated &&
			  !(opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) {
1113
			op->status = nfserr_moved;
L
Linus Torvalds 已提交
1114 1115
			goto encode_op;
		}
1116 1117 1118 1119

		if (opdesc->op_func)
			op->status = opdesc->op_func(rqstp, cstate, &op->u);
		else
L
Linus Torvalds 已提交
1120 1121 1122
			BUG_ON(op->status == nfs_ok);

encode_op:
A
Andy Adamson 已提交
1123 1124 1125
		/* Only from SEQUENCE or CREATE_SESSION */
		if (resp->cstate.status == nfserr_replay_cache) {
			dprintk("%s NFS4.1 replay from cache\n", __func__);
1126 1127 1128 1129
			if (nfsd4_not_cached(resp))
				status = nfsd4_enc_uncached_replay(args, resp);
			else
				status = op->status;
A
Andy Adamson 已提交
1130 1131
			goto out;
		}
A
Al Viro 已提交
1132
		if (op->status == nfserr_replay_me) {
1133
			op->replay = &cstate->replay_owner->so_replay;
L
Linus Torvalds 已提交
1134 1135 1136 1137 1138 1139
			nfsd4_encode_replay(resp, op);
			status = op->status = op->replay->rp_status;
		} else {
			nfsd4_encode_operation(resp, op);
			status = op->status;
		}
1140 1141 1142 1143 1144

		dprintk("nfsv4 compound op %p opcnt %d #%d: %d: status %d\n",
			args->ops, args->opcnt, resp->opcnt, op->opnum,
			be32_to_cpu(status));

1145 1146 1147
		if (cstate->replay_owner) {
			nfs4_put_stateowner(cstate->replay_owner);
			cstate->replay_owner = NULL;
L
Linus Torvalds 已提交
1148
		}
1149 1150 1151
		/* XXX Ugh, we need to get rid of this kind of special case: */
		if (op->opnum == OP_READ && op->u.read.rd_filp)
			fput(op->u.read.rd_filp);
1152 1153

		nfsd4_increment_op_stats(op->opnum);
L
Linus Torvalds 已提交
1154
	}
1155 1156 1157 1158
	if (!rqstp->rq_usedeferral && status == nfserr_dropit) {
		dprintk("%s Dropit - send NFS4ERR_DELAY\n", __func__);
		status = nfserr_jukebox;
	}
L
Linus Torvalds 已提交
1159

A
Andy Adamson 已提交
1160
	resp->cstate.status = status;
1161 1162 1163
	fh_put(&resp->cstate.current_fh);
	fh_put(&resp->cstate.save_fh);
	BUG_ON(resp->cstate.replay_owner);
L
Linus Torvalds 已提交
1164 1165
out:
	nfsd4_release_compoundargs(args);
1166 1167
	/* Reset deferral mechanism for RPC deferrals */
	rqstp->rq_usedeferral = 1;
1168
	dprintk("nfsv4 compound returned %d\n", ntohl(status));
L
Linus Torvalds 已提交
1169 1170 1171
	return status;
}

B
Benny Halevy 已提交
1172
static struct nfsd4_operation nfsd4_ops[] = {
1173 1174
	[OP_ACCESS] = {
		.op_func = (nfsd4op_func)nfsd4_access,
B
Benny Halevy 已提交
1175
		.op_name = "OP_ACCESS",
1176 1177 1178
	},
	[OP_CLOSE] = {
		.op_func = (nfsd4op_func)nfsd4_close,
B
Benny Halevy 已提交
1179
		.op_name = "OP_CLOSE",
1180 1181 1182
	},
	[OP_COMMIT] = {
		.op_func = (nfsd4op_func)nfsd4_commit,
B
Benny Halevy 已提交
1183
		.op_name = "OP_COMMIT",
1184 1185 1186
	},
	[OP_CREATE] = {
		.op_func = (nfsd4op_func)nfsd4_create,
B
Benny Halevy 已提交
1187
		.op_name = "OP_CREATE",
1188 1189 1190
	},
	[OP_DELEGRETURN] = {
		.op_func = (nfsd4op_func)nfsd4_delegreturn,
B
Benny Halevy 已提交
1191
		.op_name = "OP_DELEGRETURN",
1192 1193 1194
	},
	[OP_GETATTR] = {
		.op_func = (nfsd4op_func)nfsd4_getattr,
1195
		.op_flags = ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1196
		.op_name = "OP_GETATTR",
1197 1198 1199
	},
	[OP_GETFH] = {
		.op_func = (nfsd4op_func)nfsd4_getfh,
B
Benny Halevy 已提交
1200
		.op_name = "OP_GETFH",
1201 1202 1203
	},
	[OP_LINK] = {
		.op_func = (nfsd4op_func)nfsd4_link,
B
Benny Halevy 已提交
1204
		.op_name = "OP_LINK",
1205 1206 1207
	},
	[OP_LOCK] = {
		.op_func = (nfsd4op_func)nfsd4_lock,
B
Benny Halevy 已提交
1208
		.op_name = "OP_LOCK",
1209 1210 1211
	},
	[OP_LOCKT] = {
		.op_func = (nfsd4op_func)nfsd4_lockt,
B
Benny Halevy 已提交
1212
		.op_name = "OP_LOCKT",
1213 1214 1215
	},
	[OP_LOCKU] = {
		.op_func = (nfsd4op_func)nfsd4_locku,
B
Benny Halevy 已提交
1216
		.op_name = "OP_LOCKU",
1217 1218 1219
	},
	[OP_LOOKUP] = {
		.op_func = (nfsd4op_func)nfsd4_lookup,
B
Benny Halevy 已提交
1220
		.op_name = "OP_LOOKUP",
1221 1222 1223
	},
	[OP_LOOKUPP] = {
		.op_func = (nfsd4op_func)nfsd4_lookupp,
B
Benny Halevy 已提交
1224
		.op_name = "OP_LOOKUPP",
1225 1226 1227
	},
	[OP_NVERIFY] = {
		.op_func = (nfsd4op_func)nfsd4_nverify,
B
Benny Halevy 已提交
1228
		.op_name = "OP_NVERIFY",
1229 1230 1231
	},
	[OP_OPEN] = {
		.op_func = (nfsd4op_func)nfsd4_open,
B
Benny Halevy 已提交
1232
		.op_name = "OP_OPEN",
1233 1234 1235
	},
	[OP_OPEN_CONFIRM] = {
		.op_func = (nfsd4op_func)nfsd4_open_confirm,
B
Benny Halevy 已提交
1236
		.op_name = "OP_OPEN_CONFIRM",
1237 1238 1239
	},
	[OP_OPEN_DOWNGRADE] = {
		.op_func = (nfsd4op_func)nfsd4_open_downgrade,
B
Benny Halevy 已提交
1240
		.op_name = "OP_OPEN_DOWNGRADE",
1241 1242 1243
	},
	[OP_PUTFH] = {
		.op_func = (nfsd4op_func)nfsd4_putfh,
1244
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1245
		.op_name = "OP_PUTFH",
1246
	},
1247
	[OP_PUTPUBFH] = {
1248
		.op_func = (nfsd4op_func)nfsd4_putrootfh,
1249
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1250
		.op_name = "OP_PUTPUBFH",
1251
	},
1252 1253
	[OP_PUTROOTFH] = {
		.op_func = (nfsd4op_func)nfsd4_putrootfh,
1254
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1255
		.op_name = "OP_PUTROOTFH",
1256 1257 1258
	},
	[OP_READ] = {
		.op_func = (nfsd4op_func)nfsd4_read,
B
Benny Halevy 已提交
1259
		.op_name = "OP_READ",
1260 1261 1262
	},
	[OP_READDIR] = {
		.op_func = (nfsd4op_func)nfsd4_readdir,
B
Benny Halevy 已提交
1263
		.op_name = "OP_READDIR",
1264 1265 1266
	},
	[OP_READLINK] = {
		.op_func = (nfsd4op_func)nfsd4_readlink,
B
Benny Halevy 已提交
1267
		.op_name = "OP_READLINK",
1268 1269 1270
	},
	[OP_REMOVE] = {
		.op_func = (nfsd4op_func)nfsd4_remove,
B
Benny Halevy 已提交
1271
		.op_name = "OP_REMOVE",
1272 1273
	},
	[OP_RENAME] = {
B
Benny Halevy 已提交
1274
		.op_name = "OP_RENAME",
1275 1276 1277 1278
		.op_func = (nfsd4op_func)nfsd4_rename,
	},
	[OP_RENEW] = {
		.op_func = (nfsd4op_func)nfsd4_renew,
1279
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1280
		.op_name = "OP_RENEW",
1281 1282 1283
	},
	[OP_RESTOREFH] = {
		.op_func = (nfsd4op_func)nfsd4_restorefh,
1284
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1285
		.op_name = "OP_RESTOREFH",
1286 1287 1288
	},
	[OP_SAVEFH] = {
		.op_func = (nfsd4op_func)nfsd4_savefh,
B
Benny Halevy 已提交
1289
		.op_name = "OP_SAVEFH",
1290
	},
A
Andy Adamson 已提交
1291 1292
	[OP_SECINFO] = {
		.op_func = (nfsd4op_func)nfsd4_secinfo,
B
Benny Halevy 已提交
1293
		.op_name = "OP_SECINFO",
A
Andy Adamson 已提交
1294
	},
1295 1296
	[OP_SETATTR] = {
		.op_func = (nfsd4op_func)nfsd4_setattr,
B
Benny Halevy 已提交
1297
		.op_name = "OP_SETATTR",
1298 1299 1300
	},
	[OP_SETCLIENTID] = {
		.op_func = (nfsd4op_func)nfsd4_setclientid,
1301
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1302
		.op_name = "OP_SETCLIENTID",
1303 1304 1305
	},
	[OP_SETCLIENTID_CONFIRM] = {
		.op_func = (nfsd4op_func)nfsd4_setclientid_confirm,
1306
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1307
		.op_name = "OP_SETCLIENTID_CONFIRM",
1308 1309 1310
	},
	[OP_VERIFY] = {
		.op_func = (nfsd4op_func)nfsd4_verify,
B
Benny Halevy 已提交
1311
		.op_name = "OP_VERIFY",
1312 1313 1314
	},
	[OP_WRITE] = {
		.op_func = (nfsd4op_func)nfsd4_write,
B
Benny Halevy 已提交
1315
		.op_name = "OP_WRITE",
1316 1317 1318
	},
	[OP_RELEASE_LOCKOWNER] = {
		.op_func = (nfsd4op_func)nfsd4_release_lockowner,
1319
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
B
Benny Halevy 已提交
1320
		.op_name = "OP_RELEASE_LOCKOWNER",
1321
	},
A
Andy Adamson 已提交
1322 1323 1324 1325

	/* NFSv4.1 operations */
	[OP_EXCHANGE_ID] = {
		.op_func = (nfsd4op_func)nfsd4_exchange_id,
1326
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
A
Andy Adamson 已提交
1327 1328 1329 1330
		.op_name = "OP_EXCHANGE_ID",
	},
	[OP_CREATE_SESSION] = {
		.op_func = (nfsd4op_func)nfsd4_create_session,
1331
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
A
Andy Adamson 已提交
1332 1333 1334 1335
		.op_name = "OP_CREATE_SESSION",
	},
	[OP_DESTROY_SESSION] = {
		.op_func = (nfsd4op_func)nfsd4_destroy_session,
1336
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
A
Andy Adamson 已提交
1337 1338 1339 1340
		.op_name = "OP_DESTROY_SESSION",
	},
	[OP_SEQUENCE] = {
		.op_func = (nfsd4op_func)nfsd4_sequence,
1341
		.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
A
Andy Adamson 已提交
1342 1343
		.op_name = "OP_SEQUENCE",
	},
1344 1345
};

A
Adrian Bunk 已提交
1346
static const char *nfsd4_op_name(unsigned opnum)
B
Benny Halevy 已提交
1347 1348 1349 1350 1351 1352
{
	if (opnum < ARRAY_SIZE(nfsd4_ops))
		return nfsd4_ops[opnum].op_name;
	return "unknown_operation";
}

L
Linus Torvalds 已提交
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
#define nfsd4_voidres			nfsd4_voidargs
struct nfsd4_voidargs { int dummy; };

/*
 * TODO: At the present time, the NFSv4 server does not do XID caching
 * of requests.  Implementing XID caching would not be a serious problem,
 * although it would require a mild change in interfaces since one
 * doesn't know whether an NFSv4 request is idempotent until after the
 * XDR decode.  However, XID caching totally confuses pynfs (Peter
 * Astrand's regression testsuite for NFSv4 servers), which reuses
 * XID's liberally, so I've left it unimplemented until pynfs generates
 * better XID's.
 */
static struct svc_procedure		nfsd_procedures4[2] = {
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
	[NFSPROC4_NULL] = {
		.pc_func = (svc_procfunc) nfsd4_proc_null,
		.pc_encode = (kxdrproc_t) nfs4svc_encode_voidres,
		.pc_argsize = sizeof(struct nfsd4_voidargs),
		.pc_ressize = sizeof(struct nfsd4_voidres),
		.pc_cachetype = RC_NOCACHE,
		.pc_xdrressize = 1,
	},
	[NFSPROC4_COMPOUND] = {
		.pc_func = (svc_procfunc) nfsd4_proc_compound,
		.pc_decode = (kxdrproc_t) nfs4svc_decode_compoundargs,
		.pc_encode = (kxdrproc_t) nfs4svc_encode_compoundres,
		.pc_argsize = sizeof(struct nfsd4_compoundargs),
		.pc_ressize = sizeof(struct nfsd4_compoundres),
		.pc_cachetype = RC_NOCACHE,
		.pc_xdrressize = NFSD_BUFSIZE/4,
	},
L
Linus Torvalds 已提交
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
};

struct svc_version	nfsd_version4 = {
		.vs_vers	= 4,
		.vs_nproc	= 2,
		.vs_proc	= nfsd_procedures4,
		.vs_dispatch	= nfsd_dispatch,
		.vs_xdrsize	= NFS4_SVC_XDRSIZE,
};

/*
 * Local variables:
 *  c-basic-offset: 8
 * End:
 */