nfs4proc.c 28.9 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 50 51 52 53 54 55 56 57 58 59 60 61 62

#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>

#define NFSDDBG_FACILITY		NFSDDBG_PROC

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;
}

63
static __be32
64
do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open, int accmode)
L
Linus Torvalds 已提交
65
{
66
	__be32 status;
L
Linus Torvalds 已提交
67 68 69 70 71 72

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

	if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
73
		accmode |= MAY_READ;
74
	if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
L
Linus Torvalds 已提交
75
		accmode |= (MAY_WRITE | MAY_TRUNC);
76 77
	if (open->op_share_deny & NFS4_SHARE_DENY_WRITE)
		accmode |= MAY_WRITE;
L
Linus Torvalds 已提交
78 79 80 81 82 83

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

	return status;
}

84
static __be32
L
Linus Torvalds 已提交
85 86 87
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
	struct svc_fh resfh;
88
	__be32 status;
89
	int created = 0;
L
Linus Torvalds 已提交
90 91 92 93 94 95 96 97 98 99 100 101

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

	if (open->op_create) {
		/*
		 * 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,
102
					(u32 *)open->op_verf.data, &open->op_truncate, &created);
103
	} else {
L
Linus Torvalds 已提交
104 105 106 107
		status = nfsd_lookup(rqstp, current_fh,
				     open->op_fname.data, open->op_fname.len, &resfh);
		fh_unlock(current_fh);
	}
108 109
	if (status)
		goto out;
L
Linus Torvalds 已提交
110

111
	set_change_info(&open->op_cinfo, current_fh);
L
Linus Torvalds 已提交
112

113 114 115 116 117
	/* set reply cache */
	fh_dup2(current_fh, &resfh);
	open->op_stateowner->so_replay.rp_openfh_len = resfh.fh_handle.fh_size;
	memcpy(open->op_stateowner->so_replay.rp_openfh,
			&resfh.fh_handle.fh_base, resfh.fh_handle.fh_size);
L
Linus Torvalds 已提交
118

119 120
	if (!created)
		status = do_open_permission(rqstp, current_fh, open, MAY_NOP);
L
Linus Torvalds 已提交
121

122
out:
L
Linus Torvalds 已提交
123 124 125 126
	fh_put(&resfh);
	return status;
}

127
static __be32
L
Linus Torvalds 已提交
128 129
do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
130
	__be32 status;
L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

	/* 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 */
	open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size;
	memcpy(open->op_stateowner->so_replay.rp_openfh,
		&current_fh->fh_handle.fh_base,
		current_fh->fh_handle.fh_size);

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

151
	status = do_open_permission(rqstp, current_fh, open, MAY_OWNER_OVERRIDE);
L
Linus Torvalds 已提交
152 153 154 155 156

	return status;
}


157
static __be32
158
nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
159
	   struct nfsd4_open *open)
