From f5c4c4f3b75bde259640584761fc74a395056aad Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Tue, 7 Feb 2012 05:13:24 -0500 Subject: [PATCH] btrfsck: add code to rebuild extent records This also includes a new --repair btrfsck option. For now it can only fix errors in the extent allocation tree. Signed-off-by: Chris Mason --- btrfsck.c | 457 ++++++++++++++++++++++++++++++++++++++++++++++---- ctree.h | 1 + extent-tree.c | 24 +-- print-tree.c | 2 +- 4 files changed, 436 insertions(+), 48 deletions(-) diff --git a/btrfsck.c b/btrfsck.c index f09945f5..ab66b122 100644 --- a/btrfsck.c +++ b/btrfsck.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "kerncompat.h" #include "ctree.h" #include "disk-io.h" @@ -74,9 +75,13 @@ struct extent_record { struct cache_extent cache; struct btrfs_disk_key parent_key; u64 start; + u64 max_size; u64 nr; u64 refs; u64 extent_item_refs; + u64 generation; + u64 info_objectid; + u8 info_level; unsigned int content_checked:1; unsigned int owner_ref_checked:1; unsigned int is_root:1; @@ -1082,7 +1087,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, ret = btrfs_lookup_extent_info(NULL, root, path->nodes[*level]->start, path->nodes[*level]->len, &refs, NULL); - BUG_ON(ret); + if (ret < 0) + goto out; + if (refs > 1) { ret = enter_shared_node(root, path->nodes[*level]->start, refs, wc, *level); @@ -1109,7 +1116,8 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, blocksize = btrfs_level_size(root, *level - 1); ret = btrfs_lookup_extent_info(NULL, root, bytenr, blocksize, &refs, NULL); - BUG_ON(ret); + if (ret < 0) + refs = 0; if (refs > 1) { ret = enter_shared_node(root, bytenr, refs, @@ -1834,12 +1842,12 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs) if (!print_errs) goto out; tback = (struct tree_backref *)back; - fprintf(stderr, "Backref %llu %s %llu not referenced\n", + fprintf(stderr, "Backref %llu %s %llu not referenced back %p\n", (unsigned long long)rec->start, back->full_backref ? "parent" : "root", back->full_backref ? (unsigned long long)tback->parent : - (unsigned long long)tback->root); + (unsigned long long)tback->root, back); } if (back->is_data) { dback = (struct data_backref *)back; @@ -1849,7 +1857,7 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs) goto out; fprintf(stderr, "Incorrect local backref count" " on %llu %s %llu owner %llu" - " offset %llu found %u wanted %u\n", + " offset %llu found %u wanted %u back %p\n", (unsigned long long)rec->start, back->full_backref ? "parent" : "root", @@ -1858,7 +1866,7 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs) (unsigned long long)dback->root, (unsigned long long)dback->owner, (unsigned long long)dback->offset, - dback->found_ref, dback->num_refs); + dback->found_ref, dback->num_refs, back); } } if (!back->is_data) { @@ -1965,12 +1973,28 @@ static int check_block(struct btrfs_root *root, { struct extent_record *rec; struct cache_extent *cache; + struct btrfs_key key; int ret = 1; + int level; cache = find_cache_extent(extent_cache, buf->start, buf->len); if (!cache) return 1; rec = container_of(cache, struct extent_record, cache); + rec->generation = btrfs_header_generation(buf); + + level = btrfs_header_level(buf); + if (btrfs_header_nritems(buf) > 0) { + + if (level == 0) + btrfs_item_key_to_cpu(buf, &key, 0); + else + btrfs_node_key_to_cpu(buf, &key, 0); + + rec->info_objectid = key.objectid; + } + rec->info_level = level; + if (btrfs_is_leaf(buf)) { ret = check_leaf(root, &rec->parent_key, buf); } else { @@ -2035,6 +2059,7 @@ static struct tree_backref *alloc_tree_backref(struct extent_record *rec, ref->node.full_backref = 0; } list_add_tail(&ref->node.list, &rec->backrefs); + return ref; } @@ -2052,7 +2077,7 @@ static struct data_backref *find_data_backref(struct extent_record *rec, if (!node->is_data) continue; back = (struct data_backref *)node; - if (parent > 0) { + if (parent > 0) { if (!node->full_backref) continue; if (parent == back->parent) @@ -2070,11 +2095,13 @@ static struct data_backref *find_data_backref(struct extent_record *rec, static struct data_backref *alloc_data_backref(struct extent_record *rec, u64 parent, u64 root, - u64 owner, u64 offset) + u64 owner, u64 offset, + u64 max_size) { struct data_backref *ref = malloc(sizeof(*ref)); memset(&ref->node, 0, sizeof(ref->node)); ref->node.is_data = 1; + if (parent > 0) { ref->parent = parent; ref->owner = 0; @@ -2089,13 +2116,16 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec, ref->found_ref = 0; ref->num_refs = 0; list_add_tail(&ref->node.list, &rec->backrefs); + if (max_size > rec->max_size) + rec->max_size = max_size; return ref; } static int add_extent_rec(struct cache_tree *extent_cache, struct btrfs_key *parent_key, u64 start, u64 nr, u64 extent_item_refs, - int is_root, int inc_ref, int set_checked) + int is_root, int inc_ref, int set_checked, + u64 max_size) { struct extent_record *rec; struct cache_extent *cache; @@ -2136,11 +2166,15 @@ static int add_extent_rec(struct cache_tree *extent_cache, if (parent_key) btrfs_cpu_key_to_disk(&rec->parent_key, parent_key); + if (rec->max_size < max_size) + rec->max_size = max_size; + maybe_free_extent_rec(extent_cache, rec); return ret; } rec = malloc(sizeof(*rec)); rec->start = start; + rec->max_size = max_size; rec->nr = nr; rec->content_checked = 0; rec->owner_ref_checked = 0; @@ -2187,7 +2221,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, cache = find_cache_extent(extent_cache, bytenr, 1); if (!cache) { - add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0); + add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0, 0); cache = find_cache_extent(extent_cache, bytenr, 1); if (!cache) abort(); @@ -2226,7 +2260,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr, static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, u64 parent, u64 root, u64 owner, u64 offset, - u32 num_refs, int found_ref) + u32 num_refs, int found_ref, u64 max_size) { struct extent_record *rec; struct data_backref *back; @@ -2234,7 +2268,8 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, cache = find_cache_extent(extent_cache, bytenr, 1); if (!cache) { - add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0); + add_extent_rec(extent_cache, NULL, bytenr, 1, 0, 0, 0, 0, + max_size); cache = find_cache_extent(extent_cache, bytenr, 1); if (!cache) abort(); @@ -2244,9 +2279,13 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr, if (rec->start != bytenr) { abort(); } + if (rec->max_size < max_size) + rec->max_size = max_size; + back = find_data_backref(rec, parent, root, owner, offset); if (!back) - back = alloc_data_backref(rec, parent, root, owner, offset); + back = alloc_data_backref(rec, parent, root, owner, offset, + max_size); if (found_ref) { BUG_ON(num_refs != 1); @@ -2359,11 +2398,10 @@ static int process_extent_ref_v0(struct cache_tree *extent_cache, btrfs_item_key_to_cpu(leaf, &key, slot); ref0 = btrfs_item_ptr(leaf, slot, struct btrfs_extent_ref_v0); if (btrfs_ref_objectid_v0(leaf, ref0) < BTRFS_FIRST_FREE_OBJECTID) { - add_tree_backref(extent_cache, key.objectid, key.offset, - 0, 0); + add_tree_backref(extent_cache, key.objectid, key.offset, 0, 0); } else { add_data_backref(extent_cache, key.objectid, key.offset, 0, - 0, 0, btrfs_ref_count_v0(leaf, ref0), 0); + 0, 0, btrfs_ref_count_v0(leaf, ref0), 0, 0); } return 0; } @@ -2396,14 +2434,14 @@ static int process_extent_item(struct cache_tree *extent_cache, BUG(); #endif return add_extent_rec(extent_cache, NULL, key.objectid, - key.offset, refs, 0, 0, 0); + key.offset, refs, 0, 0, 0, key.offset); } ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); refs = btrfs_extent_refs(eb, ei); add_extent_rec(extent_cache, NULL, key.objectid, key.offset, - refs, 0, 0, 0); + refs, 0, 0, 0, key.offset); ptr = (unsigned long)(ei + 1); if (btrfs_extent_flags(eb, ei) & BTRFS_EXTENT_FLAG_TREE_BLOCK) @@ -2431,14 +2469,14 @@ static int process_extent_item(struct cache_tree *extent_cache, dref), btrfs_extent_data_ref_offset(eb, dref), btrfs_extent_data_ref_count(eb, dref), - 0); + 0, key.offset); break; case BTRFS_SHARED_DATA_REF_KEY: sref = (struct btrfs_shared_data_ref *)(iref + 1); add_data_backref(extent_cache, key.objectid, offset, 0, 0, 0, btrfs_shared_data_ref_count(eb, sref), - 0); + 0, key.offset); break; default: fprintf(stderr, "corrupt extent record: key %Lu %u %Lu\n", @@ -2515,6 +2553,8 @@ static int run_next_block(struct btrfs_root *root, nritems = btrfs_header_nritems(buf); ret = btrfs_lookup_extent_info(NULL, root, bytenr, size, NULL, &flags); + if (ret < 0) + flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { parent = bytenr; @@ -2573,7 +2613,7 @@ static int run_next_block(struct btrfs_root *root, ref), btrfs_extent_data_ref_offset(buf, ref), btrfs_extent_data_ref_count(buf, ref), - 0); + 0, root->sectorsize); continue; } if (key.type == BTRFS_SHARED_DATA_REF_KEY) { @@ -2583,7 +2623,7 @@ static int run_next_block(struct btrfs_root *root, add_data_backref(extent_cache, key.objectid, key.offset, 0, 0, 0, btrfs_shared_data_ref_count(buf, ref), - 0); + 0, root->sectorsize); continue; } if (key.type != BTRFS_EXTENT_DATA_KEY) @@ -2606,26 +2646,33 @@ static int run_next_block(struct btrfs_root *root, ret = add_extent_rec(extent_cache, NULL, btrfs_file_extent_disk_bytenr(buf, fi), btrfs_file_extent_disk_num_bytes(buf, fi), - 0, 0, 1, 1); + 0, 0, 1, 1, + btrfs_file_extent_disk_num_bytes(buf, fi)); add_data_backref(extent_cache, btrfs_file_extent_disk_bytenr(buf, fi), parent, owner, key.objectid, key.offset - - btrfs_file_extent_offset(buf, fi), 1, 1); + btrfs_file_extent_offset(buf, fi), 1, 1, + btrfs_file_extent_disk_num_bytes(buf, fi)); BUG_ON(ret); } } else { int level; + struct btrfs_key first_key; + + first_key.objectid = 0; + + if (nritems > 0) + btrfs_item_key_to_cpu(buf, &first_key, 0); level = btrfs_header_level(buf); for (i = 0; i < nritems; i++) { u64 ptr = btrfs_node_blockptr(buf, i); u32 size = btrfs_level_size(root, level - 1); btrfs_node_key_to_cpu(buf, &key, i); ret = add_extent_rec(extent_cache, &key, - ptr, size, 0, 0, 1, 0); + ptr, size, 0, 0, 1, 0, size); BUG_ON(ret); - add_tree_backref(extent_cache, ptr, parent, - owner, 1); + add_tree_backref(extent_cache, ptr, parent, owner, 1); if (level > 1) { add_pending(nodes, seen, ptr, size); @@ -2663,25 +2710,313 @@ static int add_root_to_pending(struct extent_buffer *buf, else add_pending(pending, seen, buf->start, buf->len); add_extent_rec(extent_cache, NULL, buf->start, buf->len, - 0, 1, 1, 0); + 0, 1, 1, 0, buf->len); if (root_key->objectid == BTRFS_TREE_RELOC_OBJECTID || btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) - add_tree_backref(extent_cache, buf->start, buf->start, 0, 1); + add_tree_backref(extent_cache, buf->start, buf->start, + 0, 1); else add_tree_backref(extent_cache, buf->start, 0, root_key->objectid, 1); return 0; } +static int delete_extent_records(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 bytenr) +{ + struct btrfs_key key; + struct btrfs_key found_key; + struct extent_buffer *leaf; + int ret; + int slot; + + + key.objectid = bytenr; + key.type = (u8)-1; + key.offset = (u64)-1; + + while(1) { + ret = btrfs_search_slot(trans, root->fs_info->extent_root, + &key, path, 0, 1); + if (ret < 0) + break; + + if (ret > 0) { + ret = 0; + if (path->slots[0] == 0) + break; + path->slots[0]--; + } + ret = 0; + + leaf = path->nodes[0]; + slot = path->slots[0]; + + btrfs_item_key_to_cpu(leaf, &found_key, slot); + if (found_key.objectid != bytenr) + break; + + if (found_key.type != BTRFS_EXTENT_ITEM_KEY && + found_key.type != BTRFS_TREE_BLOCK_REF_KEY && + found_key.type != BTRFS_EXTENT_DATA_REF_KEY && + found_key.type != BTRFS_EXTENT_REF_V0_KEY && + found_key.type != BTRFS_SHARED_BLOCK_REF_KEY && + found_key.type != BTRFS_SHARED_DATA_REF_KEY) { + btrfs_release_path(NULL, path); + if (found_key.type == 0) { + if (found_key.offset == 0) + break; + key.offset = found_key.offset - 1; + key.type = found_key.type; + } + key.type = found_key.type - 1; + key.offset = (u64)-1; + continue; + } + + fprintf(stderr, "repair deleting extent record: key %Lu %u %Lu\n", + found_key.objectid, found_key.type, found_key.offset); + + ret = btrfs_del_item(trans, root->fs_info->extent_root, path); + if (ret) + break; + btrfs_release_path(NULL, path); + + if (found_key.type == BTRFS_EXTENT_ITEM_KEY) { + ret = btrfs_update_block_group(trans, root, bytenr, + found_key.offset, 0, 1); + if (ret) + break; + } + } + + btrfs_release_path(NULL, path); + return ret; +} + +/* + * for a single backref, this will allocate a new extent + * and add the backref to it. + */ +static int record_extent(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *info, + struct btrfs_path *path, + struct extent_record *rec, + struct extent_backref *back, + int allocated, u64 flags) +{ + int ret; + struct btrfs_root *extent_root = info->extent_root; + struct extent_buffer *leaf; + struct btrfs_key ins_key; + struct btrfs_extent_item *ei; + struct tree_backref *tback; + struct data_backref *dback; + struct btrfs_tree_block_info *bi; + + if (!back->is_data) + rec->max_size = max_t(u64, rec->max_size, + info->extent_root->leafsize); + + if (!allocated) { + u32 item_size = sizeof(*ei); + + if (!back->is_data) + item_size += sizeof(*bi); + + ins_key.objectid = rec->start; + ins_key.offset = rec->max_size; + ins_key.type = BTRFS_EXTENT_ITEM_KEY; + + ret = btrfs_insert_empty_item(trans, extent_root, path, + &ins_key, item_size); + if (ret) + goto fail; + + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item); + + btrfs_set_extent_refs(leaf, ei, 0); + btrfs_set_extent_generation(leaf, ei, rec->generation); + + if (back->is_data) { + btrfs_set_extent_flags(leaf, ei, + BTRFS_EXTENT_FLAG_DATA); + } else { + struct btrfs_disk_key copy_key;; + + tback = (struct tree_backref *)back; + bi = (struct btrfs_tree_block_info *)(ei + 1); + memset_extent_buffer(leaf, 0, (unsigned long)bi, + sizeof(*bi)); + memset(©_key, 0, sizeof(copy_key)); + + copy_key.objectid = le64_to_cpu(rec->info_objectid); + btrfs_set_tree_block_level(leaf, bi, rec->info_level); + btrfs_set_tree_block_key(leaf, bi, ©_key); + + btrfs_set_extent_flags(leaf, ei, + BTRFS_EXTENT_FLAG_TREE_BLOCK | flags); + } + + btrfs_mark_buffer_dirty(leaf); + ret = btrfs_update_block_group(trans, extent_root, rec->start, + rec->max_size, 1, 0); + if (ret) + goto fail; + btrfs_release_path(NULL, path); + } + + if (back->is_data) { + u64 parent; + int i; + + dback = (struct data_backref *)back; + if (back->full_backref) + parent = dback->parent; + else + parent = 0; + + for (i = 0; i < dback->found_ref; i++) { + /* if parent != 0, we're doing a full backref + * passing BTRFS_FIRST_FREE_OBJECTID as the owner + * just makes the backref allocator create a data + * backref + */ + ret = btrfs_inc_extent_ref(trans, info->extent_root, + rec->start, rec->max_size, + parent, + dback->root, + parent ? + BTRFS_FIRST_FREE_OBJECTID : + dback->owner, + dback->offset); + if (ret) + break; + } + fprintf(stderr, "adding new data backref" + " on %llu %s %llu owner %llu" + " offset %llu found %d\n", + (unsigned long long)rec->start, + back->full_backref ? + "parent" : "root", + back->full_backref ? + (unsigned long long)parent : + (unsigned long long)dback->root, + (unsigned long long)dback->owner, + (unsigned long long)dback->offset, + dback->found_ref); + } else { + u64 parent; + + tback = (struct tree_backref *)back; + if (back->full_backref) + parent = tback->parent; + else + parent = 0; + + ret = btrfs_inc_extent_ref(trans, info->extent_root, + rec->start, rec->max_size, + parent, tback->root, 0, 0); + fprintf(stderr, "adding new tree backref on " + "start %llu len %llu parent %llu root %llu\n", + rec->start, rec->max_size, tback->parent, tback->root); + } + if (ret) + goto fail; +fail: + btrfs_release_path(NULL, path); + return ret; +} + +/* + * when an incorrect extent item is found, this will delete + * all of the existing entries for it and recreate them + * based on what the tree scan found. + */ +static int fixup_extent_refs(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *info, + struct extent_record *rec) +{ + int ret; + struct btrfs_path *path; + struct list_head *cur = rec->backrefs.next; + struct extent_backref *back; + int allocated = 0; + u64 flags = 0; + + /* remember our flags for recreating the extent */ + ret = btrfs_lookup_extent_info(NULL, info->extent_root, rec->start, + rec->max_size, NULL, &flags); + if (ret < 0) + flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; + + path = btrfs_alloc_path(); + + /* step one, delete all the existing records */ + ret = delete_extent_records(trans, info->extent_root, path, + rec->start); + + if (ret < 0) + goto out; + + /* step two, recreate all the refs we did find */ + while(cur != &rec->backrefs) { + back = list_entry(cur, struct extent_backref, list); + cur = cur->next; + + /* + * if we didn't find any references, don't create a + * new extent record + */ + if (!back->found_ref) + continue; + + ret = record_extent(trans, info, path, rec, back, allocated, flags); + allocated = 1; + + if (ret) + goto out; + } +out: + btrfs_free_path(path); + return ret; +} + static int check_extent_refs(struct btrfs_root *root, - struct cache_tree *extent_cache) + struct cache_tree *extent_cache, int repair) { struct extent_record *rec; struct cache_extent *cache; + struct btrfs_trans_handle *trans = NULL; int err = 0; + int ret = 0; + int fixed = 0; + if (repair) + trans = btrfs_start_transaction(root, 1); + + if (repair) { + /* + * if we're doing a repair, we have to make sure + * we don't allocate from the problem extents. + * In the worst case, this will be all the + * extents in the FS + */ + cache = find_first_cache_extent(extent_cache, 0); + while(cache) { + rec = container_of(cache, struct extent_record, cache); + btrfs_pin_extent(root->fs_info, + rec->start, rec->nr); + cache = next_cache_extent(cache); + } + } while(1) { + fixed = 0; cache = find_first_cache_extent(extent_cache, 0); if (!cache) break; @@ -2693,19 +3028,39 @@ static int check_extent_refs(struct btrfs_root *root, fprintf(stderr, "extent item %llu, found %llu\n", (unsigned long long)rec->extent_item_refs, (unsigned long long)rec->refs); + if (!fixed && repair) { + ret = fixup_extent_refs(trans, root->fs_info, rec); + if (ret) + goto repair_abort; + fixed = 1; + } err = 1; + } if (all_backpointers_checked(rec, 1)) { fprintf(stderr, "backpointer mismatch on [%llu %llu]\n", (unsigned long long)rec->start, (unsigned long long)rec->nr); + if (!fixed && repair) { + ret = fixup_extent_refs(trans, root->fs_info, rec); + if (ret) + goto repair_abort; + fixed = 1; + } + err = 1; } if (!rec->owner_ref_checked) { fprintf(stderr, "owner ref check failed [%llu %llu]\n", (unsigned long long)rec->start, (unsigned long long)rec->nr); + if (!fixed && repair) { + ret = fixup_extent_refs(trans, root->fs_info, rec); + if (ret) + goto repair_abort; + fixed = 1; + } err = 1; } @@ -2713,10 +3068,18 @@ static int check_extent_refs(struct btrfs_root *root, free_all_extent_backrefs(rec); free(rec); } +repair_abort: + if (repair) { + if (ret) { + fprintf(stderr, "failed to repair damaged filesystem, aborting\n"); + exit(1); + } + btrfs_commit_transaction(trans, root); + } return err; } -static int check_extents(struct btrfs_root *root) +static int check_extents(struct btrfs_root *root, int repair) { struct cache_tree extent_cache; struct cache_tree seen; @@ -2797,7 +3160,7 @@ static int check_extents(struct btrfs_root *root) if (ret != 0) break; } - ret = check_extent_refs(root, &extent_cache); + ret = check_extent_refs(root, &extent_cache, repair); return ret; } @@ -2808,6 +3171,12 @@ static void print_usage(void) exit(1); } +static struct option long_options[] = { + { "super", 1, NULL, 's' }, + { "repair", 0, NULL, 0 }, + { 0, 0, 0, 0} +}; + int main(int ac, char **av) { struct cache_tree root_cache; @@ -2816,10 +3185,13 @@ int main(int ac, char **av) u64 bytenr = 0; int ret; int num; + int repair = 0; + int option_index = 0; while(1) { int c; - c = getopt(ac, av, "s:"); + c = getopt_long(ac, av, "", long_options, + &option_index); if (c < 0) break; switch(c) { @@ -2829,9 +3201,14 @@ int main(int ac, char **av) printf("using SB copy %d, bytenr %llu\n", num, (unsigned long long)bytenr); break; - default: + case '?': print_usage(); } + if (option_index == 1) { + printf("enabling repair mode\n"); + repair = 1; + } + } ac = ac - optind; @@ -2849,15 +3226,23 @@ int main(int ac, char **av) return -EBUSY; } - info = open_ctree_fs_info(av[optind], bytenr, 0, 0); + info = open_ctree_fs_info(av[optind], bytenr, repair, 1); if (info == NULL) return 1; + if (!extent_buffer_uptodate(info->tree_root->node) || + !extent_buffer_uptodate(info->dev_root->node) || + !extent_buffer_uptodate(info->extent_root->node) || + !extent_buffer_uptodate(info->chunk_root->node)) { + fprintf(stderr, "Critical roots corrupted, unable to fsck the FS\n"); + return -EIO; + } + root = info->fs_root; fprintf(stderr, "checking extents\n"); - ret = check_extents(root); + ret = check_extents(root, repair); if (ret) goto out; fprintf(stderr, "checking fs roots\n"); diff --git a/ctree.h b/ctree.h index 1c3740cf..5e770d28 100644 --- a/ctree.h +++ b/ctree.h @@ -1785,6 +1785,7 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) { btrfs_item_offset_nr(leaf, slot))) /* extent-tree.c */ +void btrfs_pin_extent(struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes); int btrfs_extent_post_op(struct btrfs_trans_handle *trans, struct btrfs_root *root); int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy); diff --git a/extent-tree.c b/extent-tree.c index 5bed3c27..1f139924 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -1041,8 +1041,6 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, } if (ret) { printf("Failed to find [%llu, %u, %llu]\n", key.objectid, key.type, key.offset); - btrfs_print_leaf(root, path->nodes[0]); - btrfs_free_path(path); return -ENOENT; } @@ -1067,8 +1065,9 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, } #endif if (item_size < sizeof(*ei)) { - printf("Size is %u, needs to be %u, slot %d\n", item_size, - sizeof(*ei), path->slots[0]); + printf("Size is %u, needs to be %u, slot %d\n", + (unsigned)item_size, + (unsigned)sizeof(*ei), path->slots[0]); btrfs_print_leaf(root, leaf); return -EINVAL; } @@ -1460,10 +1459,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, if (ret < 0) goto out; if (ret != 0) { - btrfs_print_leaf(root, path->nodes[0]); - printk("failed to find block number %Lu\n", - (unsigned long long)bytenr); - BUG(); + ret = -EIO; + goto out; } l = path->nodes[0]; @@ -1484,9 +1481,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF; #else BUG(); -#endif - } - BUG_ON(num_refs == 0); +#endif + } item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); if (refs) *refs = num_refs; @@ -2033,6 +2029,12 @@ pinit: return 0; } +void btrfs_pin_extent(struct btrfs_fs_info *fs_info, + u64 bytenr, u64 num_bytes) +{ + update_pinned_extents(fs_info->extent_root, bytenr, num_bytes, 1); +} + /* * remove an extent from the root, returns 0 on success */ diff --git a/print-tree.c b/print-tree.c index 494ba8b5..fc134c08 100644 --- a/print-tree.c +++ b/print-tree.c @@ -239,7 +239,7 @@ static void print_extent_item(struct extent_buffer *eb, int slot) btrfs_shared_data_ref_count(eb, sref)); break; default: - BUG(); + return; } ptr += btrfs_extent_inline_ref_size(type); }