Skip to content

Commit

Permalink
NFSD: Return NFS4ERR_FILE_OPEN only when linking an open file
Browse files Browse the repository at this point in the history
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: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
  • Loading branch information
Chuck Lever committed Mar 10, 2025
1 parent 3b60984 commit 6e45906
Showing 1 changed file with 31 additions and 13 deletions.
44 changes: 31 additions & 13 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
@@ -1698,16 +1698,25 @@ 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,
char *name, int len, struct svc_fh *tfhp)
{
struct dentry *ddir, *dnew, *dold;
struct inode *dirp;
int type;
__be32 err;
int host_err;

@@ -1727,19 +1736,19 @@ 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);
inode_lock_nested(dirp, I_MUTEX_PARENT);

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);

0 comments on commit 6e45906

Please sign in to comment.