Commit 7fde62bf authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: buffer results in the space_info ioctl

The space_info ioctl was using copy_to_user inside rcu_read_lock.  This
commit changes things to copy into a buffer first and then dump the
result down to userland.
Signed-off-by: default avatarChris Mason <>
parent ce769a29
...@@ -1848,39 +1848,74 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) ...@@ -1848,39 +1848,74 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg)
struct btrfs_ioctl_space_args space_args; struct btrfs_ioctl_space_args space_args;
struct btrfs_ioctl_space_info space; struct btrfs_ioctl_space_info space;
struct btrfs_ioctl_space_info *dest; struct btrfs_ioctl_space_info *dest;
struct btrfs_ioctl_space_info *dest_orig;
struct btrfs_ioctl_space_info *user_dest;
struct btrfs_space_info *info; struct btrfs_space_info *info;
int alloc_size;
int ret = 0; int ret = 0;
int slot_count = 0;
if (copy_from_user(&space_args, if (copy_from_user(&space_args,
(struct btrfs_ioctl_space_args __user *)arg, (struct btrfs_ioctl_space_args __user *)arg,
sizeof(space_args))) sizeof(space_args)))
return -EFAULT; return -EFAULT;
/* first we count slots */
list_for_each_entry_rcu(info, &root->fs_info->space_info, list)
/* space_slots == 0 means they are asking for a count */
if (space_args.space_slots == 0) {
space_args.total_spaces = slot_count;
goto out;
alloc_size = sizeof(*dest) * slot_count;
/* we generally have at most 6 or so space infos, one for each raid
* level. So, a whole page should be more than enough for everyone
if (alloc_size > PAGE_CACHE_SIZE)
return -ENOMEM;
space_args.total_spaces = 0; space_args.total_spaces = 0;
dest = (struct btrfs_ioctl_space_info *) dest = kmalloc(alloc_size, GFP_NOFS);
(arg + sizeof(struct btrfs_ioctl_space_args)); if (!dest)
return -ENOMEM;
dest_orig = dest;
/* now we have a buffer to copy into */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(info, &root->fs_info->space_info, list) { list_for_each_entry_rcu(info, &root->fs_info->space_info, list) {
if (!space_args.space_slots) { /* make sure we don't copy more than we allocated
space_args.total_spaces++; * in our buffer
continue; */
} if (slot_count == 0)
/* make sure userland has enough room in their buffer */
if (space_args.total_spaces >= space_args.space_slots) if (space_args.total_spaces >= space_args.space_slots)
break; break;
space.flags = info->flags; space.flags = info->flags;
space.total_bytes = info->total_bytes; space.total_bytes = info->total_bytes;
space.used_bytes = info->bytes_used; space.used_bytes = info->bytes_used;
if (copy_to_user(dest, &space, sizeof(space))) { memcpy(dest, &space, sizeof(space));
ret = -EFAULT;
dest++; dest++;
space_args.total_spaces++; space_args.total_spaces++;
} }
rcu_read_unlock(); rcu_read_unlock();
if (copy_to_user(arg, &space_args, sizeof(space_args))) user_dest = (struct btrfs_ioctl_space_info *)
(arg + sizeof(struct btrfs_ioctl_space_args));
if (copy_to_user(user_dest, dest_orig, alloc_size))
ret = -EFAULT;
if (ret == 0 && copy_to_user(arg, &space_args, sizeof(space_args)))
ret = -EFAULT; ret = -EFAULT;
return ret; return ret;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment