Skip to content

Commit 2576143

Browse files
adam900710kdave
authored andcommitted
btrfs: handle errors properly in update_inline_extent_backref()
[PROBLEM] Inside function update_inline_extent_backref(), we have several BUG_ON()s along with some ASSERT()s which can be triggered by corrupted filesystem. [ANAYLYSE] Most of those BUG_ON()s and ASSERT()s are just a way of handling unexpected on-disk data. Although we have tree-checker to rule out obviously incorrect extent tree blocks, it's not enough for these ones. Thus we need proper error handling for them. [FIX] Thankfully all the callers of update_inline_extent_backref() would eventually handle the errror by aborting the current transaction. So this patch would do the proper error handling by: - Make update_inline_extent_backref() to return int The return value would be either 0 or -EUCLEAN. - Replace BUG_ON()s and ASSERT()s with proper error handling This includes: * Dump the bad extent tree leaf * Output an error message for the cause This would include the extent bytenr, num_bytes (if needed), the bad values and expected good values. * Return -EUCLEAN Note here we remove all the WARN_ON()s, as eventually the transaction would be aborted, thus a backtrace would be triggered anyway. - Better comments on why we expect refs == 1 and refs_to_mode == -1 for tree blocks Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 5b135b3 commit 2576143

1 file changed

Lines changed: 61 additions & 12 deletions

File tree

fs/btrfs/extent-tree.c

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,11 @@ int btrfs_get_extent_inline_ref_type(const struct extent_buffer *eb,
381381
}
382382
}
383383

384+
WARN_ON(1);
384385
btrfs_print_leaf(eb);
385386
btrfs_err(eb->fs_info,
386387
"eb %llu iref 0x%lx invalid extent inline ref type %d",
387388
eb->start, (unsigned long)iref, type);
388-
WARN_ON(1);
389389

