Re: [PATCH 1/3] btrfs-progs: check/lowmem: add the ability to detect and repair orphan subvolume trees which doesn't have orphan item
From: Qu Wenruo <hidden>
Date: 2021-06-25 09:08:21
On 2021/6/25 下午4:58, Anand Jain wrote:
On 25/06/2021 15:13, Qu Wenruo wrote:quoted
There is a bug report from the mail list that, after certain btrfs-zero-log operation, the filesystem has inaccessible subvolumes, and there is no way to delete them. Such subvolumes have no ROOT_REF/ROOT_BACKREF, and their root_refs is 0.quoted
Without an orphan item, kernel won't detect them as orphan, thus no cleanup will happen.Changes here looks good. But what if, in another case, the ghost subvolume isn't an orphan but a corruption? is it possible?
Firstly it needs to pass all the other root item check, including ROOT_BACKREF/ROOT_REF, and DIR_INDEX/DIR_ITEM. This means if there is any ROOT_BACKREF/ROOT_REF, or DIR_INDEX/DIR_ITEM referring to this inode, it should not be repaired. Furthermore, after passing above checks, it must have a root refs as 0. With all conditions met, it's really a ghost subvolume. In fact I have to do all above works to craft such test case, thus it's not resulted by some simple corruption. Thanks, Qu
Thanks, Anandquoted
Btrfs check won't report this as a problem either. Such ghost subvolumes will just waste disk space, and can be pretty hard to detect without proper btrfs check support. For the repair part, we just add orphan item so later kernel can handle it. Since the check for orphan item and repair can be reused between original and lowmem mode, move those functions to mode-common.[ch]. Reported-by: Zhenyu Wu <redacted> Signed-off-by: Qu Wenruo <redacted> --- check/mode-common.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ check/mode-common.h | 3 +++ check/mode-lowmem.c | 42 +++++++++++++++++++--------------- 3 files changed, 82 insertions(+), 19 deletions(-)diff --git a/check/mode-common.c b/check/mode-common.c index cb22f3233c00..d8c18f6603bf 100644 --- a/check/mode-common.c +++ b/check/mode-common.c@@ -1243,3 +1243,59 @@ out:btrfs_release_path(&path); return ret; } + +/* + * Check if we have orphan item for @objectid. + * + * Note: if something wrong happened during search, we consider it as no orphan + * item. + */ +bool btrfs_has_orphan_item(struct btrfs_root *root, u64 objectid) +{ + struct btrfs_path path; + struct btrfs_key key; + int ret; + + btrfs_init_path(&path); + key.objectid = BTRFS_ORPHAN_OBJECTID; + key.type = BTRFS_ORPHAN_ITEM_KEY; + key.offset = objectid; + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + btrfs_release_path(&path); + if (ret == 0) + return true; + return false; +} + +int repair_root_orphan_item(struct btrfs_fs_info *fs_info, u64 rootid) +{ + struct btrfs_path path; + struct btrfs_trans_handle *trans; + int ret; + + btrfs_init_path(&path); + + trans = btrfs_start_transaction(fs_info->tree_root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + errno = -ret; + error("failed to start transaction: %m"); + return ret; + } + ret = btrfs_add_orphan_item(trans, fs_info->tree_root, &path, rootid); + if (ret < 0) { + errno = -ret; + error("failed to insert orphan item: %m"); + goto error; + } + btrfs_release_path(&path); + ret = btrfs_commit_transaction(trans, fs_info->tree_root); + if (ret < 0) + goto error; + printf("Inserted orphan root item for root %llu\n", rootid); + return ret; +error: + btrfs_release_path(&path); + btrfs_abort_transaction(trans, ret); + return ret; +}diff --git a/check/mode-common.h b/check/mode-common.h index 3ba29ecab6cd..c44815a171ac 100644 --- a/check/mode-common.h +++ b/check/mode-common.h@@ -192,4 +192,7 @@ static inline voidbtrfs_check_subpage_eb_alignment(u64 start, u32 len) start, start + len); } +bool btrfs_has_orphan_item(struct btrfs_root *root, u64 objectid); +int repair_root_orphan_item(struct btrfs_fs_info *fs_info, u64 rootid); + #endifdiff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index 2f736712bc7f..1ef781ad20bc 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c@@ -2500,24 +2500,6 @@ out:return ret; } -static bool has_orphan_item(struct btrfs_root *root, u64 ino) -{ - struct btrfs_path path; - struct btrfs_key key; - int ret; - - btrfs_init_path(&path); - key.objectid = BTRFS_ORPHAN_OBJECTID; - key.type = BTRFS_ORPHAN_ITEM_KEY; - key.offset = ino; - - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - btrfs_release_path(&path); - if (ret == 0) - return true; - return false; -} - static int repair_inode_gen_lowmem(struct btrfs_root *root, struct btrfs_path *path) {@@ -2622,7 +2604,7 @@ static int check_inode_item(struct btrfs_root*root, struct btrfs_path *path) return err; } - is_orphan = has_orphan_item(root, inode_id); + is_orphan = btrfs_has_orphan_item(root, inode_id); ii = btrfs_item_ptr(node, slot, struct btrfs_inode_item); isize = btrfs_inode_size(node, ii); nbytes = btrfs_inode_nbytes(node, ii);@@ -5209,6 +5191,28 @@ static int check_btrfs_root(struct btrfs_root*root, int check_all) } } } + + /* Make sure orphan subvolume tree has proper orphan item for it */ + if (btrfs_root_refs(root_item) == 0 && + is_fstree(root->root_key.objectid)) { + bool is_orphan; + is_orphan = btrfs_has_orphan_item(root->fs_info->tree_root, + root->root_key.objectid); + + if (!is_orphan) { + error("orphan root %llu doesn't have orphan item", + root->root_key.objectid); + if (repair) { + ret = repair_root_orphan_item(root->fs_info, + root->root_key.objectid); + if (!ret) + is_orphan = true; + } + if (!is_orphan) + err |= ORPHAN_ITEM; + } + } + if (btrfs_root_refs(root_item) > 0 || btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { path.nodes[level] = root->node;