Skip to content

Commit

Permalink
btrfs: tree_search, search_ioctl: direct copy to userspace
Browse files Browse the repository at this point in the history
By copying each found item seperatly to userspace, we do not need extra
buffer in the kernel.

Signed-off-by: Gerhard Heift <[email protected]>
Signed-off-by: Chris Mason <[email protected]>
Acked-by: David Sterba <[email protected]>
  • Loading branch information
gheift authored and masoncl committed Jun 13, 2014
1 parent 550ac1d commit ba346b3
Showing 1 changed file with 33 additions and 15 deletions.
48 changes: 33 additions & 15 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)++;
Expand All @@ -2052,13 +2060,22 @@ static noinline int copy_to_sk(struct btrfs_root *root,
} 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;
Expand Down Expand Up @@ -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)
Expand All @@ -2124,22 +2141,24 @@ static noinline int search_ioctl(struct inode *inode,
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;

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
Expand All @@ -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;
}

Expand Down

0 comments on commit ba346b3

Please sign in to comment.