btrfs: Add graceful handling of V0 extents

Following the removal of the v0 handling code let's be courteous and
print an error message when such extents are handled. In the cases
where we have a transaction just abort it, otherwise just call
btrfs_handle_fs_error. Both cases result in the FS being re-mounted RO.

In case the error handling would be too intrusive, leave the BUG_ON in
place, like extent_data_ref_count, other proper handling would catch
that earlier.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Nikolay Borisov 2018-06-26 16:57:36 +03:00 committed by David Sterba
parent a79865c680
commit ba3c2b196b
4 changed files with 72 additions and 10 deletions

View File

@ -3432,6 +3432,13 @@ static inline void assfail(char *expr, char *file, int line)
#define ASSERT(expr) ((void)0) #define ASSERT(expr) ((void)0)
#endif #endif
__cold
static inline void btrfs_print_v0_err(struct btrfs_fs_info *fs_info)
{
btrfs_err(fs_info,
"Unsupported V0 extent filesystem detected. Aborting. Please re-create your filesystem with a newer kernel");
}
__printf(5, 6) __printf(5, 6)
__cold __cold
void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function, void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function,

View File

@ -867,8 +867,16 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
num_refs = btrfs_extent_refs(leaf, ei); num_refs = btrfs_extent_refs(leaf, ei);
extent_flags = btrfs_extent_flags(leaf, ei); extent_flags = btrfs_extent_flags(leaf, ei);
} else { } else {
BUG(); ret = -EINVAL;
btrfs_print_v0_err(fs_info);
if (trans)
btrfs_abort_transaction(trans, ret);
else
btrfs_handle_fs_error(fs_info, ret, NULL);
goto out_free;
} }
BUG_ON(num_refs == 0); BUG_ON(num_refs == 0);
} else { } else {
num_refs = 0; num_refs = 0;
@ -1296,6 +1304,10 @@ static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
ref2 = btrfs_item_ptr(leaf, path->slots[0], ref2 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_shared_data_ref); struct btrfs_shared_data_ref);
num_refs = btrfs_shared_data_ref_count(leaf, ref2); num_refs = btrfs_shared_data_ref_count(leaf, ref2);
} else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
btrfs_print_v0_err(trans->fs_info);
btrfs_abort_transaction(trans, -EINVAL);
return -EINVAL;
} else { } else {
BUG(); BUG();
} }
@ -1328,6 +1340,8 @@ static noinline u32 extent_data_ref_count(struct btrfs_path *path,
leaf = path->nodes[0]; leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
if (iref) { if (iref) {
/* /*
* If type is invalid, we should have bailed out earlier than * If type is invalid, we should have bailed out earlier than
@ -1541,7 +1555,12 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
leaf = path->nodes[0]; leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]); item_size = btrfs_item_size_nr(leaf, path->slots[0]);
BUG_ON(item_size < sizeof(*ei)); if (item_size < sizeof(*ei)) {
err = -EINVAL;
btrfs_print_v0_err(fs_info);
btrfs_abort_transaction(trans, err);
goto out;
}
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
flags = btrfs_extent_flags(leaf, ei); flags = btrfs_extent_flags(leaf, ei);
@ -2265,7 +2284,14 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
leaf = path->nodes[0]; leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]); item_size = btrfs_item_size_nr(leaf, path->slots[0]);
BUG_ON(item_size < sizeof(*ei));
if (item_size < sizeof(*ei)) {
err = -EINVAL;
btrfs_print_v0_err(fs_info);
btrfs_abort_transaction(trans, err);
goto out;
}
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
__run_delayed_extent_op(extent_op, leaf, ei); __run_delayed_extent_op(extent_op, leaf, ei);
@ -6796,7 +6822,12 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
leaf = path->nodes[0]; leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, extent_slot); item_size = btrfs_item_size_nr(leaf, extent_slot);
BUG_ON(item_size < sizeof(*ei)); if (item_size < sizeof(*ei)) {
ret = -EINVAL;
btrfs_print_v0_err(info);
btrfs_abort_transaction(trans, ret);
goto out;
}
ei = btrfs_item_ptr(leaf, extent_slot, ei = btrfs_item_ptr(leaf, extent_slot,
struct btrfs_extent_item); struct btrfs_extent_item);
if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID && if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&

View File

@ -52,8 +52,10 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
u64 offset; u64 offset;
int ref_index = 0; int ref_index = 0;
if (item_size < sizeof(*ei)) if (item_size < sizeof(*ei)) {
BUG(); btrfs_print_v0_err(eb->fs_info);
btrfs_handle_fs_error(eb->fs_info, -EINVAL, NULL);
}
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
flags = btrfs_extent_flags(eb, ei); flags = btrfs_extent_flags(eb, ei);
@ -256,7 +258,8 @@ void btrfs_print_leaf(struct extent_buffer *l)
btrfs_file_extent_ram_bytes(l, fi)); btrfs_file_extent_ram_bytes(l, fi));
break; break;
case BTRFS_EXTENT_REF_V0_KEY: case BTRFS_EXTENT_REF_V0_KEY:
BUG(); btrfs_print_v0_err(fs_info);
btrfs_handle_fs_error(fs_info, -EINVAL, NULL);
break; break;
case BTRFS_BLOCK_GROUP_ITEM_KEY: case BTRFS_BLOCK_GROUP_ITEM_KEY:
bi = btrfs_item_ptr(l, i, bi = btrfs_item_ptr(l, i,

View File

@ -598,6 +598,11 @@ int find_inline_backref(struct extent_buffer *leaf, int slot,
btrfs_item_key_to_cpu(leaf, &key, slot); btrfs_item_key_to_cpu(leaf, &key, slot);
item_size = btrfs_item_size_nr(leaf, slot); item_size = btrfs_item_size_nr(leaf, slot);
if (item_size < sizeof(*ei)) {
btrfs_print_v0_err(leaf->fs_info);
btrfs_handle_fs_error(leaf->fs_info, -EINVAL, NULL);
return 1;
}
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
WARN_ON(!(btrfs_extent_flags(leaf, ei) & WARN_ON(!(btrfs_extent_flags(leaf, ei) &
BTRFS_EXTENT_FLAG_TREE_BLOCK)); BTRFS_EXTENT_FLAG_TREE_BLOCK));
@ -782,7 +787,6 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
goto next; goto next;
} }
ASSERT(key.type != BTRFS_EXTENT_REF_V0_KEY);
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) { if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
if (key.objectid == key.offset) { if (key.objectid == key.offset) {
/* /*
@ -826,6 +830,12 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
edge->node[UPPER] = upper; edge->node[UPPER] = upper;
goto next; goto next;
} else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
err = -EINVAL;
btrfs_print_v0_err(rc->extent_root->fs_info);
btrfs_handle_fs_error(rc->extent_root->fs_info, err,
NULL);
goto out;
} else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) { } else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) {
goto next; goto next;
} }
@ -3315,6 +3325,10 @@ static int add_tree_block(struct reloc_control *rc,
level = (int)extent_key->offset; level = (int)extent_key->offset;
} }
generation = btrfs_extent_generation(eb, ei); generation = btrfs_extent_generation(eb, ei);
} else if (item_size == sizeof(struct btrfs_extent_item_v0)) {
btrfs_print_v0_err(eb->fs_info);
btrfs_handle_fs_error(eb->fs_info, -EINVAL, NULL);
return -EINVAL;
} else { } else {
BUG(); BUG();
} }
@ -3720,7 +3734,6 @@ int add_data_references(struct reloc_control *rc,
if (key.objectid != extent_key->objectid) if (key.objectid != extent_key->objectid)
break; break;
BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
if (key.type == BTRFS_SHARED_DATA_REF_KEY) { if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
ret = __add_tree_block(rc, key.offset, blocksize, ret = __add_tree_block(rc, key.offset, blocksize,
blocks); blocks);
@ -3729,6 +3742,10 @@ int add_data_references(struct reloc_control *rc,
struct btrfs_extent_data_ref); struct btrfs_extent_data_ref);
ret = find_data_references(rc, extent_key, ret = find_data_references(rc, extent_key,
eb, dref, blocks); eb, dref, blocks);
} else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
btrfs_print_v0_err(eb->fs_info);
btrfs_handle_fs_error(eb->fs_info, -EINVAL, NULL);
ret = -EINVAL;
} else { } else {
ret = 0; ret = 0;
} }
@ -3967,7 +3984,11 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
flags = btrfs_extent_flags(path->nodes[0], ei); flags = btrfs_extent_flags(path->nodes[0], ei);
ret = check_extent_flags(flags); ret = check_extent_flags(flags);
BUG_ON(ret); BUG_ON(ret);
} else if (item_size == sizeof(struct btrfs_extent_item_v0)) {
err = -EINVAL;
btrfs_print_v0_err(trans->fs_info);
btrfs_abort_transaction(trans, err);
break;
} else { } else {
BUG(); BUG();
} }