]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bch2_ioctl_subvolume_destroy(): fix locking
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 14 Nov 2023 23:52:42 +0000 (18:52 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 16 Feb 2024 18:14:30 +0000 (19:14 +0100)
commit bbe6a7c899e7f265c5a6d01a178336a405e98ed6 upstream.

make it use user_path_locked_at() to get the normal directory protection
for modifications, as well as stable ->d_parent and ->d_name in victim

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/bcachefs/fs-ioctl.c

index 14d5cc6f90d7ddb62e4ff1ecd365c3b34e972794..1b9298dc8717eb2fb837334fee973e8a8b2c92d0 100644 (file)
@@ -451,33 +451,36 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
 static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
                                struct bch_ioctl_subvolume arg)
 {
+       const char __user *name = (void __user *)(unsigned long)arg.dst_ptr;
        struct path path;
        struct inode *dir;
+       struct dentry *victim;
        int ret = 0;
 
        if (arg.flags)
                return -EINVAL;
 
-       ret = user_path_at(arg.dirfd,
-                       (const char __user *)(unsigned long)arg.dst_ptr,
-                       LOOKUP_FOLLOW, &path);
-       if (ret)
-               return ret;
+       victim = user_path_locked_at(arg.dirfd, name, &path);
+       if (IS_ERR(victim))
+               return PTR_ERR(victim);
 
-       if (path.dentry->d_sb->s_fs_info != c) {
+       if (victim->d_sb->s_fs_info != c) {
                ret = -EXDEV;
                goto err;
        }
-
-       dir = path.dentry->d_parent->d_inode;
-
-       ret = __bch2_unlink(dir, path.dentry, true);
-       if (ret)
+       if (!d_is_positive(victim)) {
+               ret = -ENOENT;
                goto err;
-
-       fsnotify_rmdir(dir, path.dentry);
-       d_delete(path.dentry);
+       }
+       dir = d_inode(path.dentry);
+       ret = __bch2_unlink(dir, victim, true);
+       if (!ret) {
+               fsnotify_rmdir(dir, victim);
+               d_delete(victim);
+       }
+       inode_unlock(dir);
 err:
+       dput(victim);
        path_put(&path);
        return ret;
 }