Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0839ffb8 authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

nfsd: Revert "nfsd: special case truncates some more"



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.

Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent d5adbfcd
Loading
Loading
Loading
Loading
+60 −37
Original line number Diff line number Diff line
@@ -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)