Skip to content

Commit ff7c4ea

Browse files
neilbrownbrauner
authored andcommitted
VFS: add start_creating_killable() and start_removing_killable()
These are similar to start_creating() and start_removing(), but allow a fatal signal to abort waiting for the lock. They are used in btrfs for subvol creation and removal. btrfs_may_create() no longer needs IS_DEADDIR() and start_creating_killable() includes that check. 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-10-neilb@ownmail.net Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 7bb1eb4 commit ff7c4ea

File tree

3 files changed

+95
-32
lines changed

3 files changed

+95
-32
lines changed

fs/btrfs/ioctl.c

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -904,14 +904,9 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
904904
struct fscrypt_str name_str = FSTR_INIT((char *)qname->name, qname->len);
905905
int ret;
906906

907-
ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
908-
if (ret == -EINTR)
909-
return ret;
910-
911-
dentry = lookup_one(idmap, qname, parent);
912-
ret = PTR_ERR(dentry);
907+
dentry = start_creating_killable(idmap, parent, qname);
913908
if (IS_ERR(dentry))
914-
goto out_unlock;
909+
return PTR_ERR(dentry);
915910

916911
ret = btrfs_may_create(idmap, dir, dentry);
917912
if (ret)
@@ -940,9 +935,7 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
940935
out_up_read:
941936
up_read(&fs_info->subvol_sem);
942937
out_dput:
943-
dput(dentry);
944-
out_unlock:
945-
btrfs_inode_unlock(BTRFS_I(dir), 0);
938+
end_creating(dentry, parent);
946939
return ret;
947940
}
948941

@@ -2417,18 +2410,10 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24172410
goto free_subvol_name;
24182411
}
24192412

2420-
ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
2421-
if (ret == -EINTR)
2422-
goto free_subvol_name;
2423-
dentry = lookup_one(idmap, &QSTR(subvol_name), parent);
2413+
dentry = start_removing_killable(idmap, parent, &QSTR(subvol_name));
24242414
if (IS_ERR(dentry)) {
24252415
ret = PTR_ERR(dentry);
2426-
goto out_unlock_dir;
2427-
}
2428-
2429-
if (d_really_is_negative(dentry)) {
2430-
ret = -ENOENT;
2431-
goto out_dput;
2416+
goto out_end_removing;
24322417
}
24332418

24342419
inode = d_inode(dentry);
@@ -2449,7 +2434,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24492434
*/
24502435
ret = -EPERM;
24512436
if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED))
2452-
goto out_dput;
2437+
goto out_end_removing;
24532438

24542439
/*
24552440
* Do not allow deletion if the parent dir is the same
@@ -2460,21 +2445,21 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24602445
*/
24612446
ret = -EINVAL;
24622447
if (root == dest)
2463-
goto out_dput;
2448+
goto out_end_removing;
24642449

24652450
ret = inode_permission(idmap, inode, MAY_WRITE | MAY_EXEC);
24662451
if (ret)
2467-
goto out_dput;
2452+
goto out_end_removing;
24682453
}
24692454

24702455
/* check if subvolume may be deleted by a user */
24712456
ret = btrfs_may_delete(idmap, dir, dentry, 1);
24722457
if (ret)
2473-
goto out_dput;
2458+
goto out_end_removing;
24742459

24752460
if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
24762461
ret = -EINVAL;
2477-
goto out_dput;
2462+
goto out_end_removing;
24782463
}
24792464

24802465
btrfs_inode_lock(BTRFS_I(inode), 0);
@@ -2483,10 +2468,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
24832468
if (!ret)
24842469
d_delete_notify(dir, dentry);
24852470

2486-
out_dput:
2487-
dput(dentry);
2488-
out_unlock_dir:
2489-
btrfs_inode_unlock(BTRFS_I(dir), 0);
2471+
out_end_removing:
2472+
end_removing(dentry);
24902473
free_subvol_name:
24912474
kfree(subvol_name_ptr);
24922475
free_parent:

