nfs4proc.c 29.4 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 41 42 43 44 45 46 47
/*
 *  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.
 *
 * Note: some routines in this file are just trivial wrappers
 * (e.g. nfsd4_lookup()) defined solely for the sake of consistent
 * naming.  Since all such routines have been declared "inline",
 * there shouldn't be any associated overhead.  At some point in
 * the future, I might inline these "by hand" to clean up a
 * little.
 */

#include <linux/param.h>
#include <linux/major.h>
#include <linux/slab.h>
48
#include <linux/file.h>
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

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

70
static __be32
71
do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open, int accmode)
L
Linus Torvalds 已提交
72
{
73
	__be32 status;
L
Linus Torvalds 已提交
74 75 76 77 78 79

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

	if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
80
		accmode |= MAY_READ;
81
	if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
L
Linus Torvalds 已提交
82
		accmode |= (MAY_WRITE | MAY_TRUNC);
83 84
	if (open->op_share_deny & NFS4_SHARE_DENY_WRITE)
		accmode |= MAY_WRITE;
L
Linus Torvalds 已提交
85 86 87 88 89 90

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

	return status;
}

91
static __be32
L
Linus Torvalds 已提交
92 93 94
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
	struct svc_fh resfh;
95
	__be32 status;
96
	int created = 0;
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106 107 108

	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,
109
					(u32 *)open->op_verf.data, &open->op_truncate, &created);
110
	} else {
L
Linus Torvalds 已提交
111 112 113 114
		status = nfsd_lookup(rqstp, current_fh,
				     open->op_fname.data, open->op_fname.len, &resfh);
		fh_unlock(current_fh);
	}
115 116
	if (status)
		goto out;
L
Linus Torvalds 已提交
117

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

120 121 122 123 124
	/* 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 已提交
125

126 127
	if (!created)
		status = do_open_permission(rqstp, current_fh, open, MAY_NOP);
L
Linus Torvalds 已提交
128

129
out:
L
Linus Torvalds 已提交
130 131 132 133
	fh_put(&resfh);
	return status;
}

134
static __be32
L
Linus Torvalds 已提交
135 136
do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
137
	__be32 status;
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157

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

158
	status = do_open_permission(rqstp, current_fh, open, MAY_OWNER_OVERRIDE);
L
Linus Torvalds 已提交
159 160 161 162 163

	return status;
}


164
static inline __be32
165 166
nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	   struct nfsd4_open *open, struct nfs4_stateowner **replay_owner)
L
Linus Torvalds 已提交
167
{
168
	__be32 status;
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178 179 180
	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 已提交
181
	if (status == nfserr_replay_me) {
L
Linus Torvalds 已提交
182
		struct nfs4_replay *rp = &open->op_stateowner->so_replay;
183 184 185
		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 已提交
186
				rp->rp_openfh_len);
187
		status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
188 189 190 191
		if (status)
			dprintk("nfsd4_open: replay failed"
				" restoring previous filehandle\n");
		else
A
Al Viro 已提交
192
			status = nfserr_replay_me;
L
Linus Torvalds 已提交
193 194 195
	}
	if (status)
		goto out;
196 197 198

	/* Openowner is now set, so sequence id will get bumped.  Now we need
	 * these checks before we do any creates: */
199
	status = nfserr_grace;
200
	if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
201 202
		goto out;
	status = nfserr_no_grace;
203
	if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
204
		goto out;
205

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

/*
 * filehandle-manipulating ops.
 */
267
static inline __be32
268
nfsd4_getfh(struct nfsd4_compound_state *cstate, struct svc_fh **getfh)
L
Linus Torvalds 已提交
269
{
270
	if (!cstate->current_fh.fh_dentry)
L
Linus Torvalds 已提交
271 272
		return nfserr_nofilehandle;

273
	*getfh = &cstate->current_fh;
L
Linus Torvalds 已提交
274 275 276
	return nfs_ok;
}

277
static inline __be32
278 279
nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	    struct nfsd4_putfh *putfh)
L
Linus Torvalds 已提交
280
{
281 282 283 284 285
	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 已提交
286 287
}

