OSDN Git Service

btrfs: Add graceful handling of V0 extents
authorNikolay Borisov <nborisov@suse.com>
Tue, 26 Jun 2018 13:57:36 +0000 (16:57 +0300)
committerDavid Sterba <dsterba@suse.com>
Mon, 6 Aug 2018 11:12:41 +0000 (13:12 +0200)
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>
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c
fs/btrfs/print-tree.c
fs/btrfs/relocation.c

index 9fa958e..427ca5d 100644 (file)
@@ -3432,6 +3432,13 @@ static inline void assfail(char *expr, char *file, int line)
 #define ASSERT(expr)   ((void)0)
 #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)
 __cold
 void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function,
index 6a73d26..71283fa 100644 (file)
@@ -867,8 +867,16 @@ search_again:
                        num_refs = btrfs_extent_refs(leaf, ei);
                        extent_flags = btrfs_extent_flags(leaf, ei);
                } 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);
        } else {
                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],
                                      struct btrfs_shared_data_ref);
                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 {
                BUG();
        }
@@ -1328,6 +1340,8 @@ static noinline u32 extent_data_ref_count(struct btrfs_path *path,
 
        leaf = path->nodes[0];
        btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+
+       BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
        if (iref) {
                /*
                 * If type is invalid, we should have bailed out earlier than
@@ -1541,7 +1555,12 @@ again:
 
        leaf = path->nodes[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);
        flags = btrfs_extent_flags(leaf, ei);
@@ -2265,7 +2284,14 @@ again:
 
        leaf = path->nodes[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);
        __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];
        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,
                            struct btrfs_extent_item);
        if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&
index c0dac9d..cc94537 100644 (file)
@@ -52,8 +52,10 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
        u64 offset;
        int ref_index = 0;
 
-       if (item_size < sizeof(*ei))
-               BUG();
+       if (item_size < sizeof(*ei)) {
+               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);
        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));
                        break;
                case BTRFS_EXTENT_REF_V0_KEY:
-                       BUG();
+                       btrfs_print_v0_err(fs_info);
+                       btrfs_handle_fs_error(fs_info, -EINVAL, NULL);
                        break;
                case BTRFS_BLOCK_GROUP_ITEM_KEY:
                        bi = btrfs_item_ptr(l, i,
index 6dfa92e..eaf59c7 100644 (file)
@@ -598,6 +598,11 @@ int find_inline_backref(struct extent_buffer *leaf, int slot,
        btrfs_item_key_to_cpu(leaf, &key, 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);
        WARN_ON(!(btrfs_extent_flags(leaf, ei) &
                  BTRFS_EXTENT_FLAG_TREE_BLOCK));
@@ -782,7 +787,6 @@ again:
                        goto next;
                }
 
-               ASSERT(key.type != BTRFS_EXTENT_REF_V0_KEY);
                if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
                        if (key.objectid == key.offset) {
                                /*
@@ -826,6 +830,12 @@ again:
                        edge->node[UPPER] = upper;
 
                        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) {
                        goto next;
                }
@@ -3315,6 +3325,10 @@ static int add_tree_block(struct reloc_control *rc,
                        level = (int)extent_key->offset;
                }
                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 {
                BUG();
        }
@@ -3720,7 +3734,6 @@ int add_data_references(struct reloc_control *rc,
                if (key.objectid != extent_key->objectid)
                        break;
 
-               BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
                if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
                        ret = __add_tree_block(rc, key.offset, blocksize,
                                               blocks);
@@ -3729,6 +3742,10 @@ int add_data_references(struct reloc_control *rc,
                                              struct btrfs_extent_data_ref);
                        ret = find_data_references(rc, extent_key,
                                                   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 {
                        ret = 0;
                }
@@ -3967,7 +3984,11 @@ restart:
                        flags = btrfs_extent_flags(path->nodes[0], ei);
                        ret = check_extent_flags(flags);
                        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 {
                        BUG();
                }