Re: [f2fs-dev] [PATCH v4 2/2] f2fs: fix to zero post-EOF data when extending file size

Zhiguo Niu <[email protected]> Mon, 10 Aug 2026 16:52:56 +0800
Newsgroups net.sourceforge.lists.linux-f2fs-devel,org.kernel.vger.linux-kernel
Message-ID <CAHJ8P3K3pBm4DodRGpWpR3PMURUeq811h1+yvp9MtXVcUZ5b2w@mail.gmail.com>
Chao Yu via Linux-f2fs-devel <[email protected]>
于2026年8月9日周日 09:21写道:
>
> generic/794  4s ... - output mismatch (see /share/git/fstests/results//generic/794.out.bad)
>     --- tests/generic/794.out   2026-06-12 08:46:32.766426241 +0800
>     +++ /share/git/fstests/results//generic/794.out.bad 2026-07-05 18:32:55.000000000 +0800
>     @@ -1,4 +1,16 @@
>      QA output created by 794
>      append_write
>     +FAIL: non-zero data in gap [4080,4096) after shutdown+remount
>     +000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  >ZZZZZZZZZZZZZZZZ<
>     +*
>     +001000
>      truncate_up
>     ...
>     (Run 'diff -u /share/git/fstests/tests/generic/794.out /share/git/fstests/results//generic/794.out.bad'  to see the entire diff)
> Ran: generic/794
> Failures: generic/794
> Failed 1 of 1 tests
>
> Steps of generic/794:
> 1. write 4096 bytes to file w/ 0x5a
> 2. use fiemap to get PBA of first block in file
> 3. truncate file to 4080
> 4. umount; write 4096 bytes to file w/ 0x5a directly via PBA; mount
> 5. extend filesize via
>    a) append 4096 from offset 4096, or
>    b) truncate 8192, or
>    c) fallocate 4096 from offset 4096
> 6. verify the gap is zeroed in memory [4080,4096)
> 7. sync range 4096 from offset 4096; shutdown -f (flush meta before shutdown)
> 8. umount; mount; verify [4080,4096) is zeroed or not.
>
> When extending file size (e.g. via truncate, fallocate, or write) across an
> unaligned EOF boundary, we need to ensure that post-EOF data in the partial
> page is zeroed out in pagecache and marked dirty, meanwhile, tagging the inode
> with FI_ZERO_POST_EOF, so that following checkpoint() and fsync() can persist
Hi Chao,
It looks like this version doesn't have this flag "FI_ZERO_POST_EOF"?
thanks!
> the page contain zeroed data before committing inode w/ updated i_size.
>
> This help to prevent stale disk data beyond the previous EOF from being exposed
> after remounting or crash recovery.
>
> Since f2fs is a LFS filesystem, we only support direct write via PBA in pinfile,
> and pinfile has section-aligned filesize, so in Android, there should no problem,
> but for other usage in different environment, let's fix this w/ fsync_mode=strict
> mount option.
>
> Cc: [email protected]
> Signed-off-by: Chao Yu <[email protected]>
> ---
> v4:
> - fix to receive return value of f2fs_zero_post_eof_page() w/ @err rather than
> @ret in f2fs_vm_page_mkwrite().
>  fs/f2fs/file.c | 100 +++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 84 insertions(+), 16 deletions(-)
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index be319a2e9830..446d2328f975 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -36,16 +36,52 @@
>  #include <trace/events/f2fs.h>
>  #include <uapi/linux/f2fs.h>
>
> -static void f2fs_zero_post_eof_page(struct inode *inode,
> -                                       loff_t new_size, bool lock)
> +static int fill_zero(struct inode *inode, pgoff_t index,
> +                                       loff_t start, loff_t len);
> +
> +static int do_zero_post_eof_page(struct inode *inode, loff_t new_size)
> +{
> +       loff_t old_size = i_size_read(inode);
> +       unsigned int offset, len;
> +       pgoff_t index;
> +       int err;
> +
> +       offset = old_size & (PAGE_SIZE - 1);
> +
> +       if (!offset)
> +               return 0;
> +
> +       len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
> +       index = old_size >> PAGE_SHIFT;
> +
> +       if (f2fs_has_inline_data(inode)) {
> +               /* data post eof should be always zero */
> +               if (new_size <= MAX_INLINE_DATA(inode))
> +                       return 0;
> +               err = f2fs_convert_inline_inode(inode);
> +               if (err)
> +                       return err;
> +       }
> +
> +       err = fill_zero(inode, index, offset, len);
> +       if (err)
> +               return err;
> +       return filemap_write_and_wait_range(inode->i_mapping,
> +                               old_size, old_size + len - 1);
> +}
> +
> +static int f2fs_zero_post_eof_page(struct inode *inode,
> +                                       loff_t new_size, bool lock, bool writeback)
>  {
>         loff_t old_size = i_size_read(inode);
> +       bool strict =
> +               F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT;
>
>         if (old_size >= new_size)
> -               return;
> +               return 0;
>
> -       if (mapping_empty(inode->i_mapping))
> -               return;
> +       if (!strict && mapping_empty(inode->i_mapping))
> +               return 0;
>
>         if (lock)
>                 filemap_invalidate_lock(inode->i_mapping);
> @@ -53,6 +89,16 @@ static void f2fs_zero_post_eof_page(struct inode *inode,
>         truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
>         if (lock)
>                 filemap_invalidate_unlock(inode->i_mapping);
> +
> +       if (!writeback || !strict)
> +               return 0;
> +       /*
> +        * In fsync_mode=strict, when we expand an unaligned EOF size, we
> +        * should zero post EOF data and writeback the data immediately,
> +        * so that it can avoid exposing stale data after metadata flush
> +        * and POR.
> +        */
> +       return do_zero_post_eof_page(inode, new_size);
>  }
>
>  static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
> @@ -132,7 +178,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
>
>         f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
>
> -       f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT, true);
> +       err = f2fs_zero_post_eof_page(inode,
> +               (folio->index + 1) << PAGE_SHIFT, true, false);
> +       if (err)
> +               goto out_pagefault;
>
>         file_update_time(vmf->vma->vm_file);
>         filemap_invalidate_lock_shared(inode->i_mapping);
> @@ -189,7 +238,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
>
>  out_sem:
>         filemap_invalidate_unlock_shared(inode->i_mapping);
> -
> +out_pagefault:
>         sb_end_pagefault(inode->i_sb);
>  out:
>         ret = vmf_fs_error(err);
> @@ -1175,8 +1224,12 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
>                 f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
>                 filemap_invalidate_lock(inode->i_mapping);
>
> -               if (attr->ia_size > old_size)
> -                       f2fs_zero_post_eof_page(inode, attr->ia_size, false);
> +               if (attr->ia_size > old_size) {
> +                       err = f2fs_zero_post_eof_page(inode,
> +                               attr->ia_size, false, true);
> +                       if (err)
> +                               goto err_out;
> +               }
>                 truncate_setsize(inode, attr->ia_size);
>
>                 if (attr->ia_size <= old_size)
> @@ -1185,6 +1238,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
>                  * do not trim all blocks after i_size if target size is
>                  * larger than i_size.
>                  */
> +err_out:
>                 filemap_invalidate_unlock(inode->i_mapping);
>                 f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
>                 if (err)
> @@ -1296,7 +1350,9 @@ static int f2fs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
>         if (ret)
>                 return ret;
>
> -       f2fs_zero_post_eof_page(inode, offset + len, true);
> +       ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
> +       if (ret)
> +               return ret;
>
>         pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
>         pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
> @@ -1583,7 +1639,9 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
>         f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>         filemap_invalidate_lock(inode->i_mapping);
>
> -       f2fs_zero_post_eof_page(inode, offset + len, false);
> +       ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
> +       if (ret)
> +               goto out_unlock;
>
>         f2fs_lock_op(sbi, &lc);
>         f2fs_drop_extent_tree(inode);
> @@ -1591,6 +1649,7 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
>         ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true);
>         f2fs_unlock_op(sbi, &lc);
>
> +out_unlock:
>         filemap_invalidate_unlock(inode->i_mapping);
>         f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>         return ret;
> @@ -1712,7 +1771,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
>         if (ret)
>                 return ret;
>
> -       f2fs_zero_post_eof_page(inode, offset + len, true);
> +       ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
> +       if (ret)
> +               return ret;
>
>         pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
>         pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
> @@ -1847,7 +1908,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
>         f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>         filemap_invalidate_lock(mapping);
>
> -       f2fs_zero_post_eof_page(inode, offset + len, false);
> +       ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
> +       if (ret)
> +               goto out_unlock;
>         truncate_pagecache(inode, offset);
>
>         while (!ret && idx > pg_start) {
> @@ -1865,6 +1928,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
>                                         idx + delta, nr, false);
>                 f2fs_unlock_op(sbi, &lc);
>         }
> +out_unlock:
>         filemap_invalidate_unlock(mapping);
>         f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>         if (ret)
> @@ -1907,7 +1971,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
>         if (err)
>                 return err;
>
> -       f2fs_zero_post_eof_page(inode, offset + len, true);
> +       err = f2fs_zero_post_eof_page(inode, offset + len, true, true);
> +       if (err)
> +               return err;
>
>         f2fs_balance_fs(sbi, true);
>
> @@ -5023,8 +5089,10 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
>         if (err)
>                 return err;
>
> -       f2fs_zero_post_eof_page(inode,
> -               iocb->ki_pos + iov_iter_count(from), true);
> +       err = f2fs_zero_post_eof_page(inode,
> +               iocb->ki_pos + iov_iter_count(from), true, true);
> +       if (err)
> +               return err;
>         return count;
>  }
>
> --
> 2.49.0
>
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel


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