mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git
synced 2024-11-23 12:14:24 +08:00
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 ptr;
|
||||
int ret;
|
||||
int type;
|
||||
int type, last_type;
|
||||
u32 item_size = btrfs_item_size(eb, slot);
|
||||
u64 refs = 0;
|
||||
u64 offset;
|
||||
u64 num_bytes;
|
||||
u64 gen;
|
||||
int metadata = 0;
|
||||
u64 last_hash, hash;
|
||||
|
||||
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)
|
||||
ptr += sizeof(struct btrfs_tree_block_info);
|
||||
|
||||
last_hash = U64_MAX;
|
||||
last_type = 0;
|
||||
|
||||
end = (unsigned long)ei + item_size;
|
||||
while (ptr < end) {
|
||||
iref = (struct btrfs_extent_inline_ref *)ptr;
|
||||
type = btrfs_extent_inline_ref_type(eb, iref);
|
||||
offset = btrfs_extent_inline_ref_offset(eb, iref);
|
||||
|
||||
switch (type) {
|
||||
case BTRFS_TREE_BLOCK_REF_KEY:
|
||||
hash = offset;
|
||||
|
||||
ret = add_tree_backref(extent_cache, key.objectid,
|
||||
0, offset, 0);
|
||||
if (ret < 0) {
|
||||
@ -5639,6 +5646,8 @@ static int process_extent_item(struct btrfs_root *root,
|
||||
}
|
||||
break;
|
||||
case BTRFS_SHARED_BLOCK_REF_KEY:
|
||||
hash = offset;
|
||||
|
||||
ret = add_tree_backref(extent_cache, key.objectid,
|
||||
offset, 0, 0);
|
||||
if (ret < 0) {
|
||||
@ -5649,6 +5658,12 @@ static int process_extent_item(struct btrfs_root *root,
|
||||
break;
|
||||
case BTRFS_EXTENT_DATA_REF_KEY:
|
||||
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,
|
||||
btrfs_extent_data_ref_root(eb, dref),
|
||||
btrfs_extent_data_ref_objectid(eb,
|
||||
@ -5658,6 +5673,8 @@ static int process_extent_item(struct btrfs_root *root,
|
||||
gen, 0, num_bytes);
|
||||
break;
|
||||
case BTRFS_SHARED_DATA_REF_KEY:
|
||||
hash = offset;
|
||||
|
||||
sref = (struct btrfs_shared_data_ref *)(iref + 1);
|
||||
add_data_backref(extent_cache, key.objectid, offset,
|
||||
0, 0, 0,
|
||||
@ -5665,6 +5682,7 @@ static int process_extent_item(struct btrfs_root *root,
|
||||
gen, 0, num_bytes);
|
||||
break;
|
||||
case BTRFS_EXTENT_OWNER_REF_KEY:
|
||||
hash = offset;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
@ -5672,6 +5690,28 @@ static int process_extent_item(struct btrfs_root *root,
|
||||
key.objectid, key.type, num_bytes);
|
||||
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);
|
||||
}
|
||||
WARN_ON(ptr > end);
|
||||
|
Loading…
Reference in New Issue
Block a user