diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ioctl.c | 48 |
1 files changed, 33 insertions, 15 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3d89fd888399..393a543a519e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -1958,7 +1958,7 @@ static noinline int copy_to_sk(struct btrfs_root *root, | |||
1958 | struct btrfs_key *key, | 1958 | struct btrfs_key *key, |
1959 | struct btrfs_ioctl_search_key *sk, | 1959 | struct btrfs_ioctl_search_key *sk, |
1960 | size_t *buf_size, | 1960 | size_t *buf_size, |
1961 | char *buf, | 1961 | char __user *ubuf, |
1962 | unsigned long *sk_offset, | 1962 | unsigned long *sk_offset, |
1963 | int *num_found) | 1963 | int *num_found) |
1964 | { | 1964 | { |
@@ -2018,14 +2018,22 @@ static noinline int copy_to_sk(struct btrfs_root *root, | |||
2018 | sh.transid = found_transid; | 2018 | sh.transid = found_transid; |
2019 | 2019 | ||
2020 | /* copy search result header */ | 2020 | /* copy search result header */ |
2021 | memcpy(buf + *sk_offset, &sh, sizeof(sh)); | 2021 | if (copy_to_user(ubuf + *sk_offset, &sh, sizeof(sh))) { |
2022 | ret = -EFAULT; | ||
2023 | goto out; | ||
2024 | } | ||
2025 | |||
2022 | *sk_offset += sizeof(sh); | 2026 | *sk_offset += sizeof(sh); |
2023 | 2027 | ||
2024 | if (item_len) { | 2028 | if (item_len) { |
2025 | char *p = buf + *sk_offset; | 2029 | char __user *up = ubuf + *sk_offset; |
2026 | /* copy the item */ | 2030 | /* copy the item */ |
2027 | read_extent_buffer(leaf, p, | 2031 | if (read_extent_buffer_to_user(leaf, up, |
2028 | item_off, item_len); | 2032 | item_off, item_len)) { |
2033 | ret = -EFAULT; | ||
2034 | goto out; | ||
2035 | } | ||
2036 | |||
2029 | *sk_offset += item_len; | 2037 | *sk_offset += item_len; |
2030 | } | 2038 | } |
2031 | (*num_found)++; | 2039 | (*num_found)++; |
@@ -2052,13 +2060,22 @@ advance_key: | |||
2052 | } else | 2060 | } else |
2053 | ret = 1; | 2061 | ret = 1; |
2054 | out: | 2062 | out: |
2063 | /* | ||
2064 | * 0: all items from this leaf copied, continue with next | ||
2065 | * 1: * more items can be copied, but unused buffer is too small | ||
2066 | * * all items were found | ||
2067 | * Either way, it will stops the loop which iterates to the next | ||
2068 | * leaf | ||
2069 | * -EOVERFLOW: item was to large for buffer | ||
2070 | * -EFAULT: could not copy extent buffer back to userspace | ||
2071 | */ | ||
2055 | return ret; | 2072 | return ret; |
2056 | } | 2073 | } |
2057 | 2074 | ||
2058 | static noinline int search_ioctl(struct inode *inode, | 2075 | static noinline int search_ioctl(struct inode *inode, |
2059 | struct btrfs_ioctl_search_key *sk, | 2076 | struct btrfs_ioctl_search_key *sk, |
2060 | size_t *buf_size, | 2077 | size_t *buf_size, |
2061 | char *buf) | 2078 | char __user *ubuf) |
2062 | { | 2079 | { |
2063 | struct btrfs_root *root; | 2080 | struct btrfs_root *root; |
2064 | struct btrfs_key key; | 2081 | struct btrfs_key key; |
@@ -2106,7 +2123,7 @@ static noinline int search_ioctl(struct inode *inode, | |||
2106 | ret = 0; | 2123 | ret = 0; |
2107 | goto err; | 2124 | goto err; |
2108 | } | 2125 | } |
2109 | ret = copy_to_sk(root, path, &key, sk, buf_size, buf, | 2126 | ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf, |
2110 | &sk_offset, &num_found); | 2127 | &sk_offset, &num_found); |
2111 | btrfs_release_path(path); | 2128 | btrfs_release_path(path); |
2112 | if (ret) | 2129 | if (ret) |
@@ -2124,7 +2141,8 @@ err: | |||
2124 | static noinline int btrfs_ioctl_tree_search(struct file *file, | 2141 | static noinline int btrfs_ioctl_tree_search(struct file *file, |
2125 | void __user *argp) | 2142 | void __user *argp) |
2126 | { | 2143 | { |
2127 | struct btrfs_ioctl_search_args *args; | 2144 | struct btrfs_ioctl_search_args __user *uargs; |
2145 | struct btrfs_ioctl_search_key sk; | ||
2128 | struct inode *inode; | 2146 | struct inode *inode; |
2129 | int ret; | 2147 | int ret; |
2130 | size_t buf_size; | 2148 | size_t buf_size; |
@@ -2132,14 +2150,15 @@ static noinline int btrfs_ioctl_tree_search(struct file *file, | |||
2132 | if (!capable(CAP_SYS_ADMIN)) | 2150 | if (!capable(CAP_SYS_ADMIN)) |
2133 | return -EPERM; | 2151 | return -EPERM; |
2134 | 2152 | ||
2135 | args = memdup_user(argp, sizeof(*args)); | 2153 | uargs = (struct btrfs_ioctl_search_args __user *)argp; |
2136 | if (IS_ERR(args)) | ||
2137 | return PTR_ERR(args); | ||
2138 | 2154 | ||
2139 | buf_size = sizeof(args->buf); | 2155 | if (copy_from_user(&sk, &uargs->key, sizeof(sk))) |
2156 | return -EFAULT; | ||
2157 | |||
2158 | buf_size = sizeof(uargs->buf); | ||
2140 | 2159 | ||
2141 | inode = file_inode(file); | 2160 | inode = file_inode(file); |
2142 | ret = search_ioctl(inode, &args->key, &buf_size, args->buf); | 2161 | ret = search_ioctl(inode, &sk, &buf_size, uargs->buf); |
2143 | 2162 | ||
2144 | /* | 2163 | /* |
2145 | * In the origin implementation an overflow is handled by returning a | 2164 | * In the origin implementation an overflow is handled by returning a |
@@ -2148,9 +2167,8 @@ static noinline int btrfs_ioctl_tree_search(struct file *file, | |||
2148 | if (ret == -EOVERFLOW) | 2167 | if (ret == -EOVERFLOW) |
2149 | ret = 0; | 2168 | ret = 0; |
2150 | 2169 | ||
2151 | if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) | 2170 | if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk))) |
2152 | ret = -EFAULT; | 2171 | ret = -EFAULT; |
2153 | kfree(args); | ||
2154 | return ret; | 2172 | return ret; |
2155 | } | 2173 | } |
2156 | 2174 | ||