From ec8ea26b714a20c38694a3e87b392424ce34d962 Mon Sep 17 00:00:00 2001
From: Josef Bacik <josef@redhat.com>
Date: Wed, 7 Dec 2011 14:13:35 -0500
Subject: [PATCH 19/46] Btrfs-progs: fix restore to fall back to the broken
 open_ctree

We don't need most of the roots when doing restore, like the extent tree.  So if
the recovery open_ctree fails because it's trying to open one of these useless
roots just fall back to open_ctree_broken.  This will just open the chunk root
which is the bare minimum of what we need to operate.  Then from there we can
setup the tree_root and the fs_root, and if either of those are gone we can't
restore anyway.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
---
 disk-io.c   | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 disk-io.h   |   1 +
 find-root.c | 166 ------------------------------------------------------------
 restore.c   |  48 +++++++++++++++++-
 4 files changed, 198 insertions(+), 167 deletions(-)

diff --git a/cmds-restore.c b/cmds-restore.c
index 3df174c..dee6d22 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -778,9 +778,13 @@ next:
 
 static struct btrfs_root *open_fs(const char *dev, u64 root_location, int super_mirror)
 {
+	struct btrfs_key key;
 	struct btrfs_root *root;
 	u64 bytenr;
+	u64 generation;
+	u32 blocksize;
 	int i;
+	int dev_fd;
 
 	for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) {
 		bytenr = btrfs_sb_offset(i);
@@ -790,7 +794,49 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location, int super_
 		fprintf(stderr, "Could not open root, trying backup super\n");
 	}
 
-	return NULL;
+	fprintf(stderr, "Ok couldn't open the root the normal way, trying "
+		"the broken way\n");
+
+	dev_fd = open(dev, O_RDONLY);
+	if (dev_fd < 0) {
+		fprintf(stderr, "Failed to open device %s\n", dev);
+		return NULL;
+	}
+
+	root = open_ctree_broken(dev_fd, dev);
+	close(dev_fd);
+	if (!root) {
+		fprintf(stderr, "Broken ctree open failed\n");
+		return NULL;
+	}
+	if (!root_location)
+		bytenr = btrfs_super_root(&root->fs_info->super_copy);
+
+	blocksize = btrfs_level_size(root,
+			btrfs_super_root_level(&root->fs_info->super_copy));
+	generation = btrfs_super_generation(&root->fs_info->super_copy);
+
+	root->fs_info->tree_root->node = read_tree_block(root, bytenr,
+							 blocksize,
+							 generation);
+	if (!root->fs_info->tree_root->node) {
+		fprintf(stderr, "Couldn't read tree node\n");
+		close_ctree(root);
+		return NULL;
+	}
+
+	key.objectid = BTRFS_FS_TREE_OBJECTID;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = (u64)-1;
+
+	root->fs_info->fs_root = btrfs_read_fs_root(root->fs_info, &key);
+	if (!root->fs_info->fs_root) {
+		fprintf(stderr, "Couldn't read fs_root\n");
+		close_ctree(root);
+		return NULL;
+	}
+
+	return root->fs_info->fs_root;
 }
 
 static int find_first_dir(struct btrfs_root *root, u64 *objectid)
diff --git a/disk-io.c b/disk-io.c
index 2180d69..eb4d6c6 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -1101,6 +1101,156 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
 	return info->fs_root;
 }
 
