diff options
author | Chris Mason <chris.mason@oracle.com> | 2010-03-16 15:40:10 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-03-16 15:40:10 -0400 |
commit | 7fde62bffb576d384ea49a3aed3403d5609ee5bc (patch) | |
tree | 0fc0be615e657ed385835b56741c3a62c2c76fff /fs | |
parent | ce769a2904bf5a9110ef534a7702397e38e2b3e9 (diff) |
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: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ioctl.c | 57 |
1 files changed, 46 insertions, 11 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 38a68863390a..4329610b141b 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -1848,39 +1848,74 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) | |||
1848 | struct btrfs_ioctl_space_args space_args; | 1848 | struct btrfs_ioctl_space_args space_args; |
1849 | struct btrfs_ioctl_space_info space; | 1849 | struct btrfs_ioctl_space_info space; |
1850 | struct btrfs_ioctl_space_info *dest; | 1850 | struct btrfs_ioctl_space_info *dest; |
1851 | struct btrfs_ioctl_space_info *dest_orig; | ||
1852 | struct btrfs_ioctl_space_info *user_dest; | ||
1851 | struct btrfs_space_info *info; | 1853 | struct btrfs_space_info *info; |
1854 | int alloc_size; | ||
1852 | int ret = 0; | 1855 | int ret = 0; |
1856 | int slot_count = 0; | ||
1853 | 1857 | ||
1854 | if (copy_from_user(&space_args, | 1858 | if (copy_from_user(&space_args, |
1855 | (struct btrfs_ioctl_space_args __user *)arg, | 1859 | (struct btrfs_ioctl_space_args __user *)arg, |
1856 | sizeof(space_args))) | 1860 | sizeof(space_args))) |
1857 | return -EFAULT; | 1861 | return -EFAULT; |
1858 | 1862 | ||
1863 | /* first we count slots */ | ||
1864 | rcu_read_lock(); | ||
1865 | list_for_each_entry_rcu(info, &root->fs_info->space_info, list) | ||
1866 | slot_count++; | ||
1867 | rcu_read_unlock(); | ||
1868 | |||
1869 | /* space_slots == 0 means they are asking for a count */ | ||
1870 | if (space_args.space_slots == 0) { | ||
1871 | space_args.total_spaces = slot_count; | ||
1872 | goto out; | ||
1873 | } | ||
1874 | alloc_size = sizeof(*dest) * slot_count; | ||
1875 | /* we generally have at most 6 or so space infos, one for each raid | ||
1876 | * level. So, a whole page should be more than enough for everyone | ||
1877 | */ | ||
1878 | if (alloc_size > PAGE_CACHE_SIZE) | ||
1879 | return -ENOMEM; | ||
1880 | |||
1859 | space_args.total_spaces = 0; | 1881 | space_args.total_spaces = 0; |
1860 | dest = (struct btrfs_ioctl_space_info *) | 1882 | dest = kmalloc(alloc_size, GFP_NOFS); |
1861 | (arg + sizeof(struct btrfs_ioctl_space_args)); | 1883 | if (!dest) |
1884 | return -ENOMEM; | ||
1885 | dest_orig = dest; | ||
1862 | 1886 | ||
1887 | /* now we have a buffer to copy into */ | ||
1863 | rcu_read_lock(); | 1888 | rcu_read_lock(); |
1864 | list_for_each_entry_rcu(info, &root->fs_info->space_info, list) { | 1889 | list_for_each_entry_rcu(info, &root->fs_info->space_info, list) { |
1865 | if (!space_args.space_slots) { | 1890 | /* make sure we don't copy more than we allocated |
1866 | space_args.total_spaces++; | 1891 | * in our buffer |
1867 | continue; | 1892 | */ |
1868 | } | 1893 | if (slot_count == 0) |
1894 | break; | ||
1895 | slot_count--; | ||
1896 | |||
1897 | /* make sure userland has enough room in their buffer */ | ||
1869 | if (space_args.total_spaces >= space_args.space_slots) | 1898 | if (space_args.total_spaces >= space_args.space_slots) |
1870 | break; | 1899 | break; |
1900 | |||
1871 | space.flags = info->flags; | 1901 | space.flags = info->flags; |
1872 | space.total_bytes = info->total_bytes; | 1902 | space.total_bytes = info->total_bytes; |
1873 | space.used_bytes = info->bytes_used; | 1903 | space.used_bytes = info->bytes_used; |
1874 | if (copy_to_user(dest, &space, sizeof(space))) { | 1904 | memcpy(dest, &space, sizeof(space)); |
1875 | ret = -EFAULT; | ||
1876 | break; | ||
1877 | } | ||
1878 | dest++; | 1905 | dest++; |
1879 | space_args.total_spaces++; | 1906 | space_args.total_spaces++; |
1880 | } | 1907 | } |
1881 | rcu_read_unlock(); | 1908 | rcu_read_unlock(); |
1882 | 1909 | ||
1883 | if (copy_to_user(arg, &space_args, sizeof(space_args))) | 1910 | user_dest = (struct btrfs_ioctl_space_info *) |
1911 | (arg + sizeof(struct btrfs_ioctl_space_args)); | ||
1912 | |||
1913 | if (copy_to_user(user_dest, dest_orig, alloc_size)) | ||
1914 | ret = -EFAULT; | ||
1915 | |||
1916 | kfree(dest_orig); | ||
1917 | out: | ||
1918 | if (ret == 0 && copy_to_user(arg, &space_args, sizeof(space_args))) | ||
1884 | ret = -EFAULT; | 1919 | ret = -EFAULT; |
1885 | 1920 | ||
1886 | return ret; | 1921 | return ret; |