diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-14 20:48:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-14 20:48:43 -0400 |
commit | 16d52ef7c026f925893a1c0fc46516349928f319 (patch) | |
tree | 1cb9bf5406e22bb3f4fb37d9e6d4e926304dfe04 /fs/btrfs/ioctl.c | |
parent | a311c480384c5aa9aaae195b89c3ec89c3b66379 (diff) | |
parent | 47a306a74842248dcd537b85f9a36c7b156c59a9 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs
Pull more btrfs updates from Chris Mason:
"This has a few fixes since our last pull and a new ioctl for doing
btree searches from userland. It's very similar to the existing
ioctl, but lets us return larger items back down to the app"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs:
btrfs: fix error handling in create_pending_snapshot
btrfs: fix use of uninit "ret" in end_extent_writepage()
btrfs: free ulist in qgroup_shared_accounting() error path
Btrfs: fix qgroups sanity test crash or hang
btrfs: prevent RCU warning when dereferencing radix tree slot
Btrfs: fix unfinished readahead thread for raid5/6 degraded mounting
btrfs: new ioctl TREE_SEARCH_V2
btrfs: tree_search, search_ioctl: direct copy to userspace
btrfs: new function read_extent_buffer_to_user
btrfs: tree_search, copy_to_sk: return needed size on EOVERFLOW
btrfs: tree_search, copy_to_sk: return EOVERFLOW for too small buffer
btrfs: tree_search, search_ioctl: accept varying buffer
btrfs: tree_search: eliminate redundant nr_items check
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 147 |
1 files changed, 121 insertions, 26 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 82c18ba12e3f..0d321c23069a 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -1957,7 +1957,8 @@ static noinline int copy_to_sk(struct btrfs_root *root, | |||
1957 | struct btrfs_path *path, | 1957 | struct btrfs_path *path, |
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 | char *buf, | 1960 | size_t *buf_size, |
1961 | char __user *ubuf, | ||
1961 | unsigned long *sk_offset, | 1962 | unsigned long *sk_offset, |
1962 | int *num_found) | 1963 | int *num_found) |
1963 | { | 1964 | { |
@@ -1989,13 +1990,25 @@ static noinline int copy_to_sk(struct btrfs_root *root, | |||
1989 | if (!key_in_sk(key, sk)) | 1990 | if (!key_in_sk(key, sk)) |
1990 | continue; | 1991 | continue; |
1991 | 1992 | ||
1992 | if (sizeof(sh) + item_len > BTRFS_SEARCH_ARGS_BUFSIZE) | 1993 | if (sizeof(sh) + item_len > *buf_size) { |
1994 | if (*num_found) { | ||
1995 | ret = 1; | ||
1996 | goto out; | ||
1997 | } | ||
1998 | |||
1999 | /* | ||
2000 | * return one empty item back for v1, which does not | ||
2001 | * handle -EOVERFLOW | ||
2002 | */ | ||
2003 | |||
2004 | *buf_size = sizeof(sh) + item_len; | ||
1993 | item_len = 0; | 2005 | item_len = 0; |
2006 | ret = -EOVERFLOW; | ||
2007 | } | ||
1994 | 2008 | ||
1995 | if (sizeof(sh) + item_len + *sk_offset > | 2009 | if (sizeof(sh) + item_len + *sk_offset > *buf_size) { |
1996 | BTRFS_SEARCH_ARGS_BUFSIZE) { | ||
1997 | ret = 1; | 2010 | ret = 1; |
1998 | goto overflow; | 2011 | goto out; |
1999 | } | 2012 | } |
2000 | 2013 | ||
2001 | sh.objectid = key->objectid; | 2014 | sh.objectid = key->objectid; |
@@ -2005,20 +2018,33 @@ static noinline int copy_to_sk(struct btrfs_root *root, | |||
2005 | sh.transid = found_transid; | 2018 | sh.transid = found_transid; |
2006 | 2019 | ||
2007 | /* copy search result header */ | 2020 | /* copy search result header */ |
2008 | 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 | |||
2009 | *sk_offset += sizeof(sh); | 2026 | *sk_offset += sizeof(sh); |
2010 | 2027 | ||
2011 | if (item_len) { | 2028 | if (item_len) { |
2012 | char *p = buf + *sk_offset; | 2029 | char __user *up = ubuf + *sk_offset; |
2013 | /* copy the item */ | 2030 | /* copy the item */ |
2014 | read_extent_buffer(leaf, p, | 2031 | if (read_extent_buffer_to_user(leaf, up, |
2015 | item_off, item_len); | 2032 | item_off, item_len)) { |
2033 | ret = -EFAULT; | ||
2034 | goto out; | ||
2035 | } | ||
2036 | |||
2016 | *sk_offset += item_len; | 2037 | *sk_offset += item_len; |
2017 | } | 2038 | } |
2018 | (*num_found)++; | 2039 | (*num_found)++; |
2019 | 2040 | ||
2020 | if (*num_found >= sk->nr_items) | 2041 | if (ret) /* -EOVERFLOW from above */ |
2021 | break; | 2042 | goto out; |
2043 | |||
2044 | if (*num_found >= sk->nr_items) { | ||
2045 | ret = 1; | ||
2046 | goto out; | ||
2047 | } | ||
2022 | } | 2048 | } |
2023 | advance_key: | 2049 | advance_key: |
2024 | ret = 0; | 2050 | ret = 0; |
@@ -2033,22 +2059,37 @@ advance_key: | |||
2033 | key->objectid++; | 2059 | key->objectid++; |
2034 | } else | 2060 | } else |
2035 | ret = 1; | 2061 | ret = 1; |
2036 | overflow: | 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 | */ | ||
2037 | return ret; | 2072 | return ret; |
2038 | } | 2073 | } |
2039 | 2074 | ||
2040 | static noinline int search_ioctl(struct inode *inode, | 2075 | static noinline int search_ioctl(struct inode *inode, |
2041 | struct btrfs_ioctl_search_args *args) | 2076 | struct btrfs_ioctl_search_key *sk, |
2077 | size_t *buf_size, | ||
2078 | char __user *ubuf) | ||
2042 | { | 2079 | { |
2043 | struct btrfs_root *root; | 2080 | struct btrfs_root *root; |
2044 | struct btrfs_key key; | 2081 | struct btrfs_key key; |
2045 | struct btrfs_path *path; | 2082 | struct btrfs_path *path; |
2046 | struct btrfs_ioctl_search_key *sk = &args->key; | ||
2047 | struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info; | 2083 | struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info; |
2048 | int ret; | 2084 | int ret; |
2049 | int num_found = 0; | 2085 | int num_found = 0; |
2050 | unsigned long sk_offset = 0; | 2086 | unsigned long sk_offset = 0; |
2051 | 2087 | ||
2088 | if (*buf_size < sizeof(struct btrfs_ioctl_search_header)) { | ||
2089 | *buf_size = sizeof(struct btrfs_ioctl_search_header); | ||
2090 | return -EOVERFLOW; | ||
2091 | } | ||
2092 | |||
2052 | path = btrfs_alloc_path(); | 2093 | path = btrfs_alloc_path(); |
2053 | if (!path) | 2094 | if (!path) |
2054 | return -ENOMEM; | 2095 | return -ENOMEM; |
@@ -2082,14 +2123,15 @@ static noinline int search_ioctl(struct inode *inode, | |||
2082 | ret = 0; | 2123 | ret = 0; |
2083 | goto err; | 2124 | goto err; |
2084 | } | 2125 | } |
2085 | ret = copy_to_sk(root, path, &key, sk, args->buf, | 2126 | ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf, |
2086 | &sk_offset, &num_found); | 2127 | &sk_offset, &num_found); |
2087 | btrfs_release_path(path); | 2128 | btrfs_release_path(path); |
2088 | if (ret || num_found >= sk->nr_items) | 2129 | if (ret) |
2089 | break; | 2130 | break; |
2090 | 2131 | ||
2091 | } | 2132 | } |
2092 | ret = 0; | 2133 | if (ret > 0) |
2134 | ret = 0; | ||
2093 | err: | 2135 | err: |
2094 | sk->nr_items = num_found; | 2136 | sk->nr_items = num_found; |
2095 | btrfs_free_path(path); | 2137 | btrfs_free_path(path); |
@@ -2099,22 +2141,73 @@ err: | |||
2099 | static noinline int btrfs_ioctl_tree_search(struct file *file, | 2141 | static noinline int btrfs_ioctl_tree_search(struct file *file, |
2100 | void __user *argp) | 2142 | void __user *argp) |
2101 | { | 2143 | { |
2102 | struct btrfs_ioctl_search_args *args; | 2144 | struct btrfs_ioctl_search_args __user *uargs; |
2103 | struct inode *inode; | 2145 | struct btrfs_ioctl_search_key sk; |
2104 | int ret; | 2146 | struct inode *inode; |
2147 | int ret; | ||
2148 | size_t buf_size; | ||
2105 | 2149 | ||
2106 | if (!capable(CAP_SYS_ADMIN)) | 2150 | if (!capable(CAP_SYS_ADMIN)) |
2107 | return -EPERM; | 2151 | return -EPERM; |
2108 | 2152 | ||
2109 | args = memdup_user(argp, sizeof(*args)); | 2153 | uargs = (struct btrfs_ioctl_search_args __user *)argp; |
2110 | if (IS_ERR(args)) | 2154 | |
2111 | return PTR_ERR(args); | 2155 | if (copy_from_user(&sk, &uargs->key, sizeof(sk))) |
2156 | return -EFAULT; | ||
2157 | |||
2158 | buf_size = sizeof(uargs->buf); | ||
2112 | 2159 | ||
2113 | inode = file_inode(file); | 2160 | inode = file_inode(file); |
2114 | ret = search_ioctl(inode, args); | 2161 | ret = search_ioctl(inode, &sk, &buf_size, uargs->buf); |
2115 | if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) | 2162 | |
2163 | /* | ||
2164 | * In the origin implementation an overflow is handled by returning a | ||
2165 | * search header with a len of zero, so reset ret. | ||
2166 | */ | ||
2167 | if (ret == -EOVERFLOW) | ||
2168 | ret = 0; | ||
2169 | |||
2170 | if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk))) | ||
2116 | ret = -EFAULT; | 2171 | ret = -EFAULT; |
2117 | kfree(args); | 2172 | return ret; |
2173 | } | ||
2174 | |||
2175 | static noinline int btrfs_ioctl_tree_search_v2(struct file *file, | ||
2176 | void __user *argp) | ||
2177 | { | ||
2178 | struct btrfs_ioctl_search_args_v2 __user *uarg; | ||
2179 | struct btrfs_ioctl_search_args_v2 args; | ||
2180 | struct inode *inode; | ||
2181 | int ret; | ||
2182 | size_t buf_size; | ||
2183 | const size_t buf_limit = 16 * 1024 * 1024; | ||
2184 | |||
2185 | if (!capable(CAP_SYS_ADMIN)) | ||
2186 | return -EPERM; | ||
2187 | |||
2188 | /* copy search header and buffer size */ | ||
2189 | uarg = (struct btrfs_ioctl_search_args_v2 __user *)argp; | ||
2190 | if (copy_from_user(&args, uarg, sizeof(args))) | ||
2191 | return -EFAULT; | ||
2192 | |||
2193 | buf_size = args.buf_size; | ||
2194 | |||
2195 | if (buf_size < sizeof(struct btrfs_ioctl_search_header)) | ||
2196 | return -EOVERFLOW; | ||
2197 | |||
2198 | /* limit result size to 16MB */ | ||
2199 | if (buf_size > buf_limit) | ||
2200 | buf_size = buf_limit; | ||
2201 | |||
2202 | inode = file_inode(file); | ||
2203 | ret = search_ioctl(inode, &args.key, &buf_size, | ||
2204 | (char *)(&uarg->buf[0])); | ||
2205 | if (ret == 0 && copy_to_user(&uarg->key, &args.key, sizeof(args.key))) | ||
2206 | ret = -EFAULT; | ||
2207 | else if (ret == -EOVERFLOW && | ||
2208 | copy_to_user(&uarg->buf_size, &buf_size, sizeof(buf_size))) | ||
2209 | ret = -EFAULT; | ||
2210 | |||
2118 | return ret; | 2211 | return ret; |
2119 | } | 2212 | } |
2120 | 2213 | ||
@@ -5198,6 +5291,8 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
5198 | return btrfs_ioctl_trans_end(file); | 5291 | return btrfs_ioctl_trans_end(file); |
5199 | case BTRFS_IOC_TREE_SEARCH: | 5292 | case BTRFS_IOC_TREE_SEARCH: |
5200 | return btrfs_ioctl_tree_search(file, argp); | 5293 | return btrfs_ioctl_tree_search(file, argp); |
5294 | case BTRFS_IOC_TREE_SEARCH_V2: | ||
5295 | return btrfs_ioctl_tree_search_v2(file, argp); | ||
5201 | case BTRFS_IOC_INO_LOOKUP: | 5296 | case BTRFS_IOC_INO_LOOKUP: |
5202 | return btrfs_ioctl_ino_lookup(file, argp); | 5297 | return btrfs_ioctl_ino_lookup(file, argp); |
5203 | case BTRFS_IOC_INO_PATHS: | 5298 | case BTRFS_IOC_INO_PATHS: |