Commit 16d52ef7 authored by Linus Torvalds's avatar Linus Torvalds

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
parents a311c480 47a306a7
...@@ -2354,7 +2354,7 @@ int end_extent_writepage(struct page *page, int err, u64 start, u64 end) ...@@ -2354,7 +2354,7 @@ int end_extent_writepage(struct page *page, int err, u64 start, u64 end)
{ {
int uptodate = (err == 0); int uptodate = (err == 0);
struct extent_io_tree *tree; struct extent_io_tree *tree;
int ret; int ret = 0;
tree = &BTRFS_I(page->mapping->host)->io_tree; tree = &BTRFS_I(page->mapping->host)->io_tree;
...@@ -5068,6 +5068,43 @@ void read_extent_buffer(struct extent_buffer *eb, void *dstv, ...@@ -5068,6 +5068,43 @@ void read_extent_buffer(struct extent_buffer *eb, void *dstv,
} }
} }
int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
unsigned long start,
unsigned long len)
{
size_t cur;
size_t offset;
struct page *page;
char *kaddr;
char __user *dst = (char __user *)dstv;
size_t start_offset = eb->start & ((u64)PAGE_CACHE_SIZE - 1);
unsigned long i = (start_offset + start) >> PAGE_CACHE_SHIFT;
int ret = 0;
WARN_ON(start > eb->len);
WARN_ON(start + len > eb->start + eb->len);
offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
while (len > 0) {
page = extent_buffer_page(eb, i);
cur = min(len, (PAGE_CACHE_SIZE - offset));
kaddr = page_address(page);
if (copy_to_user(dst, kaddr + offset, cur)) {
ret = -EFAULT;
break;
}
dst += cur;
len -= cur;
offset = 0;
i++;
}
return ret;
}
int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start, int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
unsigned long min_len, char **map, unsigned long min_len, char **map,
unsigned long *map_start, unsigned long *map_start,
......
...@@ -304,6 +304,9 @@ int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv, ...@@ -304,6 +304,9 @@ int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
void read_extent_buffer(struct extent_buffer *eb, void *dst, void read_extent_buffer(struct extent_buffer *eb, void *dst,
unsigned long start, unsigned long start,
unsigned long len); unsigned long len);
int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dst,
unsigned long start,
unsigned long len);
void write_extent_buffer(struct extent_buffer *eb, const void *src, void write_extent_buffer(struct extent_buffer *eb, const void *src,
unsigned long start, unsigned long len); unsigned long start, unsigned long len);
void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
......
...@@ -1957,7 +1957,8 @@ static noinline int copy_to_sk(struct btrfs_root *root, ...@@ -1957,7 +1957,8 @@ static noinline int copy_to_sk(struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_path *path,
struct btrfs_key *key, struct btrfs_key *key,
struct btrfs_ioctl_search_key *sk, struct btrfs_ioctl_search_key *sk,
char *buf, size_t *buf_size,
char __user *ubuf,
unsigned long *sk_offset, unsigned long *sk_offset,
int *num_found) int *num_found)
{ {
...@@ -1989,13 +1990,25 @@ static noinline int copy_to_sk(struct btrfs_root *root, ...@@ -1989,13 +1990,25 @@ static noinline int copy_to_sk(struct btrfs_root *root,
if (!key_in_sk(key, sk)) if (!key_in_sk(key, sk))
continue; continue;
if (sizeof(sh) + item_len > BTRFS_SEARCH_ARGS_BUFSIZE) if (sizeof(sh) + item_len > *buf_size) {
if (*num_found) {
ret = 1;
goto out;
}
/*
* return one empty item back for v1, which does not
* handle -EOVERFLOW
*/
*buf_size = sizeof(sh) + item_len;
item_len = 0; item_len = 0;
ret = -EOVERFLOW;
}
if (sizeof(sh) + item_len + *sk_offset > if (sizeof(sh) + item_len + *sk_offset > *buf_size) {
BTRFS_SEARCH_ARGS_BUFSIZE) {
ret = 1; ret = 1;
goto overflow; goto out;
} }
sh.objectid = key->objectid; sh.objectid = key->objectid;
...@@ -2005,20 +2018,33 @@ static noinline int copy_to_sk(struct btrfs_root *root, ...@@ -2005,20 +2018,33 @@ static noinline int copy_to_sk(struct btrfs_root *root,
sh.transid = found_transid; sh.transid = found_transid;
/* copy search result header */ /* 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); *sk_offset += sizeof(sh);
if (item_len) { if (item_len) {
char *p = buf + *sk_offset; char __user *up = ubuf + *sk_offset;
/* copy the item */ /* copy the item */
read_extent_buffer(leaf, p, if (read_extent_buffer_to_user(leaf, up,
item_off, item_len); item_off, item_len)) {
ret = -EFAULT;
goto out;
}
*sk_offset += item_len; *sk_offset += item_len;
} }
(*num_found)++; (*num_found)++;
if (*num_found >= sk->nr_items) if (ret) /* -EOVERFLOW from above */
break; goto out;
if (*num_found >= sk->nr_items) {
ret = 1;
goto out;
}
} }
advance_key: advance_key:
ret = 0; ret = 0;
...@@ -2033,22 +2059,37 @@ static noinline int copy_to_sk(struct btrfs_root *root, ...@@ -2033,22 +2059,37 @@ static noinline int copy_to_sk(struct btrfs_root *root,
key->objectid++; key->objectid++;
} else } else
ret = 1; ret = 1;
overflow: 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; return ret;
} }
static noinline int search_ioctl(struct inode *inode, 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 __user *ubuf)
{ {
struct btrfs_root *root; struct btrfs_root *root;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_ioctl_search_key *sk = &args->key;
struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info; struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info;
int ret; int ret;
int num_found = 0; int num_found = 0;
unsigned long sk_offset = 0; unsigned long sk_offset = 0;
if (*buf_size < sizeof(struct btrfs_ioctl_search_header)) {
*buf_size = sizeof(struct btrfs_ioctl_search_header);
return -EOVERFLOW;
}
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
...@@ -2082,14 +2123,15 @@ static noinline int search_ioctl(struct inode *inode, ...@@ -2082,14 +2123,15 @@ static noinline int search_ioctl(struct inode *inode,
ret = 0; ret = 0;
goto err; goto err;
} }
ret = copy_to_sk(root, path, &key, sk, args->buf, ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf,
&sk_offset, &num_found); &sk_offset, &num_found);
btrfs_release_path(path); btrfs_release_path(path);
if (ret || num_found >= sk->nr_items) if (ret)
break; break;
} }
ret = 0; if (ret > 0)
ret = 0;
err: err:
sk->nr_items = num_found; sk->nr_items = num_found;
btrfs_free_path(path); btrfs_free_path(path);
...@@ -2099,22 +2141,73 @@ static noinline int search_ioctl(struct inode *inode, ...@@ -2099,22 +2141,73 @@ static noinline int search_ioctl(struct inode *inode,
static noinline int btrfs_ioctl_tree_search(struct file *file, static noinline int btrfs_ioctl_tree_search(struct file *file,
void __user *argp) void __user *argp)
{ {
struct btrfs_ioctl_search_args *args; struct btrfs_ioctl_search_args __user *uargs;
struct inode *inode; struct btrfs_ioctl_search_key sk;
int ret; struct inode *inode;
int ret;
size_t buf_size;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
args = memdup_user(argp, sizeof(*args)); uargs = (struct btrfs_ioctl_search_args __user *)argp;
if (IS_ERR(args))
return PTR_ERR(args); if (copy_from_user(&sk, &uargs->key, sizeof(sk)))
return -EFAULT;
buf_size = sizeof(uargs->buf);
inode = file_inode(file); inode = file_inode(file);
ret = search_ioctl(inode, args); ret = search_ioctl(inode, &sk, &buf_size, uargs->buf);
if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
/*
* In the origin implementation an overflow is handled by returning a
* search header with a len of zero, so reset ret.
*/
if (ret == -EOVERFLOW)
ret = 0;
if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk)))
ret = -EFAULT; ret = -EFAULT;
kfree(args); return ret;
}
static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
void __user *argp)
{
struct btrfs_ioctl_search_args_v2 __user *uarg;
struct btrfs_ioctl_search_args_v2 args;
struct inode *inode;
int ret;
size_t buf_size;
const size_t buf_limit = 16 * 1024 * 1024;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* copy search header and buffer size */
uarg = (struct btrfs_ioctl_search_args_v2 __user *)argp;
if (copy_from_user(&args, uarg, sizeof(args)))
return -EFAULT;
buf_size = args.buf_size;
if (buf_size < sizeof(struct btrfs_ioctl_search_header))
return -EOVERFLOW;
/* limit result size to 16MB */
if (buf_size > buf_limit)
buf_size = buf_limit;
inode = file_inode(file);
ret = search_ioctl(inode, &args.key, &buf_size,
(char *)(&uarg->buf[0]));
if (ret == 0 && copy_to_user(&uarg->key, &args.key, sizeof(args.key)))
ret = -EFAULT;
else if (ret == -EOVERFLOW &&
copy_to_user(&uarg->buf_size, &buf_size, sizeof(buf_size)))
ret = -EFAULT;
return ret; return ret;
} }
...@@ -5198,6 +5291,8 @@ long btrfs_ioctl(struct file *file, unsigned int ...@@ -5198,6 +5291,8 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_trans_end(file); return btrfs_ioctl_trans_end(file);
case BTRFS_IOC_TREE_SEARCH: case BTRFS_IOC_TREE_SEARCH:
return btrfs_ioctl_tree_search(file, argp); 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: case BTRFS_IOC_INO_LOOKUP:
return btrfs_ioctl_ino_lookup(file, argp); return btrfs_ioctl_ino_lookup(file, argp);
case BTRFS_IOC_INO_PATHS: case BTRFS_IOC_INO_PATHS:
......
...@@ -1798,8 +1798,10 @@ static int qgroup_shared_accounting(struct btrfs_trans_handle *trans, ...@@ -1798,8 +1798,10 @@ static int qgroup_shared_accounting(struct btrfs_trans_handle *trans,
return -ENOMEM; return -ENOMEM;
tmp = ulist_alloc(GFP_NOFS); tmp = ulist_alloc(GFP_NOFS);
if (!tmp) if (!tmp) {
ulist_free(qgroups);
return -ENOMEM; return -ENOMEM;
}
btrfs_get_tree_mod_seq(fs_info, &elem); btrfs_get_tree_mod_seq(fs_info, &elem);
ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, elem.seq, ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, elem.seq,
......
...@@ -428,8 +428,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root, ...@@ -428,8 +428,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
continue; continue;
} }
if (!dev->bdev) { if (!dev->bdev) {
/* cannot read ahead on missing device */ /*
continue; * cannot read ahead on missing device, but for RAID5/6,
* REQ_GET_READ_MIRRORS return 1. So don't skip missing
* device for such case.
*/
if (nzones > 1)
continue;
} }
if (dev_replace_is_ongoing && if (dev_replace_is_ongoing &&
dev == fs_info->dev_replace.tgtdev) { dev == fs_info->dev_replace.tgtdev) {
......
...@@ -135,7 +135,7 @@ static void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info) ...@@ -135,7 +135,7 @@ static void btrfs_free_dummy_fs_info(struct btrfs_fs_info *fs_info)
radix_tree_for_each_slot(slot, &fs_info->buffer_radix, &iter, 0) { radix_tree_for_each_slot(slot, &fs_info->buffer_radix, &iter, 0) {
struct extent_buffer *eb; struct extent_buffer *eb;
eb = radix_tree_deref_slot(slot); eb = radix_tree_deref_slot_protected(slot, &fs_info->buffer_lock);
if (!eb) if (!eb)
continue; continue;
/* Shouldn't happen but that kind of thinking creates CVE's */ /* Shouldn't happen but that kind of thinking creates CVE's */
......
...@@ -415,6 +415,8 @@ int btrfs_test_qgroups(void) ...@@ -415,6 +415,8 @@ int btrfs_test_qgroups(void)
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
btrfs_set_header_level(root->node, 0);
btrfs_set_header_nritems(root->node, 0);
root->alloc_bytenr += 8192; root->alloc_bytenr += 8192;
tmp_root = btrfs_alloc_dummy_root(); tmp_root = btrfs_alloc_dummy_root();
......
...@@ -1284,11 +1284,13 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, ...@@ -1284,11 +1284,13 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
goto fail; goto fail;
} }
pending->error = btrfs_qgroup_inherit(trans, fs_info, ret = btrfs_qgroup_inherit(trans, fs_info,
root->root_key.objectid, root->root_key.objectid,
objectid, pending->inherit); objectid, pending->inherit);
if (pending->error) if (ret) {
goto no_free_objectid; btrfs_abort_transaction(trans, root, ret);
goto fail;
}
/* see comments in should_cow_block() */ /* see comments in should_cow_block() */
set_bit(BTRFS_ROOT_FORCE_COW, &root->state); set_bit(BTRFS_ROOT_FORCE_COW, &root->state);
......
...@@ -306,6 +306,14 @@ struct btrfs_ioctl_search_args { ...@@ -306,6 +306,14 @@ struct btrfs_ioctl_search_args {
char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
}; };
struct btrfs_ioctl_search_args_v2 {
struct btrfs_ioctl_search_key key; /* in/out - search parameters */
__u64 buf_size; /* in - size of buffer
* out - on EOVERFLOW: needed size
* to store item */
__u64 buf[0]; /* out - found items */
};
struct btrfs_ioctl_clone_range_args { struct btrfs_ioctl_clone_range_args {
__s64 src_fd; __s64 src_fd;
__u64 src_offset, src_length; __u64 src_offset, src_length;
...@@ -558,6 +566,8 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code) ...@@ -558,6 +566,8 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
struct btrfs_ioctl_defrag_range_args) struct btrfs_ioctl_defrag_range_args)
#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
struct btrfs_ioctl_search_args) 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, \ #define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
struct btrfs_ioctl_ino_lookup_args) struct btrfs_ioctl_ino_lookup_args)
#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, __u64) #define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, __u64)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment