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 https://github.com/maharmstone/ntfs2btrfs/issues/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 https://github.com/maharmstone/ntfs2btrfs/issues/51#issuecomment-1500781204 - 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 <mark@harmstone.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
0927d3056b
commit
6cf11f3e38
42
check/main.c
42
check/main.c
|
@ -5534,13 +5534,14 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
unsigned long end;
|
unsigned long end;
|
||||||
unsigned long ptr;
|
unsigned long ptr;
|
||||||
int ret;
|
int ret;
|
||||||
int type;
|
int type, last_type;
|
||||||
u32 item_size = btrfs_item_size(eb, slot);
|
u32 item_size = btrfs_item_size(eb, slot);
|
||||||
u64 refs = 0;
|
u64 refs = 0;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
u64 num_bytes;
|
u64 num_bytes;
|
||||||
u64 gen;
|
u64 gen;
|
||||||
int metadata = 0;
|
int metadata = 0;
|
||||||
|
u64 last_hash, hash;
|
||||||
|
|
||||||
btrfs_item_key_to_cpu(eb, &key, slot);
|
btrfs_item_key_to_cpu(eb, &key, slot);
|
||||||
|
|
||||||
|
@ -5623,13 +5624,19 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
key.type == BTRFS_EXTENT_ITEM_KEY)
|
key.type == BTRFS_EXTENT_ITEM_KEY)
|
||||||
ptr += sizeof(struct btrfs_tree_block_info);
|
ptr += sizeof(struct btrfs_tree_block_info);
|
||||||
|
|
||||||
|
last_hash = U64_MAX;
|
||||||
|
last_type = 0;
|
||||||
|
|
||||||
end = (unsigned long)ei + item_size;
|
end = (unsigned long)ei + item_size;
|
||||||
while (ptr < end) {
|
while (ptr < end) {
|
||||||
iref = (struct btrfs_extent_inline_ref *)ptr;
|
iref = (struct btrfs_extent_inline_ref *)ptr;
|
||||||
type = btrfs_extent_inline_ref_type(eb, iref);
|
type = btrfs_extent_inline_ref_type(eb, iref);
|
||||||
offset = btrfs_extent_inline_ref_offset(eb, iref);
|
offset = btrfs_extent_inline_ref_offset(eb, iref);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BTRFS_TREE_BLOCK_REF_KEY:
|
case BTRFS_TREE_BLOCK_REF_KEY:
|
||||||
|
hash = offset;
|
||||||
|
|
||||||
ret = add_tree_backref(extent_cache, key.objectid,
|
ret = add_tree_backref(extent_cache, key.objectid,
|
||||||
0, offset, 0);
|
0, offset, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -5639,6 +5646,8 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BTRFS_SHARED_BLOCK_REF_KEY:
|
case BTRFS_SHARED_BLOCK_REF_KEY:
|
||||||
|
hash = offset;
|
||||||
|
|
||||||
ret = add_tree_backref(extent_cache, key.objectid,
|
ret = add_tree_backref(extent_cache, key.objectid,
|
||||||
offset, 0, 0);
|
offset, 0, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -5649,6 +5658,12 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
break;
|
break;
|
||||||
case BTRFS_EXTENT_DATA_REF_KEY:
|
case BTRFS_EXTENT_DATA_REF_KEY:
|
||||||
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
|
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
|
||||||
|
|
||||||
|
hash = hash_extent_data_ref(
|
||||||
|
btrfs_extent_data_ref_root(eb, dref),
|
||||||
|
btrfs_extent_data_ref_objectid(eb, dref),
|
||||||
|
btrfs_extent_data_ref_offset(eb, dref));
|
||||||
|
|
||||||
add_data_backref(extent_cache, key.objectid, 0,
|
add_data_backref(extent_cache, key.objectid, 0,
|
||||||
btrfs_extent_data_ref_root(eb, dref),
|
btrfs_extent_data_ref_root(eb, dref),
|
||||||
btrfs_extent_data_ref_objectid(eb,
|
btrfs_extent_data_ref_objectid(eb,
|
||||||
|
@ -5658,6 +5673,8 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
gen, 0, num_bytes);
|
gen, 0, num_bytes);
|
||||||
break;
|
break;
|
||||||
case BTRFS_SHARED_DATA_REF_KEY:
|
case BTRFS_SHARED_DATA_REF_KEY:
|
||||||
|
hash = offset;
|
||||||
|
|
||||||
sref = (struct btrfs_shared_data_ref *)(iref + 1);
|
sref = (struct btrfs_shared_data_ref *)(iref + 1);
|
||||||
add_data_backref(extent_cache, key.objectid, offset,
|
add_data_backref(extent_cache, key.objectid, offset,
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
|
@ -5665,6 +5682,7 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
gen, 0, num_bytes);
|
gen, 0, num_bytes);
|
||||||
break;
|
break;
|
||||||
case BTRFS_EXTENT_OWNER_REF_KEY:
|
case BTRFS_EXTENT_OWNER_REF_KEY:
|
||||||
|
hash = offset;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -5672,6 +5690,28 @@ static int process_extent_item(struct btrfs_root *root,
|
||||||
key.objectid, key.type, num_bytes);
|
key.objectid, key.type, num_bytes);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type != last_type) {
|
||||||
|
last_hash = U64_MAX;
|
||||||
|
|
||||||
|
if (type < last_type) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"inline extent refs out of order: key [%llu,%u,%llu]\n",
|
||||||
|
key.objectid, key.type, num_bytes);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash > last_hash) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"inline extent refs out of order: key [%llu,%u,%llu]\n",
|
||||||
|
key.objectid, key.type, num_bytes);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_hash = hash;
|
||||||
ptr += btrfs_extent_inline_ref_size(type);
|
ptr += btrfs_extent_inline_ref_size(type);
|
||||||
}
|
}
|
||||||
WARN_ON(ptr > end);
|
WARN_ON(ptr > end);
|
||||||
|
|
Loading…
Reference in New Issue