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/kernel-shared/extent-tree.c b/kernel-shared/extent-tree.c index 5800320f7b..c32999055e 100644 --- a/kernel-shared/extent-tree.c +++ b/kernel-shared/extent-tree.c @@ -1980,90 +1980,86 @@ static int __free_extent(struct btrfs_trans_handle *trans, bytenr, num_bytes, parent, root_objectid, owner_objectid, owner_offset); - if (ret == 0) { - extent_slot = path->slots[0]; - while (extent_slot >= 0) { - btrfs_item_key_to_cpu(path->nodes[0], &key, - extent_slot); - if (key.objectid != bytenr) - break; - if (key.type == BTRFS_EXTENT_ITEM_KEY && - key.offset == num_bytes) { - found_extent = 1; - break; - } - if (key.type == BTRFS_METADATA_ITEM_KEY && - key.offset == owner_objectid) { - found_extent = 1; - break; - } - if (path->slots[0] - extent_slot > 5) - break; - extent_slot--; + if (ret) { + error("unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu ret %d\n", + bytenr, parent, root_objectid, owner_objectid, + owner_offset, ret); + if (path->nodes[0]) { + printf("path->slots[0]: %d path->nodes[0]:\n", path->slots[0]); + btrfs_print_leaf(path->nodes[0]); } - if (!found_extent) { - BUG_ON(iref); - ret = remove_extent_backref(trans, extent_root, path, - NULL, refs_to_drop, - is_data); - BUG_ON(ret); - btrfs_release_path(path); + ret = -EIO; + goto fail; + } + extent_slot = path->slots[0]; + while (extent_slot >= 0) { + btrfs_item_key_to_cpu(path->nodes[0], &key, + extent_slot); + if (key.objectid != bytenr) + break; + if (key.type == BTRFS_EXTENT_ITEM_KEY && + key.offset == num_bytes) { + found_extent = 1; + break; + } + if (key.type == BTRFS_METADATA_ITEM_KEY && + key.offset == owner_objectid) { + found_extent = 1; + break; + } + if (path->slots[0] - extent_slot > 5) + break; + extent_slot--; + } + if (!found_extent) { + BUG_ON(iref); + ret = remove_extent_backref(trans, extent_root, path, + NULL, refs_to_drop, + is_data); + BUG_ON(ret); + btrfs_release_path(path); - key.objectid = bytenr; + key.objectid = bytenr; - if (skinny_metadata) { - key.type = BTRFS_METADATA_ITEM_KEY; - key.offset = owner_objectid; - } else { - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = num_bytes; - } + if (skinny_metadata) { + key.type = BTRFS_METADATA_ITEM_KEY; + key.offset = owner_objectid; + } else { + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = num_bytes; + } + ret = btrfs_search_slot(trans, extent_root, + &key, path, -1, 1); + if (ret > 0 && skinny_metadata && path->slots[0]) { + path->slots[0]--; + btrfs_item_key_to_cpu(path->nodes[0], + &key, + path->slots[0]); + if (key.objectid == bytenr && + key.type == BTRFS_EXTENT_ITEM_KEY && + key.offset == num_bytes) + ret = 0; + } + + if (ret > 0 && skinny_metadata) { + skinny_metadata = 0; + btrfs_release_path(path); + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = num_bytes; ret = btrfs_search_slot(trans, extent_root, &key, path, -1, 1); - if (ret > 0 && skinny_metadata && path->slots[0]) { - path->slots[0]--; - btrfs_item_key_to_cpu(path->nodes[0], - &key, - path->slots[0]); - if (key.objectid == bytenr && - key.type == BTRFS_EXTENT_ITEM_KEY && - key.offset == num_bytes) - ret = 0; - } - - if (ret > 0 && skinny_metadata) { - skinny_metadata = 0; - btrfs_release_path(path); - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = num_bytes; - ret = btrfs_search_slot(trans, extent_root, - &key, path, -1, 1); - } + } - if (ret) { - printk(KERN_ERR "umm, got %d back from search" - ", was looking for %llu\n", ret, - (unsigned long long)bytenr); - btrfs_print_leaf(path->nodes[0]); - } - BUG_ON(ret); - extent_slot = path->slots[0]; + if (ret) { + printk(KERN_ERR "umm, got %d back from search" + ", was looking for %llu\n", ret, + (unsigned long long)bytenr); + btrfs_print_leaf(path->nodes[0]); } - } else { - printk(KERN_ERR "btrfs unable to find ref byte nr %llu " - "parent %llu root %llu owner %llu offset %llu\n", - (unsigned long long)bytenr, - (unsigned long long)parent, - (unsigned long long)root_objectid, - (unsigned long long)owner_objectid, - (unsigned long long)owner_offset); - printf("path->slots[0]: %d path->nodes[0]:\n", path->slots[0]); - btrfs_print_leaf(path->nodes[0]); - ret = -EIO; - goto fail; + BUG_ON(ret); + extent_slot = path->slots[0]; } - leaf = path->nodes[0]; item_size = btrfs_item_size(leaf, extent_slot); if (item_size < sizeof(*ei)) { 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