Commit 6e45906a authored by Chuck Lever's avatar Chuck Lever
Browse files

NFSD: Return NFS4ERR_FILE_OPEN only when linking an open file



RFC 8881 Section 18.9.4 paragraphs 1 - 2 tell us that RENAME should
return NFS4ERR_FILE_OPEN only when the target object is a file that
is currently open. If the target is a directory, some other status
must be returned.

The VFS is unlikely to return -EBUSY, but NFSD has to ensure that
errno does not leak to clients as a status code that is not
permitted by spec.

Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 3b60984e
Loading
Loading
Loading
Loading
+31 −13
Original line number Diff line number Diff line
@@ -1698,9 +1698,17 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
	return err;
}

/*
 * Create a hardlink
 * N.B. After this call _both_ ffhp and tfhp need an fh_put
/**
 * nfsd_link - create a link
 * @rqstp: RPC transaction context
 * @ffhp: the file handle of the directory where the new link is to be created
 * @name: the filename of the new link
 * @len: the length of @name in octets
 * @tfhp: the file handle of an existing file object
 *
 * After this call _both_ ffhp and tfhp need an fh_put.
 *
 * Returns a generic NFS status code in network byte-order.
 */
__be32
nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
@@ -1708,6 +1716,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
{
	struct dentry	*ddir, *dnew, *dold;
	struct inode	*dirp;
	int		type;
	__be32		err;
	int		host_err;

@@ -1727,11 +1736,11 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
	if (isdotent(name, len))
		goto out;

	err = nfs_ok;
	type = d_inode(tfhp->fh_dentry)->i_mode & S_IFMT;
	host_err = fh_want_write(tfhp);
	if (host_err) {
		err = nfserrno(host_err);
	if (host_err)
		goto out;
	}

	ddir = ffhp->fh_dentry;
	dirp = d_inode(ddir);
@@ -1739,7 +1748,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,

	dnew = lookup_one_len(name, ddir, len);
	if (IS_ERR(dnew)) {
		err = nfserrno(PTR_ERR(dnew));
		host_err = PTR_ERR(dnew);
		goto out_unlock;
	}

@@ -1755,17 +1764,26 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
	fh_fill_post_attrs(ffhp);
	inode_unlock(dirp);
	if (!host_err) {
		err = nfserrno(commit_metadata(ffhp));
		if (!err)
			err = nfserrno(commit_metadata(tfhp));
	} else {
		err = nfserrno(host_err);
		host_err = commit_metadata(ffhp);
		if (!host_err)
			host_err = commit_metadata(tfhp);
	}

	dput(dnew);
out_drop_write:
	fh_drop_write(tfhp);
	if (host_err == -EBUSY) {
		/*
		 * See RFC 8881 Section 18.9.4 para 1-2: NFSv4 LINK
		 * wants a status unique to the object type.
		 */
		if (type != S_IFDIR)
			err = nfserr_file_open;
		else
			err = nfserr_acces;
	}
out:
	return err;
	return err != nfs_ok ? err : nfserrno(host_err);

out_dput:
	dput(dnew);