From 77db6bd402c93e90f9873beeb21885705cca1648 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 11 Nov 2021 16:14:33 +0800 Subject: [PATCH] btrfs-progs: rescue: introduce clear-uuid-tree [BUG] There is a bug report that a corrupted key type (expected UUID_KEY_SUBVOL, has EXTENT_ITEM) causing newer kernel to reject a mount. Although the root cause is not determined yet, with roll out of v5.11 kernel to various distros, such problem should be prevented by tree-checker, no matter if it's hardware problem or not. And older kernel with "-o uuid_rescan" mount option won't help, as uuid_rescan will only delete items with UUID_KEY_SUBVOL/UUID_KEY_RECEIVED_SUBVOL key types, not deleting such corrupted key. [FIX] To fix such problem we have to rely on offline tool, thus there we introduce a new rescue tool, clear-uuid-tree, to empty and then remove uuid tree. Kernel will re-generate the correct uuid tree at next mount. Reported-by: S. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- Documentation/btrfs-rescue.asciidoc | 8 +++ Documentation/btrfs-rescue.rst | 9 +++ cmds/rescue.c | 103 ++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/Documentation/btrfs-rescue.asciidoc b/Documentation/btrfs-rescue.asciidoc index af544372..01251cec 100644 --- a/Documentation/btrfs-rescue.asciidoc +++ b/Documentation/btrfs-rescue.asciidoc @@ -50,6 +50,14 @@ The mismatch may also exhibit as a kernel warning: WARNING: CPU: 3 PID: 439 at fs/btrfs/ctree.h:1559 btrfs_update_device+0x1c5/0x1d0 [btrfs] ---- +*clear-uuid-tree* :: +Clear uuid tree, so that kernel can re-generate it at next read-write mount. ++ +Since kernel v4.16 there and more sanity check performed, and sometimes +non-critical trees like uuid tree can cause problems and reject the mount. +In such case, clearing uuid tree may make the filesystem to be mountable again +without much risk as it's built from other trees. + *super-recover* [options] :: Recover bad superblocks from good copies. + diff --git a/Documentation/btrfs-rescue.rst b/Documentation/btrfs-rescue.rst index f3e73f19..57feff6d 100644 --- a/Documentation/btrfs-rescue.rst +++ b/Documentation/btrfs-rescue.rst @@ -50,6 +50,15 @@ fix-device-size WARNING: CPU: 3 PID: 439 at fs/btrfs/ctree.h:1559 btrfs_update_device+0x1c5/0x1d0 [btrfs] +clear-uuid-tree + Clear uuid tree, so that kernel can re-generate it at next read-write + mount. + + Since kernel v4.16 there are more sanity check performed, and sometimes + non-critical trees like uuid tree can cause problems and reject the mount. + In such case, clearing uuid tree may make the filesystem to be mountable again + without much risk as it's built from other trees. + super-recover [options] Recover bad superblocks from good copies. diff --git a/cmds/rescue.c b/cmds/rescue.c index a98b255a..ca1c6d74 100644 --- a/cmds/rescue.c +++ b/cmds/rescue.c @@ -296,6 +296,108 @@ static int cmd_rescue_create_control_device(const struct cmd_struct *cmd, } static DEFINE_SIMPLE_COMMAND(rescue_create_control_device, "create-control-device"); +static int clear_uuid_tree(struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *uuid_root = fs_info->uuid_root; + struct btrfs_trans_handle *trans; + struct btrfs_path path = {}; + struct btrfs_key key = {}; + int ret; + + if (!uuid_root) + return 0; + + fs_info->uuid_root = NULL; + trans = btrfs_start_transaction(fs_info->tree_root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + while (1) { + int nr; + + ret = btrfs_search_slot(trans, uuid_root, &key, &path, -1, 1); + if (ret < 0) + goto out; + ASSERT(ret > 0); + ASSERT(path.slots[0] == 0); + + nr = btrfs_header_nritems(path.nodes[0]); + if (nr == 0) { + btrfs_release_path(&path); + break; + } + + ret = btrfs_del_items(trans, uuid_root, &path, 0, nr); + btrfs_release_path(&path); + if (ret < 0) + goto out; + } + ret = btrfs_del_root(trans, fs_info->tree_root, &uuid_root->root_key); + if (ret < 0) + goto out; + list_del(&uuid_root->dirty_list); + ret = clean_tree_block(uuid_root->node); + if (ret < 0) + goto out; + ret = btrfs_free_tree_block(trans, uuid_root, uuid_root->node, 0, 1); + if (ret < 0) + goto out; + free_extent_buffer(uuid_root->node); + free_extent_buffer(uuid_root->commit_root); + kfree(uuid_root); +out: + if (ret < 0) + btrfs_abort_transaction(trans, ret); + else + ret = btrfs_commit_transaction(trans, fs_info->tree_root); + return ret; +} + +static const char * const cmd_rescue_clear_uuid_tree_usage[] = { + "btrfs rescue clear-uuid-tree", + "Delete uuid tree so that kernel can rebuild it at mount time", + NULL, +}; + +static int cmd_rescue_clear_uuid_tree(const struct cmd_struct *cmd, + int argc, char **argv) +{ + struct btrfs_fs_info *fs_info; + struct open_ctree_flags ocf = {}; + char *devname; + int ret; + + clean_args_no_options(cmd, argc, argv); + if (check_argc_exact(argc, 2)) + return -EINVAL; + + devname = argv[optind]; + ret = check_mounted(devname); + if (ret < 0) { + errno = -ret; + error("could not check mount status: %m"); + goto out; + } else if (ret) { + error("%s is currently mounted", devname); + ret = -EBUSY; + goto out; + } + ocf.filename = devname; + ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL; + fs_info = open_ctree_fs_info(&ocf); + if (!fs_info) { + error("could not open btrfs"); + ret = -EIO; + goto out; + } + + ret = clear_uuid_tree(fs_info); + close_ctree(fs_info->tree_root); +out: + return !!ret; +} +static DEFINE_SIMPLE_COMMAND(rescue_clear_uuid_tree, "clear-uuid-tree"); + static const char rescue_cmd_group_info[] = "toolbox for specific rescue operations"; @@ -306,6 +408,7 @@ static const struct cmd_group rescue_cmd_group = { &cmd_struct_rescue_zero_log, &cmd_struct_rescue_fix_device_size, &cmd_struct_rescue_create_control_device, + &cmd_struct_rescue_clear_uuid_tree, NULL } };