Skip to content

Commit ac50950

Browse files
neilbrownbrauner
authored andcommitted
VFS/ovl/smb: introduce start_renaming_dentry()
Several callers perform a rename on a dentry they already have, and only require lookup for the target name. This includes smb/server and a few different places in overlayfs. start_renaming_dentry() performs the required lookup and takes the required lock using lock_rename_child() It is used in three places in overlayfs and in ksmbd_vfs_rename(). In the ksmbd case, the parent of the source is not important - the source must be renamed from wherever it is. So start_renaming_dentry() allows rd->old_parent to be NULL and only checks it if it is non-NULL. On success rd->old_parent will be the parent of old_dentry with an extra reference taken. Other start_renaming function also now take the extra reference and end_renaming() now drops this reference as well. ovl_lookup_temp(), ovl_parent_lock(), and ovl_parent_unlock() are all removed as they are no longer needed. OVL_TEMPNAME_SIZE and ovl_tempname() are now declared in overlayfs.h so that ovl_check_rename_whiteout() can access them. ovl_copy_up_workdir() now always cleans up on error. Reviewed-by: Namjae Jeon <linkinjeon@kernel.org> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20251113002050.676694-12-neilb@ownmail.net Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 5c87527 commit ac50950

8 files changed

Lines changed: 150 additions & 134 deletions

File tree

fs/namei.c

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3669,7 +3669,7 @@ EXPORT_SYMBOL(unlock_rename);
36693669

