aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2010-03-16 15:40:10 -0400
committerChris Mason <chris.mason@oracle.com>2010-03-16 15:40:10 -0400
commit7fde62bffb576d384ea49a3aed3403d5609ee5bc (patch)
tree0fc0be615e657ed385835b56741c3a62c2c76fff /fs
parentce769a2904bf5a9110ef534a7702397e38e2b3e9 (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.c57
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);
1917out:
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;