+struct btrfs_root *open_ctree_broken(int fd, const char *device)
+{
+	u32 sectorsize;
+	u32 nodesize;
+	u32 leafsize;
+	u32 blocksize;
+	u32 stripesize;
+	u64 generation;
+	struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
+	int ret;
+	struct btrfs_super_block *disk_super;
+	struct btrfs_fs_devices *fs_devices = NULL;
+	u64 total_devs;
+	u64 features;
+
+	ret = btrfs_scan_one_device(fd, device, &fs_devices,
+				    &total_devs, BTRFS_SUPER_INFO_OFFSET);
+
+	if (ret) {
+		fprintf(stderr, "No valid Btrfs found on %s\n", device);
+		goto out;
+	}
+
+	if (total_devs != 1) {
+		ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1);
+		if (ret)
+			goto out;
+	}
+
+	memset(fs_info, 0, sizeof(*fs_info));
+	fs_info->tree_root = tree_root;
+	fs_info->extent_root = extent_root;
+	fs_info->chunk_root = chunk_root;
+	fs_info->dev_root = dev_root;
+	fs_info->csum_root = csum_root;
+
+	fs_info->readonly = 1;
+
+	extent_io_tree_init(&fs_info->extent_cache);
+	extent_io_tree_init(&fs_info->free_space_cache);
+	extent_io_tree_init(&fs_info->block_group_cache);
+	extent_io_tree_init(&fs_info->pinned_extents);
+	extent_io_tree_init(&fs_info->pending_del);
+	extent_io_tree_init(&fs_info->extent_ins);
+	cache_tree_init(&fs_info->fs_root_cache);
+
+	cache_tree_init(&fs_info->mapping_tree.cache_tree);
+
+	mutex_init(&fs_info->fs_mutex);
+	fs_info->fs_devices = fs_devices;
+	INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
+	INIT_LIST_HEAD(&fs_info->space_info);
+
+	__setup_root(4096, 4096, 4096, 4096, tree_root,
+		     fs_info, BTRFS_ROOT_TREE_OBJECTID);
+
+	ret = btrfs_open_devices(fs_devices, O_RDONLY);
+	if (ret)
+		goto out_cleanup;
+
+	fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
+	disk_super = &fs_info->super_copy;
+	ret = btrfs_read_dev_super(fs_devices->latest_bdev,
+				   disk_super, BTRFS_SUPER_INFO_OFFSET);
+	if (ret) {
+		printk("No valid btrfs found\n");
+		goto out_devices;
+	}
+
+	memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
+
+
+	features = btrfs_super_incompat_flags(disk_super) &
+		   ~BTRFS_FEATURE_INCOMPAT_SUPP;
+	if (features) {
+		printk("couldn't open because of unsupported "
+		       "option features (%Lx).\n", features);
+		goto out_devices;
+	}
+
+	features = btrfs_super_incompat_flags(disk_super);
+	if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
+		features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
+		btrfs_set_super_incompat_flags(disk_super, features);
+	}
+
+	nodesize = btrfs_super_nodesize(disk_super);
+	leafsize = btrfs_super_leafsize(disk_super);
+	sectorsize = btrfs_super_sectorsize(disk_super);
+	stripesize = btrfs_super_stripesize(disk_super);
+	tree_root->nodesize = nodesize;
+	tree_root->leafsize = leafsize;
+	tree_root->sectorsize = sectorsize;
+	tree_root->stripesize = stripesize;
+
+	ret = btrfs_read_sys_array(tree_root);
+	if (ret)
+		goto out_devices;
+	blocksize = btrfs_level_size(tree_root,
+				     btrfs_super_chunk_root_level(disk_super));
+	generation = btrfs_super_chunk_root_generation(disk_super);
+
+	__setup_root(nodesize, leafsize, sectorsize, stripesize,
+		     chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
+
+	chunk_root->node = read_tree_block(chunk_root,
+					   btrfs_super_chunk_root(disk_super),
+					   blocksize, generation);
+	if (!chunk_root->node) {
+		printk("Couldn't read chunk root\n");
+		goto out_devices;
+	}
+
+	read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid,
+	         (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node),
+		 BTRFS_UUID_SIZE);
+
+	if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) {
+		ret = btrfs_read_chunk_tree(chunk_root);
+		if (ret)
+			goto out_chunk;
+	}
+
+	return fs_info->chunk_root;
+out_chunk:
+	free_extent_buffer(fs_info->chunk_root->node);
+out_devices:
+	close_all_devices(fs_info);
+out_cleanup:
+	extent_io_tree_cleanup(&fs_info->extent_cache);
+	extent_io_tree_cleanup(&fs_info->free_space_cache);
+	extent_io_tree_cleanup(&fs_info->block_group_cache);
+	extent_io_tree_cleanup(&fs_info->pinned_extents);
+	extent_io_tree_cleanup(&fs_info->pending_del);
+	extent_io_tree_cleanup(&fs_info->extent_ins);
+out:
+	free(tree_root);
+	free(extent_root);
+	free(chunk_root);
+	free(dev_root);
+	free(csum_root);
+	free(fs_info);
+	return NULL;
+}
+
 int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr)
 {
 	u8 fsid[BTRFS_FSID_SIZE];
diff --git a/disk-io.h b/disk-io.h
index 28573a9..b3fc64e 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -55,6 +55,7 @@ struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr,
 struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
 					 u64 sb_bytenr, int writes,
 					 int partial);
+struct btrfs_root *open_ctree_broken(int fd, const char *device);
 int close_ctree(struct btrfs_root *root);
 int write_all_supers(struct btrfs_root *root);
 int write_ctree_super(struct btrfs_trans_handle *trans,
diff --git a/find-root.c b/find-root.c
index e81710e..faa19eb 100644
--- a/find-root.c
+++ b/find-root.c
@@ -65,172 +65,6 @@ int csum_block(void *buf, u32 len)
 	return ret;
 }
 
