Merge branch 'for-chris' of git://github.com/idryomov/btrfs-progs
authorChris Mason <chris.mason@oracle.com>
Thu, 9 Feb 2012 02:29:56 +0000 (21:29 -0500)
committerChris Mason <chris.mason@oracle.com>
Thu, 9 Feb 2012 02:29:56 +0000 (21:29 -0500)
btrfs-corrupt-block.c
btrfsck.c
convert.c
ctree.h
debug-tree.c
disk-io.c
disk-io.h
extent-tree.c
extent_io.c
print-tree.c

index ace61c1..9ad3e05 100644 (file)
 #include "list.h"
 #include "version.h"
 
-/* we write the mirror info to stdout unless they are dumping the data
- * to stdout
- * */
-static FILE *info_file;
-
 struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
                                     u32 blocksize, int copy)
 {
@@ -62,7 +57,7 @@ struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
                device->total_ios++;
                eb->dev_bytenr = multi->stripes[0].physical;
 
-               fprintf(info_file, "mirror %d logical %Lu physical %Lu "
+               fprintf(stdout, "mirror %d logical %Lu physical %Lu "
                        "device %s\n", mirror_num, (unsigned long long)bytenr,
                        (unsigned long long)eb->dev_bytenr, device->name);
                kfree(multi);
@@ -98,32 +93,158 @@ static void print_usage(void)
        exit(1);
 }
 
+static int corrupt_extent(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *root, u64 bytenr, int copy)
+{
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       u32 item_size;
+       unsigned long ptr;
+       struct btrfs_path *path;
+       int ret;
+       int slot;
+       int should_del = rand() % 3;
+
+       path = btrfs_alloc_path();
+
+       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, -1, 1);
+               if (ret < 0)
+                       break;
+
+               if (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, &key, slot);
+               if (key.objectid != bytenr)
+                       break;
+
+               if (key.type != BTRFS_EXTENT_ITEM_KEY &&
+                   key.type != BTRFS_TREE_BLOCK_REF_KEY &&
+                   key.type != BTRFS_EXTENT_DATA_REF_KEY &&
+                   key.type != BTRFS_EXTENT_REF_V0_KEY &&
+                   key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
+                   key.type != BTRFS_SHARED_DATA_REF_KEY)
+                       goto next;
+
+               if (should_del) {
+                       fprintf(stderr, "deleting extent record: key %Lu %u %Lu\n",
+                               key.objectid, key.type, key.offset);
+
+                       if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+                               /* make sure this extent doesn't get
+                                * reused for other purposes */
+                               btrfs_pin_extent(root->fs_info,
+                                                key.objectid, key.offset);
+                       }
+
+                       btrfs_del_item(trans, root, path);
+               } else {
+                       fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n",
+                               key.objectid, key.type, key.offset);
+                       ptr = btrfs_item_ptr_offset(leaf, slot);
+                       item_size = btrfs_item_size_nr(leaf, slot);
+                       memset_extent_buffer(leaf, 0, ptr, item_size);
+                       btrfs_mark_buffer_dirty(leaf);
+               }
+next:
+               btrfs_release_path(NULL, path);
+
+               if (key.offset > 0)
+                       key.offset--;
+               if (key.offset == 0)
+                       break;
+       }
+
+       btrfs_free_path(path);
+       return 0;
+}
+
+static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
+                                     struct btrfs_root *root, struct extent_buffer *eb)
+{
+       u32 nr = btrfs_header_nritems(eb);
+       u32 victim = rand() % nr;
+       u64 objectid;
+       struct btrfs_key key;
+
+       btrfs_item_key_to_cpu(eb, &key, victim);
+       objectid = key.objectid;
+       corrupt_extent(trans, root, objectid, 1);
+}
+
+static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
+                                     struct btrfs_root *root, struct extent_buffer *eb)
+{
+       int i;
+       u32 nr;
+
+       if (!eb)
+               return;
+
+       nr = btrfs_header_nritems(eb);
+       if (btrfs_is_leaf(eb)) {
+               btrfs_corrupt_extent_leaf(trans, root, eb);
+               return;
+       }
+
+       if (btrfs_header_level(eb) == 1 && eb != root->node) {
+               if (rand() % 5)
+                       return;
+       }
+
+       for (i = 0; i < nr; i++) {
+               struct extent_buffer *next;
+
+               next = read_tree_block(root, btrfs_node_blockptr(eb, i),
+                                      root->leafsize, btrfs_node_ptr_generation(eb, i));
+               if (!next)
+                       continue;
+               btrfs_corrupt_extent_tree(trans, root, next);
+               free_extent_buffer(next);
+       }
+}
+
 static struct option long_options[] = {
        /* { "byte-count", 1, NULL, 'b' }, */
        { "logical", 1, NULL, 'l' },
        { "copy", 1, NULL, 'c' },
        { "bytes", 1, NULL, 'b' },
+       { "extent-record", 0, NULL, 'e' },
+       { "extent-tree", 0, NULL, 'E' },
        { 0, 0, 0, 0}
 };
 
