bch2_ioctl_subvolume_destroy(): fix locking
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 14 Nov 2023 23:52:42 +0000 (18:52 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 16 Nov 2023 03:47:58 +0000 (22:47 -0500)
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>
fs/bcachefs/fs-ioctl.c

index 5a39bcb597a33d42826a16a98da394de3fe23660..c5ab5a2dc9bed1e164532adbfee29048a63b8516 100644 (file)
@@ -453,33 +453,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;
 }