Btrfs-progs: record generation for tree blocks in fsck

When working with a user who had a broken file system I noticed that we were
reading a bad copy of a block when the other copy was perfectly fine.  This is
because we don't keep track of the parent generation for tree blocks, so we just
read whichever copy we damned well please with no regards for which is best.
This fixes this problem by recording the parent generation of the tree block so
we can be sure to read the most correct copy before we check it, which will give
us a better chance of fixing really broken filesystems.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <clm@fb.com>
master
Josef Bacik 2014-02-27 10:18:41 -05:00 committed by Chris Mason
parent d98a5ffe6f
commit 47c18d17c5
1 changed files with 25 additions and 7 deletions

View File

@ -98,6 +98,7 @@ struct extent_record {
u64 refs;
u64 extent_item_refs;
u64 generation;
u64 parent_generation;
u64 info_objectid;
u64 num_duplicates;
u8 info_level;
@ -2646,7 +2647,7 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec,
}
static int add_extent_rec(struct cache_tree *extent_cache,
struct btrfs_key *parent_key,
struct btrfs_key *parent_key, u64 parent_gen,
u64 start, u64 nr, u64 extent_item_refs,
int is_root, int inc_ref, int set_checked,
int metadata, int extent_rec, u64 max_size)
@ -2722,6 +2723,8 @@ static int add_extent_rec(struct cache_tree *extent_cache,
if (parent_key)
btrfs_cpu_key_to_disk(&rec->parent_key, parent_key);
if (parent_gen)
rec->parent_generation = parent_gen;
if (rec->max_size < max_size)
rec->max_size = max_size;
@ -2762,6 +2765,11 @@ static int add_extent_rec(struct cache_tree *extent_cache,
else
memset(&rec->parent_key, 0, sizeof(*parent_key));
if (parent_gen)
rec->parent_generation = parent_gen;
else
rec->parent_generation = 0;
rec->cache.start = start;
rec->cache.size = nr;
ret = insert_cache_extent(extent_cache, &rec->cache);
@ -2783,7 +2791,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache) {
add_extent_rec(extent_cache, NULL, bytenr,
add_extent_rec(extent_cache, NULL, 0, bytenr,
1, 0, 0, 0, 0, 1, 0, 0);
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
@ -2831,7 +2839,7 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
cache = lookup_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, 0, bytenr, 1, 0, 0, 0, 0,
0, 0, max_size);
cache = lookup_cache_extent(extent_cache, bytenr, 1);
if (!cache)
@ -3318,7 +3326,7 @@ static int process_extent_item(struct btrfs_root *root,
#else
BUG();
#endif
return add_extent_rec(extent_cache, NULL, key.objectid,
return add_extent_rec(extent_cache, NULL, 0, key.objectid,
num_bytes, refs, 0, 0, 0, metadata, 1,
num_bytes);
}
@ -3326,7 +3334,7 @@ static int process_extent_item(struct btrfs_root *root,
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
refs = btrfs_extent_refs(eb, ei);
add_extent_rec(extent_cache, NULL, key.objectid, num_bytes,
add_extent_rec(extent_cache, NULL, 0, key.objectid, num_bytes,
refs, 0, 0, 0, metadata, 1, num_bytes);
ptr = (unsigned long)(ei + 1);
@ -3839,6 +3847,7 @@ static int run_next_block(struct btrfs_trans_handle *trans,
u64 owner;
u64 flags;
u64 ptr;
u64 gen = 0;
int ret = 0;
int i;
int nritems;
@ -3888,8 +3897,16 @@ static int run_next_block(struct btrfs_trans_handle *trans,
free(cache);
}
cache = lookup_cache_extent(extent_cache, bytenr, size);
if (cache) {
struct extent_record *rec;
rec = container_of(cache, struct extent_record, cache);
gen = rec->parent_generation;
}
/* fixme, get the real parent transid */
buf = read_tree_block(root, bytenr, size, 0);
buf = read_tree_block(root, bytenr, size, gen);
if (!extent_buffer_uptodate(buf)) {
record_bad_block_io(root->fs_info,
extent_cache, bytenr, size);
@ -4061,6 +4078,7 @@ static int run_next_block(struct btrfs_trans_handle *trans,
}
}
ret = add_extent_rec(extent_cache, &key,
btrfs_node_ptr_generation(buf, i),
ptr, size, 0, 0, 1, 0, 1, 0,
size);
BUG_ON(ret);
@ -4102,7 +4120,7 @@ static int add_root_to_pending(struct extent_buffer *buf,
add_pending(nodes, seen, buf->start, buf->len);
else
add_pending(pending, seen, buf->start, buf->len);
add_extent_rec(extent_cache, NULL, buf->start, buf->len,
add_extent_rec(extent_cache, NULL, 0, buf->start, buf->len,
0, 1, 1, 0, 1, 0, buf->len);
if (root_key->objectid == BTRFS_TREE_RELOC_OBJECTID ||