mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v5] ntfs: handle a failed size rollback in the write error path
@ 2026-10-21  6:32 Hongling Zeng
  2026-10-22  5:52 ` Hyunchul Lee
  0 siblings, 1 reply; 2+ messages in thread
From: Hongling Zeng @ 2026-10-21  6:32 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, zhongling0719, Hongling Zeng

ntfs_file_write_iter() extends the data and initialized sizes before
it copies the user data and restores them when the write then fails.
The restoration is best-effort: both results are discarded, even
though the extension is already recorded in the dirty MFT record.
When the restoration fails too, the extended sizes reach the disk
while the VFS inode keeps reporting the old ones: after a remount the
file appears grown again, with an uninitialized tail where the failed
write never put any data, and nothing records the failure.

Keep both undo results: roll the initialized size back first, and
roll the data size back only when that rollback succeeded.  The data
rollback goes through the same VFS-facing truncate
ntfs_setattr_size() uses, which also supports compressed attributes,
and keeps the VFS size on the durable data size when it fails.  Its
shrink clamps the initialized size to the rolled-back data size,
which can sit above the old initialized size, so a failed initialized
size rollback would leave the clamp in place and expose the
uninitialized tail.  The undo runs under the mapping invalidate lock,
like ntfs_setattr_size(), which the truncate expects.  On an undo
failure, set NVolSetErrors(), which the next persistence point
persists as VOLUME_IS_DIRTY, and log a message for the errors=
policy.

Fixes: 9c87959601e8 ("ntfs: update file operations")
Suggested-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
---
Changes in v5:
- roll the initialized size back before the data size and skip the
  data rollback when it fails;
- take the mapping invalidate lock around the undo, which
  __ntfs_attr_truncate_vfs() expects.
---
 fs/ntfs/file.c | 46 ++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 8 deletions(-)

diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index d810ca5904bd..03ff5f11a0f2 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -934,23 +934,53 @@ ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 				&ntfs_iomap_folio_ops, NULL);
 out:
 	if (ret < 0 && ret != -EIOCBQUEUED) {
-		if (ni->initialized_size != old_init_size) {
-			struct ntfs_inode *mrec_ni =
-				ntfs_base_inode(ni);
+		int init_err = 0;
+		int data_err = 0;
+
+		/*
+		 * Like ntfs_setattr_size(): __ntfs_attr_truncate_vfs()
+		 * expects the invalidate lock held across the size
+		 * updates.
+		 */
+		filemap_invalidate_lock(vi->i_mapping);
+
+		/* ntfs_attr_set_initialized_size() rejects a resident one. */
+		if (ni->initialized_size != old_init_size &&
+		    NInoNonResident(ni)) {
+			struct ntfs_inode *mrec_ni = ntfs_base_inode(ni);
 
 			mutex_lock(&mrec_ni->mrec_lock);
-			ntfs_attr_set_initialized_size(ni, old_init_size);
+			init_err = ntfs_attr_set_initialized_size(ni,
+					old_init_size);
 			mutex_unlock(&mrec_ni->mrec_lock);
 		}
-		if (ni->data_size != old_data_size) {
-			struct ntfs_inode *mrec_ni =
-				ntfs_base_inode(ni);
+		/*
+		 * The shrink clamps the initialized size to the data size,
+		 * which can sit above the old initialized one and expose
+		 * the uninitialized tail, so skip it unless the rollback
+		 * above restored that size.
+		 */
+		if (ni->data_size != old_data_size && !init_err) {
+			struct ntfs_inode *mrec_ni = ntfs_base_inode(ni);
 
 			truncate_setsize(vi, old_data_size);
 			mutex_lock(&mrec_ni->mrec_lock);
-			ntfs_attr_truncate(ni, old_data_size);
+			data_err = __ntfs_attr_truncate_vfs(ni, old_data_size,
+					old_data_size);
 			mutex_unlock(&mrec_ni->mrec_lock);
+			/* Like ntfs_setattr_size(): stay on the durable size. */
+			if (data_err)
+				i_size_write(vi, ni->data_size);
 		}
+		if (init_err || data_err) {
+			/* The failed sizes stay in the dirty MFT record. */
+			NVolSetErrors(vol);
+			ntfs_error(vi->i_sb,
+				   "Failed to roll back the size update: %d",
+				   init_err ?: data_err);
+		}
+
+		filemap_invalidate_unlock(vi->i_mapping);
 	}
 out_lock:
 	inode_unlock(vi);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH v5] ntfs: handle a failed size rollback in the write error path
  2026-10-21  6:32 [PATCH v5] ntfs: handle a failed size rollback in the write error path Hongling Zeng
@ 2026-10-22  5:52 ` Hyunchul Lee
  0 siblings, 0 replies; 2+ messages in thread
From: Hyunchul Lee @ 2026-10-22  5:52 UTC (permalink / raw)
  To: Hongling Zeng; +Cc: linkinjeon, ntfs, linux-kernel, zhongling0719

On Wed, Oct 21, 2026 at 02:32:21PM +0800, Hongling Zeng wrote:
> ntfs_file_write_iter() extends the data and initialized sizes before
> it copies the user data and restores them when the write then fails.
> The restoration is best-effort: both results are discarded, even
> though the extension is already recorded in the dirty MFT record.
> When the restoration fails too, the extended sizes reach the disk
> while the VFS inode keeps reporting the old ones: after a remount the
> file appears grown again, with an uninitialized tail where the failed
> write never put any data, and nothing records the failure.
> 
> Keep both undo results: roll the initialized size back first, and
> roll the data size back only when that rollback succeeded.  The data
> rollback goes through the same VFS-facing truncate
> ntfs_setattr_size() uses, which also supports compressed attributes,
> and keeps the VFS size on the durable data size when it fails.  Its
> shrink clamps the initialized size to the rolled-back data size,
> which can sit above the old initialized size, so a failed initialized
> size rollback would leave the clamp in place and expose the
> uninitialized tail.  The undo runs under the mapping invalidate lock,
> like ntfs_setattr_size(), which the truncate expects.  On an undo
> failure, set NVolSetErrors(), which the next persistence point
> persists as VOLUME_IS_DIRTY, and log a message for the errors=
> policy.
> 
> Fixes: 9c87959601e8 ("ntfs: update file operations")
> Suggested-by: Hyunchul Lee <hyc.lee@gmail.com>
> Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>

Looks good to me.

Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>

-- 
Thanks,
Hyunchul

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-22  5:52 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-21  6:32 [PATCH v5] ntfs: handle a failed size rollback in the write error path Hongling Zeng
2026-10-22  5:52 ` Hyunchul Lee

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®