Re: [PATCH 1/2] ntfs: write back the truncated range before dropping it

From: liubaolin

Date: Tue Sep 15 2026 - 06:00:14 EST




在 2026/9/15 14:27, Hongling Zeng 写道:
On a truncate, ntfs_setattr_size() drops the page cache beyond the new
size with truncate_setsize() and only then updates the attribute on
disk. If that update fails, the size is reverted and the data beyond
the new size becomes visible again -- but the pages were already
dropped, and dirty data in them is lost, even though the truncate, the
operation the user asked for, never happened.

Write the range back before dropping it. If the writeback fails, fail
the truncate with that error and keep the page cache intact; if the
on-disk truncation fails afterwards, the reverted size can be re-read
from disk.

Fixes: 9c87959601e8 ("ntfs: update file operations")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Hongling Zeng <zenghongling@xxxxxxxxxx>
---
fs/ntfs/file.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 007d1614b9ac..1417a76788ed 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -283,12 +283,27 @@ static int ntfs_setattr_size(struct inode *vi, struct iattr *attr)
i_size_write(vi, attr->ia_size);
pagecache_isize_extended(vi, old_size, attr->ia_size);
} else {
+ /*
+ * The on-disk truncation below can fail, in which case
+ * the size is reverted and the data beyond the new size
+ * becomes visible again. Write that range back first:
+ * truncate_setsize() drops those pages and dirty data
+ * cannot be recovered afterwards.
+ */
+ if (attr->ia_size < old_size) {
+ err = filemap_write_and_wait_range(vi->i_mapping,
+ attr->ia_size, old_size - 1);
+ if (err)
+ goto out_unlock;
+ }
+
truncate_setsize(vi, attr->ia_size);
}
err = ntfs_truncate_vfs(vi, attr->ia_size, old_size);
if (err)
i_size_write(vi, old_size);
+out_unlock:
filemap_invalidate_unlock(vi->i_mapping);
return err;

Hi Hongling,
Thanks for the patch. This fixes the page-aligned truncate case.

However, this does not fully preserve the original data for a non-page-aligned truncate. For example, with 4 KiB pages, consider truncating an 8 KiB file to 3000 bytes. The writeback added by this patch preserves bytes 3000..8191 on disk, but truncate_setsize() eventually calls truncate_inode_partial_folio(), which zeroes bytes 3000..4095 in the folio containing the new EOF while leaving that folio in the page cache.
If ntfs_truncate_vfs() then fails, restoring only i_size makes 3000..4095 visible again, but the EOF folio is still uptodate. Reads can therefore return the cached zeros instead of refaulting the original data from disk.

The error path also needs to restore the folio containing the new EOF, or invalidate it so that it is refaulted from disk, with mmap writers taken into account.

Thanks,
Baolin.