|
@@ -414,13 +414,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
|
|
|
|
|
|
/*
|
|
/*
|
|
* The size case is special, it changes the file in addition to the
|
|
* The size case is special, it changes the file in addition to the
|
|
- * attributes.
|
|
|
|
|
|
+ * attributes, and file systems don't expect it to be mixed with
|
|
|
|
+ * "random" attribute changes. We thus split out the size change
|
|
|
|
+ * into a separate call to ->setattr, and do the rest as a separate
|
|
|
|
+ * setattr call.
|
|
*/
|
|
*/
|
|
if (size_change) {
|
|
if (size_change) {
|
|
err = nfsd_get_write_access(rqstp, fhp, iap);
|
|
err = nfsd_get_write_access(rqstp, fhp, iap);
|
|
if (err)
|
|
if (err)
|
|
return err;
|
|
return err;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ fh_lock(fhp);
|
|
|
|
+ if (size_change) {
|
|
/*
|
|
/*
|
|
* RFC5661, Section 18.30.4:
|
|
* RFC5661, Section 18.30.4:
|
|
* Changing the size of a file with SETATTR indirectly
|
|
* Changing the size of a file with SETATTR indirectly
|
|
@@ -428,16 +434,30 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
|
|
*
|
|
*
|
|
* (and similar for the older RFCs)
|
|
* (and similar for the older RFCs)
|
|
*/
|
|
*/
|
|
- if (iap->ia_size != i_size_read(inode))
|
|
|
|
- iap->ia_valid |= ATTR_MTIME;
|
|
|
|
|
|
+ struct iattr size_attr = {
|
|
|
|
+ .ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME,
|
|
|
|
+ .ia_size = iap->ia_size,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ host_err = notify_change(dentry, &size_attr, NULL);
|
|
|
|
+ if (host_err)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ iap->ia_valid &= ~ATTR_SIZE;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Avoid the additional setattr call below if the only other
|
|
|
|
+ * attribute that the client sends is the mtime, as we update
|
|
|
|
+ * it as part of the size change above.
|
|
|
|
+ */
|
|
|
|
+ if ((iap->ia_valid & ~ATTR_MTIME) == 0)
|
|
|
|
+ goto out_unlock;
|
|
}
|
|
}
|
|
|
|
|
|
iap->ia_valid |= ATTR_CTIME;
|
|
iap->ia_valid |= ATTR_CTIME;
|
|
-
|
|
|
|
- fh_lock(fhp);
|
|
|
|
host_err = notify_change(dentry, iap, NULL);
|
|
host_err = notify_change(dentry, iap, NULL);
|
|
- fh_unlock(fhp);
|
|
|
|
|
|
|
|
|
|
+out_unlock:
|
|
|
|
+ fh_unlock(fhp);
|
|
if (size_change)
|
|
if (size_change)
|
|
put_write_access(inode);
|
|
put_write_access(inode);
|
|
out:
|
|
out:
|