summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds-check.c143
1 files changed, 70 insertions, 73 deletions
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,