diff options
author | Gerhard Heift <gerhard@heift.name> | 2014-01-30 16:24:02 +0100 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-06-12 18:22:05 -0700 |
commit | ba346b357d70becdd8e20ff9493cd56101ee0f46 (patch) | |
tree | afb6862938a8b05f614e0a652c94de6104451cfe /fs | |
parent | 550ac1d85ef99f3390a6ea87c70b7683647f6110 (diff) | |
download | linux-ba346b357d70becdd8e20ff9493cd56101ee0f46.tar.bz2 |
btrfs: tree_search, search_ioctl: direct copy to userspace
By copying each found item seperatly to userspace, we do not need extra
buffer in the kernel.
Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
Signed-off-by: Chris Mason <clm@fb.com>
Acked-by: David Sterba <dsterba@suse.cz>
Diffstat (limited to 'fs')
-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, struct btrfs_key *key, struct btrfs_ioctl_search_key *sk, size_t *buf_size, - char *buf, + char __user *ubuf, unsigned long *sk_offset, int *num_found) { @@ -2018,14 +2018,22 @@ static noinline int copy_to_sk(struct btrfs_root *root, sh.transid = found_transid; /* copy search result header */ - memcpy(buf + *sk_offset, &sh, sizeof(sh)); + if (copy_to_user(ubuf + *sk_offset, &sh, sizeof(sh))) { + ret = -EFAULT; + goto out; + } + *sk_offset += sizeof(sh); if (item_len) { - char *p = buf + *sk_offset; + char __user *up = ubuf + *sk_offset; /* copy the item */ - read_extent_buffer(leaf, p, - item_off, item_len); + if (read_extent_buffer_to_user(leaf, up, + item_off, item_len)) { + ret = -EFAULT; + goto out; + } + *sk_offset += item_len; } (*num_found)++; @@ -2052,13 +2060,22 @@ advance_key: } else ret = 1; out: + /* + * 0: all items from this leaf copied, continue with next + * 1: * more items can be copied, but unused buffer is too small + * * all items were found + * Either way, it will stops the loop which iterates to the next + * leaf + * -EOVERFLOW: item was to large for buffer + * -EFAULT: could not copy extent buffer back to userspace + */ return ret; } static noinline int search_ioctl(struct inode *inode, struct btrfs_ioctl_search_key *sk, size_t *buf_size, - char *buf) + char __user *ubuf) { struct btrfs_root *root; struct btrfs_key key; @@ -2106,7 +2123,7 @@ static noinline int search_ioctl(struct inode *inode, ret = 0; goto err; } - ret = copy_to_sk(root, path, &key, sk, buf_size, buf, + ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf, &sk_offset, &num_found); btrfs_release_path(path); if (ret) @@ -2124,7 +2141,8 @@ err: static noinline int btrfs_ioctl_tree_search(struct file *file, void __user *argp) { - struct btrfs_ioctl_search_args *args; + struct btrfs_ioctl_search_args __user *uargs; + struct btrfs_ioctl_search_key sk; struct inode *inode; int ret; size_t buf_size; @@ -2132,14 +2150,15 @@ static noinline int btrfs_ioctl_tree_search(struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - args = memdup_user(argp, sizeof(*args)); - if (IS_ERR(args)) - return PTR_ERR(args); + uargs = (struct btrfs_ioctl_search_args __user *)argp; - buf_size = sizeof(args->buf); + if (copy_from_user(&sk, &uargs->key, sizeof(sk))) + return -EFAULT; + + buf_size = sizeof(uargs->buf); inode = file_inode(file); - ret = search_ioctl(inode, &args->key, &buf_size, args->buf); + ret = search_ioctl(inode, &sk, &buf_size, uargs->buf); /* * 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, if (ret == -EOVERFLOW) ret = 0; - if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) + if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk))) ret = -EFAULT; - kfree(args); return ret; } |