Skip to content

Commit 8c85d33

Browse files
committed
btrfs-progs: check/original: detect missing orphan items correctly
[BUG] If we have a filesystem with a subvolume that has 0 refs but without an orphan item, btrfs check won't report any error on it. $ btrfs ins dump-tree -t root /dev/test/scratch1 btrfs-progs v6.15 root tree node 5242880 level 1 items 2 free space 119 generation 11 owner ROOT_TREE node 5242880 flags 0x1(WRITTEN) backref revision 1 fs uuid ff32309e-4e90-4402-b1ef-0a1f9f28bfff chunk uuid 6c373b6d-c866-4c8c-81fa-608bf5ef25e3 key (EXTENT_TREE ROOT_ITEM 0) block 5267456 gen 11 key (ROOT_TREE_DIR DIR_ITEM 2378154706) block 5246976 gen 11 leaf 5267456 items 6 free space 2339 generation 11 owner ROOT_TREE leaf 5267456 flags 0x1(WRITTEN) backref revision 1 fs uuid ff32309e-4e90-4402-b1ef-0a1f9f28bfff [...] leaf 5246976 items 6 free space 1613 generation 11 owner ROOT_TREE leaf 5246976 flags 0x1(WRITTEN) backref revision 1 checksum stored 47620783 checksum calced 47620783 fs uuid ff32309e-4e90-4402-b1ef-0a1f9f28bfff chunk uuid 6c373b6d-c866-4c8c-81fa-608bf5ef25e3 [...] item 4 key (256 ROOT_ITEM 0) itemoff 2202 itemsize 439 generation 9 root_dirid 256 bytenr 5898240 byte_limit 0 bytes_used 581632 last_snapshot 0 flags 0x1000000000000(none) refs 0 <<< drop_progress key (0 UNKNOWN.0 0) drop_level 0 level 2 generation_v2 9 item 5 key (DATA_RELOC_TREE ROOT_ITEM 0) itemoff 1763 itemsize 439 generation 5 root_dirid 256 bytenr 5287936 byte_limit 0 bytes_used 4096 last_snapshot 0 flags 0x0(none) refs 1 drop_progress key (0 UNKNOWN.0 0) drop_level 0 level 0 generation_v2 5 ^^^ No orphan item for subvolume 256. Then "btrfs check" will not report anything wrong with it: Opening filesystem to check... Checking filesystem on /dev/test/scratch1 UUID: ff32309e-4e90-4402-b1ef-0a1f9f28bfff [1/8] checking log skipped (none written) [2/8] checking root items [3/8] checking extents [4/8] checking free space tree [5/8] checking fs roots [6/8] checking only csums items (without verifying data) [7/8] checking root refs [8/8] checking quota groups skipped (not enabled on this FS) found 638976 bytes used, no error found <<< total csum bytes: 0 total tree bytes: 638976 total fs tree bytes: 589824 total extent tree bytes: 16384 btree space waste bytes: 289501 file data blocks allocated: 0 referenced 0 [CAUSE] Although we have check_orphan_item() call inside check_root_refs(), it relies the root record to have its 'found_root_item' member set. Otherwise it will not report this as an error. But that 'found_root_item' is always set to 0, if the subvolume has zero ref. This means any subvolume with 0 refs will not have its orphan item checked. [FIX] - Introduce root_record::expected_ref to record the refs in the root item So that we can properly compare the number of refs for each subvolume tree. - Set root_record::found_root_item to one unconditionally in check_fs_root() Since we're accessing the fs root through the root item, the root item must exist, no need to bother if the root_refs is zero or not. - Enhance check_root_refs() * Always check if btrfs_root_item::refs match root_record::found_ref * If the fs tree has no ref, check for the orphan item Signed-off-by: Qu Wenruo <[email protected]>
1 parent e39ed66 commit 8c85d33

File tree

2 files changed

+26
-16
lines changed

2 files changed

+26
-16
lines changed

check/main.c

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,22 +3491,29 @@ static int check_root_refs(struct btrfs_root *root,
34913491
rec = container_of(cache, struct root_record, cache);
34923492
cache = next_cache_extent(cache);
34933493

3494-
if (rec->found_ref == 0 &&
3495-
rec->objectid >= BTRFS_FIRST_FREE_OBJECTID &&
3494+
/* A subvolume tree, should check if its refs match. */
3495+
if (rec->objectid >= BTRFS_FIRST_CHUNK_TREE_OBJECTID &&
34963496
rec->objectid <= BTRFS_LAST_FREE_OBJECTID) {
3497-
ret = check_orphan_item(gfs_info->tree_root, rec->objectid);
3498-
if (ret == 0)
3499-
continue;
3500-
3501-
/*
3502-
* If we don't have a root item then we likely just have
3503-
* a dir item in a snapshot for this root but no actual
3504-
* ref key or anything so it's meaningless.
3505-
*/
3506-
if (!rec->found_root_item)
3497+
if (rec->found_ref != rec->expected_ref) {
3498+
errors++;
3499+
fprintf(stderr, "fs tree %llu refs mismatch found %u expect %u\n",
3500+
rec->objectid, rec->found_ref, rec->expected_ref);
35073501
continue;
3508-
errors++;
3509-
fprintf(stderr, "fs tree %llu not referenced\n", rec->objectid);
3502+
}
3503+
if (rec->expected_ref == 0) {
3504+
ret = check_orphan_item(gfs_info->tree_root, rec->objectid);
3505+
if (ret == 0)
3506+
continue;
3507+
/*
3508+
* If we don't have a root item then we likely just have
3509+
* a dir item in a snapshot for this root but no actual
3510+
* ref key or anything so it's meaningless.
3511+
*/
3512+
if (!rec->found_root_item)
3513+
continue;
3514+
errors++;
3515+
fprintf(stderr, "fs tree %llu missing orphan item\n", rec->objectid);
3516+
}
35103517
}
35113518

35123519
error = 0;
@@ -3728,8 +3735,8 @@ static int check_fs_root(struct btrfs_root *root,
37283735
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
37293736
rec = get_root_rec(root_cache, root->root_key.objectid);
37303737
BUG_ON(IS_ERR(rec));
3731-
if (btrfs_root_refs(root_item) > 0)
3732-
rec->found_root_item = 1;
3738+
rec->found_root_item = 1;
3739+
rec->expected_ref = btrfs_root_refs(&root->root_item);
37333740
}
37343741

37353742
memset(&root_node, 0, sizeof(root_node));

check/mode-original.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,10 @@ struct root_record {
263263
struct cache_extent cache;
264264
unsigned int found_root_item:1;
265265
u64 objectid;
266+
/* The found number of refs in tree root. */
266267
u32 found_ref;
268+
/* The expected number of refs in root item. */
269+
u32 expected_ref;
267270
};
268271

269272
struct ptr_node {

0 commit comments

Comments
 (0)