390390
return BTRFS_REF_TYPE_INVALID;
391391
}
@@ -1058,13 +1058,13 @@ static int lookup_extent_backref(struct btrfs_trans_handle *trans,
10581058
/*
10591059
* helper to update/remove inline back ref
10601060
*/
1061-
static noinline_for_stack
1062-
void update_inline_extent_backref(struct btrfs_path *path,
1061+
static noinline_for_stack int update_inline_extent_backref(struct btrfs_path *path,
10631062
struct btrfs_extent_inline_ref *iref,
10641063
int refs_to_mod,
10651064
struct btrfs_delayed_extent_op *extent_op)
10661065
{
10671066
struct extent_buffer *leaf = path->nodes[0];
1067+
struct btrfs_fs_info *fs_info = leaf->fs_info;
10681068
struct btrfs_extent_item *ei;
10691069
struct btrfs_extent_data_ref *dref = NULL;
10701070
struct btrfs_shared_data_ref *sref = NULL;
@@ -1077,18 +1077,33 @@ void update_inline_extent_backref(struct btrfs_path *path,
10771077

10781078
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
10791079
refs = btrfs_extent_refs(leaf, ei);
1080-
WARN_ON(refs_to_mod < 0 && refs + refs_to_mod <= 0);
1080+
if (unlikely(refs_to_mod < 0 && refs + refs_to_mod <= 0)) {
1081+
struct btrfs_key key;
1082+
u32 extent_size;
1083+
1084+
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
1085+
if (key.type == BTRFS_METADATA_ITEM_KEY)
1086+
extent_size = fs_info->nodesize;
1087+
else
1088+
extent_size = key.offset;
1089+
btrfs_print_leaf(leaf);
1090+
btrfs_err(fs_info,
1091+
"invalid refs_to_mod for extent %llu num_bytes %u, has %d expect >= -%llu",
1092+
key.objectid, extent_size, refs_to_mod, refs);
1093+
return -EUCLEAN;
1094+
}
10811095
refs += refs_to_mod;
10821096
btrfs_set_extent_refs(leaf, ei, refs);
10831097
if (extent_op)
10841098
__run_delayed_extent_op(extent_op, leaf, ei);
10851099

1100+
type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_ANY);
10861101
/*
1087-
* If type is invalid, we should have bailed out after
1088-
* lookup_inline_extent_backref().
1102+
* Function btrfs_get_extent_inline_ref_type() has already printed
1103+
* error messages.
10891104
*/
1090-
type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_ANY);
1091-
ASSERT(type != BTRFS_REF_TYPE_INVALID);
1105+
if (unlikely(type == BTRFS_REF_TYPE_INVALID))
1106+
return -EUCLEAN;
10921107

10931108
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
10941109
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
@@ -1098,10 +1113,43 @@ void update_inline_extent_backref(struct btrfs_path *path,
10981113
refs = btrfs_shared_data_ref_count(leaf, sref);
10991114
} else {
11001115
refs = 1;
1101-
BUG_ON(refs_to_mod != -1);
1116+
/*
1117+
* For tree blocks we can only drop one ref for it, and tree
1118+
* blocks should not have refs > 1.
1119+
*
1120+
* Furthermore if we're inserting a new inline backref, we
1121+
* won't reach this path either. That would be
1122+
* setup_inline_extent_backref().
1123+
*/
1124+
if (unlikely(refs_to_mod != -1)) {
1125+
struct btrfs_key key;
1126+
1127+
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
1128+
1129+
btrfs_print_leaf(leaf);
1130+
btrfs_err(fs_info,
1131+
"invalid refs_to_mod for tree block %llu, has %d expect -1",
1132+
key.objectid, refs_to_mod);
1133+
return -EUCLEAN;
1134+
}
11021135
}
11031136

1104-
BUG_ON(refs_to_mod < 0 && refs < -refs_to_mod);
1137+
if (unlikely(refs_to_mod < 0 && refs < -refs_to_mod)) {
1138+
struct btrfs_key key;
1139+
u32 extent_size;
1140+
1141+
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
1142+
if (key.type == BTRFS_METADATA_ITEM_KEY)
1143+
extent_size = fs_info->nodesize;
1144+
else
1145+
extent_size = key.offset;
1146+
btrfs_print_leaf(leaf);
1147+
btrfs_err(fs_info,
1148+
"invalid refs_to_mod for backref entry, iref %lu extent %llu num_bytes %u, has %d expect >= -%llu",
1149+
(unsigned long)iref, key.objectid, extent_size,
1150+
refs_to_mod, refs);
1151+
return -EUCLEAN;
1152+
}
11051153
refs += refs_to_mod;
11061154

11071155
if (refs > 0) {
@@ -1121,6 +1169,7 @@ void update_inline_extent_backref(struct btrfs_path *path,
11211169
btrfs_truncate_item(path, item_size, 1);
11221170
}
11231171
btrfs_mark_buffer_dirty(leaf);
1172+
return 0;
11241173
}
11251174

11261175
static noinline_for_stack
@@ -1149,7 +1198,7 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
11491198
bytenr, num_bytes, root_objectid, path->slots[0]);
11501199
return -EUCLEAN;
11511200
}
1152-
update_inline_extent_backref(path, iref, refs_to_add, extent_op);
1201+
ret = update_inline_extent_backref(path, iref, refs_to_add, extent_op);
11531202
} else if (ret == -ENOENT) {
11541203
setup_inline_extent_backref(trans->fs_info, path, iref, parent,
11551204
root_objectid, owner, offset,
@@ -1169,7 +1218,7 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
11691218

11701219
BUG_ON(!is_data && refs_to_drop != 1);
11711220
if (iref)
1172-
update_inline_extent_backref(path, iref, -refs_to_drop, NULL);
1221+
ret = update_inline_extent_backref(path, iref, -refs_to_drop, NULL);
11731222
else if (is_data)
11741223
ret = remove_extent_data_ref(trans, root, path, refs_to_drop);
11751224
else

0 commit comments

Comments
 (0)