Skip to content

Commit 833d2b3

Browse files
neilbrownbrauner
authored andcommitted
Add start_renaming_two_dentries()
A few callers want to lock for a rename and already have both dentries. Also debugfs does want to perform a lookup but doesn't want permission checking, so start_renaming_dentry() cannot be used. This patch introduces start_renaming_two_dentries() which is given both dentries. debugfs performs one lookup itself. As it will only continue with a negative dentry and as those cannot be renamed or unlinked, it is safe to do the lookup before getting the rename locks. overlayfs uses start_renaming_two_dentries() in three places and selinux uses it twice in sel_make_policy_nodes(). In sel_make_policy_nodes() we now lock for rename twice instead of just once so the combined operation is no longer atomic w.r.t the parent directory locks. As selinux_state.policy_mutex is held across the whole operation this does not open up any interesting races. 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-13-neilb@ownmail.net Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent ac50950 commit 833d2b3

5 files changed

Lines changed: 131 additions & 42 deletions

File tree

fs/debugfs/inode.c

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,8 @@ int __printf(2, 3) debugfs_change_name(struct dentry *dentry, const char *fmt, .
842842
int error = 0;
843843
const char *new_name;
844844
struct name_snapshot old_name;
845-
struct dentry *parent, *target;
845+
struct dentry *target;
846+
struct renamedata rd = {};
846847
struct inode *dir;
847848
va_list ap;
848849

@@ -855,36 +856,31 @@ int __printf(2, 3) debugfs_change_name(struct dentry *dentry, const char *fmt, .
855856
if (!new_name)
856857
return -ENOMEM;
857858

858-
parent = dget_parent(dentry);
859-
dir = d_inode(parent);
860-
inode_lock(dir);
859+
rd.old_parent = dget_parent(dentry);
860+
rd.new_parent = rd.old_parent;
861+
rd.flags = RENAME_NOREPLACE;
862+
target = lookup_noperm_unlocked(&QSTR(new_name), rd.new_parent);
863+
if (IS_ERR(target))
864+
return PTR_ERR(target);
861865

862-
take_dentry_name_snapshot(&old_name, dentry);
863-
864-
if (WARN_ON_ONCE(dentry->d_parent != parent)) {
865-
error = -EINVAL;
866-
goto out;
867-
}
868-
if (strcmp(old_name.name.name, new_name) == 0)
869-
goto out;
870-
target = lookup_noperm(&QSTR(new_name), parent);
871-
if (IS_ERR(target)) {
872-
error = PTR_ERR(target);
873-
goto out;
874-
}
875-
if (d_really_is_positive(target)) {
876-
dput(target);
877-
error = -EINVAL;
866+
error = start_renaming_two_dentries(&rd, dentry, target);
867+
if (error) {
868+
if (error == -EEXIST && target == dentry)
869+
/* it isn't an error to rename a thing to itself */
870+
error = 0;
878871
goto out;
879872
}
880-
simple_rename_timestamp(dir, dentry, dir, target);
881-
d_move(dentry, target);
882-
dput(target);
873+
874+
dir = d_inode(rd.old_parent);
875+
take_dentry_name_snapshot(&old_name, dentry);
876+
simple_rename_timestamp(dir, dentry, dir, rd.new_dentry);
877+
d_move(dentry, rd.new_dentry);
883878
fsnotify_move(dir, dir, &old_name.name, d_is_dir(dentry), NULL, dentry);
884-
out:
885879
release_dentry_name_snapshot(&old_name);
886-
inode_unlock(dir);
887-
dput(parent);
880+
end_renaming(&rd);
881+
out:
882+
dput(rd.old_parent);
883+
dput(target);
888884
kfree_const(new_name);
889885
return error;
890886
}

fs/namei.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3877,6 +3877,71 @@ int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
38773877
}
38783878
EXPORT_SYMBOL(start_renaming_dentry);
38793879

3880+
/**
3881+
* start_renaming_two_dentries - Lock to dentries in given parents for rename
3882+
* @rd: rename data containing parent
3883+
* @old_dentry: dentry of name to move
3884+
* @new_dentry: dentry to move to
3885+
*
3886+
* Ensure locks are in place for rename and check parentage is still correct.
3887+
*
3888+
* On success the two dentries are stored in @rd.old_dentry and
3889+
* @rd.new_dentry and @rd.old_parent and @rd.new_parent are confirmed to
3890+
* be the parents of the dentries.
3891+
*
3892+
* References and the lock can be dropped with end_renaming()
3893+
*
3894+
* Returns: zero or an error.
3895+
*/
3896+
int
3897+
start_renaming_two_dentries(struct renamedata *rd,
3898+
struct dentry *old_dentry, struct dentry *new_dentry)
3899+
{
3900+
struct dentry *trap;
3901+
int err;
3902+
3903+
/* Already have the dentry - need to be sure to lock the correct parent */
3904+
trap = lock_rename_child(old_dentry, rd->new_parent);
3905+
if (IS_ERR(trap))
3906+
return PTR_ERR(trap);
3907+
err = -EINVAL;
3908+
if (d_unhashed(old_dentry) ||
3909+
(rd->old_parent && rd->old_parent != old_dentry->d_parent))
3910+
/* old_dentry was removed, or moved and explicit parent requested */
3911+
goto out_unlock;
3912+
if (d_unhashed(new_dentry) ||
3913+
rd->new_parent != new_dentry->d_parent)
3914+
/* new_dentry was removed or moved */
3915+
goto out_unlock;
3916+
3917+
if (old_dentry == trap)
3918+
/* source is an ancestor of target */
3919+
goto out_unlock;
3920+
3921+
if (new_dentry == trap) {
3922+
/* target is an ancestor of source */
3923+
if (rd->flags & RENAME_EXCHANGE)
3924+
err = -EINVAL;
3925+
else
3926+
err = -ENOTEMPTY;
3927+
goto out_unlock;
3928+
}
3929+
3930+
err = -EEXIST;
3931+
if (d_is_positive(new_dentry) && (rd->flags & RENAME_NOREPLACE))
3932+
goto out_unlock;
3933+
3934+
rd->old_dentry = dget(old_dentry);
3935+
rd->new_dentry = dget(new_dentry);
3936+
rd->old_parent = dget(old_dentry->d_parent);
3937+
return 0;
3938+
3939+
out_unlock:
3940+
unlock_rename(old_dentry->d_parent, rd->new_parent);
3941+
return err;
3942+
}
3943+
EXPORT_SYMBOL(start_renaming_two_dentries);
3944+
38803945
void end_renaming(struct renamedata *rd)
38813946
{
38823947
unlock_rename(rd->old_parent, rd->new_parent);

fs/overlayfs/dir.c

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *dir,
123123
struct dentry *dentry)
124124
{
125125
struct dentry *whiteout;
126+
struct renamedata rd = {};
126127
int err;
127128
int flags = 0;
128129

@@ -134,10 +135,14 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *dir,
134135
if (d_is_dir(dentry))
135136
flags = RENAME_EXCHANGE;
136137

137-
err = ovl_lock_rename_workdir(ofs->workdir, whiteout, dir, dentry);
138+
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
139+
rd.old_parent = ofs->workdir;
140+
rd.new_parent = dir;
141+
rd.flags = flags;
142+
err = start_renaming_two_dentries(&rd, whiteout, dentry);
138143
if (!err) {
139-
err = ovl_do_rename(ofs, ofs->workdir, whiteout, dir, dentry, flags);
140-
unlock_rename(ofs->workdir, dir);
144+
err = ovl_do_rename_rd(&rd);
145+
end_renaming(&rd);
141146
}
142147
if (err)
143148
goto kill_whiteout;
@@ -388,6 +393,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
388393
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
389394
struct dentry *workdir = ovl_workdir(dentry);
390395
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
396+
struct renamedata rd = {};
391397
struct path upperpath;
392398
struct dentry *upper;
393399
struct dentry *opaquedir;
@@ -413,7 +419,11 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
413419
if (IS_ERR(opaquedir))
414420
goto out;
415421

416-
err = ovl_lock_rename_workdir(workdir, opaquedir, upperdir, upper);
422+
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
423+
rd.old_parent = workdir;
424+
rd.new_parent = upperdir;
425+
rd.flags = RENAME_EXCHANGE;
426+
err = start_renaming_two_dentries(&rd, opaquedir, upper);
417427
if (err)
418428
goto out_cleanup_unlocked;
419429

@@ -431,8 +441,8 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
431441
if (err)
432442
goto out_cleanup;
433443

434-
err = ovl_do_rename(ofs, workdir, opaquedir, upperdir, upper, RENAME_EXCHANGE);
435-
unlock_rename(workdir, upperdir);
444+
err = ovl_do_rename_rd(&rd);
445+
end_renaming(&rd);
436446
if (err)
437447
goto out_cleanup_unlocked;
438448

@@ -445,7 +455,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
445455
return opaquedir;
446456

447457
out_cleanup:
448-
unlock_rename(workdir, upperdir);
458+
end_renaming(&rd);
449459
out_cleanup_unlocked:
450460
ovl_cleanup(ofs, workdir, opaquedir);
451461
dput(opaquedir);
@@ -468,6 +478,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
468478
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
469479
struct dentry *workdir = ovl_workdir(dentry);
470480
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
481+
struct renamedata rd = {};
471482
struct dentry *upper;
472483
struct dentry *newdentry;
473484
int err;
@@ -499,7 +510,11 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
499510
if (IS_ERR(newdentry))
500511
goto out_dput;
501512

502-
err = ovl_lock_rename_workdir(workdir, newdentry, upperdir, upper);
513+
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
514+
rd.old_parent = workdir;
515+
rd.new_parent = upperdir;
516+
rd.flags = 0;
517+
err = start_renaming_two_dentries(&rd, newdentry, upper);
503518
if (err)
504519
goto out_cleanup_unlocked;
505520

@@ -536,16 +551,16 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
536551
if (err)
537552
goto out_cleanup;
538553

539-
err = ovl_do_rename(ofs, workdir, newdentry, upperdir, upper,
540-
RENAME_EXCHANGE);
541-
unlock_rename(workdir, upperdir);
554+
rd.flags = RENAME_EXCHANGE;
555+
err = ovl_do_rename_rd(&rd);
556+
end_renaming(&rd);
542557
if (err)
543558
goto out_cleanup_unlocked;
544559

545560
ovl_cleanup(ofs, workdir, upper);
546561
} else {
547-
err = ovl_do_rename(ofs, workdir, newdentry, upperdir, upper, 0);
548-
unlock_rename(workdir, upperdir);
562+
err = ovl_do_rename_rd(&rd);
563+
end_renaming(&rd);
549564
if (err)
550565
goto out_cleanup_unlocked;
551566
}
@@ -565,7 +580,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
565580
return err;
566581

567582
out_cleanup:
568-
unlock_rename(workdir, upperdir);
583+
end_renaming(&rd);
569584
out_cleanup_unlocked:
570585
ovl_cleanup(ofs, workdir, newdentry);
571586
dput(newdentry);

include/linux/namei.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ int start_renaming(struct renamedata *rd, int lookup_flags,
160160
struct qstr *old_last, struct qstr *new_last);
161161
int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
162162
struct dentry *old_dentry, struct qstr *new_last);
163+
int start_renaming_two_dentries(struct renamedata *rd,
164+
struct dentry *old_dentry, struct dentry *new_dentry);
163165
void end_renaming(struct renamedata *rd);
164166