+
 int main(int ac, char **av)
 {
        struct cache_tree root_cache;
        struct btrfs_root *root;
        struct extent_buffer *eb;
        char *dev;
-       char *output_file = NULL;
        u64 logical = 0;
        int ret = 0;
        int option_index = 0;
        int copy = 0;
        u64 bytes = 4096;
-       int out_fd = 0;
-       int err;
+       int extent_rec = 0;
+       int extent_tree = 0;
+
+       srand(128);
 
        while(1) {
                int c;
-               c = getopt_long(ac, av, "l:c:", long_options,
+               c = getopt_long(ac, av, "l:c:eE", long_options,
                                &option_index);
                if (c < 0)
                        break;
@@ -152,6 +273,12 @@ int main(int ac, char **av)
                                        print_usage();
                                }
                                break;
+                       case 'e':
+                               extent_rec = 1;
+                               break;
+                       case 'E':
+                               extent_tree = 1;
+                               break;
                        default:
                                print_usage();
                }
@@ -159,7 +286,7 @@ int main(int ac, char **av)
        ac = ac - optind;
        if (ac == 0)
                print_usage();
-       if (logical == 0)
+       if (logical == 0 && !extent_tree)
                print_usage();
        if (copy < 0)
                print_usage();
@@ -174,23 +301,20 @@ int main(int ac, char **av)
                fprintf(stderr, "Open ctree failed\n");
                exit(1);
        }
-
-       info_file = stdout;
-       if (output_file) {
-               if (strcmp(output_file, "-") == 0) {
-                       out_fd = 1;
-                       info_file = stderr;
-               } else {
-                       out_fd = open(output_file, O_RDWR | O_CREAT, 0600);
-                       if (out_fd < 0)
-                               goto close;
-                       err = ftruncate(out_fd, 0);
-                       if (err) {
-                               close(out_fd);
-                               goto close;
-                       }
-                       info_file = stdout;
-               }
+       if (extent_rec) {
+               struct btrfs_trans_handle *trans;
+               trans = btrfs_start_transaction(root, 1);
+               ret = corrupt_extent (trans, root, logical, 0);
+               btrfs_commit_transaction(trans, root);
+               goto out_close;
+       }
+       if (extent_tree) {
+               struct btrfs_trans_handle *trans;
+               trans = btrfs_start_transaction(root, 1);
+               btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
+                                         root->fs_info->extent_root->node);
+               btrfs_commit_transaction(trans, root);
+               goto out_close;
        }
 
        if (bytes == 0)
@@ -201,21 +325,12 @@ int main(int ac, char **av)
 
        while (bytes > 0) {
                eb = debug_corrupt_block(root, logical, root->sectorsize, copy);
-               if (eb && output_file) {
-                       err = write(out_fd, eb->data, eb->len);
-                       if (err < 0 || err != eb->len) {
-                               fprintf(stderr, "output file write failed\n");
-                               goto out_close_fd;
-                       }
-               }
                free_extent_buffer(eb);
                logical += root->sectorsize;
                bytes -= root->sectorsize;
        }
-
-out_close_fd:
-       if (output_file && out_fd != 1)
-               close(out_fd);
-close:
+       return ret;
+out_close:
+       close_ctree(root);
        return ret;
 }
index 3a23e66..a5dbbee 100644 (file)
--- a/btrfsck.c
+++ b/btrfsck.c
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <getopt.h>
 #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,21 +2469,24 @@ 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:
-                       BUG();
+                       fprintf(stderr, "corrupt extent record: key %Lu %u %Lu\n",
+                               key.objectid, key.type, key.offset);
+                       goto out;
                }
                ptr += btrfs_extent_inline_ref_size(type);
        }
        WARN_ON(ptr > end);
+out:
        return 0;
 }
 
@@ -2512,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;
@@ -2570,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) {
@@ -2580,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)
@@ -2603,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);
@@ -2660,25 +2710,310 @@ 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 check_extent_refs(struct btrfs_root *root,
-                     struct cache_tree *extent_cache)
+static int delete_extent_records(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                u64 bytenr, u64 new_len)
+{
+       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, 0);
+                       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(&copy_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, &copy_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, rec->max_size);
+
+       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_trans_handle *trans,
+                            struct btrfs_root *root,
+                            struct cache_tree *extent_cache, int repair)
 {
        struct extent_record *rec;
        struct cache_extent *cache;
        int err = 0;
+       int ret = 0;
+       int fixed = 0;
 
+       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;
@@ -2690,19 +3025,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;
                }
 
@@ -2710,10 +3065,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);
+               }
+       }
        return err;
 }
 
-static int check_extents(struct btrfs_root *root)
+static int check_extents(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root, int repair)
 {
        struct cache_tree extent_cache;
        struct cache_tree seen;
@@ -2794,7 +3157,7 @@ static int check_extents(struct btrfs_root *root)
                if (ret != 0)
                        break;
        }
-       ret = check_extent_refs(root, &extent_cache);
+       ret = check_extent_refs(trans, root, &extent_cache, repair);
        return ret;
 }
 
@@ -2805,17 +3168,28 @@ 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;
        struct btrfs_root *root;
+       struct btrfs_fs_info *info;
+       struct btrfs_trans_handle *trans = NULL;
        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) {
@@ -2825,9 +3199,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;
 
@@ -2845,21 +3224,46 @@ int main(int ac, char **av)
                return -EBUSY;
        }
 
-       root = open_ctree(av[optind], bytenr, 0);
+       info = open_ctree_fs_info(av[optind], bytenr, repair, 1);
 
-       if (root == NULL)
+       if (info == NULL)
                return 1;
 
-       ret = check_extents(root);
+       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");
+       if (repair)
+               trans = btrfs_start_transaction(root, 1);
+
+       ret = check_extents(trans, root, repair);
        if (ret)
                goto out;
+
+       if (repair)
+               btrfs_fix_block_accounting(trans, root);
+
+       fprintf(stderr, "checking fs roots\n");
        ret = check_fs_roots(root, &root_cache);
        if (ret)
                goto out;
 
+       fprintf(stderr, "checking root refs\n");
        ret = check_root_refs(root, &root_cache);
 out:
        free_root_recs(&root_cache);
+       if (repair) {
+               ret = btrfs_commit_transaction(trans, root);
+               if (ret)
+                       exit(1);
+       }
        close_ctree(root);
 
        if (found_old_backref) {
index 291dc27..8f572ef 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1504,66 +1504,6 @@ fail:
        return new_root;
 }
 
