From 337f86fea1d06e189f3cf234adba2567a07678bd Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 23 Oct 2013 16:24:03 -0400 Subject: Btrfs-progs: add -b to btrfsck to look at backup roots In some cases the tree root is so hosed we can't get anything useful out of it. So add the -b option to btrfsck to make us look for the most recent backup tree root to use for repair. Then we can hopefully get ourselves into a working state. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- btrfs-debug-tree.c | 2 +- cmds-check.c | 10 ++++++++-- cmds-restore.c | 2 +- disk-io.c | 54 ++++++++++++++++++++++++++++++++++++++++++++---------- disk-io.h | 5 +++-- 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c index 078dac5d..4a9d89de 100644 --- a/btrfs-debug-tree.c +++ b/btrfs-debug-tree.c @@ -171,7 +171,7 @@ int main(int ac, char **av) if (ac != 1) print_usage(); - info = open_ctree_fs_info(av[optind], 0, 0, 0, 1); + info = open_ctree_fs_info(av[optind], 0, 0, 0, 1, 0); if (!info) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); diff --git a/cmds-check.c b/cmds-check.c index 668af158..3453cac2 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -6022,6 +6022,7 @@ static struct option long_options[] = { { "repair", 0, NULL, 0 }, { "init-csum-tree", 0, NULL, 0 }, { "init-extent-tree", 0, NULL, 0 }, + { "backup", 0, NULL, 0 }, { NULL, 0, NULL, 0} }; @@ -6030,6 +6031,7 @@ const char * const cmd_check_usage[] = { "Check an unmounted btrfs filesystem.", "", "-s|--super use this superblock copy", + "-b|--backup use the backup root copy", "--repair try to repair the filesystem", "--init-csum-tree create a new CRC tree", "--init-extent-tree create a new extent tree", @@ -6043,6 +6045,7 @@ int cmd_check(int argc, char **argv) struct btrfs_fs_info *info; u64 bytenr = 0; char uuidbuf[37]; + int backup_root = 0; int ret; int num; int option_index = 0; @@ -6052,12 +6055,15 @@ int cmd_check(int argc, char **argv) while(1) { int c; - c = getopt_long(argc, argv, "as:", long_options, + c = getopt_long(argc, argv, "as:b", long_options, &option_index); if (c < 0) break; switch(c) { case 'a': /* ignored */ break; + case 'b': + backup_root = 1; + break; case 's': num = atol(optarg); bytenr = btrfs_sb_offset(num); @@ -6099,7 +6105,7 @@ int cmd_check(int argc, char **argv) return -EBUSY; } - info = open_ctree_fs_info(argv[optind], bytenr, 0, rw, 1); + info = open_ctree_fs_info(argv[optind], bytenr, 0, rw, 1, backup_root); if (!info) { fprintf(stderr, "Couldn't open file system\n"); return -EIO; diff --git a/cmds-restore.c b/cmds-restore.c index ae0527b6..e315d2e6 100644 --- a/cmds-restore.c +++ b/cmds-restore.c @@ -974,7 +974,7 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location, for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) { bytenr = btrfs_sb_offset(i); - fs_info = open_ctree_fs_info(dev, bytenr, root_location, 0, 1); + fs_info = open_ctree_fs_info(dev, bytenr, root_location, 0, 1, 0); if (fs_info) break; fprintf(stderr, "Could not open root, trying backup super\n"); diff --git a/disk-io.c b/disk-io.c index ca76c425..733714df 100644 --- a/disk-io.c +++ b/disk-io.c @@ -808,8 +808,27 @@ int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable) return 0; } +static int find_best_backup_root(struct btrfs_super_block *super) +{ + struct btrfs_root_backup *backup; + u64 orig_gen = btrfs_super_generation(super); + u64 gen = 0; + int best_index = 0; + int i; + + for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) { + backup = super->super_roots + i; + if (btrfs_backup_tree_root_gen(backup) != orig_gen && + btrfs_backup_tree_root_gen(backup) > gen) { + best_index = i; + gen = btrfs_backup_tree_root_gen(backup); + } + } + return best_index; +} + int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, - u64 root_tree_bytenr, int partial) + u64 root_tree_bytenr, int partial, int backup_root) { struct btrfs_super_block *sb = fs_info->super_copy; struct btrfs_root *root; @@ -833,8 +852,20 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, blocksize = btrfs_level_size(root, btrfs_super_root_level(sb)); generation = btrfs_super_generation(sb); - if (!root_tree_bytenr) + if (!root_tree_bytenr && !backup_root) { root_tree_bytenr = btrfs_super_root(sb); + } else if (backup_root) { + struct btrfs_root_backup *backup; + int index = find_best_backup_root(sb); + if (index >= BTRFS_NUM_BACKUP_ROOTS) { + fprintf(stderr, "Invalid backup root number\n"); + return -EIO; + } + backup = fs_info->super_copy->super_roots + index; + root_tree_bytenr = btrfs_backup_tree_root(backup); + generation = btrfs_backup_tree_root_gen(backup); + } + root->node = read_tree_block(root, root_tree_bytenr, blocksize, generation); if (!extent_buffer_uptodate(root->node)) { @@ -1005,7 +1036,8 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, u64 root_tree_bytenr, int writes, int partial, int restore, - int recover_super) + int recover_super, + int backup_root) { struct btrfs_fs_info *fs_info; struct btrfs_super_block *disk_super; @@ -1068,7 +1100,8 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path, (unsigned long)btrfs_header_chunk_tree_uuid(eb), BTRFS_UUID_SIZE); - ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, partial); + ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, partial, + backup_root); if (ret) goto out_failed; @@ -1105,14 +1138,15 @@ struct btrfs_fs_info *open_ctree_fs_info_restore(const char *filename, return NULL; } info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr, - writes, partial, restore, 0); + writes, partial, restore, 0, 0); close(fp); return info; } struct btrfs_fs_info *open_ctree_fs_info(const char *filename, u64 sb_bytenr, u64 root_tree_bytenr, - int writes, int partial) + int writes, int partial, + int backup_root) { int fp; struct btrfs_fs_info *info; @@ -1127,7 +1161,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename, return NULL; } info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr, - writes, partial, 0, 0); + writes, partial, 0, 0, backup_root); close(fp); return info; } @@ -1148,7 +1182,7 @@ struct btrfs_root *open_ctree_with_broken_super(const char *filename, return NULL; } info = __open_ctree_fd(fp, filename, sb_bytenr, 0, - writes, 0, 0, 1); + writes, 0, 0, 1, 0); close(fp); if (info) return info->fs_root; @@ -1159,7 +1193,7 @@ 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, 0, writes, 0); + info = open_ctree_fs_info(filename, sb_bytenr, 0, writes, 0, 0); if (!info) return NULL; return info->fs_root; @@ -1169,7 +1203,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr, int writes) { struct btrfs_fs_info *info; - info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0, 0, 0); + info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0, 0, 0, 0); if (!info) return NULL; return info->fs_root; diff --git a/disk-io.h b/disk-io.h index 6f2d4f41..b0292db2 100644 --- a/disk-io.h +++ b/disk-io.h @@ -53,7 +53,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info); struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr); int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable); int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, - u64 root_tree_bytenr, int partial); + u64 root_tree_bytenr, int partial, int backup_root); void btrfs_release_all_roots(struct btrfs_fs_info *fs_info); void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info); int btrfs_scan_fs_devices(int fd, const char *path, @@ -69,7 +69,8 @@ struct btrfs_fs_info *open_ctree_fs_info_restore(const char *filename, int writes, int partial); struct btrfs_fs_info *open_ctree_fs_info(const char *filename, u64 sb_bytenr, u64 root_tree_bytenr, - int writes, int partial); + int writes, int partial, + int backup_root); struct btrfs_root *open_ctree_with_broken_super(const char *filename, u64 sb_bytenr, int writes); int close_ctree(struct btrfs_root *root); -- cgit v1.2.3