fs/namei.c

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,19 +2778,33 @@ static int filename_parentat(int dfd, struct filename *name,
27782778
* Returns: a locked dentry, or an error.
27792779
*
27802780
*/
2781-
struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
2782-
unsigned int lookup_flags)
2781+
static struct dentry *__start_dirop(struct dentry *parent, struct qstr *name,
2782+
unsigned int lookup_flags,
2783+
unsigned int state)
27832784
{
27842785
struct dentry *dentry;
27852786
struct inode *dir = d_inode(parent);
27862787

2787-
inode_lock_nested(dir, I_MUTEX_PARENT);
2788+
if (state == TASK_KILLABLE) {
2789+
int ret = down_write_killable_nested(&dir->i_rwsem,
2790+
I_MUTEX_PARENT);
2791+
if (ret)
2792+
return ERR_PTR(ret);
2793+
} else {
2794+
inode_lock_nested(dir, I_MUTEX_PARENT);
2795+
}
27882796
dentry = lookup_one_qstr_excl(name, parent, lookup_flags);
27892797
if (IS_ERR(dentry))
27902798
inode_unlock(dir);
27912799
return dentry;
27922800
}
27932801

2802+
struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
2803+
unsigned int lookup_flags)
2804+
{
2805+
return __start_dirop(parent, name, lookup_flags, TASK_NORMAL);
2806+
}
2807+
27942808
/**
27952809
* end_dirop - signal completion of a dirop
27962810
* @de: the dentry which was returned by start_dirop or similar.
@@ -3275,6 +3289,66 @@ struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
32753289
}
32763290
EXPORT_SYMBOL(start_removing);
32773291

3292+
/**
3293+
* start_creating_killable - prepare to create a given name with permission checking
3294+
* @idmap: idmap of the mount
3295+
* @parent: directory in which to prepare to create the name
3296+
* @name: the name to be created
3297+
*
3298+
* Locks are taken and a lookup in performed prior to creating
3299+
* an object in a directory. Permission checking (MAY_EXEC) is performed
3300+
* against @idmap.
3301+
*
3302+
* If the name already exists, a positive dentry is returned.
3303+
*
3304+
* If a signal is received or was already pending, the function aborts
3305+
* with -EINTR;
3306+
*
3307+
* Returns: a negative or positive dentry, or an error.
3308+
*/
3309+
struct dentry *start_creating_killable(struct mnt_idmap *idmap,
3310+
struct dentry *parent,
3311+
struct qstr *name)
3312+
{
3313+
int err = lookup_one_common(idmap, name, parent);
3314+
3315+
if (err)
3316+
return ERR_PTR(err);
3317+
return __start_dirop(parent, name, LOOKUP_CREATE, TASK_KILLABLE);
3318+
}
3319+
EXPORT_SYMBOL(start_creating_killable);
3320+
3321+
/**
3322+
* start_removing_killable - prepare to remove a given name with permission checking
3323+
* @idmap: idmap of the mount
3324+
* @parent: directory in which to find the name
3325+
* @name: the name to be removed
3326+
*
3327+
* Locks are taken and a lookup in performed prior to removing
3328+
* an object from a directory. Permission checking (MAY_EXEC) is performed
3329+
* against @idmap.
3330+
*
3331+
* If the name doesn't exist, an error is returned.
3332+
*
3333+
* end_removing() should be called when removal is complete, or aborted.
3334+
*
3335+
* If a signal is received or was already pending, the function aborts
3336+
* with -EINTR;
3337+
*
3338+
* Returns: a positive dentry, or an error.
3339+
*/
3340+
struct dentry *start_removing_killable(struct mnt_idmap *idmap,
3341+
struct dentry *parent,
3342+
struct qstr *name)
3343+
{
3344+
int err = lookup_one_common(idmap, name, parent);
3345+
3346+
if (err)
3347+
return ERR_PTR(err);
3348+
return __start_dirop(parent, name, 0, TASK_KILLABLE);
3349+
}
3350+
EXPORT_SYMBOL(start_removing_killable);
3351+
32783352
/**
32793353
* start_creating_noperm - prepare to create a given name without permission checking
32803354
* @parent: directory in which to prepare to create the name

include/linux/namei.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
9292
struct qstr *name);
9393
struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
9494
struct qstr *name);
95+
struct dentry *start_creating_killable(struct mnt_idmap *idmap,
96+
struct dentry *parent,
97+
struct qstr *name);
98+
struct dentry *start_removing_killable(struct mnt_idmap *idmap,
99+
struct dentry *parent,
100+
struct qstr *name);
95101
struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *name);
96102
struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *name);
97103
struct dentry *start_removing_dentry(struct dentry *parent,

0 commit comments

Comments
 (0)