L
Linus Torvalds 已提交
160
{
161
	__be32 status;
L
Linus Torvalds 已提交
162 163 164 165 166 167 168 169 170 171 172 173
	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;

	nfs4_lock_state();

	/* check seqid for replay. set nfs4_owner */
	status = nfsd4_process_open1(open);
A
Al Viro 已提交
174
	if (status == nfserr_replay_me) {
L
Linus Torvalds 已提交
175
		struct nfs4_replay *rp = &open->op_stateowner->so_replay;
176 177 178
		fh_put(&cstate->current_fh);
		cstate->current_fh.fh_handle.fh_size = rp->rp_openfh_len;
		memcpy(&cstate->current_fh.fh_handle.fh_base, rp->rp_openfh,
L
Linus Torvalds 已提交
179
				rp->rp_openfh_len);
180
		status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
181 182 183 184
		if (status)
			dprintk("nfsd4_open: replay failed"
				" restoring previous filehandle\n");
		else
A
Al Viro 已提交
185
			status = nfserr_replay_me;
L
Linus Torvalds 已提交
186 187 188
	}
	if (status)
		goto out;
189 190 191

	/* Openowner is now set, so sequence id will get bumped.  Now we need
	 * these checks before we do any creates: */
192
	status = nfserr_grace;
193
	if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
194 195
		goto out;
	status = nfserr_no_grace;
196
	if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
197
		goto out;
198

L
Linus Torvalds 已提交
199
	switch (open->op_claim_type) {
200 201 202 203 204
		case NFS4_OPEN_CLAIM_DELEGATE_CUR:
			status = nfserr_inval;
			if (open->op_create)
				goto out;
			/* fall through */
L
Linus Torvalds 已提交
205 206 207 208 209 210 211
		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.
			 */
212 213
			status = do_open_lookup(rqstp, &cstate->current_fh,
						open);
L
Linus Torvalds 已提交
214 215 216 217
			if (status)
				goto out;
			break;
		case NFS4_OPEN_CLAIM_PREVIOUS:
218
			open->op_stateowner->so_confirmed = 1;
L
Linus Torvalds 已提交
219 220 221 222 223 224
			/*
			 * 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.
			*/
225 226
			status = do_open_fhandle(rqstp, &cstate->current_fh,
						 open);
L
Linus Torvalds 已提交
227 228 229 230
			if (status)
				goto out;
			break;
             	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
231
			open->op_stateowner->so_confirmed = 1;
L
Linus Torvalds 已提交
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
			printk("NFSD: unsupported OPEN claim type %d\n",
				open->op_claim_type);
			status = nfserr_notsupp;
			goto out;
		default:
			printk("NFSD: Invalid OPEN claim type %d\n",
				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.
	 */
247
	status = nfsd4_process_open2(rqstp, &cstate->current_fh, open);
L
Linus Torvalds 已提交
248
out:
N
Neil Brown 已提交
249
	if (open->op_stateowner) {
L
Linus Torvalds 已提交
250
		nfs4_get_stateowner(open->op_stateowner);
251
		cstate->replay_owner = open->op_stateowner;
N
Neil Brown 已提交
252
	}
L
Linus Torvalds 已提交
253 254 255 256 257 258 259
	nfs4_unlock_state();
	return status;
}

/*
 * filehandle-manipulating ops.
 */
260
static __be32
261 262
nfsd4_getfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct svc_fh **getfh)
L
Linus Torvalds 已提交
263
{
264
	if (!cstate->current_fh.fh_dentry)
L
Linus Torvalds 已提交
265 266
		return nfserr_nofilehandle;

267
	*getfh = &cstate->current_fh;
L
Linus Torvalds 已提交
268 269 270
	return nfs_ok;
}

271
static __be32
272 273
nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct nfsd4_putfh *putfh)
L
Linus Torvalds 已提交
274
{
275 276 277 278 279
	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);
	return fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
280 281
}

282
static __be32
283 284
nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		void *arg)
L
Linus Torvalds 已提交
285
{
286
	__be32 status;
L
Linus Torvalds 已提交
287

288 289
	fh_put(&cstate->current_fh);
	status = exp_pseudoroot(rqstp->rq_client, &cstate->current_fh,
L
Linus Torvalds 已提交
290 291 292 293
			      &rqstp->rq_chandle);
	return status;
}

294
static __be32
295 296
nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		void *arg)
L
Linus Torvalds 已提交
297
{
298
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
299 300
		return nfserr_restorefh;

301
	fh_dup2(&cstate->current_fh, &cstate->save_fh);
L
Linus Torvalds 已提交
302 303 304
	return nfs_ok;
}

305
static __be32
306 307
nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     void *arg)
L
Linus Torvalds 已提交
308
{
309
	if (!cstate->current_fh.fh_dentry)
L
Linus Torvalds 已提交
310 311
		return nfserr_nofilehandle;

312
	fh_dup2(&cstate->save_fh, &cstate->current_fh);
L
Linus Torvalds 已提交
313 314 315 316 317 318
	return nfs_ok;
}

/*
 * misc nfsv4 ops
 */
319
static __be32
320 321
nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_access *access)
L
Linus Torvalds 已提交
322 323 324 325 326
{
	if (access->ac_req_access & ~NFS3_ACCESS_FULL)
		return nfserr_inval;

	access->ac_resp_access = access->ac_req_access;
327 328
	return nfsd_access(rqstp, &cstate->current_fh, &access->ac_resp_access,
			   &access->ac_supported);
L
Linus Torvalds 已提交
329 330
}

331
static __be32
332 333
nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_commit *commit)
L
Linus Torvalds 已提交
334
{
335
	__be32 status;
L
Linus Torvalds 已提交
336 337 338 339 340

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

341 342
	status = nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
			     commit->co_count);
