* [PATCH RFC] new ioctl TREE_SEARCH_V2
@ 2014-01-18 0:15 Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 1/5] btrfs: search_ioctl accepts varying buffer Gerhard Heift
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Gerhard Heift @ 2014-01-18 0:15 UTC (permalink / raw)
To: linux-btrfs
This patch series adds a new ioctl TREE_SEARCH_V2 with which we could store the
results in a varying buffer. Now even items larger than 3992 bytes or a large
amount of items can be returned.
I have a few questions:
Which value should I assign to TREE_SEARCH_V2?
Should we limit the buffer size?
What about documentation?
Gerhard
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH RFC 1/5] btrfs: search_ioctl accepts varying buffer
2014-01-18 0:15 [PATCH RFC] new ioctl TREE_SEARCH_V2 Gerhard Heift
@ 2014-01-18 0:15 ` Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 2/5] btrfs: search_ioctl rejects unused setted values Gerhard Heift
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Gerhard Heift @ 2014-01-18 0:15 UTC (permalink / raw)
To: linux-btrfs
rewrite search_ioctl to accept a buffer with varying size
Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
---
fs/btrfs/ioctl.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 3970f32..be4c780 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1850,6 +1850,7 @@ static noinline int copy_to_sk(struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_key *key,
struct btrfs_ioctl_search_key *sk,
+ size_t buf_size,
char *buf,
unsigned long *sk_offset,
int *num_found)
@@ -1882,11 +1883,10 @@ static noinline int copy_to_sk(struct btrfs_root *root,
if (!key_in_sk(key, sk))
continue;
- if (sizeof(sh) + item_len > BTRFS_SEARCH_ARGS_BUFSIZE)
+ if (sizeof(sh) + item_len > buf_size)
item_len = 0;
- if (sizeof(sh) + item_len + *sk_offset >
- BTRFS_SEARCH_ARGS_BUFSIZE) {
+ if (sizeof(sh) + item_len + *sk_offset > buf_size) {
ret = 1;
goto overflow;
}
@@ -1931,17 +1931,22 @@ overflow:
}
static noinline int search_ioctl(struct inode *inode,
- struct btrfs_ioctl_search_args *args)
+ struct btrfs_ioctl_search_key *sk,
+ size_t buf_size,
+ char *buf
+ )
{
struct btrfs_root *root;
struct btrfs_key key;
struct btrfs_path *path;
- struct btrfs_ioctl_search_key *sk = &args->key;
struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info;
int ret;
int num_found = 0;
unsigned long sk_offset = 0;
+ if (buf_size < sizeof(struct btrfs_ioctl_search_header))
+ return -EOVERFLOW;
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -1975,7 +1980,7 @@ static noinline int search_ioctl(struct inode *inode,
ret = 0;
goto err;
}
- ret = copy_to_sk(root, path, &key, sk, args->buf,
+ ret = copy_to_sk(root, path, &key, sk, buf_size, buf,
&sk_offset, &num_found);
btrfs_release_path(path);
if (ret || num_found >= sk->nr_items)
@@ -2004,7 +2009,7 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
return PTR_ERR(args);
inode = file_inode(file);
- ret = search_ioctl(inode, args);
+ ret = search_ioctl(inode, &args->key, sizeof(args->buf), args->buf);
if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
ret = -EFAULT;
kfree(args);
--
1.8.5.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH RFC 2/5] btrfs: search_ioctl rejects unused setted values
2014-01-18 0:15 [PATCH RFC] new ioctl TREE_SEARCH_V2 Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 1/5] btrfs: search_ioctl accepts varying buffer Gerhard Heift
@ 2014-01-18 0:15 ` Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 3/5] btrfs: copy_to_sk returns EOVERFLOW for too small buffer Gerhard Heift
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Gerhard Heift @ 2014-01-18 0:15 UTC (permalink / raw)
To: linux-btrfs
To prevent unexpectet values in the unused fields of the search key fail early.
Otherwise future extensions would break the behavior of the search if current
implementations in userspace set them to values other than zero.
Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
---
fs/btrfs/ioctl.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index be4c780..919d928 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1947,6 +1947,9 @@ static noinline int search_ioctl(struct inode *inode,
if (buf_size < sizeof(struct btrfs_ioctl_search_header))
return -EOVERFLOW;
+ if (sk->unused || sk->unused1 | sk->unused2 | sk->unused3 | sk->unused4)
+ return -EINVAL;
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
--
1.8.5.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH RFC 3/5] btrfs: copy_to_sk returns EOVERFLOW for too small buffer
2014-01-18 0:15 [PATCH RFC] new ioctl TREE_SEARCH_V2 Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 1/5] btrfs: search_ioctl accepts varying buffer Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 2/5] btrfs: search_ioctl rejects unused setted values Gerhard Heift
@ 2014-01-18 0:15 ` Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 4/5] btrfs: new ioctl TREE_SEARCH_V2 Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 5/5] btrfs: search_ioctl: direct copy to userspace Gerhard Heift
4 siblings, 0 replies; 6+ messages in thread
From: Gerhard Heift @ 2014-01-18 0:15 UTC (permalink / raw)
To: linux-btrfs
In copy_to_sk, if an item is too large for the given buffer, it now returns
-EOVERFLOW instead of copying a search_header with len = 0. For backward
compatibility for the first item it still copies such a header to the buffer,
but not any other following items, which could have fitted.
tree_search changes -EOVERFLOW back to 0 to behave similiar to the way it
behaved before this patch.
Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
---
fs/btrfs/ioctl.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 919d928..9fa222d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1883,8 +1883,15 @@ static noinline int copy_to_sk(struct btrfs_root *root,
if (!key_in_sk(key, sk))
continue;
- if (sizeof(sh) + item_len > buf_size)
+ if (sizeof(sh) + item_len > buf_size) {
+ if (*num_found) {
+ ret = 1;
+ goto overflow;
+ }
+
item_len = 0;
+ ret = -EOVERFLOW;
+ }
if (sizeof(sh) + item_len + *sk_offset > buf_size) {
ret = 1;
@@ -1910,6 +1917,9 @@ static noinline int copy_to_sk(struct btrfs_root *root,
}
(*num_found)++;
+ if (ret) /* -EOVERFLOW from above */
+ goto overflow;
+
if (*num_found >= sk->nr_items)
break;
}
@@ -1990,7 +2000,8 @@ static noinline int search_ioctl(struct inode *inode,
break;
}
- ret = 0;
+ if (ret > 0)
+ ret = 0;
err:
sk->nr_items = num_found;
btrfs_free_path(path);
@@ -2013,6 +2024,8 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
inode = file_inode(file);
ret = search_ioctl(inode, &args->key, sizeof(args->buf), args->buf);
+ if (ret == -EOVERFLOW)
+ ret = 0;
if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
ret = -EFAULT;
kfree(args);
--
1.8.5.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH RFC 4/5] btrfs: new ioctl TREE_SEARCH_V2
2014-01-18 0:15 [PATCH RFC] new ioctl TREE_SEARCH_V2 Gerhard Heift
` (2 preceding siblings ...)
2014-01-18 0:15 ` [PATCH RFC 3/5] btrfs: copy_to_sk returns EOVERFLOW for too small buffer Gerhard Heift
@ 2014-01-18 0:15 ` Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 5/5] btrfs: search_ioctl: direct copy to userspace Gerhard Heift
4 siblings, 0 replies; 6+ messages in thread
From: Gerhard Heift @ 2014-01-18 0:15 UTC (permalink / raw)
To: linux-btrfs
This new ioctl call allows the user to supply a buffer of varying size in which
a tree search can store its results. This is much more flexible if you want to
receive items which are larger than the current fixed buffer of 3992 bytes or
if you want to fetch mor item at once.
Currently the buffer is limited to 32 pages.
Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
---
fs/btrfs/ioctl.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/btrfs.h | 8 ++++++++
2 files changed, 56 insertions(+)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9fa222d..cc82bd9 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2032,6 +2032,52 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
return ret;
}
+static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
+ void __user *argp)
+{
+ struct btrfs_ioctl_search_args_v2 *args;
+ struct inode *inode;
+ int ret;
+ char *buf;
+ size_t buf_size;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ /* copy search header and buffer size */
+ args = memdup_user(argp, sizeof(*args));
+ if (IS_ERR(args))
+ return PTR_ERR(args);
+
+ buf_size = args->buf_size;
+
+ if (buf_size < sizeof(struct btrfs_ioctl_search_header)) {
+ kfree(args);
+ return -ENOMEM;
+ }
+
+ /* limit memory */
+ if (buf_size > PAGE_SIZE * 32)
+ buf_size = PAGE_SIZE * 32;
+
+ buf = memdup_user(argp->buf, buf_size);
+ if (IS_ERR(buf)) {
+ kfree(args);
+ return PTR_ERR(buf);
+ }
+
+ inode = file_inode(file);
+ ret = search_ioctl(inode, &args->key, buf_size, buf);
+ if (ret == 0 && (
+ copy_to_user(argp, args, sizeof(*args)) ||
+ copy_to_user(argp->buf, buf, buf_size)
+ ))
+ ret = -EFAULT;
+ kfree(buf);
+ kfree(args);
+ return ret;
+}
+
/*
* Search INODE_REFs to identify path name of 'dirid' directory
* in a 'tree_id' tree. and sets path name to 'name'.
@@ -4777,6 +4823,8 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_trans_end(file);
case BTRFS_IOC_TREE_SEARCH:
return btrfs_ioctl_tree_search(file, argp);
+ case BTRFS_IOC_TREE_SEARCH_V2:
+ return btrfs_ioctl_tree_search_v2(file, argp);
case BTRFS_IOC_INO_LOOKUP:
return btrfs_ioctl_ino_lookup(file, argp);
case BTRFS_IOC_INO_PATHS:
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 1b8a0f4..6ba0d0f 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -301,6 +301,12 @@ struct btrfs_ioctl_search_args {
char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
};
+struct btrfs_ioctl_search_args_v2 {
+ struct btrfs_ioctl_search_key key;
+ size_t buf_size;
+ char buf[0];
+};
+
struct btrfs_ioctl_clone_range_args {
__s64 src_fd;
__u64 src_offset, src_length;
@@ -553,6 +559,8 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
struct btrfs_ioctl_defrag_range_args)
#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
struct btrfs_ioctl_search_args)
+#define BTRFS_IOC_TREE_SEARCH_V2 _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args_v2)
#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
struct btrfs_ioctl_ino_lookup_args)
#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, __u64)
--
1.8.5.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH RFC 5/5] btrfs: search_ioctl: direct copy to userspace
2014-01-18 0:15 [PATCH RFC] new ioctl TREE_SEARCH_V2 Gerhard Heift
` (3 preceding siblings ...)
2014-01-18 0:15 ` [PATCH RFC 4/5] btrfs: new ioctl TREE_SEARCH_V2 Gerhard Heift
@ 2014-01-18 0:15 ` Gerhard Heift
4 siblings, 0 replies; 6+ messages in thread
From: Gerhard Heift @ 2014-01-18 0:15 UTC (permalink / raw)
To: linux-btrfs
By copying each found item seperatly to userspace, we only need a small amount
of memory in the kernel. This allows to run a large search inside of a single
call.
Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
---
fs/btrfs/ioctl.c | 105 +++++++++++++++++++++++++++++++++++++------------------
1 file changed, 71 insertions(+), 34 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index cc82bd9..103023c 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1851,7 +1851,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 *buf,
unsigned long *sk_offset,
int *num_found)
{
@@ -1865,6 +1865,11 @@ static noinline int copy_to_sk(struct btrfs_root *root,
int slot;
int ret = 0;
+ char *ext_buf;
+ unsigned long ext_buf_size;
+
+ ext_buf_size = 0;
+
leaf = path->nodes[0];
slot = path->slots[0];
nritems = btrfs_header_nritems(leaf);
@@ -1886,7 +1891,7 @@ static noinline int copy_to_sk(struct btrfs_root *root,
if (sizeof(sh) + item_len > buf_size) {
if (*num_found) {
ret = 1;
- goto overflow;
+ goto err;
}
item_len = 0;
@@ -1895,7 +1900,7 @@ static noinline int copy_to_sk(struct btrfs_root *root,
if (sizeof(sh) + item_len + *sk_offset > buf_size) {
ret = 1;
- goto overflow;
+ goto err;
}
sh.objectid = key->objectid;
@@ -1905,20 +1910,47 @@ 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(buf + *sk_offset, &sh, sizeof(sh))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
*sk_offset += sizeof(sh);
if (item_len) {
- char *p = buf + *sk_offset;
+ /* resize internal buffer if needed */
+ if (ext_buf_size < item_len) {
+ if (ext_buf_size)
+ kfree(ext_buf);
+
+ ext_buf_size = (item_len + PAGE_SIZE - 1)
+ & ~(PAGE_SIZE - 1);
+
+ ext_buf = kmalloc_track_caller(
+ ext_buf_size, GFP_KERNEL);
+
+ if (!ext_buf) {
+ ext_buf_size = 0;
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
/* copy the item */
- read_extent_buffer(leaf, p,
+ read_extent_buffer(leaf, ext_buf,
item_off, item_len);
+
+ if (copy_to_user(buf + *sk_offset, ext_buf, item_len)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
*sk_offset += item_len;
}
(*num_found)++;
if (ret) /* -EOVERFLOW from above */
- goto overflow;
+ goto err;
if (*num_found >= sk->nr_items)
break;
@@ -1936,14 +1968,26 @@ advance_key:
key->objectid++;
} else
ret = 1;
-overflow:
+err:
+ /*
+ 0: all found
+ 1: more items can be copied, but buffer is too small or all items
+ were found, either way it will stops the loop which iterate to
+ the next leaf
+ -EOVERFLOW: item was to large for buffer
+ -ENOMEM: could not allocate memory to temporary the extent_buffer
+ -EFAULT: could not copy extent buffer back to userspace
+ */
+ if (ext_buf_size)
+ kfree(ext_buf);
+
return ret;
}
static noinline int search_ioctl(struct inode *inode,
struct btrfs_ioctl_search_key *sk,
size_t buf_size,
- char *buf
+ char __user *buf
)
{
struct btrfs_root *root;
@@ -1996,6 +2040,7 @@ static noinline int search_ioctl(struct inode *inode,
ret = copy_to_sk(root, path, &key, sk, buf_size, buf,
&sk_offset, &num_found);
btrfs_release_path(path);
+
if (ret || num_found >= sk->nr_items)
break;
@@ -2011,22 +2056,25 @@ err:
static noinline int btrfs_ioctl_tree_search(struct file *file,
void __user *argp)
{
- struct btrfs_ioctl_search_args *args;
- struct inode *inode;
- int ret;
+ struct btrfs_ioctl_search_args __user *usarg;
+ struct btrfs_ioctl_search_key *sk;
+ struct inode *inode;
+ int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- args = memdup_user(argp, sizeof(*args));
- if (IS_ERR(args))
- return PTR_ERR(args);
+ usarg = (struct btrfs_ioctl_search_args __user *)argp;
+
+ sk = memdup_user(&usarg->key, sizeof(*sk));
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
inode = file_inode(file);
- ret = search_ioctl(inode, &args->key, sizeof(args->buf), args->buf);
+ ret = search_ioctl(inode, sk, sizeof(usarg->buf), usarg->buf);
if (ret == -EOVERFLOW)
ret = 0;
- if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
+ if (ret == 0 && copy_to_user(argp, sk, sizeof(*sk)))
ret = -EFAULT;
kfree(args);
return ret;
@@ -2035,6 +2083,7 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
void __user *argp)
{
+ struct btrfs_ioctl_search_args_v2 __user *usarg;
struct btrfs_ioctl_search_args_v2 *args;
struct inode *inode;
int ret;
@@ -2049,31 +2098,19 @@ static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
if (IS_ERR(args))
return PTR_ERR(args);
+ usarg = (struct btrfs_ioctl_search_args_v2 __user *)argp;
+
buf_size = args->buf_size;
if (buf_size < sizeof(struct btrfs_ioctl_search_header)) {
kfree(args);
- return -ENOMEM;
- }
-
- /* limit memory */
- if (buf_size > PAGE_SIZE * 32)
- buf_size = PAGE_SIZE * 32;
-
- buf = memdup_user(argp->buf, buf_size);
- if (IS_ERR(buf)) {
- kfree(args);
- return PTR_ERR(buf);
+ return -EOVERFLOW;
}
inode = file_inode(file);
- ret = search_ioctl(inode, &args->key, buf_size, buf);
- if (ret == 0 && (
- copy_to_user(argp, args, sizeof(*args)) ||
- copy_to_user(argp->buf, buf, buf_size)
- ))
+ ret = search_ioctl(inode, &args->key, buf_size, usarg->buf);
+ if (ret == 0 && copy_to_user(argp, args, sizeof(*args))
ret = -EFAULT;
- kfree(buf);
kfree(args);
return ret;
}
--
1.8.5.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2014-01-18 0:16 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-18 0:15 [PATCH RFC] new ioctl TREE_SEARCH_V2 Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 1/5] btrfs: search_ioctl accepts varying buffer Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 2/5] btrfs: search_ioctl rejects unused setted values Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 3/5] btrfs: copy_to_sk returns EOVERFLOW for too small buffer Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 4/5] btrfs: new ioctl TREE_SEARCH_V2 Gerhard Heift
2014-01-18 0:15 ` [PATCH RFC 5/5] btrfs: search_ioctl: direct copy to userspace Gerhard Heift
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox