From 3b35deeaddcb5510b6ed1067c3836a6791a56e0f Mon Sep 17 00:00:00 2001 From: Su Yanjun Date: Wed, 27 Feb 2019 14:05:48 +0800 Subject: [PATCH] btrfs-progs: check: fix wrong @offset used in find_possible_backrefs() Function find_possible_backrefs() is used to locate the file extents referring to an data extent. For data extent backref, its btrfs_extent_data_ref structure has the following members: - root Which root refers to this data extent - objectid Which inode refers to this data extent - offset Search *hint*. Its value is @file_offset - @extent_offset. While for @file_offset, it's directly recorded in (INO EXTENT_DATA FILE_OFFSET) key. So when searching the file extents refers to this data extent, we can't use btrfs_extent_data_ref::offset as search key::offset. We must search from file offset 0, and iterate all file extents until we hit a file extent matches the data backref. Thankfully such time consuming behavior is not triggered frequently, it only gets called for repair, so it shouldn't affect normal check routine. Signed-off-by: Su Yanjun [Update commit message] Signed-off-by: Qu Wenruo --- check/main.c | 110 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 92 insertions(+), 18 deletions(-) diff --git a/check/main.c b/check/main.c index 4e03da66..5755530f 100644 --- a/check/main.c +++ b/check/main.c @@ -7132,6 +7132,89 @@ out: return ret ? ret : nr_del; } +/* + * Based extent backref item, we find all file extent items in the fs tree. By + * the info we can rebuild the extent backref item + */ +static int __find_possible_backrefs(struct btrfs_root *root, + u64 owner, u64 offset, u64 bytenr, u64 *refs_ret, + u64 *bytes_ret) +{ + int ret = 0; + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_key found_key; + struct btrfs_file_extent_item *fi; + struct extent_buffer *leaf; + u64 backref_offset, disk_bytenr; + int slot; + + btrfs_init_path(&path); + + key.objectid = owner; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret > 0) + ret = -ENOENT; + if (ret) { + btrfs_release_path(&path); + return ret; + } + + btrfs_release_path(&path); + + key.objectid = owner; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) { + btrfs_release_path(&path); + return ret; + } + + while (1) { + leaf = path.nodes[0]; + slot = path.slots[0]; + + if (slot >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, &path); + if (ret) { + if (ret > 0) + ret = 0; + break; + } + + leaf = path.nodes[0]; + slot = path.slots[0]; + } + + btrfs_item_key_to_cpu(leaf, &found_key, slot); + if ((found_key.objectid != owner) || + (found_key.type != BTRFS_EXTENT_DATA_KEY)) + break; + + fi = btrfs_item_ptr(leaf, slot, + struct btrfs_file_extent_item); + + backref_offset = found_key.offset - + btrfs_file_extent_offset(leaf, fi); + disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + *bytes_ret = btrfs_file_extent_disk_num_bytes(leaf, + fi); + if ((disk_bytenr == bytenr) && + (backref_offset == offset)) { + (*refs_ret)++; + } + path.slots[0]++; + } + + btrfs_release_path(&path); + return ret; +} + static int find_possible_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path, struct cache_tree *extent_cache, @@ -7141,9 +7224,9 @@ static int find_possible_backrefs(struct btrfs_fs_info *info, struct extent_backref *back, *tmp; struct data_backref *dback; struct cache_extent *cache; - struct btrfs_file_extent_item *fi; struct btrfs_key key; u64 bytenr, bytes; + u64 refs; int ret; rbtree_postorder_for_each_entry_safe(back, tmp, @@ -7171,24 +7254,15 @@ static int find_possible_backrefs(struct btrfs_fs_info *info, if (IS_ERR(root)) return PTR_ERR(root); - key.objectid = dback->owner; - key.type = BTRFS_EXTENT_DATA_KEY; - key.offset = dback->offset; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret) { - btrfs_release_path(path); - if (ret < 0) - return ret; - /* Didn't find it, we can carry on */ - ret = 0; + refs = 0; + bytes = 0; + ret = __find_possible_backrefs(root, dback->owner, + dback->offset, rec->start, &refs, &bytes); + if (ret) continue; - } - fi = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_file_extent_item); - bytenr = btrfs_file_extent_disk_bytenr(path->nodes[0], fi); - bytes = btrfs_file_extent_disk_num_bytes(path->nodes[0], fi); - btrfs_release_path(path); + bytenr = rec->start; + cache = lookup_cache_extent(extent_cache, bytenr, 1); if (cache) { struct extent_record *tmp; @@ -7207,7 +7281,7 @@ static int find_possible_backrefs(struct btrfs_fs_info *info, continue; } - dback->found_ref += 1; + dback->found_ref += refs; dback->disk_bytenr = bytenr; dback->bytes = bytes;