Skip to content

Commit 6cf11f3

Browse files
maharmstonekdave
authored andcommitted
btrfs-progs: check: check order of inline extent refs
The kernel seems to order inline extent items in a particular way: forward by sub-type, then reverse by hash. Having these out of order can cause a volume to go readonly when deleting an inode. See maharmstone/ntfs2btrfs#51 With additional comments from the pull request: - lookup_inline_extent_backref() is skipping the remaining backref item if data/metadata item is smaller (either through the data hash, or metadata parent/ref_root) than the target range - the fix could be still missing in lowmem mode - image could be created according this comment maharmstone/ntfs2btrfs#51 (comment) - due to late merge, the squota newly added key BTRFS_EXTENT_OWNER_REF_KEY was not part of the patch and the value of 'hash' needs to be verified Pull-request: #622 Signed-off-by: Mark Harmstone <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 0927d30 commit 6cf11f3

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

check/main.c

+41-1
Original file line numberDiff line numberDiff line change
@@ -5534,13 +5534,14 @@ static int process_extent_item(struct btrfs_root *root,
55345534
unsigned long end;
55355535
unsigned long ptr;
55365536
int ret;
5537-
int type;
5537+
int type, last_type;
55385538
u32 item_size = btrfs_item_size(eb, slot);
55395539
u64 refs = 0;
55405540
u64 offset;
55415541
u64 num_bytes;
55425542
u64 gen;
55435543
int metadata = 0;
5544+
u64 last_hash, hash;
55445545

55455546
btrfs_item_key_to_cpu(eb, &key, slot);
55465547

@@ -5623,13 +5624,19 @@ static int process_extent_item(struct btrfs_root *root,
56235624
key.type == BTRFS_EXTENT_ITEM_KEY)
56245625
ptr += sizeof(struct btrfs_tree_block_info);
56255626

5627+
last_hash = U64_MAX;
5628+
last_type = 0;
5629+
56265630
end = (unsigned long)ei + item_size;
56275631
while (ptr < end) {
56285632
iref = (struct btrfs_extent_inline_ref *)ptr;
56295633
type = btrfs_extent_inline_ref_type(eb, iref);
56305634
offset = btrfs_extent_inline_ref_offset(eb, iref);
5635+
56315636
switch (type) {
56325637
case BTRFS_TREE_BLOCK_REF_KEY:
5638+
hash = offset;
5639+
56335640
ret = add_tree_backref(extent_cache, key.objectid,
56345641
0, offset, 0);
56355642
if (ret < 0) {
@@ -5639,6 +5646,8 @@ static int process_extent_item(struct btrfs_root *root,
56395646
}
56405647
break;
56415648
case BTRFS_SHARED_BLOCK_REF_KEY:
5649+
hash = offset;
5650+
56425651
ret = add_tree_backref(extent_cache, key.objectid,
56435652
offset, 0, 0);
56445653
if (ret < 0) {
@@ -5649,6 +5658,12 @@ static int process_extent_item(struct btrfs_root *root,
56495658
break;
56505659
case BTRFS_EXTENT_DATA_REF_KEY:
56515660
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
5661+
5662+
hash = hash_extent_data_ref(
5663+
btrfs_extent_data_ref_root(eb, dref),
5664+
btrfs_extent_data_ref_objectid(eb, dref),
5665+
btrfs_extent_data_ref_offset(eb, dref));
5666+
56525667
add_data_backref(extent_cache, key.objectid, 0,
56535668
btrfs_extent_data_ref_root(eb, dref),
56545669
btrfs_extent_data_ref_objectid(eb,
@@ -5658,20 +5673,45 @@ static int process_extent_item(struct btrfs_root *root,
56585673
gen, 0, num_bytes);
56595674
break;
56605675
case BTRFS_SHARED_DATA_REF_KEY:
5676+
hash = offset;
5677+
56615678
sref = (struct btrfs_shared_data_ref *)(iref + 1);
56625679
add_data_backref(extent_cache, key.objectid, offset,
56635680
0, 0, 0,
56645681
btrfs_shared_data_ref_count(eb, sref),
56655682
gen, 0, num_bytes);
56665683
break;
56675684
case BTRFS_EXTENT_OWNER_REF_KEY:
5685+
hash = offset;
56685686
break;
56695687
default:
56705688
fprintf(stderr,
56715689
"corrupt extent record: key [%llu,%u,%llu]\n",
56725690
key.objectid, key.type, num_bytes);
56735691
goto out;
56745692
}
5693+
5694+
if (type != last_type) {
5695+
last_hash = U64_MAX;
5696+
5697+
if (type < last_type) {
5698+
fprintf(stderr,
5699+
"inline extent refs out of order: key [%llu,%u,%llu]\n",
5700+
key.objectid, key.type, num_bytes);
5701+
goto out;
5702+
}
5703+
5704+
last_type = type;
5705+
}
5706+
5707+
if (hash > last_hash) {
5708+
fprintf(stderr,
5709+
"inline extent refs out of order: key [%llu,%u,%llu]\n",
5710+
key.objectid, key.type, num_bytes);
5711+
goto out;
5712+
}
5713+
5714+
last_hash = hash;
56755715
ptr += btrfs_extent_inline_ref_size(type);
56765716
}
56775717
WARN_ON(ptr > end);

0 commit comments

Comments
 (0)