]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - fs/nfsd/vfs.c
nfsd: special case truncates some more
[mirror_ubuntu-zesty-kernel.git] / fs / nfsd / vfs.c
index 4bd464cd7c544361c23ed639ce7b5885f4f7d697..3c36ed5a1f07bd5897dd85f0295ce128be286eaf 100644 (file)
@@ -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
-        * 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) {
                err = nfsd_get_write_access(rqstp, fhp, iap);
                if (err)
                        return err;
+       }
 
+       fh_lock(fhp);
+       if (size_change) {
                /*
                 * RFC5661, Section 18.30.4:
                 *   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)
                 */
-               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;
-
-       fh_lock(fhp);
        host_err = notify_change(dentry, iap, NULL);
-       fh_unlock(fhp);
 
+out_unlock:
+       fh_unlock(fhp);
        if (size_change)
                put_write_access(inode);
 out: