#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)
{
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);
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;
print_usage();
}
break;
+ case 'e':
+ extent_rec = 1;
+ break;
+ case 'E':
+ extent_tree = 1;
+ break;
default:
print_usage();
}
ac = ac - optind;
if (ac == 0)
print_usage();
- if (logical == 0)
+ if (logical == 0 && !extent_tree)
print_usage();
if (copy < 0)
print_usage();
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)
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;
}
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <getopt.h>
#include "kerncompat.h"
#include "ctree.h"
#include "disk-io.h"
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;
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);
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,
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;
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",
(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) {
{
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 {
ref->node.full_backref = 0;
}
list_add_tail(&ref->node.list, &rec->backrefs);
+
return ref;
}
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)
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;
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;
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;
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();
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;
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();
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);
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;
}
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)
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;
}
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;
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) {
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)
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);
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(©_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, 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;
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;
}
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;
if (ret != 0)
break;
}
- ret = check_extent_refs(root, &extent_cache);
+ ret = check_extent_refs(trans, root, &extent_cache, repair);
return ret;
}
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) {
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;
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) {
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;
}
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;
}
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);
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;
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;
}
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,
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;
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;
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;
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:
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)
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)
}
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;
}
}
#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;
}
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;
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];
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;
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;
}
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;
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
*/
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,
(unsigned long long)root_objectid,
(unsigned long long)owner_objectid,
(unsigned long long)owner_offset);
+ ret = -EIO;
+ goto fail;
}
leaf = path->nodes[0];
mark_free);
BUG_ON(ret);
}
+fail:
btrfs_free_path(path);
finish_current_insert(trans, extent_root);
return ret;
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;
+}