aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-22 11:41:17 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-22 11:41:17 -0500
commit533db9b3d4e1a0405161e66f4da66baf5863b863 (patch)
tree3ed4d35f249c5191e79cf02062b3f92cfc5bad36
parentc85e07278e50bdfbb50fe038d9cca01cdf1074d1 (diff)
parent365da4adebb1c012febf81019ad3dc5bb52e2a13 (diff)
Merge branch 'for-3.13' of git://linux-nfs.org/~bfields/linux
Pull nfsd bugfixes from Bruce Fields: "A couple nfsd bugfixes" * 'for-3.13' of git://linux-nfs.org/~bfields/linux: nfsd4: fix xdr decoding of large non-write compounds nfsd: make sure to balance get/put_write_access nfsd: split up nfsd_setattr
-rw-r--r--fs/nfsd/nfs4xdr.c3
-rw-r--r--fs/nfsd/vfs.c173
2 files changed, 101 insertions, 75 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 088de1355e93..ee7237f99f54 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -141,8 +141,8 @@ xdr_error: \
141 141
142static void next_decode_page(struct nfsd4_compoundargs *argp) 142static void next_decode_page(struct nfsd4_compoundargs *argp)
143{ 143{
144 argp->pagelist++;
145 argp->p = page_address(argp->pagelist[0]); 144 argp->p = page_address(argp->pagelist[0]);
145 argp->pagelist++;
146 if (argp->pagelen < PAGE_SIZE) { 146 if (argp->pagelen < PAGE_SIZE) {
147 argp->end = argp->p + (argp->pagelen>>2); 147 argp->end = argp->p + (argp->pagelen>>2);
148 argp->pagelen = 0; 148 argp->pagelen = 0;
@@ -1229,6 +1229,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
1229 len -= pages * PAGE_SIZE; 1229 len -= pages * PAGE_SIZE;
1230 1230
1231 argp->p = (__be32 *)page_address(argp->pagelist[0]); 1231 argp->p = (__be32 *)page_address(argp->pagelist[0]);
1232 argp->pagelist++;
1232 argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE); 1233 argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
1233 } 1234 }
1234 argp->p += XDR_QUADLEN(len); 1235 argp->p += XDR_QUADLEN(len);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 94b5f5d2bfed..7eea63cada1d 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp)
298} 298}
299 299
300/* 300/*
301 * Set various file attributes. 301 * Go over the attributes and take care of the small differences between
302 * N.B. After this call fhp needs an fh_put 302 * NFS semantics and what Linux expects.
303 */ 303 */
304__be32 304static void
305nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, 305nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
306 int check_guard, time_t guardtime)
307{ 306{
308 struct dentry *dentry;
309 struct inode *inode;
310 int accmode = NFSD_MAY_SATTR;
311 umode_t ftype = 0;
312 __be32 err;
313 int host_err;
314 int size_change = 0;
315
316 if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
317 accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
318 if (iap->ia_valid & ATTR_SIZE)
319 ftype = S_IFREG;
320
321 /* Get inode */
322 err = fh_verify(rqstp, fhp, ftype, accmode);
323 if (err)
324 goto out;
325
326 dentry = fhp->fh_dentry;
327 inode = dentry->d_inode;
328
329 /* Ignore any mode updates on symlinks */
330 if (S_ISLNK(inode->i_mode))
331 iap->ia_valid &= ~ATTR_MODE;
332
333 if (!iap->ia_valid)
334 goto out;
335
336 /* 307 /*
337 * NFSv2 does not differentiate between "set-[ac]time-to-now" 308 * NFSv2 does not differentiate between "set-[ac]time-to-now"
338 * which only requires access, and "set-[ac]time-to-X" which 309 * which only requires access, and "set-[ac]time-to-X" which
@@ -342,8 +313,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
342 * convert to "set to now" instead of "set to explicit time" 313 * convert to "set to now" instead of "set to explicit time"
343 * 314 *
344 * We only call inode_change_ok as the last test as technically 315 * We only call inode_change_ok as the last test as technically
345 * it is not an interface that we should be using. It is only 316 * it is not an interface that we should be using.
346 * valid if the filesystem does not define it's own i_op->setattr.
347 */ 317 */
348#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) 318#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
349#define MAX_TOUCH_TIME_ERROR (30*60) 319#define MAX_TOUCH_TIME_ERROR (30*60)
@@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
369 iap->ia_valid &= ~BOTH_TIME_SET; 339 iap->ia_valid &= ~BOTH_TIME_SET;
370 } 340 }
371 } 341 }
372
373 /*
374 * The size case is special.
375 * It changes the file as well as the attributes.
376 */
377 if (iap->ia_valid & ATTR_SIZE) {
378 if (iap->ia_size < inode->i_size) {
379 err = nfsd_permission(rqstp, fhp->fh_export, dentry,
380 NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
381 if (err)
382 goto out;
383 }
384
385 host_err = get_write_access(inode);
386 if (host_err)
387 goto out_nfserr;
388
389 size_change = 1;
390 host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
391 if (host_err) {
392 put_write_access(inode);
393 goto out_nfserr;
394 }
395 }
396 342
397 /* sanitize the mode change */ 343 /* sanitize the mode change */
398 if (iap->ia_valid & ATTR_MODE) { 344 if (iap->ia_valid & ATTR_MODE) {
@@ -415,32 +361,111 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
415 iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID); 361 iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
416 } 362 }
417 } 363 }
364}
418 365
419 /* Change the attributes. */ 366static __be32
367nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
368 struct iattr *iap)
369{
370 struct inode *inode = fhp->fh_dentry->d_inode;
371 int host_err;
420 372
421 iap->ia_valid |= ATTR_CTIME; 373 if (iap->ia_size < inode->i_size) {
374 __be32 err;
422 375
423 err = nfserr_notsync; 376 err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
424 if (!check_guard || guardtime == inode->i_ctime.tv_sec) { 377 NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
425 host_err = nfsd_break_lease(inode); 378 if (err)
426 if (host_err) 379 return err;
427 goto out_nfserr; 380 }
428 fh_lock(fhp);
429 381
430 host_err = notify_change(dentry, iap, NULL); 382 host_err = get_write_access(inode);
431 err = nfserrno(host_err); 383 if (host_err)
432 fh_unlock(fhp); 384 goto out_nfserrno;
385
386 host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
387 if (host_err)
388 goto out_put_write_access;
389 return 0;
390
391out_put_write_access:
392 put_write_access(inode);
393out_nfserrno:
394 return nfserrno(host_err);
395}
396
397/*
398 * Set various file attributes. After this call fhp needs an fh_put.
399 */
400__be32
401nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
402 int check_guard, time_t guardtime)
403{
404 struct dentry *dentry;
405 struct inode *inode;
406 int accmode = NFSD_MAY_SATTR;
407 umode_t ftype = 0;
408 __be32 err;
409 int host_err;
410 int size_change = 0;
411
412 if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
413 accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
414 if (iap->ia_valid & ATTR_SIZE)
415 ftype = S_IFREG;
416
417 /* Get inode */
418 err = fh_verify(rqstp, fhp, ftype, accmode);
419 if (err)
420 goto out;
421
422 dentry = fhp->fh_dentry;
423 inode = dentry->d_inode;
424
425 /* Ignore any mode updates on symlinks */
426 if (S_ISLNK(inode->i_mode))
427 iap->ia_valid &= ~ATTR_MODE;
428
429 if (!iap->ia_valid)
430 goto out;
431
432 nfsd_sanitize_attrs(inode, iap);
433
434 /*
435 * The size case is special, it changes the file in addition to the
436 * attributes.
437 */
438 if (iap->ia_valid & ATTR_SIZE) {
439 err = nfsd_get_write_access(rqstp, fhp, iap);
440 if (err)
441 goto out;
442 size_change = 1;
433 } 443 }
444
445 iap->ia_valid |= ATTR_CTIME;
446
447 if (check_guard && guardtime != inode->i_ctime.tv_sec) {
448 err = nfserr_notsync;
449 goto out_put_write_access;
450 }
451
452 host_err = nfsd_break_lease(inode);
453 if (host_err)
454 goto out_put_write_access_nfserror;
455
456 fh_lock(fhp);
457 host_err = notify_change(dentry, iap, NULL);
458 fh_unlock(fhp);
459
460out_put_write_access_nfserror:
461 err = nfserrno(host_err);
462out_put_write_access:
434 if (size_change) 463 if (size_change)
435 put_write_access(inode); 464 put_write_access(inode);
436 if (!err) 465 if (!err)
437 commit_metadata(fhp); 466 commit_metadata(fhp);
438out: 467out:
439 return err; 468 return err;
440
441out_nfserr:
442 err = nfserrno(host_err);
443 goto out;
444} 469}
445 470
446#if defined(CONFIG_NFSD_V2_ACL) || \ 471#if defined(CONFIG_NFSD_V2_ACL) || \