btrfs: fix race between quota disable and quota assign ioctls
[platform/kernel/linux-rpi.git] / fs / btrfs / ioctl.c
index d9ff069..f417329 100644 (file)
@@ -3415,13 +3415,10 @@ static long btrfs_ioctl_dev_info(struct btrfs_fs_info *fs_info,
        di_args->bytes_used = btrfs_device_get_bytes_used(dev);
        di_args->total_bytes = btrfs_device_get_total_bytes(dev);
        memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
-       if (dev->name) {
-               strncpy(di_args->path, rcu_str_deref(dev->name),
-                               sizeof(di_args->path) - 1);
-               di_args->path[sizeof(di_args->path) - 1] = 0;
-       } else {
+       if (dev->name)
+               strscpy(di_args->path, rcu_str_deref(dev->name), sizeof(di_args->path));
+       else
                di_args->path[0] = '\0';
-       }
 
 out:
        rcu_read_unlock();
@@ -3911,26 +3908,6 @@ out:
        return ret;
 }
 
-static int build_ino_list(u64 inum, u64 offset, u64 root, void *ctx)
-{
-       struct btrfs_data_container *inodes = ctx;
-       const size_t c = 3 * sizeof(u64);
-
-       if (inodes->bytes_left >= c) {
-               inodes->bytes_left -= c;
-               inodes->val[inodes->elem_cnt] = inum;
-               inodes->val[inodes->elem_cnt + 1] = offset;
-               inodes->val[inodes->elem_cnt + 2] = root;
-               inodes->elem_cnt += 3;
-       } else {
-               inodes->bytes_missing += c - inodes->bytes_left;
-               inodes->bytes_left = 0;
-               inodes->elem_missed += 3;
-       }
-
-       return 0;
-}
-
 static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info,
                                        void __user *arg, int version)
 {
@@ -3966,21 +3943,20 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info,
                size = min_t(u32, loi->size, SZ_16M);
        }
 
-       path = btrfs_alloc_path();
-       if (!path) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
        inodes = init_data_container(size);
        if (IS_ERR(inodes)) {
                ret = PTR_ERR(inodes);
-               inodes = NULL;
-               goto out;
+               goto out_loi;
        }
 
+       path = btrfs_alloc_path();
+       if (!path) {
+               ret = -ENOMEM;
+               goto out;
+       }
        ret = iterate_inodes_from_logical(loi->logical, fs_info, path,
-                                         build_ino_list, inodes, ignore_offset);
+                                         inodes, ignore_offset);
+       btrfs_free_path(path);
        if (ret == -EINVAL)
                ret = -ENOENT;
        if (ret < 0)
@@ -3992,7 +3968,6 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info,
                ret = -EFAULT;
 
 out:
-       btrfs_free_path(path);
        kvfree(inodes);
 out_loi:
        kfree(loi);
@@ -4292,7 +4267,9 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
        }
 
        /* update qgroup status and info */
+       mutex_lock(&fs_info->qgroup_ioctl_lock);
        err = btrfs_run_qgroups(trans);
+       mutex_unlock(&fs_info->qgroup_ioctl_lock);
        if (err < 0)
                btrfs_handle_fs_error(fs_info, err,
                                      "failed to update qgroup status and info");