diff --git a/Documentation/mkfs.btrfs.rst b/Documentation/mkfs.btrfs.rst index 13ec41d58e..7734354fd6 100644 --- a/Documentation/mkfs.btrfs.rst +++ b/Documentation/mkfs.btrfs.rst @@ -58,7 +58,7 @@ OPTIONS Default on a single device filesystem is *DUP* and is recommended for metadata in general. The duplication might not be necessary in some use cases and it's - up to the user to changed that at mkfs time or later. This depends on hardware + up to the user to change that at mkfs time or later. This depends on hardware that could potentially deduplicate the blocks again but this cannot be detected at mkfs time. diff --git a/check/main.c b/check/main.c index 77458a7690..db055ae194 100644 --- a/check/main.c +++ b/check/main.c @@ -3550,6 +3550,11 @@ static int check_root_refs(struct btrfs_root *root, */ if (!rec->found_root_item) continue; + if (opt_check_repair) { + ret = repair_subvol_orphan_item(gfs_info, rec->objectid); + if (!ret) + continue; + } errors++; fprintf(stderr, "fs tree %llu missing orphan item\n", rec->objectid); } diff --git a/check/mode-common.c b/check/mode-common.c index 0467ba2839..2d11a96dfb 100644 --- a/check/mode-common.c +++ b/check/mode-common.c @@ -1672,3 +1672,36 @@ int check_and_repair_super_num_devs(struct btrfs_fs_info *fs_info) printf("Successfully reset super num devices to %u\n", found_devs); return 0; } + +int repair_subvol_orphan_item(struct btrfs_fs_info *fs_info, u64 rootid) +{ + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_trans_handle *trans; + struct btrfs_path path = { 0 }; + int ret; + + trans = btrfs_start_transaction(tree_root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + errno = -ret; + error_msg(ERROR_MSG_START_TRANS, "%m"); + return ret; + } + ret = btrfs_add_orphan_item(trans, tree_root, &path, rootid); + btrfs_release_path(&path); + if (ret < 0) { + errno = -ret; + error("failed to insert orphan item for subvolume %llu: %m", rootid); + btrfs_abort_transaction(trans, ret); + btrfs_commit_transaction(trans, tree_root); + return ret; + } + ret = btrfs_commit_transaction(trans, tree_root); + if (ret < 0) { + errno = -ret; + error_msg(ERROR_MSG_COMMIT_TRANS, "%m"); + return ret; + } + printf("Added back missing orphan item for subvolume %llu\n", rootid); + return 0; +} diff --git a/check/mode-common.h b/check/mode-common.h index c37b4dc00e..e97835a5b6 100644 --- a/check/mode-common.h +++ b/check/mode-common.h @@ -197,5 +197,6 @@ int repair_dev_item_bytes_used(struct btrfs_fs_info *fs_info, int fill_csum_tree(struct btrfs_trans_handle *trans, bool search_fs_tree); int check_and_repair_super_num_devs(struct btrfs_fs_info *fs_info); +int repair_subvol_orphan_item(struct btrfs_fs_info *fs_info, u64 rootid); #endif diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index 363dc4ae19..ea4d401782 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -5569,9 +5569,16 @@ static int check_btrfs_root(struct btrfs_root *root, int check_all) * If this tree is a subvolume (not a reloc tree) and has no refs, there * should be an orphan item for it, or this subvolume will never be deleted. */ - if (btrfs_root_refs(root_item) == 0 && is_fstree(btrfs_root_id(root))) { - if (!has_orphan_item(root->fs_info->tree_root, - btrfs_root_id(root))) { + if (btrfs_root_refs(root_item) == 0 && is_fstree(btrfs_root_id(root)) && + !has_orphan_item(root->fs_info->tree_root, btrfs_root_id(root))) { + bool repaired = false; + + if (opt_check_repair) { + ret = repair_subvol_orphan_item(root->fs_info, btrfs_root_id(root)); + if (!ret) + repaired = true; + } + if (!repaired) { error("missing orphan item for root %lld", btrfs_root_id(root)); err |= REFERENCER_MISSING; } diff --git a/tests/fsck-tests/066-missing-root-orphan-item/.lowmem_repairable b/tests/fsck-tests/066-missing-root-orphan-item/.lowmem_repairable new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/fsck-tests/066-missing-root-orphan-item/test.sh b/tests/fsck-tests/066-missing-root-orphan-item/test.sh deleted file mode 100755 index 9db625714c..0000000000 --- a/tests/fsck-tests/066-missing-root-orphan-item/test.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# -# Verify that check can report missing orphan root itemm as an error - -source "$TEST_TOP/common" || exit - -check_prereq btrfs - -check_image() { - run_mustfail "missing root orphan item not reported as an error" \ - "$TOP/btrfs" check "$1" -} - -check_all_images