This patch incorrectly attempted nested mnt_want_write, and incorrectly disabled nfsd's owner override for truncate. We'll fix those problems and make another attempt soon, for the moment I think the safest is to revert.
Unified · Split
| @@ -332,6 +332,37 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap) · 331 unmodified lines | |||
| 332 | } | 332 | } |
| 333 | } | 333 | } |
| 334 | 334 | ||
| 335 | static __be32 | ||
| 336 | nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp, | ||
| 337 | struct iattr *iap) | ||
| 338 | { | ||
| 339 | struct inode *inode = d_inode(fhp->fh_dentry); | ||
| 340 | int host_err; | ||
| 341 | |||
| 342 | if (iap->ia_size < inode->i_size) { | ||
| 343 | __be32 err; | ||
| 344 | |||
| 345 | err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, | ||
| 346 | NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); | ||
| 347 | if (err) | ||
| 348 | return err; | ||
| 349 | } | ||
| 350 | |||
| 351 | host_err = get_write_access(inode); | ||
| 352 | if (host_err) | ||
| 353 | goto out_nfserrno; | ||
| 354 | |||
| 355 | host_err = locks_verify_truncate(inode, NULL, iap->ia_size); | ||
| 356 | if (host_err) | ||
| 357 | goto out_put_write_access; | ||
| 358 | return 0; | ||
| 359 | |||
| 360 | out_put_write_access: | ||
| 361 | put_write_access(inode); | ||
| 362 | out_nfserrno: | ||
| 363 | return nfserrno(host_err); | ||
| 364 | } | ||
| 365 | |||
| 335 | /* | 366 | /* |
| 336 | * Set various file attributes. After this call fhp needs an fh_put. | 367 | * Set various file attributes. After this call fhp needs an fh_put. |
| 337 | */ | 368 | */ |
| @@ -346,6 +377,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, · 8 unmodified lines | |||
| 346 | __be32 err; | 377 | __be32 err; |
| 347 | int host_err; | 378 | int host_err; |
| 348 | bool get_write_count; | 379 | bool get_write_count; |
| 380 | int size_change = 0; | ||
| 349 | 381 | ||
| 350 | if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) | 382 | if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) |
| 351 | accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; | 383 | accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; |
| @@ -358,11 +390,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, · 6 unmodified lines | |||
| 358 | /* Get inode */ | 390 | /* Get inode */ |
| 359 | err = fh_verify(rqstp, fhp, ftype, accmode); | 391 | err = fh_verify(rqstp, fhp, ftype, accmode); |
| 360 | if (err) | 392 | if (err) |
| 361 | return err; | 393 | goto out; |
| 362 | if (get_write_count) { | 394 | if (get_write_count) { |
| 363 | host_err = fh_want_write(fhp); | 395 | host_err = fh_want_write(fhp); |
| 364 | if (host_err) | 396 | if (host_err) |
| 365 | goto out_host_err; | 397 | return nfserrno(host_err); |
| 366 | } | 398 | } |
| 367 | 399 | ||
| 368 | dentry = fhp->fh_dentry; | 400 | dentry = fhp->fh_dentry; |
| @@ -373,59 +405,50 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, · 4 unmodified lines | |||
| 373 | iap->ia_valid &= ~ATTR_MODE; | 405 | iap->ia_valid &= ~ATTR_MODE; |
| 374 | 406 | ||
| 375 | if (!iap->ia_valid) | 407 | if (!iap->ia_valid) |
| 376 | return 0; | 408 | goto out; |
| 377 | 409 | ||
| 378 | nfsd_sanitize_attrs(inode, iap); | 410 | nfsd_sanitize_attrs(inode, iap); |
| 379 | 411 | ||
| 380 | if (check_guard && guardtime != inode->i_ctime.tv_sec) | ||
| 381 | return nfserr_notsync; | ||
| 382 | |||
| 383 | /* | 412 | /* |
| 384 | * The size case is special, it changes the file in addition to the | 413 | * The size case is special, it changes the file in addition to the |
| 385 | * attributes, and file systems don't expect it to be mixed with | 414 | * attributes. |
| 386 | * "random" attribute changes. We thus split out the size change | ||
| 387 | * into a separate call for vfs_truncate, and do the rest as a | ||
| 388 | * a separate setattr call. | ||
| 389 | */ | 415 | */ |
| 390 | if (iap->ia_valid & ATTR_SIZE) { | 416 | if (iap->ia_valid & ATTR_SIZE) { |
| 391 | struct path path = { | 417 | err = nfsd_get_write_access(rqstp, fhp, iap); |
| 392 | .mnt = fhp->fh_export->ex_path.mnt, | 418 | if (err) |
| 393 | .dentry = dentry, | 419 | goto out; |
| 394 | }; | 420 | size_change = 1; |
| 395 | bool implicit_mtime = false; | ||
| 396 | 421 | ||
| 397 | /* | 422 | /* |
| 398 | * vfs_truncate implicity updates the mtime IFF the file size | 423 | * RFC5661, Section 18.30.4: |
| 399 | * actually changes. Avoid the additional seattr call below if | 424 | * Changing the size of a file with SETATTR indirectly |
| 400 | * the only other attribute that the client sends is the mtime. | 425 | * changes the time_modify and change attributes. |
| 426 | * | ||
| 427 | * (and similar for the older RFCs) | ||
| 401 | */ | 428 | */ |
| 402 | if (iap->ia_size != i_size_read(inode) && | 429 | if (iap->ia_size != i_size_read(inode)) |
| 403 | ((iap->ia_valid & ~(ATTR_SIZE | ATTR_MTIME)) == 0)) | 430 | iap->ia_valid |= ATTR_MTIME; |
| 404 | implicit_mtime = true; | ||
| 405 | |||
| 406 | host_err = vfs_truncate(&path, iap->ia_size); | ||
| 407 | if (host_err) | ||
| 408 | goto out_host_err; | ||
| 409 | |||
| 410 | iap->ia_valid &= ~ATTR_SIZE; | ||
| 411 | if (implicit_mtime) | ||
| 412 | iap->ia_valid &= ~ATTR_MTIME; | ||
| 413 | if (!iap->ia_valid) | ||
| 414 | goto done; | ||
| 415 | } | 431 | } |
| 416 | 432 | ||
| 417 | iap->ia_valid |= ATTR_CTIME; | 433 | iap->ia_valid |= ATTR_CTIME; |
| 418 | 434 | ||
| 435 | if (check_guard && guardtime != inode->i_ctime.tv_sec) { | ||
| 436 | err = nfserr_notsync; | ||
| 437 | goto out_put_write_access; | ||
| 438 | } | ||
| 439 | |||
| 419 | fh_lock(fhp); | 440 | fh_lock(fhp); |
| 420 | host_err = notify_change(dentry, iap, NULL); | 441 | host_err = notify_change(dentry, iap, NULL); |
| 421 | fh_unlock(fhp); | 442 | fh_unlock(fhp); |
| 422 | if (host_err) | 443 | err = nfserrno(host_err); |
| 423 | goto out_host_err; | ||
| 424 | 444 | ||
| 425 | done: | 445 | out_put_write_access: |
| 426 | host_err = commit_metadata(fhp); | 446 | if (size_change) |
| 427 | out_host_err: | 447 | put_write_access(inode); |
| 428 | return nfserrno(host_err); | 448 | if (!err) |
| 449 | err = nfserrno(commit_metadata(fhp)); | ||
| 450 | out: | ||
| 451 | return err; | ||
| 429 | } | 452 | } |
| 430 | 453 | ||
| 431 | #if defined(CONFIG_NFSD_V4) | 454 | #if defined(CONFIG_NFSD_V4) |