fs/f2fs/file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
From: Zhan Xusheng <zhanxusheng1024@gmail.com>
From: Zhan Xusheng <zhanxusheng@xiaomi.com>
Commit 4275b59673eb ("f2fs: fix to round down start offset of fallocate
for pin file") moved the allocation loop's start down to a section
boundary, but the error path still converts @expanded against @pg_start,
which holds the unrounded start.
@pg_start exists for that conversion: commit 88f2cfc5fa90 ("f2fs: fix to
update last i_size if fallocate partially succeeds") added it as an
immutable base because map.m_lblk moves every round. Each round now maps
exactly sec_blks blocks starting from rounddown(pg_start, sec_blks), so
pg_start + expanded overshoots the last allocated block by
pg_start % sec_blks, and a partial failure leaves i_size covering a tail
that was never allocated. Nothing corrects that afterwards either, since
file_dont_truncate() has already cleared FADVISE_TRUNC_BIT.
Keep @pg_start pointing at where allocation actually begins.
Fixes: 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file")
Cc: stable@vger.kernel.org
Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
---
fs/f2fs/file.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 4b52c56d71f0..cc0d2b8c4684 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1919,8 +1919,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
block_t sec_len;
if (map.m_lblk % sec_blks) {
- map.m_lblk = rounddown(map.m_lblk, sec_blks);
- map.m_len = pg_end - map.m_lblk;
+ pg_start = rounddown(map.m_lblk, sec_blks);
+ map.m_lblk = pg_start;
+ map.m_len = pg_end - pg_start;
if (off_end)
map.m_len++;
}
--
2.43.0
On 8/17/26 10:27, Zhan Xusheng wrote:
> From: Zhan Xusheng <zhanxusheng1024@gmail.com>
>
> From: Zhan Xusheng <zhanxusheng@xiaomi.com>
>
> Commit 4275b59673eb ("f2fs: fix to round down start offset of fallocate
> for pin file") moved the allocation loop's start down to a section
> boundary, but the error path still converts @expanded against @pg_start,
> which holds the unrounded start.
>
> @pg_start exists for that conversion: commit 88f2cfc5fa90 ("f2fs: fix to
> update last i_size if fallocate partially succeeds") added it as an
> immutable base because map.m_lblk moves every round. Each round now maps
> exactly sec_blks blocks starting from rounddown(pg_start, sec_blks), so
> pg_start + expanded overshoots the last allocated block by
> pg_start % sec_blks, and a partial failure leaves i_size covering a tail
> that was never allocated. Nothing corrects that afterwards either, since
> file_dont_truncate() has already cleared FADVISE_TRUNC_BIT.
>
> Keep @pg_start pointing at where allocation actually begins.
Hi Xuesheng,
Can you please provide a reproducer?
Thanks,
>
> Fixes: 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file")
> Cc: stable@vger.kernel.org
> Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
> ---
> fs/f2fs/file.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 4b52c56d71f0..cc0d2b8c4684 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -1919,8 +1919,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
> block_t sec_len;
>
> if (map.m_lblk % sec_blks) {
> - map.m_lblk = rounddown(map.m_lblk, sec_blks);
> - map.m_len = pg_end - map.m_lblk;
> + pg_start = rounddown(map.m_lblk, sec_blks);
> + map.m_lblk = pg_start;
> + map.m_len = pg_end - pg_start;
> if (off_end)
> map.m_len++;
> }
On 8/17/26 11:12, Chao Yu wrote:
> Can you please provide a reproducer?
It needs a start offset that is not section aligned, plus a fallocate that
hits ENOSPC partway so the error path runs with expanded > 0.
truncate -s 80M img
mkfs.f2fs -s 1 -f img # 2 MiB sections, sec_blks = 512
mount -o loop img /mnt
touch /mnt/pinned
f2fs_io pinfile set /mnt/pinned
# 2093056 = block 511, so pg_start % sec_blks = 511
f2fs_io fallocate 0 2093056 536870912 /mnt/pinned
stat -c %s /mnt/pinned
filefrag -v /mnt/pinned
7.2, last extent and i_size:
ext: logical_offset: length: flags:
2: 5120.. 10737: 5618: last,merged
i_size=46075904
i_size block 11249, allocated through 10738, overshoot 511
With this patch:
ext: logical_offset: length: flags:
2: 5120.. 10737: 5618: last,merged,eof
i_size=43982848
i_size block 10738, allocated through 10738, overshoot 0
filefrag prints eof only once the last extent reaches i_size, so its
absence in the first run shows the mismatch on its own.
7.0, before 4275b59673eb, also gives overshoot 0. Absolute block numbers
differ there because the start rounding changes how much fits before
ENOSPC, so only the overshoot compares across the three.
I will add this to the changelog in v2.
Thanks,
Zhan Xusheng
© 2016 - 2026 Red Hat, Inc.