288
static inline __be32
289
nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate)
L
Linus Torvalds 已提交
290
{
291
	__be32 status;
L
Linus Torvalds 已提交
292

293 294
	fh_put(&cstate->current_fh);
	status = exp_pseudoroot(rqstp->rq_client, &cstate->current_fh,
L
Linus Torvalds 已提交
295 296 297 298
			      &rqstp->rq_chandle);
	return status;
}

299
static inline __be32
300
nfsd4_restorefh(struct nfsd4_compound_state *cstate)
L
Linus Torvalds 已提交
301
{
302
	if (!cstate->save_fh.fh_dentry)
L
Linus Torvalds 已提交
303 304
		return nfserr_restorefh;

305
	fh_dup2(&cstate->current_fh, &cstate->save_fh);
L
Linus Torvalds 已提交
306 307 308
	return nfs_ok;
}

309
static inline __be32
310
nfsd4_savefh(struct nfsd4_compound_state *cstate)
L
Linus Torvalds 已提交
311
{
312
	if (!cstate->current_fh.fh_dentry)
L
Linus Torvalds 已提交
313 314
		return nfserr_nofilehandle;

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

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

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

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

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

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

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

	fh_init(&resfh, NFS4_FHSIZE);

361
	status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, MAY_CREATE);
L
Linus Torvalds 已提交
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
	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;

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

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

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

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

	default:
		status = nfserr_badtype;
	}

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

	fh_put(&resfh);
	return status;
}

437
static inline __be32
438 439
nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_getattr *getattr)
L
Linus Torvalds 已提交
440
{
441
	__be32 status;
L
Linus Torvalds 已提交
442

443
	status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
444 445 446 447 448 449 450 451 452
	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;

453
	getattr->ga_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
454 455 456
	return nfs_ok;
}

457
static inline __be32
458 459
nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	   struct nfsd4_link *link)
L
Linus Torvalds 已提交
460
{
461
	__be32 status = nfserr_nofilehandle;
L
Linus Torvalds 已提交
462

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

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

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

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

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

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

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

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

530
static inline __be32
531 532
nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	      struct nfsd4_readdir *readdir)
L
Linus Torvalds 已提交
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
{
	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;
550
	readdir->rd_fhp = &cstate->current_fh;
L
Linus Torvalds 已提交
551 552 553
	return nfs_ok;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
668 669 670 671 672 673
	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;

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

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

698
	status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP);
L
Linus Torvalds 已提交
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
	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;

719 720 721
	status = nfsd4_encode_fattr(&cstate->current_fh,
				    cstate->current_fh.fh_export,
				    cstate->current_fh.fh_dentry, buf,
L
Linus Torvalds 已提交
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
				    &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;
}

/*
 * NULL call.
 */
A
Al Viro 已提交
746
static __be32
L
Linus Torvalds 已提交
747 748 749 750 751
nfsd4_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
	return nfs_ok;
}

752 753 754 755 756 757
static inline void nfsd4_increment_op_stats(u32 opnum)
{
	if (opnum >= FIRST_NFS4_OP && opnum <= LAST_NFS4_OP)
		nfsdstats.nfs4_opcount[opnum]++;
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
static void cstate_free(struct nfsd4_compound_state *cstate)
{
	if (cstate == NULL)
		return;
	fh_put(&cstate->current_fh);
	fh_put(&cstate->save_fh);
	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);
	return cstate;
}
L
Linus Torvalds 已提交
778 779 780 781

/*
 * COMPOUND call.
 */
A
Al Viro 已提交
782
static __be32
L
Linus Torvalds 已提交
783 784 785 786 787
nfsd4_proc_compound(struct svc_rqst *rqstp,
		    struct nfsd4_compoundargs *args,
		    struct nfsd4_compoundres *resp)
{
	struct nfsd4_op	*op;
788
	struct nfsd4_compound_state *cstate = NULL;
L
Linus Torvalds 已提交
789
	struct nfs4_stateowner *replay_owner = NULL;
790
	int		slack_bytes;
791
	__be32		status;
L
Linus Torvalds 已提交
792 793

	status = nfserr_resource;
794 795
	cstate = cstate_alloc();
	if (cstate == NULL)
L
Linus Torvalds 已提交
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
		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++];

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

L
Linus Torvalds 已提交
822 823 824 825 826 827 828 829 830 831 832 833 834
		/*
		 * 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.
		 */
835 836 837 838
		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 已提交
