Btrfs-progs: add an option to btrfs-image to walk the trees

When working with a user with a broken file system I noticed I wasn't able to
read some of the blocks properly from the restored image.  This is because his
extent tree was corrupt and was missing references to some of the blocks, which
means they weren't copied into the image when he generated it.  So add a -w
option which will walk all of the trees manually and copy them into the image.
This way we can run fsck against a complete file system image and fix any bugs
in fsck.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
Josef Bacik 2013-03-28 10:26:09 -04:00 committed by David Sterba
parent 1997b9dc79
commit 1ed8193d19
2 changed files with 138 additions and 100 deletions

View File

@ -605,9 +605,8 @@ static int is_tree_block(struct btrfs_root *extent_root,
} }
#endif #endif
static int copy_log_blocks(struct btrfs_root *root, struct extent_buffer *eb, static int copy_tree_blocks(struct btrfs_root *root, struct extent_buffer *eb,
struct metadump_struct *metadump, struct metadump_struct *metadump, int root_tree)
int log_root_tree)
{ {
struct extent_buffer *tmp; struct extent_buffer *tmp;
struct btrfs_root_item *ri; struct btrfs_root_item *ri;
@ -624,7 +623,7 @@ static int copy_log_blocks(struct btrfs_root *root, struct extent_buffer *eb,
return ret; return ret;
} }
if (btrfs_header_level(eb) == 0 && !log_root_tree) if (btrfs_header_level(eb) == 0 && !root_tree)
return 0; return 0;
level = btrfs_header_level(eb); level = btrfs_header_level(eb);
@ -642,7 +641,7 @@ static int copy_log_blocks(struct btrfs_root *root, struct extent_buffer *eb,
"Error reading log root block\n"); "Error reading log root block\n");
return -EIO; return -EIO;
} }
ret = copy_log_blocks(root, tmp, metadump, 0); ret = copy_tree_blocks(root, tmp, metadump, 0);
free_extent_buffer(tmp); free_extent_buffer(tmp);
if (ret) if (ret)
return ret; return ret;
@ -653,8 +652,7 @@ static int copy_log_blocks(struct btrfs_root *root, struct extent_buffer *eb,
fprintf(stderr, "Error reading log block\n"); fprintf(stderr, "Error reading log block\n");
return -EIO; return -EIO;
} }
ret = copy_log_blocks(root, tmp, metadump, ret = copy_tree_blocks(root, tmp, metadump, root_tree);
log_root_tree);
free_extent_buffer(tmp); free_extent_buffer(tmp);
if (ret) if (ret)
return ret; return ret;
@ -679,7 +677,7 @@ static int copy_log_trees(struct btrfs_root *root,
return -EIO; return -EIO;
} }
return copy_log_blocks(root, root->fs_info->log_root_tree->node, return copy_tree_blocks(root, root->fs_info->log_root_tree->node,
metadump, 1); metadump, 1);
} }
@ -749,19 +747,114 @@ static int copy_space_cache(struct btrfs_root *root,
return 0; return 0;
} }
static int create_metadump(const char *input, FILE *out, int num_threads, static int copy_from_extent_tree(struct metadump_struct *metadump,
int compress_level) struct btrfs_path *path)
{ {
struct btrfs_root *root;
struct btrfs_root *extent_root; struct btrfs_root *extent_root;
struct btrfs_path *path = NULL;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_extent_item *ei; struct btrfs_extent_item *ei;
struct btrfs_key key; struct btrfs_key key;
struct metadump_struct metadump;
u64 bytenr; u64 bytenr;
u64 num_bytes; u64 num_bytes;
int ret; int ret;
extent_root = metadump->root->fs_info->extent_root;
bytenr = BTRFS_SUPER_INFO_OFFSET + 4096;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
if (ret < 0) {
fprintf(stderr, "Error searching extent root %d\n", ret);
return ret;
}
ret = 0;
while (1) {
leaf = path->nodes[0];
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(extent_root, path);
if (ret < 0) {
fprintf(stderr, "Error going to next leaf %d"
"\n", ret);
break;
}
if (ret > 0) {
ret = 0;
break;
}
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.objectid < bytenr ||
(key.type != BTRFS_EXTENT_ITEM_KEY &&
key.type != BTRFS_METADATA_ITEM_KEY)) {
path->slots[0]++;
continue;
}
bytenr = key.objectid;
if (key.type == BTRFS_METADATA_ITEM_KEY)
num_bytes = key.offset;
else
num_bytes = extent_root->leafsize;
if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) {
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item);
if (btrfs_extent_flags(leaf, ei) &
BTRFS_EXTENT_FLAG_TREE_BLOCK) {
ret = add_extent(bytenr, num_bytes, metadump,
0);
if (ret) {
fprintf(stderr, "Error adding block "
"%d\n", ret);
break;
}
}
} else {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
ret = is_tree_block(extent_root, path, bytenr);
if (ret < 0) {
fprintf(stderr, "Error checking tree block "
"%d\n", ret);
break;
}
if (ret) {
ret = add_extent(bytenr, num_bytes, metadump,
0);
if (ret) {
fprintf(stderr, "Error adding block "
"%d\n", ret);
break;
}
}
ret = 0;
#else
fprintf(stderr, "Either extent tree corruption or "
"you haven't built with V0 support\n");
ret = -EIO;
break;
#endif
}
bytenr += num_bytes;
}
btrfs_release_path(extent_root, path);
return ret;
}
static int create_metadump(const char *input, FILE *out, int num_threads,
int compress_level, int walk_trees)
{
struct btrfs_root *root;
struct btrfs_path *path = NULL;
struct metadump_struct metadump;
int ret;
int err = 0; int err = 0;
root = open_ctree(input, 0, 0); root = open_ctree(input, 0, 0);
@ -787,99 +880,34 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
goto out; goto out;
} }
extent_root = root->fs_info->extent_root;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) { if (!path) {
fprintf(stderr, "Out of memory allocing path\n"); fprintf(stderr, "Out of memory allocing path\n");
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
bytenr = BTRFS_SUPER_INFO_OFFSET + 4096;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); if (walk_trees) {
if (ret < 0) { ret = copy_tree_blocks(root, root->fs_info->chunk_root->node,
fprintf(stderr, "Error searching extent root %d\n", ret); &metadump, 1);
err = ret;
goto out;
}
while (1) {
leaf = path->nodes[0];
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(extent_root, path);
if (ret < 0) {
fprintf(stderr, "Error going to next leaf %d"
"\n", ret);
err = ret;
goto out;
}
if (ret > 0)
break;
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.objectid < bytenr ||
(key.type != BTRFS_EXTENT_ITEM_KEY &&
key.type != BTRFS_METADATA_ITEM_KEY)) {
path->slots[0]++;
continue;
}
bytenr = key.objectid;
if (key.type == BTRFS_METADATA_ITEM_KEY)
num_bytes = key.offset;
else
num_bytes = root->leafsize;
if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) {
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item);
if (btrfs_extent_flags(leaf, ei) &
BTRFS_EXTENT_FLAG_TREE_BLOCK) {
ret = add_extent(bytenr, num_bytes, &metadump,
0);
if (ret) { if (ret) {
fprintf(stderr, "Error adding block "
"%d\n", ret);
err = ret; err = ret;
goto out; goto out;
} }
ret = copy_tree_blocks(root, root->fs_info->tree_root->node,
&metadump, 1);
if (ret) {
err = ret;
goto out;
} }
} else { } else {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 ret = copy_from_extent_tree(&metadump, path);
ret = is_tree_block(extent_root, path, bytenr);
if (ret < 0) {
fprintf(stderr, "Error checking tree block "
"%d\n", ret);
err = ret;
goto out;
}
if (ret) { if (ret) {
ret = add_extent(bytenr, num_bytes, &metadump,
0);
if (ret) {
fprintf(stderr, "Error adding block "
"%d\n", ret);
err = ret; err = ret;
goto out; goto out;
} }
} }
#else
fprintf(stderr, "Either extent tree corruption or "
"you haven't built with V0 support\n");
err = -EIO;
goto out;
#endif
}
bytenr += num_bytes;
}
btrfs_release_path(root, path);
ret = copy_log_trees(root, &metadump, path); ret = copy_log_trees(root, &metadump, path);
if (ret) { if (ret) {
@ -892,7 +920,7 @@ out:
ret = flush_pending(&metadump, 1); ret = flush_pending(&metadump, 1);
if (ret) { if (ret) {
if (!err) if (!err)
ret = err; err = ret;
fprintf(stderr, "Error flushing pending %d\n", ret); fprintf(stderr, "Error flushing pending %d\n", ret);
} }
@ -1498,6 +1526,7 @@ static void print_usage(void)
fprintf(stderr, "\t-c value\tcompression level (0 ~ 9)\n"); fprintf(stderr, "\t-c value\tcompression level (0 ~ 9)\n");
fprintf(stderr, "\t-t value\tnumber of threads (1 ~ 32)\n"); fprintf(stderr, "\t-t value\tnumber of threads (1 ~ 32)\n");
fprintf(stderr, "\t-o \tdon't mess with the chunk tree when restoring\n"); fprintf(stderr, "\t-o \tdon't mess with the chunk tree when restoring\n");
fprintf(stderr, "\t-w \twalk all trees instead of using extent tree, do this if your extent tree is broken\n");
exit(1); exit(1);
} }
@ -1509,11 +1538,12 @@ int main(int argc, char *argv[])
int compress_level = 0; int compress_level = 0;
int create = 1; int create = 1;
int old_restore = 0; int old_restore = 0;
int walk_trees = 0;
int ret; int ret;
FILE *out; FILE *out;
while (1) { while (1) {
int c = getopt(argc, argv, "rc:t:o"); int c = getopt(argc, argv, "rc:t:ow");
if (c < 0) if (c < 0)
break; break;
switch (c) { switch (c) {
@ -1533,6 +1563,9 @@ int main(int argc, char *argv[])
case 'o': case 'o':
old_restore = 1; old_restore = 1;
break; break;
case 'w':
walk_trees = 1;
break;
default: default:
print_usage(); print_usage();
} }
@ -1565,7 +1598,7 @@ int main(int argc, char *argv[])
if (create) if (create)
ret = create_metadump(source, out, num_threads, ret = create_metadump(source, out, num_threads,
compress_level); compress_level, walk_trees);
else else
ret = restore_metadump(source, out, old_restore, 1); ret = restore_metadump(source, out, old_restore, 1);

View File

@ -28,6 +28,11 @@ number of threads (1 ~ 32) to be used to process the image dump or restore.
\fB\-o\fP \fB\-o\fP
use the old restore method, this does not fixup the chunk tree so the restored use the old restore method, this does not fixup the chunk tree so the restored
file system will not be able to be mounted. file system will not be able to be mounted.
.TP
\fB\-w\fP
Walk all the trees manually and copy any blocks that are referenced. Use this
option if your extent tree is corrupted to make sure that all of the metadata is
captured.
.SH AVAILABILITY .SH AVAILABILITY
.B btrfs-image .B btrfs-image
is part of btrfs-progs. Btrfs is currently under heavy development, is part of btrfs-progs. Btrfs is currently under heavy development,