From e3966ec9d099c0e8ac7261a62720528ef901f182 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 10 Oct 2014 16:57:10 -0400 Subject: Btrfs-progs: lookup all roots that point to a corrupt block If we have a corrupt block that multiple snapshots point to we will only fix the guy who originally pointed to the block, and then simply loop forever because we keep finding the same bad block. So instead lookup all roots that point to this block, and then search down to the block for each root and fix the block in all snapshots. Thanks, Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- cmds-check.c | 143 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 70 insertions(+), 73 deletions(-) (limited to 'cmds-check.c') diff --git a/cmds-check.c b/cmds-check.c index acdf312f..99d1a94c 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -40,6 +40,8 @@ #include "btrfsck.h" #include "qgroup-verify.h" #include "rbtree-utils.h" +#include "backref.h" +#include "ulist.h" static u64 bytes_used = 0; static u64 total_csum_bytes = 0; @@ -2776,42 +2778,14 @@ static int swap_values(struct btrfs_root *root, struct btrfs_path *path, static int fix_key_order(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf) + struct btrfs_path *path) { - struct btrfs_path *path; + struct extent_buffer *buf; struct btrfs_key k1, k2; int i; - int level; + int level = path->lowest_level; int ret; - k1.objectid = btrfs_header_owner(buf); - k1.type = BTRFS_ROOT_ITEM_KEY; - k1.offset = (u64)-1; - - root = btrfs_read_fs_root(root->fs_info, &k1); - if (IS_ERR(root)) - return -EIO; - - record_root_in_trans(trans, root); - - path = btrfs_alloc_path(); - if (!path) - return -EIO; - - level = btrfs_header_level(buf); - path->lowest_level = level; - path->skip_check_block = 1; - if (level) - btrfs_node_key_to_cpu(buf, &k1, 0); - else - btrfs_item_key_to_cpu(buf, &k1, 0); - - ret = btrfs_search_slot(trans, root, &k1, path, 0, 1); - if (ret) { - btrfs_free_path(path); - return -EIO; - } - buf = path->nodes[level]; for (i = 0; i < btrfs_header_nritems(buf) - 1; i++) { if (level) { @@ -2829,8 +2803,6 @@ static int fix_key_order(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(buf); i = 0; } - - btrfs_free_path(path); return ret; } @@ -2872,43 +2844,15 @@ static int delete_bogus_item(struct btrfs_trans_handle *trans, static int fix_item_offset(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf) + struct btrfs_path *path) { - struct btrfs_path *path; - struct btrfs_key k1; + struct extent_buffer *buf; int i; - int level; - int ret; - - k1.objectid = btrfs_header_owner(buf); - k1.type = BTRFS_ROOT_ITEM_KEY; - k1.offset = (u64)-1; - - root = btrfs_read_fs_root(root->fs_info, &k1); - if (IS_ERR(root)) - return -EIO; - - record_root_in_trans(trans, root); - - path = btrfs_alloc_path(); - if (!path) - return -EIO; - - level = btrfs_header_level(buf); - path->lowest_level = level; - path->skip_check_block = 1; - if (level) - btrfs_node_key_to_cpu(buf, &k1, 0); - else - btrfs_item_key_to_cpu(buf, &k1, 0); - - ret = btrfs_search_slot(trans, root, &k1, path, 0, 1); - if (ret) { - btrfs_free_path(path); - return -EIO; - } + int ret = 0; - buf = path->nodes[level]; + /* We should only get this for leaves */ + BUG_ON(path->lowest_level); + buf = path->nodes[0]; again: for (i = 0; i < btrfs_header_nritems(buf); i++) { unsigned int shift = 0, offset; @@ -2964,7 +2908,6 @@ again: * progs this can be changed to something nicer. */ BUG_ON(ret); - btrfs_free_path(path); return ret; } @@ -2977,11 +2920,65 @@ static int try_to_fix_bad_block(struct btrfs_trans_handle *trans, struct extent_buffer *buf, enum btrfs_tree_block_status status) { - if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER) - return fix_key_order(trans, root, buf); - if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS) - return fix_item_offset(trans, root, buf); - return -EIO; + struct ulist *roots; + struct ulist_node *node; + struct btrfs_root *search_root; + struct btrfs_path *path; + struct ulist_iterator iter; + struct btrfs_key root_key, key; + int ret; + + if (status != BTRFS_TREE_BLOCK_BAD_KEY_ORDER && + status != BTRFS_TREE_BLOCK_INVALID_OFFSETS) + return -EIO; + + path = btrfs_alloc_path(); + if (!path) + return -EIO; + + ret = btrfs_find_all_roots(trans, root->fs_info, buf->start, + 0, &roots); + if (ret) { + btrfs_free_path(path); + return -EIO; + } + + ULIST_ITER_INIT(&iter); + while ((node = ulist_next(roots, &iter))) { + root_key.objectid = node->val; + root_key.type = BTRFS_ROOT_ITEM_KEY; + root_key.offset = (u64)-1; + + search_root = btrfs_read_fs_root(root->fs_info, &root_key); + if (IS_ERR(root)) { + ret = -EIO; + break; + } + + record_root_in_trans(trans, search_root); + + path->lowest_level = btrfs_header_level(buf); + path->skip_check_block = 1; + if (path->lowest_level) + btrfs_node_key_to_cpu(buf, &key, 0); + else + btrfs_item_key_to_cpu(buf, &key, 0); + ret = btrfs_search_slot(trans, search_root, &key, path, 0, 1); + if (ret) { + ret = -EIO; + break; + } + if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER) + ret = fix_key_order(trans, search_root, path); + else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS) + ret = fix_item_offset(trans, search_root, path); + if (ret) + break; + btrfs_release_path(path); + } + ulist_free(roots); + btrfs_free_path(path); + return ret; } static int check_block(struct btrfs_trans_handle *trans, -- cgit v1.2.3