btrfs: backref, only collect file extent items matching backref offset
When resolving one backref of type EXTENT_DATA_REF, we collect all references that simply reference the EXTENT_ITEM even though their (file_pos - file_extent_item::offset) are not the same as the btrfs_extent_data_ref::offset we are searching for. This patch adds additional check so that we only collect references whose (file_pos - file_extent_item::offset) == btrfs_extent_data_ref::offset. Reviewed-by: Josef Bacik <josef@toxicpanda.com> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Signed-off-by: ethanwu <ethanwu@synology.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
9da2b242e2
commit
7ac8b88ee6
|
@ -347,33 +347,10 @@ static int add_prelim_ref(const struct btrfs_fs_info *fs_info,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ref->root_id = root_id;
|
ref->root_id = root_id;
|
||||||
if (key) {
|
if (key)
|
||||||
ref->key_for_search = *key;
|
ref->key_for_search = *key;
|
||||||
/*
|
else
|
||||||
* We can often find data backrefs with an offset that is too
|
|
||||||
* large (>= LLONG_MAX, maximum allowed file offset) due to
|
|
||||||
* underflows when subtracting a file's offset with the data
|
|
||||||
* offset of its corresponding extent data item. This can
|
|
||||||
* happen for example in the clone ioctl.
|
|
||||||
* So if we detect such case we set the search key's offset to
|
|
||||||
* zero to make sure we will find the matching file extent item
|
|
||||||
* at add_all_parents(), otherwise we will miss it because the
|
|
||||||
* offset taken form the backref is much larger then the offset
|
|
||||||
* of the file extent item. This can make us scan a very large
|
|
||||||
* number of file extent items, but at least it will not make
|
|
||||||
* us miss any.
|
|
||||||
* This is an ugly workaround for a behaviour that should have
|
|
||||||
* never existed, but it does and a fix for the clone ioctl
|
|
||||||
* would touch a lot of places, cause backwards incompatibility
|
|
||||||
* and would not fix the problem for extents cloned with older
|
|
||||||
* kernels.
|
|
||||||
*/
|
|
||||||
if (ref->key_for_search.type == BTRFS_EXTENT_DATA_KEY &&
|
|
||||||
ref->key_for_search.offset >= LLONG_MAX)
|
|
||||||
ref->key_for_search.offset = 0;
|
|
||||||
} else {
|
|
||||||
memset(&ref->key_for_search, 0, sizeof(ref->key_for_search));
|
memset(&ref->key_for_search, 0, sizeof(ref->key_for_search));
|
||||||
}
|
|
||||||
|
|
||||||
ref->inode_list = NULL;
|
ref->inode_list = NULL;
|
||||||
ref->level = level;
|
ref->level = level;
|
||||||
|
@ -424,6 +401,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
|
||||||
u64 disk_byte;
|
u64 disk_byte;
|
||||||
u64 wanted_disk_byte = ref->wanted_disk_byte;
|
u64 wanted_disk_byte = ref->wanted_disk_byte;
|
||||||
u64 count = 0;
|
u64 count = 0;
|
||||||
|
u64 data_offset;
|
||||||
|
|
||||||
if (level != 0) {
|
if (level != 0) {
|
||||||
eb = path->nodes[level];
|
eb = path->nodes[level];
|
||||||
|
@ -457,11 +435,15 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
|
||||||
|
|
||||||
fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
|
fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
|
||||||
disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
|
disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
|
||||||
|
data_offset = btrfs_file_extent_offset(eb, fi);
|
||||||
|
|
||||||
if (disk_byte == wanted_disk_byte) {
|
if (disk_byte == wanted_disk_byte) {
|
||||||
eie = NULL;
|
eie = NULL;
|
||||||
old = NULL;
|
old = NULL;
|
||||||
count++;
|
if (ref->key_for_search.offset == key.offset - data_offset)
|
||||||
|
count++;
|
||||||
|
else
|
||||||
|
goto next;
|
||||||
if (extent_item_pos) {
|
if (extent_item_pos) {
|
||||||
ret = check_extent_in_eb(&key, eb, fi,
|
ret = check_extent_in_eb(&key, eb, fi,
|
||||||
*extent_item_pos,
|
*extent_item_pos,
|
||||||
|
@ -513,6 +495,7 @@ static int resolve_indirect_ref(struct btrfs_fs_info *fs_info,
|
||||||
int root_level;
|
int root_level;
|
||||||
int level = ref->level;
|
int level = ref->level;
|
||||||
int index;
|
int index;
|
||||||
|
struct btrfs_key search_key = ref->key_for_search;
|
||||||
|
|
||||||
root_key.objectid = ref->root_id;
|
root_key.objectid = ref->root_id;
|
||||||
root_key.type = BTRFS_ROOT_ITEM_KEY;
|
root_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||||
|
@ -545,13 +528,33 @@ static int resolve_indirect_ref(struct btrfs_fs_info *fs_info,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can often find data backrefs with an offset that is too large
|
||||||
|
* (>= LLONG_MAX, maximum allowed file offset) due to underflows when
|
||||||
|
* subtracting a file's offset with the data offset of its
|
||||||
|
* corresponding extent data item. This can happen for example in the
|
||||||
|
* clone ioctl.
|
||||||
|
*
|
||||||
|
* So if we detect such case we set the search key's offset to zero to
|
||||||
|
* make sure we will find the matching file extent item at
|
||||||
|
* add_all_parents(), otherwise we will miss it because the offset
|
||||||
|
* taken form the backref is much larger then the offset of the file
|
||||||
|
* extent item. This can make us scan a very large number of file
|
||||||
|
* extent items, but at least it will not make us miss any.
|
||||||
|
*
|
||||||
|
* This is an ugly workaround for a behaviour that should have never
|
||||||
|
* existed, but it does and a fix for the clone ioctl would touch a lot
|
||||||
|
* of places, cause backwards incompatibility and would not fix the
|
||||||
|
* problem for extents cloned with older kernels.
|
||||||
|
*/
|
||||||
|
if (search_key.type == BTRFS_EXTENT_DATA_KEY &&
|
||||||
|
search_key.offset >= LLONG_MAX)
|
||||||
|
search_key.offset = 0;
|
||||||
path->lowest_level = level;
|
path->lowest_level = level;
|
||||||
if (time_seq == SEQ_LAST)
|
if (time_seq == SEQ_LAST)
|
||||||
ret = btrfs_search_slot(NULL, root, &ref->key_for_search, path,
|
ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
|
||||||
0, 0);
|
|
||||||
else
|
else
|
||||||
ret = btrfs_search_old_slot(root, &ref->key_for_search, path,
|
ret = btrfs_search_old_slot(root, &search_key, path, time_seq);
|
||||||
time_seq);
|
|
||||||
|
|
||||||
/* root node has been locked, we can release @subvol_srcu safely here */
|
/* root node has been locked, we can release @subvol_srcu safely here */
|
||||||
srcu_read_unlock(&fs_info->subvol_srcu, index);
|
srcu_read_unlock(&fs_info->subvol_srcu, index);
|
||||||
|
|
Loading…
Reference in New Issue