Commit 42437a63 authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba
Browse files

btrfs: introduce mount option rescue=ignorebadroots



In the face of extent root corruption, or any other core fs wide root
corruption we will fail to mount the file system.  This makes recovery
kind of a pain, because you need to fall back to userspace tools to
scrape off data.  Instead provide a mechanism to gracefully handle bad
roots, so we can at least mount read-only and possibly recover data from
the file system.

Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 68319c18
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -1985,6 +1985,51 @@ static int read_one_block_group(struct btrfs_fs_info *info,
	return ret;
}

static int fill_dummy_bgs(struct btrfs_fs_info *fs_info)
{
	struct extent_map_tree *em_tree = &fs_info->mapping_tree;
	struct btrfs_space_info *space_info;
	struct rb_node *node;
	int ret = 0;

	for (node = rb_first_cached(&em_tree->map); node; node = rb_next(node)) {
		struct extent_map *em;
		struct map_lookup *map;
		struct btrfs_block_group *bg;

		em = rb_entry(node, struct extent_map, rb_node);
		map = em->map_lookup;
		bg = btrfs_create_block_group_cache(fs_info, em->start);
		if (!bg) {
			ret = -ENOMEM;
			break;
		}

		/* Fill dummy cache as FULL */
		bg->length = em->len;
		bg->flags = map->type;
		bg->last_byte_to_unpin = (u64)-1;
		bg->cached = BTRFS_CACHE_FINISHED;
		bg->used = em->len;
		bg->flags = map->type;
		ret = btrfs_add_block_group_cache(fs_info, bg);
		if (ret) {
			btrfs_remove_free_space_cache(bg);
			btrfs_put_block_group(bg);
			break;
		}
		btrfs_update_space_info(fs_info, bg->flags, em->len, em->len,
					0, &space_info);
		bg->space_info = space_info;
		link_block_group(bg);

		set_avail_alloc_bits(fs_info, bg->flags);
	}
	if (!ret)
		btrfs_init_global_block_rsv(fs_info);
	return ret;
}

int btrfs_read_block_groups(struct btrfs_fs_info *info)
{
	struct btrfs_path *path;
@@ -1995,6 +2040,9 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info)
	int need_clear = 0;
	u64 cache_gen;

	if (!info->extent_root)
		return fill_dummy_bgs(info);

	key.objectid = 0;
	key.offset = 0;
	key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+8 −0
Original line number Diff line number Diff line
@@ -426,6 +426,14 @@ void btrfs_init_global_block_rsv(struct btrfs_fs_info *fs_info)
	fs_info->delayed_block_rsv.space_info = space_info;
	fs_info->delayed_refs_rsv.space_info = space_info;

	/*
	 * Our various recovery options can leave us with NULL roots, so check
	 * here and just bail before we go dereferencing NULLs everywhere.
	 */
	if (!fs_info->extent_root || !fs_info->csum_root ||
	    !fs_info->dev_root || !fs_info->chunk_root || !fs_info->tree_root)
		return;

	fs_info->extent_root->block_rsv = &fs_info->delayed_refs_rsv;
	fs_info->csum_root->block_rsv = &fs_info->delayed_refs_rsv;
	fs_info->dev_root->block_rsv = &fs_info->global_block_rsv;
+1 −1
Original line number Diff line number Diff line
@@ -150,7 +150,7 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,
	struct compressed_bio *cb = bio->bi_private;
	u8 *cb_sum = cb->sums;

	if (inode->flags & BTRFS_INODE_NODATASUM)
	if (!fs_info->csum_root || (inode->flags & BTRFS_INODE_NODATASUM))
		return 0;

	shash->tfm = fs_info->csum_shash;
+1 −0
Original line number Diff line number Diff line
@@ -1298,6 +1298,7 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)
#define BTRFS_MOUNT_NOLOGREPLAY		(1 << 27)
#define BTRFS_MOUNT_REF_VERIFY		(1 << 28)
#define BTRFS_MOUNT_DISCARD_ASYNC	(1 << 29)
#define BTRFS_MOUNT_IGNOREBADROOTS	(1 << 30)

#define BTRFS_DEFAULT_COMMIT_INTERVAL	(30)
#define BTRFS_DEFAULT_MAX_INLINE	(2048)
+41 −24
Original line number Diff line number Diff line
@@ -2307,30 +2307,39 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info)

	root = btrfs_read_tree_root(tree_root, &location);
	if (IS_ERR(root)) {
		if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
			ret = PTR_ERR(root);
			goto out;
		}
	} else {
		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
		fs_info->extent_root = root;
	}

	location.objectid = BTRFS_DEV_TREE_OBJECTID;
	root = btrfs_read_tree_root(tree_root, &location);
	if (IS_ERR(root)) {
		if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
			ret = PTR_ERR(root);
			goto out;
		}
	} else {
		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
		fs_info->dev_root = root;
		btrfs_init_devices_late(fs_info);
	}

	location.objectid = BTRFS_CSUM_TREE_OBJECTID;
	root = btrfs_read_tree_root(tree_root, &location);
	if (IS_ERR(root)) {
		if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
			ret = PTR_ERR(root);
			goto out;
		}
	} else {
		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
		fs_info->csum_root = root;
	}

	/*
	 * This tree can share blocks with some other fs tree during relocation
@@ -2339,11 +2348,14 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info)
	root = btrfs_get_fs_root(tree_root->fs_info,
				 BTRFS_DATA_RELOC_TREE_OBJECTID, true);
	if (IS_ERR(root)) {
		if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
			ret = PTR_ERR(root);
			goto out;
		}
	} else {
		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
		fs_info->data_reloc_root = root;
	}

	location.objectid = BTRFS_QUOTA_TREE_OBJECTID;
	root = btrfs_read_tree_root(tree_root, &location);
@@ -2356,9 +2368,11 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info)
	location.objectid = BTRFS_UUID_TREE_OBJECTID;
	root = btrfs_read_tree_root(tree_root, &location);
	if (IS_ERR(root)) {
		if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
			ret = PTR_ERR(root);
			if (ret != -ENOENT)
				goto out;
		}
	} else {
		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
		fs_info->uuid_root = root;
@@ -2368,12 +2382,15 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info)
		location.objectid = BTRFS_FREE_SPACE_TREE_OBJECTID;
		root = btrfs_read_tree_root(tree_root, &location);
		if (IS_ERR(root)) {
			if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
				ret = PTR_ERR(root);
				goto out;
			}
		}  else {
			set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
			fs_info->free_space_root = root;
		}
	}

	return 0;
out:
Loading