diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-22 11:41:17 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-22 11:41:17 -0500 |
commit | 533db9b3d4e1a0405161e66f4da66baf5863b863 (patch) | |
tree | 3ed4d35f249c5191e79cf02062b3f92cfc5bad36 | |
parent | c85e07278e50bdfbb50fe038d9cca01cdf1074d1 (diff) | |
parent | 365da4adebb1c012febf81019ad3dc5bb52e2a13 (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.c | 3 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 173 |
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 | ||
142 | static void next_decode_page(struct nfsd4_compoundargs *argp) | 142 | static 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 | 304 | static void |
305 | nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | 305 | nfsd_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. */ | 366 | static __be32 |
367 | nfsd_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 | |||
391 | out_put_write_access: | ||
392 | put_write_access(inode); | ||
393 | out_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 | ||
401 | nfsd_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 | |||
460 | out_put_write_access_nfserror: | ||
461 | err = nfserrno(host_err); | ||
462 | out_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); |
438 | out: | 467 | out: |
439 | return err; | 468 | return err; |
440 | |||
441 | out_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) || \ |