From: Yongpeng Yang <[email protected]>

The following scenario can cause fiemap to report incorrect extents:

$ mkfs.f2fs /dev/vdb -f
$ mount -o mode=lfs /dev/vdb /mnt/f2fs/
$ dd if=/dev/urandom of=data bs=4K count=874 conv=notrunc
$ f2fs_io fiemap 0 1000000 data 1
$ shrink all extent
$ dd if=/dev/urandom of=data bs=4K count=150 seek=874 conv=notrunc
$ f2fs_io fiemap 0 1000000 data 1
Fiemap: offset = 0 len = 1000000
        logical addr.    physical addr.   length           flags
0       0000000000000000 00000002868d4000 000000000036a000 00001000
1       000000000036a000 0000000286c3e000 0000000000096000 00001001

The root cause is that when the largest extent is not in the extent
tree, mergeable extents are not merged, causing f2fs_map_blocks to
misjudge and output an incorrect extent list.

Fix this by allowing the extent being inserted to merge with the largest
extent. When updating the extent tree range, if the new extent can be
front-merged or back-merged with the largest extent and the largest
extent is not in the rb-tree, merge them before the normal lookup.

Fixes: 429511cdf8b3 ("f2fs: add core functions for rb-tree extent cache")
Signed-off-by: Yongpeng Yang <[email protected]>
---
 fs/f2fs/extent_cache.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 61f6b9714366..aa368a01b035 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -702,6 +702,27 @@ static void __update_extent_tree_range(struct inode *inode,
                __drop_largest_extent(et, fofs, len);
        }
 
+       if (et->largest.len != 0 &&
+                       (__is_front_mergeable(tei, &et->largest, type) ||
+                        __is_back_mergeable(tei, &et->largest, type))) {
+               /* 0. try to merge with largest extent. */
+               en = __lookup_extent_node_ret(&et->root,
+                               et->cached_en, et->largest.fofs,
+                               &prev_en, &next_en,
+                               &insert_p, &insert_parent,
+                               &leftmost);
+               if (!en) {
+                       if (__is_back_mergeable(tei, &et->largest, type)) {
+                               tei->fofs = et->largest.fofs;
+                               tei->blk = et->largest.blk;
+                               fofs = tei->fofs;
+                       }
+                       tei->len += et->largest.len;
+                       len = tei->len;
+                       end = fofs + len;
+               }
+       }
+
        /* 1. lookup first extent node in range [fofs, fofs + len - 1] */
        en = __lookup_extent_node_ret(&et->root,
                                        et->cached_en, fofs,
-- 
2.43.0



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to