-/*
- * Fixup block accounting. The initial block accounting created by
- * make_block_groups isn't accuracy in this case.
- */
-static int fixup_block_accounting(struct btrfs_trans_handle *trans,
-                                 struct btrfs_root *root)
-{
-       int ret;
-       int slot;
-       u64 start = 0;
-       u64 bytes_used = 0;
-       struct btrfs_path path;
-       struct btrfs_key key;
-       struct extent_buffer *leaf;
-       struct btrfs_block_group_cache *cache;
-       struct btrfs_fs_info *fs_info = root->fs_info;
-
-       while(1) {
-               cache = btrfs_lookup_block_group(fs_info, start);
-               if (!cache)
-                       break;
-               start = cache->key.objectid + cache->key.offset;
-               btrfs_set_block_group_used(&cache->item, 0);
-               cache->space_info->bytes_used = 0;
-       }
-
-       btrfs_init_path(&path);
-       key.offset = 0;
-       key.objectid = 0;
-       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
-       ret = btrfs_search_slot(trans, root->fs_info->extent_root,
-                               &key, &path, 0, 0);
-       if (ret < 0)
-               return ret;
-       while(1) {
-               leaf = path.nodes[0];
-               slot = path.slots[0];
-               if (slot >= btrfs_header_nritems(leaf)) {
-                       ret = btrfs_next_leaf(root, &path);
-                       if (ret < 0)
-                               return ret;
-                       if (ret > 0)
-                               break;
-                       leaf = path.nodes[0];
-                       slot = path.slots[0];
-               }
-               btrfs_item_key_to_cpu(leaf, &key, slot);
-               if (key.type == BTRFS_EXTENT_ITEM_KEY) {
-                       bytes_used += key.offset;
-                       ret = btrfs_update_block_group(trans, root,
-                                 key.objectid, key.offset, 1, 0);
-                       BUG_ON(ret);
-               }
-               path.slots[0]++;
-       }
-       btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used);
-       btrfs_release_path(root, &path);
-       return 0;
-}
-
 static int create_chunk_mapping(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root)
 {
@@ -1735,7 +1675,7 @@ static int init_btrfs(struct btrfs_root *root)
        ret = btrfs_make_block_groups(trans, root);
        if (ret)
                goto err;
-       ret = fixup_block_accounting(trans, root);
+       ret = btrfs_fixup_block_accounting(trans, root);
        if (ret)
                goto err;
        ret = create_chunk_mapping(trans, root);
diff --git a/ctree.h b/ctree.h
index 1c3740c..6f12869 100644 (file)
--- a/ctree.h
+++ b/ctree.h
@@ -1785,6 +1785,10 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) {
        btrfs_item_offset_nr(leaf, slot)))
 
 /* extent-tree.c */
+int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root);
+int btrfs_check_block_accounting(struct btrfs_root *root);
+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);
index 2aeabfd..c497892 100644 (file)
@@ -104,6 +104,7 @@ static void print_old_roots(struct btrfs_super_block *super)
 int main(int ac, char **av)
 {
        struct btrfs_root *root;
+       struct btrfs_fs_info *info;
        struct btrfs_path path;
        struct btrfs_key key;
        struct btrfs_root_item ri;
@@ -152,12 +153,18 @@ int main(int ac, char **av)
        if (ac != 1)
                print_usage();
 
-       root = open_ctree(av[optind], 0, 0);
-       if (!root) {
+       info = open_ctree_fs_info(av[optind], 0, 0, 1);
+       if (!info) {
                fprintf(stderr, "unable to open %s\n", av[optind]);
                exit(1);
        }
+       root = info->fs_root;
+
        if (block_only) {
+               if (!root) {
+                       fprintf(stderr, "unable to open %s\n", av[optind]);
+                       exit(1);
+               }
                leaf = read_tree_block(root,
                                      block_only,
                                      root->leafsize, 0);
@@ -184,25 +191,32 @@ int main(int ac, char **av)
        if (!extent_only) {
                if (roots_only) {
                        printf("root tree: %llu level %d\n",
-                            (unsigned long long)root->fs_info->tree_root->node->start,
-                            btrfs_header_level(root->fs_info->tree_root->node));
+                            (unsigned long long)info->tree_root->node->start,
+                            btrfs_header_level(info->tree_root->node));
                        printf("chunk tree: %llu level %d\n",
-                            (unsigned long long)root->fs_info->chunk_root->node->start,
-                            btrfs_header_level(root->fs_info->chunk_root->node));
+                            (unsigned long long)info->chunk_root->node->start,
+                            btrfs_header_level(info->chunk_root->node));
                } else {
-                       printf("root tree\n");
-                       btrfs_print_tree(root->fs_info->tree_root,
-                                        root->fs_info->tree_root->node, 1);
+                       if (info->tree_root->node) {
+                               printf("root tree\n");
+                               btrfs_print_tree(info->tree_root,
+                                                info->tree_root->node, 1);
+                       }
 
-                       printf("chunk tree\n");
-                       btrfs_print_tree(root->fs_info->chunk_root,
-                                        root->fs_info->chunk_root->node, 1);
+                       if (info->chunk_root->node) {
+                               printf("chunk tree\n");
+                               btrfs_print_tree(info->chunk_root,
+                                                info->chunk_root->node, 1);
+                       }
                }
        }
-       tree_root_scan = root->fs_info->tree_root;
+       tree_root_scan = info->tree_root;
 
        btrfs_init_path(&path);
 again:
+       if (!extent_buffer_uptodate(tree_root_scan->node))
+               goto no_node;
+
        key.offset = 0;
        key.objectid = 0;
        btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
@@ -232,6 +246,9 @@ again:
                                              btrfs_level_size(tree_root_scan,
                                                        btrfs_root_level(&ri)),
                                              0);
