diff options
| -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; |