L
Linus Torvalds 已提交
343 344 345 346 347
	if (status == nfserr_symlink)
		status = nfserr_inval;
	return status;
}

348
static __be32
349 350
nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_create *create)
L
Linus Torvalds 已提交
351 352
{
	struct svc_fh resfh;
353
	__be32 status;
L
Linus Torvalds 已提交
354 355 356 357
	dev_t rdev;

	fh_init(&resfh, NFS4_FHSIZE);

358
	status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, MAY_CREATE);
L
Linus Torvalds 已提交
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
	if (status == nfserr_symlink)
		status = nfserr_notdir;
	if (status)
		return status;

	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;

375 376 377 378
		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 已提交
379 380 381 382 383 384 385
		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;
386 387 388
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFBLK, rdev, &resfh);
L
Linus Torvalds 已提交
389 390 391 392 393 394 395
		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;
396 397 398
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr,S_IFCHR, rdev, &resfh);
L
Linus Torvalds 已提交
399 400 401
		break;

	case NF4SOCK:
402 403 404
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFSOCK, 0, &resfh);
L
Linus Torvalds 已提交
405 406 407
		break;

	case NF4FIFO:
408 409 410
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFIFO, 0, &resfh);
L
Linus Torvalds 已提交
411 412 413 414
		break;

	case NF4DIR:
		create->cr_iattr.ia_valid &= ~ATTR_SIZE;
415 416 417
		status = nfsd_create(rqstp, &cstate->current_fh,
				     create->cr_name, create->cr_namelen,
				     &create->cr_iattr, S_IFDIR, 0, &resfh);
L
Linus Torvalds 已提交
418 419 420 421 422 423 424
		break;

	default:
		status = nfserr_badtype;
	}

	if (!status) {
425 426 427
		fh_unlock(&cstate->current_fh);
		set_change_info(&create->cr_cinfo, &cstate->current_fh);
		fh_dup2(&cstate->current_fh, &resfh);
L
Linus Torvalds 已提交
428 429 430 431 432 433
	}

	fh_put(&resfh);
	return status;
}

434
static __be32
435 436
nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_getattr *getattr)
L
Linus Torvalds 已提交
437
{
438
	__be32 status;
L
Linus Torvalds 已提交
439

440
	status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
441 442 443 444 445 446 447 448 449
	if (status)
		return status;

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

	getattr->ga_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0;
	getattr->ga_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1;

450
	getattr->ga_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
451 452 453
	return nfs_ok;
}

454
static __be32
455 456
nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	   struct nfsd4_link *link)
L
Linus Torvalds 已提交
457
{
458
	__be32 status = nfserr_nofilehandle;
L
Linus Torvalds 已提交
459

460
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
461
		return status;
462 463
	status = nfsd_link(rqstp, &cstate->current_fh,
			   link->li_name, link->li_namelen, &cstate->save_fh);
L
Linus Torvalds 已提交
464
	if (!status)
465
		set_change_info(&link->li_cinfo, &cstate->current_fh);
L
Linus Torvalds 已提交
466 467 468
	return status;
}

469
static __be32
470 471
nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      void *arg)
L
Linus Torvalds 已提交
472 473
{
	struct svc_fh tmp_fh;
474
	__be32 ret;
L
Linus Torvalds 已提交
475 476 477 478 479

	fh_init(&tmp_fh, NFS4_FHSIZE);
	if((ret = exp_pseudoroot(rqstp->rq_client, &tmp_fh,
			      &rqstp->rq_chandle)) != 0)
		return ret;
480
	if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) {
L
Linus Torvalds 已提交
481 482 483 484
		fh_put(&tmp_fh);
		return nfserr_noent;
	}
	fh_put(&tmp_fh);
485 486
	return nfsd_lookup(rqstp, &cstate->current_fh,
			   "..", 2, &cstate->current_fh);
L
Linus Torvalds 已提交
487 488
}

489
static __be32
490 491
nfsd4_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_lookup *lookup)
L
Linus Torvalds 已提交
492
{
493 494 495
	return nfsd_lookup(rqstp, &cstate->current_fh,
			   lookup->lo_name, lookup->lo_len,
			   &cstate->current_fh);
L
Linus Torvalds 已提交
496 497
}

498
static __be32
499 500
nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	   struct nfsd4_read *read)
L
Linus Torvalds 已提交
501
{
502
	__be32 status;
L
Linus Torvalds 已提交
503 504 505

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

506
	read->rd_filp = NULL;
L
Linus Torvalds 已提交
507 508 509 510 511
	if (read->rd_offset >= OFFSET_MAX)
		return nfserr_inval;

	nfs4_lock_state();
	/* check stateid */
512 513
	if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh,
				&read->rd_stateid,
514
				CHECK_FH | RD_STATE, &read->rd_filp))) {
L
Linus Torvalds 已提交
515 516 517
		dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
		goto out;
	}
518 519
	if (read->rd_filp)
		get_file(read->rd_filp);
L
Linus Torvalds 已提交
520 521 522 523
	status = nfs_ok;
out:
	nfs4_unlock_state();
	read->rd_rqstp = rqstp;
524
	read->rd_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
525 526 527
	return status;
}

528
static __be32
529 530
nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_readdir *readdir)
L
Linus Torvalds 已提交
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
{
	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;

	readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0;
	readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1;

	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;
548
	readdir->rd_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
549 550 551
	return nfs_ok;
}

552
static __be32
553 554
nfsd4_readlink(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	       struct nfsd4_readlink *readlink)
L
Linus Torvalds 已提交
555 556
{
	readlink->rl_rqstp = rqstp;
557
	readlink->rl_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
558 559 560
	return nfs_ok;
}

561
static __be32
562 563
nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_remove *remove)
L
Linus Torvalds 已提交
564
{
565
	__be32 status;
L
Linus Torvalds 已提交
566

567 568
	if (nfs4_in_grace())
		return nfserr_grace;
569 570
	status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
			     remove->rm_name, remove->rm_namelen);
L
Linus Torvalds 已提交
571 572 573
	if (status == nfserr_symlink)
		return nfserr_notdir;
	if (!status) {
574 575
		fh_unlock(&cstate->current_fh);
		set_change_info(&remove->rm_cinfo, &cstate->current_fh);
L
Linus Torvalds 已提交
576 577 578 579
	}
	return status;
}

580
static __be32
581 582
nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	     struct nfsd4_rename *rename)
L
Linus Torvalds 已提交
583
{
584
	__be32 status = nfserr_nofilehandle;
L
Linus Torvalds 已提交
585

586
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
587
		return status;
588
	if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags
589 590
					& NFSEXP_NOSUBTREECHECK))
		return nfserr_grace;
591 592
	status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
			     rename->rn_snamelen, &cstate->current_fh,
L
Linus Torvalds 已提交
593 594 595 596 597 598 599
			     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) &&
600 601
                  (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 已提交
602 603 604 605 606
		status = nfserr_exist;
	else if (status == nfserr_symlink)
		status = nfserr_notdir;

	if (!status) {
607 608
		set_change_info(&rename->rn_sinfo, &cstate->current_fh);
		set_change_info(&rename->rn_tinfo, &cstate->save_fh);
L
Linus Torvalds 已提交
609 610 611 612
	}
	return status;
}

613
static __be32
614 615
nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_setattr *setattr)
L
Linus Torvalds 已提交
616
{
617
	__be32 status = nfs_ok;
L
Linus Torvalds 已提交
618 619 620

	if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
		nfs4_lock_state();
621
		status = nfs4_preprocess_stateid_op(&cstate->current_fh,
622
			&setattr->sa_stateid, CHECK_FH | WR_STATE, NULL);
L
Linus Torvalds 已提交
623
		nfs4_unlock_state();
624
		if (status) {
625
			dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
626 627
			return status;
		}
L
Linus Torvalds 已提交
628 629 630
	}
	status = nfs_ok;
	if (setattr->sa_acl != NULL)
631 632
		status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
					    setattr->sa_acl);
L
Linus Torvalds 已提交
633
	if (status)
634
		return status;
635
	status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
L
Linus Torvalds 已提交
636 637 638 639
				0, (time_t)0);
	return status;
}