839 840 841 842 843 844 845 846
			op->status = nfserr_resource;
			goto encode_op;
		}

		/* All operations except RENEW, SETCLIENTID, RESTOREFH
		* SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH
		* require a valid current filehandle
		*/
847
		if (!cstate->current_fh.fh_dentry) {
848 849 850 851 852 853 854 855 856 857 858 859 860 861
			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;
			}
		}
		/* Check must be done at start of each operation, except
		 * for GETATTR and ops not listed as returning NFS4ERR_MOVED
		 */
862
		else if (cstate->current_fh.fh_export->ex_fslocs.migrated &&
863 864 865 866 867 868 869
			 !((op->opnum == OP_GETATTR) ||
			   (op->opnum == OP_PUTROOTFH) ||
			   (op->opnum == OP_PUTPUBFH) ||
			   (op->opnum == OP_RENEW) ||
			   (op->opnum == OP_SETCLIENTID) ||
			   (op->opnum == OP_RELEASE_LOCKOWNER))) {
			op->status = nfserr_moved;
L
Linus Torvalds 已提交
870 871 872 873
			goto encode_op;
		}
		switch (op->opnum) {
		case OP_ACCESS:
874 875
			op->status = nfsd4_access(rqstp, cstate,
						  &op->u.access);
L
Linus Torvalds 已提交
876 877
			break;
		case OP_CLOSE:
878 879
			op->status = nfsd4_close(rqstp, cstate,
						 &op->u.close, &replay_owner);
L
Linus Torvalds 已提交
880 881
			break;
		case OP_COMMIT:
882 883
			op->status = nfsd4_commit(rqstp, cstate,
						  &op->u.commit);
L
Linus Torvalds 已提交
884 885
			break;
		case OP_CREATE:
886 887
			op->status = nfsd4_create(rqstp, cstate,
						  &op->u.create);
L
Linus Torvalds 已提交
888 889
			break;
		case OP_DELEGRETURN:
890 891
			op->status = nfsd4_delegreturn(rqstp, cstate,
						       &op->u.delegreturn);
L
Linus Torvalds 已提交
892 893
			break;
		case OP_GETATTR:
894 895
			op->status = nfsd4_getattr(rqstp, cstate,
						   &op->u.getattr);
L
Linus Torvalds 已提交
896 897
			break;
		case OP_GETFH:
898
			op->status = nfsd4_getfh(cstate, &op->u.getfh);
L
Linus Torvalds 已提交
899 900
			break;
		case OP_LINK:
901
			op->status = nfsd4_link(rqstp, cstate, &op->u.link);
L
Linus Torvalds 已提交
902 903
			break;
		case OP_LOCK:
904 905
			op->status = nfsd4_lock(rqstp, cstate, &op->u.lock,
						&replay_owner);
L
Linus Torvalds 已提交
906 907
			break;
		case OP_LOCKT:
908
			op->status = nfsd4_lockt(rqstp, cstate, &op->u.lockt);
L
Linus Torvalds 已提交
909 910
			break;
		case OP_LOCKU:
911 912
			op->status = nfsd4_locku(rqstp, cstate, &op->u.locku,
						 &replay_owner);
L
Linus Torvalds 已提交
913 914
			break;
		case OP_LOOKUP:
915 916
			op->status = nfsd4_lookup(rqstp, cstate,
						  &op->u.lookup);
L
Linus Torvalds 已提交
917 918
			break;
		case OP_LOOKUPP:
919
			op->status = nfsd4_lookupp(rqstp, cstate);
L
Linus Torvalds 已提交
920 921
			break;
		case OP_NVERIFY:
922 923
			op->status = nfsd4_verify(rqstp, cstate,
						  &op->u.nverify);
L
Linus Torvalds 已提交
924 925 926 927
			if (op->status == nfserr_not_same)
				op->status = nfs_ok;
			break;
		case OP_OPEN:
928 929
			op->status = nfsd4_open(rqstp, cstate,
						&op->u.open, &replay_owner);
L
Linus Torvalds 已提交
930 931
			break;
		case OP_OPEN_CONFIRM:
932 933 934
			op->status = nfsd4_open_confirm(rqstp, cstate,
							&op->u.open_confirm,
							&replay_owner);
L
Linus Torvalds 已提交
935 936
			break;
		case OP_OPEN_DOWNGRADE:
937 938 939
			op->status = nfsd4_open_downgrade(rqstp, cstate,
							 &op->u.open_downgrade,
							 &replay_owner);
L
Linus Torvalds 已提交
940 941
			break;
		case OP_PUTFH:
942
			op->status = nfsd4_putfh(rqstp, cstate, &op->u.putfh);
L
Linus Torvalds 已提交
943 944
			break;
		case OP_PUTROOTFH:
945
			op->status = nfsd4_putrootfh(rqstp, cstate);
L
Linus Torvalds 已提交
946 947
			break;
		case OP_READ:
948
			op->status = nfsd4_read(rqstp, cstate, &op->u.read);
L
Linus Torvalds 已提交
949 950
			break;
		case OP_READDIR:
951 952
			op->status = nfsd4_readdir(rqstp, cstate,
						   &op->u.readdir);
L
Linus Torvalds 已提交
953 954
			break;
		case OP_READLINK:
955 956
			op->status = nfsd4_readlink(rqstp, cstate,
						    &op->u.readlink);
L
Linus Torvalds 已提交
957 958
			break;
		case OP_REMOVE:
959 960
			op->status = nfsd4_remove(rqstp, cstate,
						  &op->u.remove);
L
Linus Torvalds 已提交
961 962
			break;
		case OP_RENAME:
963 964
			op->status = nfsd4_rename(rqstp, cstate,
						  &op->u.rename);
L
Linus Torvalds 已提交
965 966 967 968 969
			break;
		case OP_RENEW:
			op->status = nfsd4_renew(&op->u.renew);
			break;
		case OP_RESTOREFH:
970
			op->status = nfsd4_restorefh(cstate);
L
Linus Torvalds 已提交
971 972
			break;
		case OP_SAVEFH:
973
			op->status = nfsd4_savefh(cstate);
L
Linus Torvalds 已提交
974 975
			break;
		case OP_SETATTR:
976 977
			op->status = nfsd4_setattr(rqstp, cstate,
						   &op->u.setattr);
L
Linus Torvalds 已提交
978 979 980 981 982 983 984 985
			break;
		case OP_SETCLIENTID:
			op->status = nfsd4_setclientid(rqstp, &op->u.setclientid);
			break;
		case OP_SETCLIENTID_CONFIRM:
			op->status = nfsd4_setclientid_confirm(rqstp, &op->u.setclientid_confirm);
			break;
		case OP_VERIFY:
986 987
			op->status = nfsd4_verify(rqstp, cstate,
						  &op->u.verify);
L
Linus Torvalds 已提交
988 989 990 991
			if (op->status == nfserr_same)
				op->status = nfs_ok;
			break;
		case OP_WRITE:
992
			op->status = nfsd4_write(rqstp, cstate, &op->u.write);
L
Linus Torvalds 已提交
993 994 995 996 997 998 999 1000 1001 1002
			break;
		case OP_RELEASE_LOCKOWNER:
			op->status = nfsd4_release_lockowner(rqstp, &op->u.release_lockowner);
			break;
		default:
			BUG_ON(op->status == nfs_ok);
			break;
		}

encode_op:
A
Al Viro 已提交
1003
		if (op->status == nfserr_replay_me) {
L
Linus Torvalds 已提交
1004 1005 1006 1007 1008 1009 1010
			op->replay = &replay_owner->so_replay;
			nfsd4_encode_replay(resp, op);
			status = op->status = op->replay->rp_status;
		} else {
			nfsd4_encode_operation(resp, op);
			status = op->status;
		}
1011
		if (replay_owner) {
L
Linus Torvalds 已提交
1012 1013 1014
			nfs4_put_stateowner(replay_owner);
			replay_owner = NULL;
		}
1015 1016 1017
		/* 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);
1018 1019

		nfsd4_increment_op_stats(op->opnum);
L
Linus Torvalds 已提交
1020 1021 1022 1023
	}

out:
	nfsd4_release_compoundargs(args);
1024
	cstate_free(cstate);
L
Linus Torvalds 已提交
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
	return status;
}

#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),
1058
  PROC(compound, compound,	compound,	compound, RC_NOCACHE, NFSD_BUFSIZE/4)
L
Linus Torvalds 已提交
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
};

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