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 | 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 | 377 | __be32 err; |
| 347 | 378 | int host_err; |
| 348 | 379 | bool get_write_count; |
| 380 | + int size_change = 0; | |
| 349 | 381 | |
| 350 | 382 | if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) |
| 351 | 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 | 390 | /* Get inode */ |
| 359 | 391 | err = fh_verify(rqstp, fhp, ftype, accmode); |
| 360 | 392 | if (err) |
| 361 | − return err; | |
| 393 | + goto out; | |
| 362 | 394 | if (get_write_count) { |
| 363 | 395 | host_err = fh_want_write(fhp); |
| 364 | 396 | if (host_err) |
| 365 | − goto out_host_err; | |
| 397 | + return nfserrno(host_err); | |
| 366 | 398 | } |
| 367 | 399 | |
| 368 | 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 | 405 | iap->ia_valid &= ~ATTR_MODE; |
| 374 | 406 | |
| 375 | 407 | if (!iap->ia_valid) |
| 376 | − return 0; | |
| 408 | + goto out; | |
| 377 | 409 | |
| 378 | 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 | 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 | |
| 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. | |
| 414 | + * attributes. | |
| 389 | 415 | */ |
| 390 | 416 | if (iap->ia_valid & ATTR_SIZE) { |
| 391 | − struct path path = { | |
| 392 | − .mnt = fhp->fh_export->ex_path.mnt, | |
| 393 | − .dentry = dentry, | |
| 394 | − }; | |
| 395 | − bool implicit_mtime = false; | |
| 417 | + err = nfsd_get_write_access(rqstp, fhp, iap); | |
| 418 | + if (err) | |
| 419 | + goto out; | |
| 420 | + size_change = 1; | |
| 396 | 421 | |
| 397 | 422 | /* |
| 398 | − * vfs_truncate implicity updates the mtime IFF the file size | |
| 399 | − * actually changes. Avoid the additional seattr call below if | |
| 400 | − * the only other attribute that the client sends is the mtime. | |
| 423 | + * RFC5661, Section 18.30.4: | |
| 424 | + * Changing the size of a file with SETATTR indirectly | |
| 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) && | |
| 403 | − ((iap->ia_valid & ~(ATTR_SIZE | ATTR_MTIME)) == 0)) | |
| 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; | |
| 429 | + if (iap->ia_size != i_size_read(inode)) | |
| 430 | + iap->ia_valid |= ATTR_MTIME; | |
| 415 | 431 | } |
| 416 | 432 | |
| 417 | 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 | 440 | fh_lock(fhp); |
| 420 | 441 | host_err = notify_change(dentry, iap, NULL); |
| 421 | 442 | fh_unlock(fhp); |
| 422 | − if (host_err) | |
| 423 | − goto out_host_err; | |
| 443 | + err = nfserrno(host_err); | |
| 424 | 444 | |
| 425 | −done: | |
| 426 | − host_err = commit_metadata(fhp); | |
| 427 | −out_host_err: | |
| 428 | − return nfserrno(host_err); | |
| 445 | +out_put_write_access: | |
| 446 | + if (size_change) | |
| 447 | + put_write_access(inode); | |
| 448 | + if (!err) | |
| 449 | + err = nfserrno(commit_metadata(fhp)); | |
| 450 | +out: | |
| 451 | + return err; | |
| 429 | 452 | } |
| 430 | 453 | |
| 431 | 454 | #if defined(CONFIG_NFSD_V4) |