Skip to content

Commit 5c87527

Browse files
neilbrownbrauner
authored andcommitted
VFS/nfsd/ovl: introduce start_renaming() and end_renaming()
start_renaming() combines name lookup and locking to prepare for rename. It is used when two names need to be looked up as in nfsd and overlayfs - cases where one or both dentries are already available will be handled separately. __start_renaming() avoids the inode_permission check and hash calculation and is suitable after filename_parentat() in do_renameat2(). It subsumes quite a bit of code from that function. start_renaming() does calculate the hash and check X permission and is suitable elsewhere: - nfsd_rename() - ovl_rename() In ovl, ovl_do_rename_rd() is factored out of ovl_do_rename(), which itself will be gone by the end of the series. Acked-by: Chuck Lever <chuck.lever@oracle.com> (for nfsd parts) Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: NeilBrown <neil@brown.name> -- Changes since v3: - added missig dput() in ovl_rename when "whiteout" is not-NULL. Changes since v2: - in __start_renaming() some label have been renamed, and err is always set before a "goto out_foo" rather than passing the error in a dentry*. - ovl_do_rename() changed to call the new ovl_do_rename_rd() rather than keeping duplicate code - code around ovl_cleanup() call in ovl_rename() restructured. Link: https://patch.msgid.link/20251113002050.676694-11-neilb@ownmail.net Tested-by: syzbot@syzkaller.appspotmail.com Acked-by: Chuck Lever <chuck.lever@oracle.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent ff7c4ea commit 5c87527

5 files changed

Lines changed: 218 additions & 152 deletions

File tree

fs/namei.c

Lines changed: 143 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3667,6 +3667,129 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
36673667
}
36683668
EXPORT_SYMBOL(unlock_rename);
36693669

