btrfs-progs: fsck: Avoid abort and BUG_ON in add_tree_backref

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>
This commit is contained in:
Qu Wenruo 2016-08-30 15:22:16 +08:00 committed by David Sterba
parent 77deae9cb7
commit 9a0f2f1e53

View File

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