diff mbox

Btrfs-progs: add -b to btrfsck to look at backup roots

Message ID 1382559843-13624-1-git-send-email-jbacik@fusionio.com (mailing list archive)
State Accepted, archived
Headers show

Commit Message

Josef Bacik Oct. 23, 2013, 8:24 p.m. UTC
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 <jbacik@fusionio.com>
---
 btrfs-debug-tree.c |  2 +-
 cmds-check.c       | 10 ++++++++--
 disk-io.c          | 54 ++++++++++++++++++++++++++++++++++++++++++++----------
 disk-io.h          |  5 +++--
 4 files changed, 56 insertions(+), 15 deletions(-)
diff mbox

Patch

diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 078dac5..4a9d89d 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 a6047ea..69b0327 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -6024,6 +6024,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}
 };
 
@@ -6032,6 +6033,7 @@  const char * const cmd_check_usage[] = {
 	"Check an unmounted btrfs filesystem.",
 	"",
 	"-s|--super <superblock>     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",
@@ -6045,6 +6047,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;
@@ -6054,12 +6057,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);
@@ -6101,7 +6107,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/disk-io.c b/disk-io.c
index ca76c42..733714d 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 6f2d4f4..b0292db 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);