165167
/**

security/selinux/selinuxfs.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
506506
{
507507
int ret = 0;
508508
struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir;
509+
struct renamedata rd = {};
509510
unsigned int bool_num = 0;
510511
char **bool_names = NULL;
511512
int *bool_values = NULL;
@@ -539,22 +540,32 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
539540
if (ret)
540541
goto out;
541542

542-
lock_rename(tmp_parent, fsi->sb->s_root);
543+
rd.old_parent = tmp_parent;
544+
rd.new_parent = fsi->sb->s_root;
543545

544546
/* booleans */
547+
ret = start_renaming_two_dentries(&rd, tmp_bool_dir, fsi->bool_dir);
548+
if (ret)
549+
goto out;
550+
545551
d_exchange(tmp_bool_dir, fsi->bool_dir);
546552

547553
swap(fsi->bool_num, bool_num);
548554
swap(fsi->bool_pending_names, bool_names);
549555
swap(fsi->bool_pending_values, bool_values);
550556

551557
fsi->bool_dir = tmp_bool_dir;
558+
end_renaming(&rd);
552559

553560
/* classes */
561+
ret = start_renaming_two_dentries(&rd, tmp_class_dir, fsi->class_dir);
562+
if (ret)
563+
goto out;
564+
554565
d_exchange(tmp_class_dir, fsi->class_dir);
555566
fsi->class_dir = tmp_class_dir;
556567

557-
unlock_rename(tmp_parent, fsi->sb->s_root);
568+
end_renaming(&rd);
558569

559570
out:
560571
sel_remove_old_bool_data(bool_num, bool_names, bool_values);

0 commit comments

Comments
 (0)