640
static __be32
641 642
nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct nfsd4_write *write)
L
Linus Torvalds 已提交
643 644 645 646
{
	stateid_t *stateid = &write->wr_stateid;
	struct file *filp = NULL;
	u32 *p;
647
	__be32 status = nfs_ok;
L
Linus Torvalds 已提交
648 649 650 651 652 653 654

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

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

	nfs4_lock_state();
655
	status = nfs4_preprocess_stateid_op(&cstate->current_fh, stateid,
656
					CHECK_FH | WR_STATE, &filp);
657 658
	if (filp)
		get_file(filp);
L
Linus Torvalds 已提交
659 660
	nfs4_unlock_state();

661 662 663 664 665
	if (status) {
		dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
		return status;
	}

L
Linus Torvalds 已提交
666 667 668 669 670 671
	write->wr_bytes_written = write->wr_buflen;
	write->wr_how_written = write->wr_stable_how;
	p = (u32 *)write->wr_verifier.data;
	*p++ = nfssvc_boot.tv_sec;
	*p++ = nfssvc_boot.tv_usec;

672 673 674
	status =  nfsd_write(rqstp, &cstate->current_fh, filp,
			     write->wr_offset, rqstp->rq_vec, write->wr_vlen,
			     write->wr_buflen, &write->wr_how_written);
675 676
	if (filp)
		fput(filp);
L
Linus Torvalds 已提交
677 678 679 680 681 682 683 684 685 686 687

	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.
 */
688
static __be32
689
_nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
690
	     struct nfsd4_verify *verify)
L
Linus Torvalds 已提交
691
{
A
Al Viro 已提交
692
	__be32 *buf, *p;
L
Linus Torvalds 已提交
693
	int count;
694
	__be32 status;
L
Linus Torvalds 已提交
695

696
	status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
	if (status)
		return status;

	if ((verify->ve_bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0)
	    || (verify->ve_bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1))
		return nfserr_attrnotsupp;
	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;

717 718 719
	status = nfsd4_encode_fattr(&cstate->current_fh,
				    cstate->current_fh.fh_export,
				    cstate->current_fh.fh_dentry, buf,
L
Linus Torvalds 已提交
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
				    &count, verify->ve_bmval,
				    rqstp);

	/* 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;

	p = buf + 3;
	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;
}

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
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 已提交
761 762 763
/*
 * NULL call.
 */
A
Al Viro 已提交
764
static __be32
L
Linus Torvalds 已提交
765 766 767 768 769
nfsd4_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
	return nfs_ok;
}

770 771 772 773 774 775
static inline void nfsd4_increment_op_stats(u32 opnum)
{
	if (opnum >= FIRST_NFS4_OP && opnum <= LAST_NFS4_OP)
		nfsdstats.nfs4_opcount[opnum]++;
}

776 777 778 779 780 781
static void cstate_free(struct nfsd4_compound_state *cstate)
{
	if (cstate == NULL)
		return;
	fh_put(&cstate->current_fh);
	fh_put(&cstate->save_fh);
782
	BUG_ON(cstate->replay_owner);
783 784 785 786 787 788 789 790 791 792 793 794
	kfree(cstate);
}

static struct nfsd4_compound_state *cstate_alloc(void)
{
	struct nfsd4_compound_state *cstate;

	cstate = kmalloc(sizeof(struct nfsd4_compound_state), GFP_KERNEL);
	if (cstate == NULL)
		return NULL;
	fh_init(&cstate->current_fh, NFS4_FHSIZE);
	fh_init(&cstate->save_fh, NFS4_FHSIZE);
795
	cstate->replay_owner = NULL;
796 797
	return cstate;
}
L
Linus Torvalds 已提交
798

799 800 801 802 803 804
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
			      void *);

struct nfsd4_operation {
	nfsd4op_func op_func;
	u32 op_flags;
805 806
/* GETATTR and ops not listed as returning NFS4ERR_MOVED: */
#define ALLOWED_ON_ABSENT_FS 1
807 808 809 810
};

static struct nfsd4_operation nfsd4_ops[];

L
Linus Torvalds 已提交
811 812 813
/*
 * COMPOUND call.
 */
A
Al Viro 已提交
814
static __be32
L
Linus Torvalds 已提交
815 816 817 818 819
nfsd4_proc_compound(struct svc_rqst *rqstp,
		    struct nfsd4_compoundargs *args,
		    struct nfsd4_compoundres *resp)
{
	struct nfsd4_op	*op;
820
	struct nfsd4_operation *opdesc;
821
	struct nfsd4_compound_state *cstate = NULL;
822
	int		slack_bytes;
823
	__be32		status;
L
Linus Torvalds 已提交
824 825

