Message ID | dd34707ffd1cd5a458980a209cfcc06a1817b848.1726149878.git.fdmanana@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs-progs: map-logical: fix search miss when extent is the first in a leaf | expand |
在 2024/9/12 23:35, fdmanana@kernel.org 写道: > From: Filipe Manana <fdmanana@suse.com> > > When searching the extent tree for the target extent item, we can miss it > if the extent item is the first item in a leaf and if there is a previous > leaf in the extent tree. > > For example, if we call btrfs-map-logical like this: > > $ btrfs-map-logical -l 5382144 /dev/sdc > > And we have the following extent tree layout: > > leaf 5386240 items 26 free space 2505 generation 7 owner EXTENT_TREE > leaf 5386240 flags 0x1(WRITTEN) backref revision 1 > (...) > item 25 key (5373952 METADATA_ITEM 0) itemoff 3155 itemsize 33 > refs 1 gen 7 flags TREE_BLOCK > tree block skinny level 0 > (176 0x5) tree block backref root FS_TREE > > leaf 5480448 items 56 free space 276 generation 7 owner EXTENT_TREE > leaf 5480448 flags 0x1(WRITTEN) backref revision 1 > (...) > item 0 key (5382144 METADATA_ITEM 0) itemoff 3962 itemsize 33 > refs 1 gen 7 flags TREE_BLOCK > tree block skinny level 0 > (176 0x7) tree block backref root CSUM_TREE > (...) > > Then the following happens: > > 1) We enter map_one_extent() with search_forward == 0 and > *logical_ret == 5382144; > > 2) We search for the key (5382144 0 0) which leaves us with a path > pointing to leaf 5386240 at slot 26 - one slot beyond the last item; > > 3) We then call: > > btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]) > > Which is not valid since there's no item at that slot, but since the > area of the leaf where an item at that slot should be is zeroed out, > we end up getting a key of (0 0 0); > > 4) We then enter the "if" statement bellow, since key.type is 0, and call > btrfs_previous_extent_item(), which leaves at slot 25 of leaf 5386240, > point to the extent item of the extent 5373952. > > The requested extent, 5382144, is the first item of the next leaf > (5480448), but we totally miss it; > > 5) We return to the caller, the main() function, with 'cur_logical' > pointing to the metadata extent at 5373952, and not to the requested > one at 5382144. > > In the last while loop of main() we have 'cur_logical' == 5373952, > which makes the loop have no iterations and therefore the local > variable 'found' remans with a value of 0, and then the program fails > like this: > > $ btrfs-map-logical -l 5382144 /dev/sdc > ERROR: no extent found at range [5382144,5386240) > > Fix this by never accessing beyond the last slot of a leaf. If we ever end > up at a slot beyond the last item in a leaf, just call btrfs_next_leaf() > and process the first item in the returned path. > > Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: Qu Wenruo <wqu@suse.com> Thanks, Qu > --- > btrfs-map-logical.c | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c > index 2984e645..a97afea4 100644 > --- a/btrfs-map-logical.c > +++ b/btrfs-map-logical.c > @@ -74,6 +74,11 @@ static int map_one_extent(struct btrfs_fs_info *fs_info, > BUG_ON(ret == 0); > ret = 0; > > + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { > + ret = btrfs_next_leaf(extent_root, path); > + if (ret) > + goto out; > + } > again: > btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); > if ((search_forward && key.objectid < logical) ||
On Thu, Sep 12, 2024 at 03:05:44PM +0100, fdmanana@kernel.org wrote: > From: Filipe Manana <fdmanana@suse.com> > > When searching the extent tree for the target extent item, we can miss it > if the extent item is the first item in a leaf and if there is a previous > leaf in the extent tree. > > For example, if we call btrfs-map-logical like this: > > $ btrfs-map-logical -l 5382144 /dev/sdc > > And we have the following extent tree layout: > > leaf 5386240 items 26 free space 2505 generation 7 owner EXTENT_TREE > leaf 5386240 flags 0x1(WRITTEN) backref revision 1 > (...) > item 25 key (5373952 METADATA_ITEM 0) itemoff 3155 itemsize 33 > refs 1 gen 7 flags TREE_BLOCK > tree block skinny level 0 > (176 0x5) tree block backref root FS_TREE > > leaf 5480448 items 56 free space 276 generation 7 owner EXTENT_TREE > leaf 5480448 flags 0x1(WRITTEN) backref revision 1 > (...) > item 0 key (5382144 METADATA_ITEM 0) itemoff 3962 itemsize 33 > refs 1 gen 7 flags TREE_BLOCK > tree block skinny level 0 > (176 0x7) tree block backref root CSUM_TREE > (...) > > Then the following happens: > > 1) We enter map_one_extent() with search_forward == 0 and > *logical_ret == 5382144; > > 2) We search for the key (5382144 0 0) which leaves us with a path > pointing to leaf 5386240 at slot 26 - one slot beyond the last item; > > 3) We then call: > > btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]) > > Which is not valid since there's no item at that slot, but since the > area of the leaf where an item at that slot should be is zeroed out, > we end up getting a key of (0 0 0); > > 4) We then enter the "if" statement bellow, since key.type is 0, and call > btrfs_previous_extent_item(), which leaves at slot 25 of leaf 5386240, > point to the extent item of the extent 5373952. > > The requested extent, 5382144, is the first item of the next leaf > (5480448), but we totally miss it; > > 5) We return to the caller, the main() function, with 'cur_logical' > pointing to the metadata extent at 5373952, and not to the requested > one at 5382144. > > In the last while loop of main() we have 'cur_logical' == 5373952, > which makes the loop have no iterations and therefore the local > variable 'found' remans with a value of 0, and then the program fails > like this: > > $ btrfs-map-logical -l 5382144 /dev/sdc > ERROR: no extent found at range [5382144,5386240) > > Fix this by never accessing beyond the last slot of a leaf. If we ever end > up at a slot beyond the last item in a leaf, just call btrfs_next_leaf() > and process the first item in the returned path. > > Signed-off-by: Filipe Manana <fdmanana@suse.com> Added to devel, thanks.
diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c index 2984e645..a97afea4 100644 --- a/btrfs-map-logical.c +++ b/btrfs-map-logical.c @@ -74,6 +74,11 @@ static int map_one_extent(struct btrfs_fs_info *fs_info, BUG_ON(ret == 0); ret = 0; + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + ret = btrfs_next_leaf(extent_root, path); + if (ret) + goto out; + } again: btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if ((search_forward && key.objectid < logical) ||