btrfs-progs: fsck: Avoid abort and BUG_ON in add_tree_backref
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Tue, 30 Aug 2016 07:22:16 +0000 (15:22 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 5 Sep 2016 08:05:01 +0000 (10:05 +0200)
Add_tree_backref() can cause BUG_ON() and abort() in quite a lot of
cases, from the ENOMEM to existing tree backref records.

Change all these BUG_ON() and abort() to return proper values.
And modify all callers to handle such problems.

Reported-by: Lukas Lueg <lukas.lueg@gmail.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
cmds-check.c

index c56b176..ef3e3a1 100644 (file)
@@ -4864,20 +4864,25 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
 
                add_extent_rec_nolookup(extent_cache, &tmpl);
 
+               /* really a bug in cache_extent implement now */
                cache = lookup_cache_extent(extent_cache, bytenr, 1);
                if (!cache)
-                       abort();
+                       return -ENOENT;
        }
 
        rec = container_of(cache, struct extent_record, cache);
        if (rec->start != bytenr) {
-               abort();
+               /*
+                * Several cause, from unaligned bytenr to over lapping extents
+                */
+               return -EEXIST;
        }
 
        back = find_tree_backref(rec, parent, root);
        if (!back) {
                back = alloc_tree_backref(rec, parent, root);
-               BUG_ON(!back);
+               if (!back)
+                       return -ENOMEM;
        }
 
        if (found_ref) {
@@ -5154,16 +5159,18 @@ static int process_extent_ref_v0(struct cache_tree *extent_cache,
 {
        struct btrfs_extent_ref_v0 *ref0;
        struct btrfs_key key;
+       int ret;
 
        btrfs_item_key_to_cpu(leaf, &key, slot);
        ref0 = btrfs_item_ptr(leaf, slot, struct btrfs_extent_ref_v0);
        if (btrfs_ref_objectid_v0(leaf, ref0) < BTRFS_FIRST_FREE_OBJECTID) {
-               add_tree_backref(extent_cache, key.objectid, key.offset, 0, 0);
+               ret = add_tree_backref(extent_cache, key.objectid, key.offset,
+                               0, 0);
        } else {
-               add_data_backref(extent_cache, key.objectid, key.offset, 0,
-                                0, 0, btrfs_ref_count_v0(leaf, ref0), 0, 0);
+               ret = add_data_backref(extent_cache, key.objectid, key.offset,
+                               0, 0, 0, btrfs_ref_count_v0(leaf, ref0), 0, 0);
        }
-       return 0;
+       return ret;
 }
 #endif
 
@@ -5406,6 +5413,7 @@ static int process_extent_item(struct btrfs_root *root,
        struct extent_record tmpl;
        unsigned long end;
        unsigned long ptr;
+       int ret;
        int type;
        u32 item_size = btrfs_item_size_nr(eb, slot);
        u64 refs = 0;
@@ -5485,12 +5493,18 @@ static int process_extent_item(struct btrfs_root *root,
                offset = btrfs_extent_inline_ref_offset(eb, iref);
                switch (type) {
                case BTRFS_TREE_BLOCK_REF_KEY:
-                       add_tree_backref(extent_cache, key.objectid,
-                                        0, offset, 0);
+                       ret = add_tree_backref(extent_cache, key.objectid,
+                                       0, offset, 0);
+                       if (ret < 0)
+                               error("add_tree_backref failed: %s",
+                                     strerror(-ret));
                        break;
                case BTRFS_SHARED_BLOCK_REF_KEY:
-                       add_tree_backref(extent_cache, key.objectid,
-                                        offset, 0, 0);
+                       ret = add_tree_backref(extent_cache, key.objectid,
+                                       offset, 0, 0);
+                       if (ret < 0)
+                               error("add_tree_backref failed: %s",
+                                     strerror(-ret));
                        break;
                case BTRFS_EXTENT_DATA_REF_KEY:
                        dref = (struct btrfs_extent_data_ref *)(&iref->offset);
@@ -6413,13 +6427,19 @@ static int run_next_block(struct btrfs_root *root,
                        }
 
                        if (key.type == BTRFS_TREE_BLOCK_REF_KEY) {
-                               add_tree_backref(extent_cache, key.objectid, 0,
-                                                key.offset, 0);
+                               ret = add_tree_backref(extent_cache,
+                                               key.objectid, 0, key.offset, 0);
+                               if (ret < 0)
+                                       error("add_tree_backref failed: %s",
+                                             strerror(-ret));
                                continue;
                        }
                        if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
-                               add_tree_backref(extent_cache, key.objectid,
-                                                key.offset, 0, 0);
+                               ret = add_tree_backref(extent_cache,
+                                               key.objectid, key.offset, 0, 0);
+                               if (ret < 0)
+                                       error("add_tree_backref failed: %s",
+                                             strerror(-ret));
                                continue;
                        }
                        if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
@@ -6517,9 +6537,16 @@ static int run_next_block(struct btrfs_root *root,
                        tmpl.metadata = 1;
                        tmpl.max_size = size;
                        ret = add_extent_rec(extent_cache, &tmpl);
-                       BUG_ON(ret);
+                       if (ret < 0)
+                               goto out;
 
-                       add_tree_backref(extent_cache, ptr, parent, owner, 1);
+                       ret = add_tree_backref(extent_cache, ptr, parent,
+                                       owner, 1);
+                       if (ret < 0) {
+                               error("add_tree_backref failed: %s",
+                                     strerror(-ret));
+                               continue;
+                       }
 
                        if (level > 1) {
                                add_pending(nodes, seen, ptr, size);
@@ -6553,6 +6580,7 @@ static int add_root_to_pending(struct extent_buffer *buf,
                               u64 objectid)
 {
        struct extent_record tmpl;
+       int ret;
 
        if (btrfs_header_level(buf) > 0)
                add_pending(nodes, seen, buf->start, buf->len);
@@ -6570,11 +6598,12 @@ static int add_root_to_pending(struct extent_buffer *buf,
 
        if (objectid == BTRFS_TREE_RELOC_OBJECTID ||
            btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
-               add_tree_backref(extent_cache, buf->start, buf->start,
-                                0, 1);
+               ret = add_tree_backref(extent_cache, buf->start, buf->start,
+                               0, 1);
        else
-               add_tree_backref(extent_cache, buf->start, 0, objectid, 1);
-       return 0;
+               ret = add_tree_backref(extent_cache, buf->start, 0, objectid,
+                               1);
+       return ret;
 }
 
 /* as we fix the tree, we might be deleting blocks that
@@ -8425,8 +8454,10 @@ static int deal_root_from_list(struct list_head *list,
                        ret = -EIO;
                        break;
                }
-               add_root_to_pending(buf, extent_cache, pending,
+               ret = add_root_to_pending(buf, extent_cache, pending,
                                    seen, nodes, rec->objectid);
+               if (ret < 0)
+                       break;
                /*
                 * To rebuild extent tree, we need deal with snapshot
                 * one by one, otherwise we deal with node firstly which