+                       if (!extent_buffer_uptodate(buf))
+                               goto next;
+
                        switch(found_key.objectid) {
                        case BTRFS_ROOT_TREE_OBJECTID:
                                if (!skip)
@@ -320,13 +337,15 @@ again:
                                }
                        }
                }
+next:
                path.slots[0]++;
        }
+no_node:
        btrfs_release_path(root, &path);
 
-       if (tree_root_scan == root->fs_info->tree_root &&
-           root->fs_info->log_root_tree) {
-               tree_root_scan = root->fs_info->log_root_tree;
+       if (tree_root_scan == info->tree_root &&
+           info->log_root_tree) {
+               tree_root_scan = info->log_root_tree;
                goto again;
        }
 
@@ -334,14 +353,14 @@ again:
                return 0;
 
        if (root_backups)
-               print_old_roots(&root->fs_info->super_copy);
+               print_old_roots(&info->super_copy);
 
        printf("total bytes %llu\n",
-              (unsigned long long)btrfs_super_total_bytes(&root->fs_info->super_copy));
+              (unsigned long long)btrfs_super_total_bytes(&info->super_copy));
        printf("bytes used %llu\n",
-              (unsigned long long)btrfs_super_bytes_used(&root->fs_info->super_copy));
+              (unsigned long long)btrfs_super_bytes_used(&info->super_copy));
        uuidbuf[36] = '\0';
-       uuid_unparse(root->fs_info->super_copy.fsid, uuidbuf);
+       uuid_unparse(info->super_copy.fsid, uuidbuf);
        printf("uuid %s\n", uuidbuf);
        printf("%s\n", BTRFS_BUILD_VERSION);
        return 0;
index 408b2d5..58aec02 100644 (file)
--- a/disk-io.c
+++ b/disk-io.c
@@ -444,7 +444,9 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
        generation = btrfs_root_generation(&root->root_item);
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
                                     blocksize, generation);
-       BUG_ON(!root->node);
+       if (!extent_buffer_uptodate(root->node))
+               return -EIO;
+
        return 0;
 }
 
@@ -471,7 +473,9 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
                                     btrfs_super_generation(disk_super) + 1);
 
        fs_info->log_root_tree = log_root;
-       BUG_ON(!log_root->node);
+
+       if (!extent_buffer_uptodate(log_root->node))
+               return -EIO;
        return 0;
 }
 
@@ -580,7 +584,7 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
                return fs_info->dev_root;
        if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
                return fs_info->csum_root;
-       
+
        BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
               location->offset != (u64)-1);
 
@@ -601,8 +605,10 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
        return root;
 }
 
-struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
-                                  u64 root_tree_bytenr, int writes)
+static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
+                                            u64 sb_bytenr,
+                                            u64 root_tree_bytenr, int writes,
+                                            int partial)
 {
        u32 sectorsize;
        u32 nodesize;
@@ -733,7 +739,7 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
        chunk_root->node = read_tree_block(chunk_root,
                                           btrfs_super_chunk_root(disk_super),
                                           blocksize, generation);
-       if (!chunk_root->node) {
+       if (!extent_buffer_uptodate(chunk_root->node)) {
                printk("Couldn't read chunk root\n");
                goto out_devices;
        }
@@ -745,7 +751,7 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
        if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) {
                ret = btrfs_read_chunk_tree(chunk_root);
                if (ret)
-                       goto out_chunk;
+                       goto out_failed;
        }
 
        blocksize = btrfs_level_size(tree_root,
@@ -757,15 +763,15 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
        tree_root->node = read_tree_block(tree_root,
                                          root_tree_bytenr,
                                          blocksize, generation);
-       if (!tree_root->node) {
+       if (!extent_buffer_uptodate(tree_root->node)) {
                printk("Couldn't read tree root\n");
-               goto out_chunk;
+               goto out_failed;
        }
        ret = find_and_setup_root(tree_root, fs_info,
                                  BTRFS_EXTENT_TREE_OBJECTID, extent_root);
        if (ret) {
                printk("Couldn't setup extent tree\n");
-               goto out_tree;
+               goto out_failed;
        }
        extent_root->track_dirty = 1;
 
@@ -773,7 +779,7 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
                                  BTRFS_DEV_TREE_OBJECTID, dev_root);
        if (ret) {
                printk("Couldn't setup device tree\n");
-               goto out_extent;
+               goto out_failed;
        }
        dev_root->track_dirty = 1;
 
@@ -781,7 +787,7 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
                                  BTRFS_CSUM_TREE_OBJECTID, csum_root);
        if (ret) {
                printk("Couldn't setup csum tree\n");
-               goto out_dev;
+               goto out_failed;
        }
        csum_root->track_dirty = 1;
 
@@ -797,23 +803,28 @@ struct btrfs_root *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
        fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
 
        if (!fs_info->fs_root)
-               goto out_csum;
+               goto out_failed;
 
        fs_info->data_alloc_profile = (u64)-1;
        fs_info->metadata_alloc_profile = (u64)-1;
        fs_info->system_alloc_profile = fs_info->metadata_alloc_profile;
 
-       return fs_info->fs_root;
-out_csum:
-       free_extent_buffer(fs_info->csum_root->node);
-out_dev:
-       free_extent_buffer(fs_info->dev_root->node);
-out_extent:
-       free_extent_buffer(fs_info->extent_root->node);
-out_tree:
-       free_extent_buffer(fs_info->tree_root->node);
-out_chunk:
-       free_extent_buffer(fs_info->chunk_root->node);
+       return fs_info;
+
+out_failed:
+       if (partial)
+               return fs_info;
+
+       if (fs_info->csum_root)
+               free_extent_buffer(fs_info->csum_root->node);
+       if (fs_info->dev_root)
+               free_extent_buffer(fs_info->dev_root->node);
+       if (fs_info->extent_root)
+               free_extent_buffer(fs_info->extent_root->node);
+       if (fs_info->tree_root)
+               free_extent_buffer(fs_info->tree_root->node);
+       if (fs_info->chunk_root)
+               free_extent_buffer(fs_info->chunk_root->node);
 out_devices:
        close_all_devices(fs_info);
 out_cleanup:
@@ -833,10 +844,12 @@ out:
        return NULL;
 }
 
-struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
+struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
+                                        u64 sb_bytenr, int writes,
+                                        int partial)
 {
        int fp;
-       struct btrfs_root *root;
+       struct btrfs_fs_info *info;
        int flags = O_CREAT | O_RDWR;
 
        if (!writes)
@@ -847,33 +860,50 @@ struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
                fprintf (stderr, "Could not open %s\n", filename);
                return NULL;
        }
-       root = __open_ctree_fd(fp, filename, sb_bytenr, 0, writes);
+       info = __open_ctree_fd(fp, filename, sb_bytenr, 0, writes, partial);
        close(fp);
+       return info;
+}
 
-       return root;
+struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
+{
+       struct btrfs_fs_info *info;
+
+       info = open_ctree_fs_info(filename, sb_bytenr, writes, 0);
+       if (!info)
+               return NULL;
+       return info->fs_root;
 }
 
 struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr,
                                       u64 root_tree_bytenr)
 {
        int fp;
-       struct btrfs_root *root;
+       struct btrfs_fs_info *info;
+
 
        fp = open(filename, O_RDONLY);
        if (fp < 0) {
                fprintf (stderr, "Could not open %s\n", filename);
                return NULL;
        }
-       root = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr, 0);
+       info = __open_ctree_fd(fp, filename, sb_bytenr,
+                              root_tree_bytenr, 0, 0);
        close(fp);
 
-       return root;
+       if (!info)
+               return NULL;
+       return info->fs_root;
 }
 
 struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
                                 int writes)
 {
-       return __open_ctree_fd(fp, path, sb_bytenr, 0, writes);
+       struct btrfs_fs_info *info;
+       info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0);
+       if (!info)
+               return NULL;
+       return info->fs_root;
 }
 
 int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr)
