Skip to content

Commit b42e2ae

Browse files
ethanwu-synoksacilotto
authored andcommitted
btrfs: backref, only collect file extent items matching backref offset
BugLink: https://bugs.launchpad.net/bugs/1916061 commit 7ac8b88 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Kamal Mostafa <kamal@canonical.com> Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
1 parent 94695e5 commit b42e2ae

File tree

1 file changed

+33
-30
lines changed

1 file changed

+33
-30
lines changed

fs/btrfs/backref.c

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -347,33 +347,10 @@ static int add_prelim_ref(const struct btrfs_fs_info *fs_info,
347347
return -ENOMEM;
348348

349349
ref->root_id = root_id;
350-
if (key) {
350+
if (key)
351351
ref->key_for_search = *key;
352-
/*
353-
* We can often find data backrefs with an offset that is too
354-
* large (>= LLONG_MAX, maximum allowed file offset) due to
355-
* underflows when subtracting a file's offset with the data
356-
* offset of its corresponding extent data item. This can
357-
* happen for example in the clone ioctl.
358-
* So if we detect such case we set the search key's offset to
359-
* zero to make sure we will find the matching file extent item
360-
* at add_all_parents(), otherwise we will miss it because the
361-
* offset taken form the backref is much larger then the offset
362-
* of the file extent item. This can make us scan a very large
363-
* number of file extent items, but at least it will not make
364-
* us miss any.
365-
* This is an ugly workaround for a behaviour that should have
366-
* never existed, but it does and a fix for the clone ioctl
367-
* would touch a lot of places, cause backwards incompatibility
368-
* and would not fix the problem for extents cloned with older
369-
* kernels.
370-
*/
371-
if (ref->key_for_search.type == BTRFS_EXTENT_DATA_KEY &&
372-
ref->key_for_search.offset >= LLONG_MAX)
373-
ref->key_for_search.offset = 0;
374-
} else {
352+
else
375353
memset(&ref->key_for_search, 0, sizeof(ref->key_for_search));
376-
}
377354

378355
ref->inode_list = NULL;
379356
ref->level = level;
@@ -424,6 +401,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
424401
u64 disk_byte;
425402
u64 wanted_disk_byte = ref->wanted_disk_byte;
426403
u64 count = 0;
404+
u64 data_offset;
427405

428406
if (level != 0) {
429407
eb = path->nodes[level];
@@ -457,11 +435,15 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
457435

458436
fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
459437
disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
438+
data_offset = btrfs_file_extent_offset(eb, fi);
460439

461440
if (disk_byte == wanted_disk_byte) {
462441
eie = NULL;
463442
old = NULL;
464-
count++;
443+
if (ref->key_for_search.offset == key.offset - data_offset)
444+
count++;
445+
else
446+
goto next;
465447
if (extent_item_pos) {
466448
ret = check_extent_in_eb(&key, eb, fi,
467449
*extent_item_pos,
@@ -513,6 +495,7 @@ static int resolve_indirect_ref(struct btrfs_fs_info *fs_info,
513495
int root_level;
514496
int level = ref->level;
515497
int index;
498+
struct btrfs_key search_key = ref->key_for_search;
516499

517500
root_key.objectid = ref->root_id;
518501
root_key.type = BTRFS_ROOT_ITEM_KEY;
@@ -545,13 +528,33 @@ static int resolve_indirect_ref(struct btrfs_fs_info *fs_info,
545528
goto out;
546529
}
547530

531+
/*
532+
* We can often find data backrefs with an offset that is too large
533+
* (>= LLONG_MAX, maximum allowed file offset) due to underflows when
534+
* subtracting a file's offset with the data offset of its
535+
* corresponding extent data item. This can happen for example in the
536+
* clone ioctl.
537+
*
538+
* So if we detect such case we set the search key's offset to zero to
539+
* make sure we will find the matching file extent item at
540+
* add_all_parents(), otherwise we will miss it because the offset
541+
* taken form the backref is much larger then the offset of the file
542+
* extent item. This can make us scan a very large number of file
543+
* extent items, but at least it will not make us miss any.
544+
*
545+
* This is an ugly workaround for a behaviour that should have never
546+
* existed, but it does and a fix for the clone ioctl would touch a lot
547+
* of places, cause backwards incompatibility and would not fix the
548+
* problem for extents cloned with older kernels.
549+
*/
550+
if (search_key.type == BTRFS_EXTENT_DATA_KEY &&
551+
search_key.offset >= LLONG_MAX)
552+
search_key.offset = 0;
548553
path->lowest_level = level;
549554
if (time_seq == SEQ_LAST)
550-
ret = btrfs_search_slot(NULL, root, &ref->key_for_search, path,
551-
0, 0);
555+
ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
552556
else
553-
ret = btrfs_search_old_slot(root, &ref->key_for_search, path,
554-
time_seq);
557+
ret = btrfs_search_old_slot(root, &search_key, path, time_seq);
555558

556559
/* root node has been locked, we can release @subvol_srcu safely here */
557560
srcu_read_unlock(&fs_info->subvol_srcu, index);

0 commit comments

Comments
 (0)