Skip to content

Commit

Permalink
NFSD: Return NFS4ERR_FILE_OPEN only when renaming over an open file
Browse files Browse the repository at this point in the history
RFC 8881 Section 18.26.4 paragraphs 1 - 3 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.

Generally I expect that a delegation recall will be triggered in
some of these circumstances. In other cases, the VFS might return
-EBUSY for other reasons, and NFSD has to ensure that errno does
not leak to clients as a status code that is not permitted by spec.

There are some error flows where the target dentry hasn't been
found yet. The default value for @type therefore is S_IFDIR to return
an alternate status code in those cases.

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 370345b commit 3b60984
Showing 1 changed file with 29 additions and 4 deletions.
33 changes: 29 additions & 4 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1794,16 +1794,27 @@ nfsd_has_cached_files(struct dentry *dentry)
return ret;
}

/*
* Rename a file
* N.B. After this call _both_ ffhp and tfhp need an fh_put
/**
* nfsd_rename - rename a directory entry
* @rqstp: RPC transaction context
* @ffhp: the file handle of parent directory containing the entry to be renamed
* @fname: the filename of directory entry to be renamed
* @flen: the length of @fname in octets
* @tfhp: the file handle of parent directory to contain the renamed entry
* @tname: the filename of the new entry
* @tlen: the length of @tlen in octets
*
* After this call _both_ ffhp and tfhp need an fh_put.
*
* Returns a generic NFS status code in network byte-order.
*/
__be32
nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
struct svc_fh *tfhp, char *tname, int tlen)
{
struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap;
struct inode *fdir, *tdir;
int type = S_IFDIR;
__be32 err;
int host_err;
bool close_cached = false;
Expand Down Expand Up @@ -1861,11 +1872,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
host_err = -EINVAL;
if (odentry == trap)
goto out_dput_old;
type = d_inode(odentry)->i_mode & S_IFMT;

ndentry = lookup_one_len(tname, tdentry, tlen);
host_err = PTR_ERR(ndentry);
if (IS_ERR(ndentry))
goto out_dput_old;
if (d_inode(ndentry))
type = d_inode(ndentry)->i_mode & S_IFMT;
host_err = -ENOTEMPTY;
if (ndentry == trap)
goto out_dput_new;
Expand Down Expand Up @@ -1903,7 +1917,18 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
out_dput_old:
dput(odentry);
out_nfserr:
err = nfserrno(host_err);
if (host_err == -EBUSY) {
/*
* See RFC 8881 Section 18.26.4 para 1-3: NFSv4 RENAME
* wants a status unique to the object type.
*/
if (type != S_IFDIR)
err = nfserr_file_open;
else
err = nfserr_acces;
} else {
err = nfserrno(host_err);
}

if (!close_cached) {
fh_fill_post_attrs(ffhp);
Expand Down

0 comments on commit 3b60984

Please sign in to comment.