3670+
/**
3671+
* __start_renaming - lookup and lock names for rename
3672+
* @rd: rename data containing parent and flags, and
3673+
* for receiving found dentries
3674+
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
3675+
* LOOKUP_NO_SYMLINKS etc).
3676+
* @old_last: name of object in @rd.old_parent
3677+
* @new_last: name of object in @rd.new_parent
3678+
*
3679+
* Look up two names and ensure locks are in place for
3680+
* rename.
3681+
*
3682+
* On success the found dentries are stored in @rd.old_dentry,
3683+
* @rd.new_dentry. These references and the lock are dropped by
3684+
* end_renaming().
3685+
*
3686+
* The passed in qstrs must have the hash calculated, and no permission
3687+
* checking is performed.
3688+
*
3689+
* Returns: zero or an error.
3690+
*/
3691+
static int
3692+
__start_renaming(struct renamedata *rd, int lookup_flags,
3693+
struct qstr *old_last, struct qstr *new_last)
3694+
{
3695+
struct dentry *trap;
3696+
struct dentry *d1, *d2;
3697+
int target_flags = LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
3698+
int err;
3699+
3700+
if (rd->flags & RENAME_EXCHANGE)
3701+
target_flags = 0;
3702+
if (rd->flags & RENAME_NOREPLACE)
3703+
target_flags |= LOOKUP_EXCL;
3704+
3705+
trap = lock_rename(rd->old_parent, rd->new_parent);
3706+
if (IS_ERR(trap))
3707+
return PTR_ERR(trap);
3708+
3709+
d1 = lookup_one_qstr_excl(old_last, rd->old_parent,
3710+
lookup_flags);
3711+
err = PTR_ERR(d1);
3712+
if (IS_ERR(d1))
3713+
goto out_unlock;
3714+
3715+
d2 = lookup_one_qstr_excl(new_last, rd->new_parent,
3716+
lookup_flags | target_flags);
3717+
err = PTR_ERR(d2);
3718+
if (IS_ERR(d2))
3719+
goto out_dput_d1;
3720+
3721+
if (d1 == trap) {
3722+
/* source is an ancestor of target */
3723+
err = -EINVAL;
3724+
goto out_dput_d2;
3725+
}
3726+
3727+
if (d2 == trap) {
3728+
/* target is an ancestor of source */
3729+
if (rd->flags & RENAME_EXCHANGE)
3730+
err = -EINVAL;
3731+
else
3732+
err = -ENOTEMPTY;
3733+
goto out_dput_d2;
3734+
}
3735+
3736+
rd->old_dentry = d1;
3737+
rd->new_dentry = d2;
3738+
return 0;
3739+
3740+
out_dput_d2:
3741+
dput(d2);
3742+
out_dput_d1:
3743+
dput(d1);
3744+
out_unlock:
3745+
unlock_rename(rd->old_parent, rd->new_parent);
3746+
return err;
3747+
}
3748+
3749+
/**
3750+
* start_renaming - lookup and lock names for rename with permission checking
3751+
* @rd: rename data containing parent and flags, and
3752+
* for receiving found dentries
3753+
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
3754+
* LOOKUP_NO_SYMLINKS etc).
3755+
* @old_last: name of object in @rd.old_parent
3756+
* @new_last: name of object in @rd.new_parent
3757+
*
3758+
* Look up two names and ensure locks are in place for
3759+
* rename.
3760+
*
3761+
* On success the found dentries are stored in @rd.old_dentry,
3762+
* @rd.new_dentry. These references and the lock are dropped by
3763+
* end_renaming().
3764+
*
3765+
* The passed in qstrs need not have the hash calculated, and basic
3766+
* eXecute permission checking is performed against @rd.mnt_idmap.
3767+
*
3768+
* Returns: zero or an error.
3769+
*/
3770+
int start_renaming(struct renamedata *rd, int lookup_flags,
3771+
struct qstr *old_last, struct qstr *new_last)
3772+
{
3773+
int err;
3774+
3775+
err = lookup_one_common(rd->mnt_idmap, old_last, rd->old_parent);
3776+
if (err)
3777+
return err;
3778+
err = lookup_one_common(rd->mnt_idmap, new_last, rd->new_parent);
3779+
if (err)
3780+
return err;
3781+
return __start_renaming(rd, lookup_flags, old_last, new_last);
3782+
}
3783+
EXPORT_SYMBOL(start_renaming);
3784+
3785+
void end_renaming(struct renamedata *rd)
3786+
{
3787+
unlock_rename(rd->old_parent, rd->new_parent);
3788+
dput(rd->old_dentry);
3789+
dput(rd->new_dentry);
3790+
}
3791+
EXPORT_SYMBOL(end_renaming);
3792+
36703793
/**
36713794
* vfs_prepare_mode - prepare the mode to be used for a new inode
36723795
* @idmap: idmap of the mount the inode was found from
@@ -5504,14 +5627,11 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
55045627
struct filename *to, unsigned int flags)
55055628
{
55065629
struct renamedata rd;
5507-
struct dentry *old_dentry, *new_dentry;
5508-
struct dentry *trap;
55095630
struct path old_path, new_path;
55105631
struct qstr old_last, new_last;
55115632
int old_type, new_type;
55125633
struct inode *delegated_inode = NULL;
5513-
unsigned int lookup_flags = 0, target_flags =
5514-
LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
5634+
unsigned int lookup_flags = 0;
55155635
bool should_retry = false;
55165636
int error = -EINVAL;
55175637

@@ -5522,11 +5642,6 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
55225642
(flags & RENAME_EXCHANGE))
55235643
goto put_names;
55245644

5525-
if (flags & RENAME_EXCHANGE)
5526-
target_flags = 0;
5527-
if (flags & RENAME_NOREPLACE)
5528-
target_flags |= LOOKUP_EXCL;
5529-
55305645
retry:
55315646
error = filename_parentat(olddfd, from, lookup_flags, &old_path,
55325647
&old_last, &old_type);
@@ -5556,66 +5671,40 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
55565671
goto exit2;
55575672

55585673
retry_deleg:
5559-
trap = lock_rename(new_path.dentry, old_path.dentry);
5560-
if (IS_ERR(trap)) {
5561-
error = PTR_ERR(trap);
5674+
rd.old_parent = old_path.dentry;
5675+
rd.mnt_idmap = mnt_idmap(old_path.mnt);
5676+
rd.new_parent = new_path.dentry;
5677+
rd.delegated_inode = &delegated_inode;
5678+
rd.flags = flags;
5679+
5680+
error = __start_renaming(&rd, lookup_flags, &old_last, &new_last);
5681+
if (error)
55625682
goto exit_lock_rename;
5563-
}
55645683

5565-
old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry,
5566-
lookup_flags);
5567-
error = PTR_ERR(old_dentry);
5568-
if (IS_ERR(old_dentry))
5569-
goto exit3;
5570-
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
5571-
lookup_flags | target_flags);
5572-
error = PTR_ERR(new_dentry);
5573-
if (IS_ERR(new_dentry))
5574-
goto exit4;
55755684
if (flags & RENAME_EXCHANGE) {
5576-
if (!d_is_dir(new_dentry)) {
5685+
if (!d_is_dir(rd.new_dentry)) {
55775686
error = -ENOTDIR;
55785687
if (new_last.name[new_last.len])
5579-
goto exit5;
5688+
goto exit_unlock;
55805689
}
55815690
}
55825691
/* unless the source is a directory trailing slashes give -ENOTDIR */
5583-
if (!d_is_dir(old_dentry)) {
5692+
if (!d_is_dir(rd.old_dentry)) {
55845693
error = -ENOTDIR;
55855694
if (old_last.name[old_last.len])
5586-
goto exit5;
5695+
goto exit_unlock;
55875696
if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len])
5588-
goto exit5;
5589-
}
5590-
/* source should not be ancestor of target */
5591-
error = -EINVAL;
5592-
if (old_dentry == trap)
5593-
goto exit5;
5594-
/* target should not be an ancestor of source */
5595-
if (!(flags & RENAME_EXCHANGE))
5596-
error = -ENOTEMPTY;
5597-
if (new_dentry == trap)
5598-
goto exit5;
5697+
goto exit_unlock;
5698+
}
55995699

5600-
error = security_path_rename(&old_path, old_dentry,
5601-
&new_path, new_dentry, flags);
5700+
error = security_path_rename(&old_path, rd.old_dentry,
5701+
&new_path, rd.new_dentry, flags);
56025702
if (error)
5603-
goto exit5;
5703+
goto exit_unlock;
56045704

5605-
rd.old_parent = old_path.dentry;
5606-
rd.old_dentry = old_dentry;
5607-
rd.mnt_idmap = mnt_idmap(old_path.mnt);
5608-
rd.new_parent = new_path.dentry;
5609-
rd.new_dentry = new_dentry;
5610-
rd.delegated_inode = &delegated_inode;
5611-
rd.flags = flags;
56125705
error = vfs_rename(&rd);
5613-
exit5:
5614-
dput(new_dentry);
5615-
exit4:
5616-
dput(old_dentry);
5617-
exit3:
5618-
unlock_rename(new_path.dentry, old_path.dentry);
5706+
exit_unlock:
5707+
end_renaming(&rd);
56195708
exit_lock_rename:
56205709
if (delegated_inode) {
56215710
error = break_deleg_wait(&delegated_inode);

fs/nfsd/vfs.c

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,11 +1885,12 @@ __be32
18851885
nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
18861886
struct svc_fh *tfhp, char *tname, int tlen)
18871887
{
1888-
struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap;
1888+
struct dentry *fdentry, *tdentry;
18891889
int type = S_IFDIR;
1890+
struct renamedata rd = {};
18901891
__be32 err;
18911892
int host_err;
1892-
bool close_cached = false;
1893+
struct dentry *close_cached;
18931894

18941895
trace_nfsd_vfs_rename(rqstp, ffhp, tfhp, fname, flen, tname, tlen);
18951896

@@ -1915,15 +1916,22 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
19151916
goto out;
19161917

19171918
retry:
1919+
close_cached = NULL;
19181920
host_err = fh_want_write(ffhp);
19191921
if (host_err) {
19201922
err = nfserrno(host_err);
19211923
goto out;
19221924
}
19231925

1924-
trap = lock_rename(tdentry, fdentry);
1925-
if (IS_ERR(trap)) {
1926-
err = nfserr_xdev;
1926+
rd.mnt_idmap = &nop_mnt_idmap;
1927+
rd.old_parent = fdentry;
1928+
rd.new_parent = tdentry;
1929+
1930+
host_err = start_renaming(&rd, 0, &QSTR_LEN(fname, flen),
1931+
&QSTR_LEN(tname, tlen));
1932+
1933+
if (host_err) {
1934+
err = nfserrno(host_err);
19271935
goto out_want_write;
19281936
}
19291937
err = fh_fill_pre_attrs(ffhp);
@@ -1933,48 +1941,23 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
19331941
if (err != nfs_ok)
19341942
goto out_unlock;
19351943

1936-
odentry = lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), fdentry);
1937-
host_err = PTR_ERR(odentry);
1938-
if (IS_ERR(odentry))
1939-
goto out_nfserr;
1944+
type = d_inode(rd.old_dentry)->i_mode & S_IFMT;
1945+
1946+
if (d_inode(rd.new_dentry))
1947+
type = d_inode(rd.new_dentry)->i_mode & S_IFMT;
19401948

1941-
host_err = -ENOENT;
1942-
if (d_really_is_negative(odentry))
1943-
goto out_dput_old;
1944-
host_err = -EINVAL;
1945-
if (odentry == trap)
1946-
goto out_dput_old;
1947-
type = d_inode(odentry)->i_mode & S_IFMT;
1948-
1949-
ndentry = lookup_one(&nop_mnt_idmap, &QSTR_LEN(tname, tlen), tdentry);
1950-
host_err = PTR_ERR(ndentry);
1951-
if (IS_ERR(ndentry))
1952-
goto out_dput_old;
1953-
if (d_inode(ndentry))
1954-
type = d_inode(ndentry)->i_mode & S_IFMT;
1955-
host_err = -ENOTEMPTY;
1956-
if (ndentry == trap)
1957-
goto out_dput_new;
1958-
1959-
if ((ndentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) &&
1960-
nfsd_has_cached_files(ndentry)) {
1961-
close_cached = true;
1962-
goto out_dput_old;
1949+
if ((rd.new_dentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) &&
1950+
nfsd_has_cached_files(rd.new_dentry)) {
1951+
close_cached = dget(rd.new_dentry);
1952+
goto out_unlock;
19631953
} else {
1964-
struct renamedata rd = {
1965-
.mnt_idmap = &nop_mnt_idmap,
1966-
.old_parent = fdentry,
1967-
.old_dentry = odentry,
1968-
.new_parent = tdentry,
1969-
.new_dentry = ndentry,
1970-
};
19711954
int retries;
19721955

19731956
for (retries = 1;;) {
19741957
host_err = vfs_rename(&rd);
19751958
if (host_err != -EAGAIN || !retries--)
19761959
break;
1977-
if (!nfsd_wait_for_delegreturn(rqstp, d_inode(odentry)))
1960+
if (!nfsd_wait_for_delegreturn(rqstp, d_inode(rd.old_dentry)))
19781961
break;
19791962
}
19801963
if (!host_err) {
@@ -1983,11 +1966,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
19831966
host_err = commit_metadata(ffhp);
19841967
}
19851968
}
1986-
out_dput_new:
1987-
dput(ndentry);
1988-
out_dput_old:
1989-
dput(odentry);
1990-
out_nfserr:
19911969
if (host_err == -EBUSY) {
19921970
/*
19931971
* See RFC 8881 Section 18.26.4 para 1-3: NFSv4 RENAME
@@ -2006,7 +1984,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
20061984
fh_fill_post_attrs(tfhp);
20071985
}
20081986
out_unlock:
2009-
unlock_rename(tdentry, fdentry);
1987+
end_renaming(&rd);
20101988
out_want_write:
20111989
fh_drop_write(ffhp);
20121990

@@ -2017,9 +1995,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
20171995
* until this point and then reattempt the whole shebang.
20181996
*/
20191997
if (close_cached) {
2020-
close_cached = false;
2021-
nfsd_close_cached_files(ndentry);
2022-
dput(ndentry);
1998+
nfsd_close_cached_files(close_cached);
1999+
dput(close_cached);
20232000
goto retry;
20242001
}
20252002
out:

0 commit comments

Comments
 (0)