From 58442927b729acbe2b7c7f7ad888d040c44faa3e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 7 Oct 2026 17:12:45 +0900 Subject: [PATCH] ntfs: clear stale pagecache on initialized-size extension iomap_zero_range() skips holes, so EOF-polluted cache data can survive gap zeroing. Discard the uninitialized cache range before advancing initialized_size so later reads cannot expose stale bytes. Perform the cleanup in ntfs_extend_initialized_size() so both file writes and shared writable mmap preparation use it. Hold the invalidate lock while reading initialized_size, clearing the cache, zeroing the gap, and updating the attribute. Release the lock on every exit path. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: Namjae Jeon --- fs/ntfs/inode.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index eca0724c4358..6cfe8f24e4cc 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -8,6 +8,7 @@ #include #include +#include #include "lcnalloc.h" #include "time.h" @@ -2484,38 +2485,50 @@ int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset, const loff_t new_size) { struct ntfs_inode *ni = NTFS_I(vi); - loff_t old_init_size; + loff_t old_init_size, clear_size; unsigned long flags; - int err; + int err = 0; + if (!NInoNonResident(ni)) + return -EINVAL; + + filemap_invalidate_lock(vi->i_mapping); read_lock_irqsave(&ni->size_lock, flags); old_init_size = ni->initialized_size; read_unlock_irqrestore(&ni->size_lock, flags); - if (!NInoNonResident(ni)) - return -EINVAL; if (old_init_size >= new_size) - return 0; + goto out; err = ntfs_attr_map_whole_runlist(ni); if (err) - return err; + goto out; - if (!NInoCompressed(ni) && old_init_size < offset) { - err = iomap_zero_range(vi, old_init_size, - offset - old_init_size, - NULL, &ntfs_seek_iomap_ops, - &ntfs_iomap_folio_ops, NULL); - if (err) - return err; + if (!NInoCompressed(ni)) { + /* + * Zeroing skips holes, so discard cached uninitialized bytes + * before making them visible to writes or shared mappings. + */ + clear_size = max_t(loff_t, i_size_read(vi), new_size); + truncate_pagecache_range(vi, old_init_size, clear_size - 1); + + if (old_init_size < offset) { + err = iomap_zero_range(vi, old_init_size, + offset - old_init_size, + NULL, &ntfs_seek_iomap_ops, + &ntfs_iomap_folio_ops, NULL); + if (err) + goto out; + } } - mutex_lock(&ni->mrec_lock); err = ntfs_attr_set_initialized_size(ni, new_size); mutex_unlock(&ni->mrec_lock); if (err) truncate_setsize(vi, old_init_size); +out: + filemap_invalidate_unlock(vi->i_mapping); return err; }