	status = nfserr_resource;
826 827
	cstate = cstate_alloc();
	if (cstate == NULL)
L
Linus Torvalds 已提交
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
		goto out;

	resp->xbuf = &rqstp->rq_res;
	resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len;
	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;

	/*
	 * According to RFC3010, this takes precedence over all other errors.
	 */
	status = nfserr_minor_vers_mismatch;
	if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
		goto out;

	status = nfs_ok;
	while (!status && resp->opcnt < args->opcnt) {
		op = &args->ops[resp->opcnt++];

852 853
		dprintk("nfsv4 compound op #%d: %d\n", resp->opcnt, op->opnum);

L
Linus Torvalds 已提交
854 855 856 857 858 859 860 861 862 863 864 865 866
		/*
		 * 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.
		 */
867 868 869 870
		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 已提交
871 872 873 874
			op->status = nfserr_resource;
			goto encode_op;
		}

875 876
		opdesc = &nfsd4_ops[op->opnum];

L
Linus Torvalds 已提交
877 878 879 880
		/* All operations except RENEW, SETCLIENTID, RESTOREFH
		* SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH
		* require a valid current filehandle
		*/
881
		if (!cstate->current_fh.fh_dentry) {
882 883 884 885 886 887 888 889 890 891
			if (!((op->opnum == OP_PUTFH) ||
			      (op->opnum == OP_PUTROOTFH) ||
			      (op->opnum == OP_SETCLIENTID) ||
			      (op->opnum == OP_SETCLIENTID_CONFIRM) ||
			      (op->opnum == OP_RENEW) ||
			      (op->opnum == OP_RESTOREFH) ||
			      (op->opnum == OP_RELEASE_LOCKOWNER))) {
				op->status = nfserr_nofilehandle;
				goto encode_op;
			}
892 893
		} else if (cstate->current_fh.fh_export->ex_fslocs.migrated &&
			  !(opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) {
894
			op->status = nfserr_moved;
L
Linus Torvalds 已提交
895 896
			goto encode_op;
		}
897 898 899 900

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

encode_op:
A
Al Viro 已提交
904
		if (op->status == nfserr_replay_me) {
905
			op->replay = &cstate->replay_owner->so_replay;
L
Linus Torvalds 已提交
906 907 908 909 910 911
			nfsd4_encode_replay(resp, op);
			status = op->status = op->replay->rp_status;
		} else {
			nfsd4_encode_operation(resp, op);
			status = op->status;
		}
912 913 914
		if (cstate->replay_owner) {
			nfs4_put_stateowner(cstate->replay_owner);
			cstate->replay_owner = NULL;
L
Linus Torvalds 已提交
915
		}
916 917 918
		/* 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);
919 920

		nfsd4_increment_op_stats(op->opnum);
L
Linus Torvalds 已提交
921 922 923 924
	}

out:
	nfsd4_release_compoundargs(args);
925
	cstate_free(cstate);
L
Linus Torvalds 已提交
926 927 928
	return status;
}

929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = {
	[OP_ACCESS] = {
		.op_func = (nfsd4op_func)nfsd4_access,
	},
	[OP_CLOSE] = {
		.op_func = (nfsd4op_func)nfsd4_close,
	},
	[OP_COMMIT] = {
		.op_func = (nfsd4op_func)nfsd4_commit,
	},
	[OP_CREATE] = {
		.op_func = (nfsd4op_func)nfsd4_create,
	},
	[OP_DELEGRETURN] = {
		.op_func = (nfsd4op_func)nfsd4_delegreturn,
	},
	[OP_GETATTR] = {
		.op_func = (nfsd4op_func)nfsd4_getattr,
947
		.op_flags = ALLOWED_ON_ABSENT_FS,
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
	},
	[OP_GETFH] = {
		.op_func = (nfsd4op_func)nfsd4_getfh,
	},
	[OP_LINK] = {
		.op_func = (nfsd4op_func)nfsd4_link,
	},
	[OP_LOCK] = {
		.op_func = (nfsd4op_func)nfsd4_lock,
	},
	[OP_LOCKT] = {
		.op_func = (nfsd4op_func)nfsd4_lockt,
	},
	[OP_LOCKU] = {
		.op_func = (nfsd4op_func)nfsd4_locku,
	},
	[OP_LOOKUP] = {
		.op_func = (nfsd4op_func)nfsd4_lookup,
	},
	[OP_LOOKUPP] = {
		.op_func = (nfsd4op_func)nfsd4_lookupp,
	},
	[OP_NVERIFY] = {
		.op_func = (nfsd4op_func)nfsd4_nverify,
	},
	[OP_OPEN] = {
		.op_func = (nfsd4op_func)nfsd4_open,
	},
	[OP_OPEN_CONFIRM] = {
		.op_func = (nfsd4op_func)nfsd4_open_confirm,
	},
	[OP_OPEN_DOWNGRADE] = {
		.op_func = (nfsd4op_func)nfsd4_open_downgrade,
	},
	[OP_PUTFH] = {
		.op_func = (nfsd4op_func)nfsd4_putfh,
	},
985 986 987 988
	[OP_PUTPUBFH] = {
		/* unsupported; just for future reference: */
		.op_flags = ALLOWED_ON_ABSENT_FS,
	},
989 990
	[OP_PUTROOTFH] = {
		.op_func = (nfsd4op_func)nfsd4_putrootfh,
991
		.op_flags = ALLOWED_ON_ABSENT_FS,
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
	},
	[OP_READ] = {
		.op_func = (nfsd4op_func)nfsd4_read,
	},
	[OP_READDIR] = {
		.op_func = (nfsd4op_func)nfsd4_readdir,
	},
	[OP_READLINK] = {
		.op_func = (nfsd4op_func)nfsd4_readlink,
	},
	[OP_REMOVE] = {
		.op_func = (nfsd4op_func)nfsd4_remove,
	},
	[OP_RENAME] = {
		.op_func = (nfsd4op_func)nfsd4_rename,
	},
	[OP_RENEW] = {
		.op_func = (nfsd4op_func)nfsd4_renew,
1010
		.op_flags = ALLOWED_ON_ABSENT_FS,
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
	},
	[OP_RESTOREFH] = {
		.op_func = (nfsd4op_func)nfsd4_restorefh,
	},
	[OP_SAVEFH] = {
		.op_func = (nfsd4op_func)nfsd4_savefh,
	},
	[OP_SETATTR] = {
		.op_func = (nfsd4op_func)nfsd4_setattr,
	},
	[OP_SETCLIENTID] = {
		.op_func = (nfsd4op_func)nfsd4_setclientid,
1023
		.op_flags = ALLOWED_ON_ABSENT_FS,
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
	},
	[OP_SETCLIENTID_CONFIRM] = {
		.op_func = (nfsd4op_func)nfsd4_setclientid_confirm,
	},
	[OP_VERIFY] = {
		.op_func = (nfsd4op_func)nfsd4_verify,
	},
	[OP_WRITE] = {
		.op_func = (nfsd4op_func)nfsd4_write,
	},
	[OP_RELEASE_LOCKOWNER] = {
		.op_func = (nfsd4op_func)nfsd4_release_lockowner,
1036
		.op_flags = ALLOWED_ON_ABSENT_FS,
1037 1038 1039
	},
};

L
Linus Torvalds 已提交
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
#define nfs4svc_decode_voidargs		NULL
#define nfs4svc_release_void		NULL
#define nfsd4_voidres			nfsd4_voidargs
#define nfs4svc_release_compound	NULL
struct nfsd4_voidargs { int dummy; };

#define PROC(name, argt, rest, relt, cache, respsize)	\
 { (svc_procfunc) nfsd4_proc_##name,		\
   (kxdrproc_t) nfs4svc_decode_##argt##args,	\
   (kxdrproc_t) nfs4svc_encode_##rest##res,	\
   (kxdrproc_t) nfs4svc_release_##relt,		\
   sizeof(struct nfsd4_##argt##args),		\
   sizeof(struct nfsd4_##rest##res),		\
   0,						\
   cache,					\
   respsize,					\
 }

/*
 * 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] = {
  PROC(null,	 void,		void,		void,	  RC_NOCACHE, 1),
1070
  PROC(compound, compound,	compound,	compound, RC_NOCACHE, NFSD_BUFSIZE/4)
L
Linus Torvalds 已提交
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
};

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:
 */