summary refs log tree commit diff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/nfsd/vfs.c97
1 files changed, 60 insertions, 37 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ca13236dbb1f..26c6fdb4bf67 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -332,6 +332,37 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
 	}
 }
 
+static __be32
+nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
+		struct iattr *iap)
+{
+	struct inode *inode = d_inode(fhp->fh_dentry);
+	int host_err;
+
+	if (iap->ia_size < inode->i_size) {
+		__be32 err;
+
+		err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
+				NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
+		if (err)
+			return err;
+	}
+
+	host_err = get_write_access(inode);
+	if (host_err)
+		goto out_nfserrno;
+
+	host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
+	if (host_err)
+		goto out_put_write_access;
+	return 0;
+
+out_put_write_access:
+	put_write_access(inode);
+out_nfserrno:
+	return nfserrno(host_err);
+}
+
 /*
  * Set various file attributes.  After this call fhp needs an fh_put.
  */
@@ -346,6 +377,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
 	__be32		err;
 	int		host_err;
 	bool		get_write_count;
+	int		size_change = 0;
 
 	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
 		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,
 	/* Get inode */
 	err = fh_verify(rqstp, fhp, ftype, accmode);
 	if (err)
-		return err;
+		goto out;
 	if (get_write_count) {
 		host_err = fh_want_write(fhp);
 		if (host_err)
-			goto out_host_err;
+			return nfserrno(host_err);
 	}
 
 	dentry = fhp->fh_dentry;
@@ -373,59 +405,50 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
 		iap->ia_valid &= ~ATTR_MODE;
 
 	if (!iap->ia_valid)
-		return 0;
+		goto out;
 
 	nfsd_sanitize_attrs(inode, iap);
 
-	if (check_guard && guardtime != inode->i_ctime.tv_sec)
-		return nfserr_notsync;
-
 	/*
 	 * The size case is special, it changes the file in addition to the
-	 * 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 for vfs_truncate, and do the rest as a
-	 * a separate setattr call.
+	 * attributes.
 	 */
 	if (iap->ia_valid & ATTR_SIZE) {
-		struct path path = {
-			.mnt	= fhp->fh_export->ex_path.mnt,
-			.dentry	= dentry,
-		};
-		bool implicit_mtime = false;
+		err = nfsd_get_write_access(rqstp, fhp, iap);
+		if (err)
+			goto out;
+		size_change = 1;
 
 		/*
-		 * vfs_truncate implicity updates the mtime IFF the file size
-		 * actually changes.  Avoid the additional seattr call below if
-		 * the only other attribute that the client sends is the mtime.
+		 * RFC5661, Section 18.30.4:
+		 *   Changing the size of a file with SETATTR indirectly
+		 *   changes the time_modify and change attributes.
+		 *
+		 * (and similar for the older RFCs)
 		 */
-		if (iap->ia_size != i_size_read(inode) &&
-		    ((iap->ia_valid & ~(ATTR_SIZE | ATTR_MTIME)) == 0))
-			implicit_mtime = true;
-
-		host_err = vfs_truncate(&path, iap->ia_size);
-		if (host_err)
-			goto out_host_err;
-
-		iap->ia_valid &= ~ATTR_SIZE;
-		if (implicit_mtime)
-			iap->ia_valid &= ~ATTR_MTIME;
-		if (!iap->ia_valid)
-			goto done;
+		if (iap->ia_size != i_size_read(inode))
+			iap->ia_valid |= ATTR_MTIME;
 	}
 
 	iap->ia_valid |= ATTR_CTIME;
 
+	if (check_guard && guardtime != inode->i_ctime.tv_sec) {
+		err = nfserr_notsync;
+		goto out_put_write_access;
+	}
+
 	fh_lock(fhp);
 	host_err = notify_change(dentry, iap, NULL);
 	fh_unlock(fhp);
-	if (host_err)
-		goto out_host_err;
+	err = nfserrno(host_err);
 
-done:
-	host_err = commit_metadata(fhp);
-out_host_err:
-	return nfserrno(host_err);
+out_put_write_access:
+	if (size_change)
+		put_write_access(inode);
+	if (!err)
+		err = nfserrno(commit_metadata(fhp));
+out:
+	return err;
 }
 
 #if defined(CONFIG_NFSD_V4)