36703670
/**
36713671
* __start_renaming - lookup and lock names for rename
3672-
* @rd: rename data containing parent and flags, and
3672+
* @rd: rename data containing parents and flags, and
36733673
* for receiving found dentries
36743674
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
36753675
* LOOKUP_NO_SYMLINKS etc).
@@ -3680,8 +3680,8 @@ EXPORT_SYMBOL(unlock_rename);
36803680
* rename.
36813681
*
36823682
* 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().
3683+
* @rd.new_dentry and an extra ref is taken on @rd.old_parent.
3684+
* These references and the lock are dropped by end_renaming().
36853685
*
36863686
* The passed in qstrs must have the hash calculated, and no permission
36873687
* checking is performed.
@@ -3735,6 +3735,7 @@ __start_renaming(struct renamedata *rd, int lookup_flags,
37353735

37363736
rd->old_dentry = d1;
37373737
rd->new_dentry = d2;
3738+
dget(rd->old_parent);
37383739
return 0;
37393740

37403741
out_dput_d2:
@@ -3748,7 +3749,7 @@ __start_renaming(struct renamedata *rd, int lookup_flags,
37483749

37493750
/**
37503751
* start_renaming - lookup and lock names for rename with permission checking
3751-
* @rd: rename data containing parent and flags, and
3752+
* @rd: rename data containing parents and flags, and
37523753
* for receiving found dentries
37533754
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
37543755
* LOOKUP_NO_SYMLINKS etc).
@@ -3759,8 +3760,8 @@ __start_renaming(struct renamedata *rd, int lookup_flags,
37593760
* rename.
37603761
*
37613762
* 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().
3763+
* @rd.new_dentry. Also the refcount on @rd->old_parent is increased.
3764+
* These references and the lock are dropped by end_renaming().
37643765
*
37653766
* The passed in qstrs need not have the hash calculated, and basic
37663767
* eXecute permission checking is performed against @rd.mnt_idmap.
@@ -3782,11 +3783,106 @@ int start_renaming(struct renamedata *rd, int lookup_flags,
37823783
}
37833784
EXPORT_SYMBOL(start_renaming);
37843785

3786+
static int
3787+
__start_renaming_dentry(struct renamedata *rd, int lookup_flags,
3788+
struct dentry *old_dentry, struct qstr *new_last)
3789+
{
3790+
struct dentry *trap;
3791+
struct dentry *d2;
3792+
int target_flags = LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
3793+
int err;
3794+
3795+
if (rd->flags & RENAME_EXCHANGE)
3796+
target_flags = 0;
3797+
if (rd->flags & RENAME_NOREPLACE)
3798+
target_flags |= LOOKUP_EXCL;
3799+
3800+
/* Already have the dentry - need to be sure to lock the correct parent */
3801+
trap = lock_rename_child(old_dentry, rd->new_parent);
3802+
if (IS_ERR(trap))
3803+
return PTR_ERR(trap);
3804+
if (d_unhashed(old_dentry) ||
3805+
(rd->old_parent && rd->old_parent != old_dentry->d_parent)) {
3806+
/* dentry was removed, or moved and explicit parent requested */
3807+
err = -EINVAL;
3808+
goto out_unlock;
3809+
}
3810+
3811+
d2 = lookup_one_qstr_excl(new_last, rd->new_parent,
3812+
lookup_flags | target_flags);
3813+
err = PTR_ERR(d2);
3814+
if (IS_ERR(d2))
3815+
goto out_unlock;
3816+
3817+
if (old_dentry == trap) {
3818+
/* source is an ancestor of target */
3819+
err = -EINVAL;
3820+
goto out_dput_d2;
3821+
}
3822+
3823+
if (d2 == trap) {
3824+
/* target is an ancestor of source */
3825+
if (rd->flags & RENAME_EXCHANGE)
3826+
err = -EINVAL;
3827+
else
3828+
err = -ENOTEMPTY;
3829+
goto out_dput_d2;
3830+
}
3831+
3832+
rd->old_dentry = dget(old_dentry);
3833+
rd->new_dentry = d2;
3834+
rd->old_parent = dget(old_dentry->d_parent);
3835+
return 0;
3836+
3837+
out_dput_d2:
3838+
dput(d2);
3839+
out_unlock:
3840+
unlock_rename(old_dentry->d_parent, rd->new_parent);
3841+
return err;
3842+
}
3843+
3844+
/**
3845+
* start_renaming_dentry - lookup and lock name for rename with permission checking
3846+
* @rd: rename data containing parents and flags, and
3847+
* for receiving found dentries
3848+
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
3849+
* LOOKUP_NO_SYMLINKS etc).
3850+
* @old_dentry: dentry of name to move
3851+
* @new_last: name of target in @rd.new_parent
3852+
*
3853+
* Look up target name and ensure locks are in place for
3854+
* rename.
3855+
*
3856+
* On success the found dentry is stored in @rd.new_dentry and
3857+
* @rd.old_parent is confirmed to be the parent of @old_dentry. If it
3858+
* was originally %NULL, it is set. In either case a reference is taken
3859+
* so that end_renaming() can have a stable reference to unlock.
3860+
*
3861+
* References and the lock can be dropped with end_renaming()
3862+
*
3863+
* The passed in qstr need not have the hash calculated, and basic
3864+
* eXecute permission checking is performed against @rd.mnt_idmap.
3865+
*
3866+
* Returns: zero or an error.
3867+
*/
3868+
int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
3869+
struct dentry *old_dentry, struct qstr *new_last)
3870+
{
3871+
int err;
3872+
3873+
err = lookup_one_common(rd->mnt_idmap, new_last, rd->new_parent);
3874+
if (err)
3875+
return err;
3876+
return __start_renaming_dentry(rd, lookup_flags, old_dentry, new_last);
3877+
}
3878+
EXPORT_SYMBOL(start_renaming_dentry);
3879+
37853880
void end_renaming(struct renamedata *rd)
37863881
{
37873882
unlock_rename(rd->old_parent, rd->new_parent);
37883883
dput(rd->old_dentry);
37893884
dput(rd->new_dentry);
3885+
dput(rd->old_parent);
37903886
}
37913887
EXPORT_SYMBOL(end_renaming);
37923888