index 2048fcf..53e9b17 100644 (file)
--- a/disk-io.h
+++ b/disk-io.h
@@ -48,6 +48,9 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
                                 int writes);
 struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr,
                                       u64 root_tree_bytenr);
+struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
+                                        u64 sb_bytenr, int writes,
+                                        int partial);
 int close_ctree(struct btrfs_root *root);
 int write_all_supers(struct btrfs_root *root);
 int write_ctree_super(struct btrfs_trans_handle *trans,
index 5bed3c2..544ab2f 100644 (file)
@@ -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;
        }
@@ -1084,7 +1083,9 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
                ptr += sizeof(struct btrfs_tree_block_info);
                BUG_ON(ptr > end);
        } else {
-               BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
+               if (!(flags & BTRFS_EXTENT_FLAG_DATA)) {
+                       return -EIO;
+               }
        }
 
        err = -ENOENT;
@@ -1460,10 +1461,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 +1483,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;
@@ -1736,7 +1734,12 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        if (found) {
                found->total_bytes += total_bytes;
                found->bytes_used += bytes_used;
-               WARN_ON(found->total_bytes < found->bytes_used);
+               if (found->total_bytes < found->bytes_used) {
+                       fprintf(stderr, "warning, bad space info total_bytes "
+                               "%llu used %llu\n",
+                              (unsigned long long)found->total_bytes,
+                              (unsigned long long)found->bytes_used);
+               }
                *space_info = found;
                return 0;
        }
@@ -1855,6 +1858,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
 
                old_val = btrfs_block_group_used(&cache->item);
                num_bytes = min(total, cache->key.offset - byte_in_group);
+
                if (alloc) {
                        old_val += num_bytes;
                        cache->space_info->bytes_used += num_bytes;
@@ -2033,6 +2037,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
  */
@@ -2118,8 +2128,6 @@ static int __free_extent(struct btrfs_trans_handle *trans,
                        extent_slot = path->slots[0];
                }
        } else {
-               btrfs_print_leaf(extent_root, path->nodes[0]);
-               WARN_ON(1);
                printk(KERN_ERR "btrfs unable to find ref byte nr %llu "
                       "parent %llu root %llu  owner %llu offset %llu\n",
                       (unsigned long long)bytenr,
@@ -2127,6 +2135,8 @@ static int __free_extent(struct btrfs_trans_handle *trans,
                       (unsigned long long)root_objectid,
                       (unsigned long long)owner_objectid,
                       (unsigned long long)owner_offset);
+               ret = -EIO;
+               goto fail;
        }
 
        leaf = path->nodes[0];
@@ -2236,6 +2246,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
                                         mark_free);
                BUG_ON(ret);
        }
+fail:
        btrfs_free_path(path);
        finish_current_insert(trans, extent_root);
        return ret;
@@ -3269,3 +3280,143 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
        return update_block_group(trans, root, bytenr, num_bytes,
                                  alloc, mark_free);
 }
+
+static int btrfs_count_extents_in_block_group(struct btrfs_root *root,
+                                             struct btrfs_path *path, u64 start,
+                                             u64 len,
+                                             u64 *total)
+{
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       u64 bytes_used = 0;
+       int ret;
+       int slot;
+
+       key.offset = 0;
+       key.objectid = start;
+       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+       ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+                               &key, path, 0, 0);
+       if (ret < 0)
+               return ret;
+       while(1) {
+               leaf = path->nodes[0];
+               slot = path->slots[0];
+               if (slot >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret < 0)
+                               return ret;
+                       if (ret > 0)
+                               break;
+                       leaf = path->nodes[0];
+                       slot = path->slots[0];
+               }
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+               if (key.objectid > start + len)
+                       break;
+               if (key.type == BTRFS_EXTENT_ITEM_KEY)
+                       bytes_used += key.offset;
+               path->slots[0]++;
+       }
+       *total = bytes_used;
+       btrfs_release_path(root, path);
+       return 0;
+}
+
+int btrfs_check_block_accounting(struct btrfs_root *root)
+{
+       int ret;
+       u64 start = 0;
+       u64 bytes_used = 0;
+       struct btrfs_path path;
+       struct btrfs_block_group_cache *cache;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+
+       btrfs_init_path(&path);
+
+       while(1) {
+               cache = btrfs_lookup_block_group(fs_info, start);
+               if (!cache)
+                       break;
+
+               ret = btrfs_count_extents_in_block_group(root, &path,
+                                                        cache->key.objectid,
+                                                        cache->key.offset,
+                                                        &bytes_used);
+
+               if (ret == 0) {
+                       u64 on_disk = btrfs_block_group_used(&cache->item);
+                       if (on_disk != bytes_used) {
+                               fprintf(stderr, "bad block group accounting found %llu "
+                                       "expected %llu block group %llu\n",
+                                       (unsigned long long)bytes_used,
+                                       (unsigned long long)on_disk,
+                                       (unsigned long long)cache->key.objectid);
+                       }
+               }
+               start = cache->key.objectid + cache->key.offset;
+
+               cache->space_info->bytes_used = 0;
+       }
+       return 0;
+}
+
+/*
+ * Fixup block accounting. The initial block accounting created by
+ * make_block_groups isn't accuracy in this case.
+ */
+int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *root)
+{
+       int ret;
+       int slot;
+       u64 start = 0;
+       u64 bytes_used = 0;
+       struct btrfs_path path;
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       struct btrfs_block_group_cache *cache;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+
+       while(1) {
+               cache = btrfs_lookup_block_group(fs_info, start);
+               if (!cache)
+                       break;
+               start = cache->key.objectid + cache->key.offset;
+               btrfs_set_block_group_used(&cache->item, 0);
+               cache->space_info->bytes_used = 0;
+       }
+
+       btrfs_init_path(&path);
+       key.offset = 0;
+       key.objectid = 0;
+       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+       ret = btrfs_search_slot(trans, root->fs_info->extent_root,
+                               &key, &path, 0, 0);
+       if (ret < 0)
+               return ret;
+       while(1) {
+               leaf = path.nodes[0];
+               slot = path.slots[0];
+               if (slot >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, &path);
+                       if (ret < 0)
+                               return ret;
+                       if (ret > 0)
+                               break;
+                       leaf = path.nodes[0];
+                       slot = path.slots[0];
+               }
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+               if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+                       bytes_used += key.offset;
+                       ret = btrfs_update_block_group(trans, root,
+                                 key.objectid, key.offset, 1, 0);
+                       BUG_ON(ret);
+               }
+               path.slots[0]++;
+       }
+       btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used);
+       btrfs_release_path(root, &path);
+       return 0;
+}
index 973e918..ebb35b2 100644 (file)
@@ -28,7 +28,8 @@
 #include "extent_io.h"
 #include "list.h"
 
-u64 cache_max = 1024 * 1024 * 32;
+u64 cache_soft_max = 1024 * 1024 * 256;
+u64 cache_hard_max = 1 * 1024 * 1024 * 1024;
 
 void extent_io_tree_init(struct extent_io_tree *tree)
 {
@@ -540,18 +541,19 @@ static int free_some_buffers(struct extent_io_tree *tree)
        struct extent_buffer *eb;
        struct list_head *node, *next;
 
-       if (tree->cache_size < cache_max)
+       if (tree->cache_size < cache_soft_max)
                return 0;
+
        list_for_each_safe(node, next, &tree->lru) {
                eb = list_entry(node, struct extent_buffer, lru);
                if (eb->refs == 1) {
                        free_extent_buffer(eb);
-                       if (tree->cache_size < cache_max)
+                       if (tree->cache_size < cache_hard_max)
                                break;
                } else {
                        list_move_tail(&eb->lru, &tree->lru);
                }
-               if (nrscan++ > 64)
+               if (nrscan++ > 64 && tree->cache_size < cache_hard_max)
                        break;
        }
        return 0;
@@ -706,6 +708,9 @@ int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
 
 int extent_buffer_uptodate(struct extent_buffer *eb)
 {
+       if (!eb)
+               return 0;
+
        if (eb->flags & EXTENT_UPTODATE)
                return 1;
        return 0;
index 494ba8b..fc134c0 100644 (file)
@@ -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);
        }