Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 15 Jun 2014 00:48:43 +0000 (19:48 -0500)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 15 Jun 2014 00:48:43 +0000 (19:48 -0500)
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

fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/ioctl.c
fs/btrfs/qgroup.c
fs/btrfs/reada.c
fs/btrfs/tests/btrfs-tests.c
fs/btrfs/tests/qgroup-tests.c
fs/btrfs/transaction.c
include/uapi/linux/btrfs.h

index f25a9092b946758af5f063591011ff30acf8a623..a389820d158b5b7ae64a2c6dfecabbb6ed3ed9bb 100644 (file)
@@ -2354,7 +2354,7 @@ int end_extent_writepage(struct page *page, int err, u64 start, u64 end)
 {
        int uptodate = (err == 0);
        struct extent_io_tree *tree;
-       int ret;
+       int ret = 0;
 
        tree = &BTRFS_I(page->mapping->host)->io_tree;
 
@@ -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,
                               unsigned long min_len, char **map,
                               unsigned long *map_start,
index 8b63f2d46518e82a9e140a398c2bd12df3a7f93f..15ce5f2a2b624ff0a28d9276637084327a9afdcd 100644 (file)
@@ -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,
                        unsigned long start,
                        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,
                         unsigned long start, unsigned long len);
 void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
index 82c18ba12e3f62e4ff3c71daf2269d353c4f18ce..0d321c23069a0aa9ea1aeac570bdf837cf208d24 100644 (file)
@@ -1957,7 +1957,8 @@ static noinline int copy_to_sk(struct btrfs_root *root,
                               struct btrfs_path *path,
                               struct btrfs_key *key,
                               struct btrfs_ioctl_search_key *sk,
-                              char *buf,
+                              size_t *buf_size,
+                              char __user *ubuf,
                               unsigned long *sk_offset,
                               int *num_found)
 {
@@ -1989,13 +1990,25 @@ 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) {
+                       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;
+                       ret = -EOVERFLOW;
+               }
 
-               if (sizeof(sh) + item_len + *sk_offset >
-                   BTRFS_SEARCH_ARGS_BUFSIZE) {
+               if (sizeof(sh) + item_len + *sk_offset > *buf_size) {
                        ret = 1;
-                       goto overflow;
+                       goto out;
                }
 
                sh.objectid = key->objectid;
@@ -2005,20 +2018,33 @@ 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)++;
 
-               if (*num_found >= sk->nr_items)
-                       break;
+               if (ret) /* -EOVERFLOW from above */
+                       goto out;
+
+               if (*num_found >= sk->nr_items) {
+                       ret = 1;
+                       goto out;
+               }
        }
 advance_key:
        ret = 0;
@@ -2033,22 +2059,37 @@ advance_key:
                key->objectid++;
        } else
                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;
 }
 
 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_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)) {
+               *buf_size = sizeof(struct btrfs_ioctl_search_header);
+               return -EOVERFLOW;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -2082,14 +2123,15 @@ 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, ubuf,
                                 &sk_offset, &num_found);
                btrfs_release_path(path);
-               if (ret || num_found >= sk->nr_items)
+               if (ret)
                        break;
 
        }
-       ret = 0;
+       if (ret > 0)
+               ret = 0;
 err:
        sk->nr_items = num_found;
        btrfs_free_path(path);
@@ -2099,22 +2141,73 @@ 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 *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;
+
+       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);
-       if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
+       ret = search_ioctl(inode, &sk, &buf_size, uargs->buf);
+
+       /*
+        * 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;
-       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;
 }
 
@@ -5198,6 +5291,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:
index cf5aead95a7f6d2eac95c3cd47fc7d56cab92e4b..98cb6b2630f9aac60972b5aea3c372e9e99eeba5 100644 (file)
@@ -1798,8 +1798,10 @@ static int qgroup_shared_accounting(struct btrfs_trans_handle *trans,
                return -ENOMEM;
 
        tmp = ulist_alloc(GFP_NOFS);
-       if (!tmp)
+       if (!tmp) {
+               ulist_free(qgroups);
                return -ENOMEM;
+       }
 
        btrfs_get_tree_mod_seq(fs_info, &elem);
        ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, elem.seq,
index 30947f923620678f9bc1a27fda49359c6b36e9a4..09230cf3a2447b3541826f21b2f3fe1dcce202ce 100644 (file)
@@ -428,8 +428,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
                        continue;
                }
                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 &&
                    dev == fs_info->dev_replace.tgtdev) {
index a5dcacb5df9cc16027240549101e078c03b9220f..9626252ee6b47d2b391f3383cfa9b3bb80e4110c 100644 (file)
@@ -135,7 +135,7 @@ restart:
        radix_tree_for_each_slot(slot, &fs_info->buffer_radix, &iter, 0) {
                struct extent_buffer *eb;
 
-               eb = radix_tree_deref_slot(slot);
+               eb = radix_tree_deref_slot_protected(slot, &fs_info->buffer_lock);
                if (!eb)
                        continue;
                /* Shouldn't happen but that kind of thinking creates CVE's */
index fa691b754aafff33bcc0dd8080a200df5421ef36..ec3dcb20235774044e4b92e1230b1b0af593aea4 100644 (file)
@@ -415,6 +415,8 @@ int btrfs_test_qgroups(void)
                ret = -ENOMEM;
                goto out;
        }
+       btrfs_set_header_level(root->node, 0);
+       btrfs_set_header_nritems(root->node, 0);
        root->alloc_bytenr += 8192;
 
        tmp_root = btrfs_alloc_dummy_root();
index 9630f10f8e1ea3eea4f724e6835d9ba25a1a1436..511839c04f11bf1130475815aca9cd79bb775d0a 100644 (file)
@@ -1284,11 +1284,13 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
                goto fail;
        }
 
-       pending->error = btrfs_qgroup_inherit(trans, fs_info,
-                                             root->root_key.objectid,
-                                             objectid, pending->inherit);
-       if (pending->error)
-               goto no_free_objectid;
+       ret = btrfs_qgroup_inherit(trans, fs_info,
+                                  root->root_key.objectid,
+                                  objectid, pending->inherit);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto fail;
+       }
 
        /* see comments in should_cow_block() */
        set_bit(BTRFS_ROOT_FORCE_COW, &root->state);
index 7554fd381a566a8f4a70043365799028377d4037..6f9c38ce45c7d89ff7564bfb6a911eb1683ac00b 100644 (file)
@@ -306,6 +306,14 @@ struct btrfs_ioctl_search_args {
        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 {
   __s64 src_fd;
   __u64 src_offset, src_length;
@@ -558,6 +566,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)