fs/overlayfs/copy_up.c

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
523523
{
524524
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
525525
struct dentry *indexdir = ovl_indexdir(dentry->d_sb);
526-
struct dentry *index = NULL;
527526
struct dentry *temp = NULL;
527+
struct renamedata rd = {};
528528
struct qstr name = { };
529529
int err;
530530

@@ -556,17 +556,15 @@ static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
556556
if (err)
557557
goto out;
558558

559-
err = ovl_parent_lock(indexdir, temp);
559+
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
560+
rd.old_parent = indexdir;
561+
rd.new_parent = indexdir;
562+
err = start_renaming_dentry(&rd, 0, temp, &name);
560563
if (err)
561564
goto out;
562-
index = ovl_lookup_upper(ofs, name.name, indexdir, name.len);
563-
if (IS_ERR(index)) {
564-
err = PTR_ERR(index);
565-
} else {
566-
err = ovl_do_rename(ofs, indexdir, temp, indexdir, index, 0);
567-
dput(index);
568-
}
569-
ovl_parent_unlock(indexdir);
565+
566+
err = ovl_do_rename_rd(&rd);
567+
end_renaming(&rd);
570568
out:
571569
if (err)
572570
ovl_cleanup(ofs, indexdir, temp);
@@ -763,7 +761,8 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
763761
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
764762
struct inode *inode;
765763
struct path path = { .mnt = ovl_upper_mnt(ofs) };
766-
struct dentry *temp, *upper, *trap;
764+
struct renamedata rd = {};
765+
struct dentry *temp;
767766
struct ovl_cu_creds cc;
768767
int err;
769768
struct ovl_cattr cattr = {
@@ -807,29 +806,24 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
807806
* ovl_copy_up_data(), so lock workdir and destdir and make sure that
808807
* temp wasn't moved before copy up completion or cleanup.
809808
*/
810-
trap = lock_rename(c->workdir, c->destdir);
811-
if (trap || temp->d_parent != c->workdir) {
812-
/* temp or workdir moved underneath us? abort without cleanup */
813-
dput(temp);
809+
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
810+
rd.old_parent = c->workdir;
811+
rd.new_parent = c->destdir;
812+
rd.flags = 0;
813+
err = start_renaming_dentry(&rd, 0, temp,
814+
&QSTR_LEN(c->destname.name, c->destname.len));
815+
if (err) {
816+
/* temp or workdir moved underneath us? map to -EIO */
814817
err = -EIO;
815-
if (!IS_ERR(trap))
816-
unlock_rename(c->workdir, c->destdir);
817-
goto out;
818818
}
819-
820-
err = ovl_copy_up_metadata(c, temp);
821819
if (err)
822-
goto cleanup;
820+
goto cleanup_unlocked;
823821

824-
upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
825-
c->destname.len);
826-
err = PTR_ERR(upper);
827-
if (IS_ERR(upper))
828-
goto cleanup;
822+
err = ovl_copy_up_metadata(c, temp);
823+
if (!err)
824+
err = ovl_do_rename_rd(&rd);
825+
end_renaming(&rd);
829826

830-
err = ovl_do_rename(ofs, c->workdir, temp, c->destdir, upper, 0);
831-
unlock_rename(c->workdir, c->destdir);
832-
dput(upper);
833827
if (err)
834828
goto cleanup_unlocked;
835829

@@ -850,8 +844,6 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
850844

851845
return err;
852846

853-
cleanup:
854-
unlock_rename(c->workdir, c->destdir);
855847
cleanup_unlocked:
856848
ovl_cleanup(ofs, c->workdir, temp);
857849
dput(temp);

fs/overlayfs/dir.c

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,31 +57,14 @@ int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir,
5757
return 0;
5858
}
5959

60-
#define OVL_TEMPNAME_SIZE 20
61-
static void ovl_tempname(char name[OVL_TEMPNAME_SIZE])
60+
void ovl_tempname(char name[OVL_TEMPNAME_SIZE])
6261
{
6362
static atomic_t temp_id = ATOMIC_INIT(0);
6463

6564
/* counter is allowed to wrap, since temp dentries are ephemeral */
6665
snprintf(name, OVL_TEMPNAME_SIZE, "#%x", atomic_inc_return(&temp_id));
6766
}
6867

69-
struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir)
70-
{
71-
struct dentry *temp;
72-
char name[OVL_TEMPNAME_SIZE];
73-
74-
ovl_tempname(name);
75-
temp = ovl_lookup_upper(ofs, name, workdir, strlen(name));
76-
if (!IS_ERR(temp) && temp->d_inode) {
77-
pr_err("workdir/%s already exists\n", name);
78-
dput(temp);
79-
temp = ERR_PTR(-EIO);
80-
}
81-
82-
return temp;
83-
}
84-
8568
static struct dentry *ovl_start_creating_temp(struct ovl_fs *ofs,
8669
struct dentry *workdir)
8770
{

fs/overlayfs/overlayfs.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -447,11 +447,6 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
447447
}
448448

449449
/* util.c */
450-
int ovl_parent_lock(struct dentry *parent, struct dentry *child);
451-
static inline void ovl_parent_unlock(struct dentry *parent)
452-
{
453-
inode_unlock(parent->d_inode);
454-
}
455450
int ovl_get_write_access(struct dentry *dentry);
456451
void ovl_put_write_access(struct dentry *dentry);
457452
void ovl_start_write(struct dentry *dentry);
@@ -888,7 +883,8 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs,
888883
struct dentry *parent, struct dentry *newdentry,
889884
struct ovl_cattr *attr);
890885
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *dentry);
891-
struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir);
886+
#define OVL_TEMPNAME_SIZE 20
887+
void ovl_tempname(char name[OVL_TEMPNAME_SIZE]);
892888
struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
893889
struct ovl_cattr *attr);
894890

fs/overlayfs/super.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -566,33 +566,32 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
566566
{
567567
struct dentry *workdir = ofs->workdir;
568568
struct dentry *temp;
569-
struct dentry *dest;
570569
struct dentry *whiteout;
571570
struct name_snapshot name;
571+
struct renamedata rd = {};
572+
char name2[OVL_TEMPNAME_SIZE];
572573
int err;
573574

574575
temp = ovl_create_temp(ofs, workdir, OVL_CATTR(S_IFREG | 0));
575576
err = PTR_ERR(temp);
576577
if (IS_ERR(temp))
577578
return err;
578579

579-
err = ovl_parent_lock(workdir, temp);
580+
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
581+
rd.old_parent = workdir;
582+
rd.new_parent = workdir;
583+
rd.flags = RENAME_WHITEOUT;
584+
ovl_tempname(name2);
585+
err = start_renaming_dentry(&rd, 0, temp, &QSTR(name2));
580586
if (err) {
581587
dput(temp);
582588
return err;
583589
}
584-
dest = ovl_lookup_temp(ofs, workdir);
585-
err = PTR_ERR(dest);
586-
if (IS_ERR(dest)) {
587-
dput(temp);
588-
ovl_parent_unlock(workdir);
589-
return err;
590-
}
591590

592591
/* Name is inline and stable - using snapshot as a copy helper */
593592
take_dentry_name_snapshot(&name, temp);
594-
err = ovl_do_rename(ofs, workdir, temp, workdir, dest, RENAME_WHITEOUT);
595-
ovl_parent_unlock(workdir);
593+
err = ovl_do_rename_rd(&rd);
594+
end_renaming(&rd);
596595
if (err) {
597596
if (err == -EINVAL)
598597
err = 0;
@@ -616,7 +615,6 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
616615
ovl_cleanup(ofs, workdir, temp);
617616
release_dentry_name_snapshot(&name);
618617
dput(temp);
619-
dput(dest);
620618

621619
return err;
622620
}

fs/overlayfs/util.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,14 +1548,3 @@ void ovl_copyattr(struct inode *inode)
15481548
i_size_write(inode, i_size_read(realinode));
15491549
spin_unlock(&inode->i_lock);
15501550
}
1551-
1552-
int ovl_parent_lock(struct dentry *parent, struct dentry *child)
1553-
{
1554-
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
1555-
if (!child ||
1556-
(!d_unhashed(child) && child->d_parent == parent))
1557-
return 0;
1558-
1559-
inode_unlock(parent->d_inode);
1560-
return -EINVAL;
1561-
}

0 commit comments

Comments
 (0)