-static int close_all_devices(struct btrfs_fs_info *fs_info)
-{
-	struct list_head *list;
-	struct list_head *next;
-	struct btrfs_device *device;
-
-	return 0;
-
-	list = &fs_info->fs_devices->devices;
-	list_for_each(next, list) {
-		device = list_entry(next, struct btrfs_device, dev_list);
-		close(device->fd);
-	}
-	return 0;
-}
-
-static struct btrfs_root *open_ctree_broken(int fd, const char *device)
-{
-	u32 sectorsize;
-	u32 nodesize;
-	u32 leafsize;
-	u32 blocksize;
-	u32 stripesize;
-	u64 generation;
-	struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
-	struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
-	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
-	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
-	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
-	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
-	int ret;
-	struct btrfs_super_block *disk_super;
-	struct btrfs_fs_devices *fs_devices = NULL;
-	u64 total_devs;
-	u64 features;
-
-	ret = btrfs_scan_one_device(fd, device, &fs_devices,
-				    &total_devs, BTRFS_SUPER_INFO_OFFSET);
-
-	if (ret) {
-		fprintf(stderr, "No valid Btrfs found on %s\n", device);
-		goto out;
-	}
-
-	if (total_devs != 1) {
-		ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1);
-		if (ret)
-			goto out;
-	}
-
-	memset(fs_info, 0, sizeof(*fs_info));
-	fs_info->tree_root = tree_root;
-	fs_info->extent_root = extent_root;
-	fs_info->chunk_root = chunk_root;
-	fs_info->dev_root = dev_root;
-	fs_info->csum_root = csum_root;
-
-	fs_info->readonly = 1;
-
-	extent_io_tree_init(&fs_info->extent_cache);
-	extent_io_tree_init(&fs_info->free_space_cache);
-	extent_io_tree_init(&fs_info->block_group_cache);
-	extent_io_tree_init(&fs_info->pinned_extents);
-	extent_io_tree_init(&fs_info->pending_del);
-	extent_io_tree_init(&fs_info->extent_ins);
-	cache_tree_init(&fs_info->fs_root_cache);
-
-	cache_tree_init(&fs_info->mapping_tree.cache_tree);
-
-	mutex_init(&fs_info->fs_mutex);
-	fs_info->fs_devices = fs_devices;
-	INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
-	INIT_LIST_HEAD(&fs_info->space_info);
-
-	__setup_root(4096, 4096, 4096, 4096, tree_root,
-		     fs_info, BTRFS_ROOT_TREE_OBJECTID);
-
-	ret = btrfs_open_devices(fs_devices, O_RDONLY);
-	if (ret)
-		goto out_cleanup;
-
-	fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
-	disk_super = &fs_info->super_copy;
-	ret = btrfs_read_dev_super(fs_devices->latest_bdev,
-				   disk_super, BTRFS_SUPER_INFO_OFFSET);
-	if (ret) {
-		printk("No valid btrfs found\n");
-		goto out_devices;
-	}
-
-	memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
-
-
-	features = btrfs_super_incompat_flags(disk_super) &
-		   ~BTRFS_FEATURE_INCOMPAT_SUPP;
-	if (features) {
-		printk("couldn't open because of unsupported "
-		       "option features (%Lx).\n", features);
-		goto out_devices;
-	}
-
-	features = btrfs_super_incompat_flags(disk_super);
-	if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
-		features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
-		btrfs_set_super_incompat_flags(disk_super, features);
-	}
-
-	nodesize = btrfs_super_nodesize(disk_super);
-	leafsize = btrfs_super_leafsize(disk_super);
-	sectorsize = btrfs_super_sectorsize(disk_super);
-	stripesize = btrfs_super_stripesize(disk_super);
-	tree_root->nodesize = nodesize;
-	tree_root->leafsize = leafsize;
-	tree_root->sectorsize = sectorsize;
-	tree_root->stripesize = stripesize;
-
-	ret = btrfs_read_sys_array(tree_root);
-	if (ret)
-		goto out_devices;
-	blocksize = btrfs_level_size(tree_root,
-				     btrfs_super_chunk_root_level(disk_super));
-	generation = btrfs_super_chunk_root_generation(disk_super);
-
-	__setup_root(nodesize, leafsize, sectorsize, stripesize,
-		     chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
-
-	chunk_root->node = read_tree_block(chunk_root,
-					   btrfs_super_chunk_root(disk_super),
-					   blocksize, generation);
-	if (!chunk_root->node) {
-		printk("Couldn't read chunk root\n");
-		goto out_devices;
-	}
-
-	read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid,
-	         (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node),
-		 BTRFS_UUID_SIZE);
-
-	if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) {
-		ret = btrfs_read_chunk_tree(chunk_root);
-		if (ret)
-			goto out_chunk;
-	}
-
-	return fs_info->chunk_root;
-out_chunk:
-	free_extent_buffer(fs_info->chunk_root->node);
-out_devices:
-	close_all_devices(fs_info);
-out_cleanup:
-	extent_io_tree_cleanup(&fs_info->extent_cache);
-	extent_io_tree_cleanup(&fs_info->free_space_cache);
-	extent_io_tree_cleanup(&fs_info->block_group_cache);
-	extent_io_tree_cleanup(&fs_info->pinned_extents);
-	extent_io_tree_cleanup(&fs_info->pending_del);
-	extent_io_tree_cleanup(&fs_info->extent_ins);
-out:
-	free(tree_root);
-	free(extent_root);
-	free(chunk_root);
-	free(dev_root);
-	free(csum_root);
-	free(fs_info);
-	return NULL;
-}
-
 static int dump_root_bytenr(struct btrfs_root *root, u64 bytenr, u64 gen)
 {
 	struct btrfs_root *tmp = malloc(sizeof(struct btrfs_root));
-- 
1.8.0.2

