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: af0db57d42 ("ntfs: update inode operations")
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-10-07 17:12:45 +09:00
parent 3e38885ff6
commit 58442927b7

View File

@ -8,6 +8,7 @@
#include <linux/writeback.h>
#include <linux/seq_file.h>
#include <linux/mm.h>
#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;
}