* [PATCH v4] ntfs: handle a failed size rollback in the write error path
@ 2026-10-20 7:06 Hongling Zeng
2026-10-21 1:52 ` Hyunchul Lee
0 siblings, 1 reply; 3+ messages in thread
From: Hongling Zeng @ 2026-10-20 7:06 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 data size back through the same
VFS-facing truncate ntfs_setattr_size() uses, which also supports
compressed attributes, and on failure keep the VFS size on the
durable data size. Roll the data size back first: its shrink clamps
the initialized size to the rolled-back data size, which can sit
above the old initialized size and expose the uninitialized tail, so
the initialized size rollback runs after it. 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 v4:
- roll the data size back before the initialized size: the shrink
clamps the initialized size to the rolled-back data size, which
can sit above the old initialized size and expose the
uninitialized tail.
Changes in v3:
- skip the initialized size rollback on a resident attribute;
- roll the data size back through __ntfs_attr_truncate_vfs(), which
ntfs_setattr_size() uses, so compressed attributes roll back too;
- keep the VFS size on the durable data size if the rollback fails.
---
fs/ntfs/file.c | 41 ++++++++++++++++++++++++++++++++---------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index d810ca5904bd..ad032cccfb15 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -934,23 +934,46 @@ 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;
+ if (ni->data_size != old_data_size) {
+ struct ntfs_inode *mrec_ni = ntfs_base_inode(ni);
+
+ truncate_setsize(vi, old_data_size);
+ /*
+ * ntfs_attr_truncate() would reject a compressed
+ * attribute.
+ */
mutex_lock(&mrec_ni->mrec_lock);
- ntfs_attr_set_initialized_size(ni, old_init_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 (ni->data_size != old_data_size) {
- struct ntfs_inode *mrec_ni =
- ntfs_base_inode(ni);
+ /*
+ * The shrink above may leave the initialized size above
+ * the old one; ntfs_attr_set_initialized_size() rejects
+ * a resident attribute.
+ */
+ if (ni->initialized_size != old_init_size &&
+ NInoNonResident(ni)) {
+ 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);
+ init_err = ntfs_attr_set_initialized_size(ni,
+ old_init_size);
mutex_unlock(&mrec_ni->mrec_lock);
}
+ 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);
+ }
}
out_lock:
inode_unlock(vi);
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v4] ntfs: handle a failed size rollback in the write error path
2026-10-20 7:06 [PATCH v4] ntfs: handle a failed size rollback in the write error path Hongling Zeng
@ 2026-10-21 1:52 ` Hyunchul Lee
2026-10-21 2:52 ` Hongling Zeng
0 siblings, 1 reply; 3+ messages in thread
From: Hyunchul Lee @ 2026-10-21 1:52 UTC (permalink / raw)
To: Hongling Zeng; +Cc: linkinjeon, ntfs, linux-kernel, zhongling0719
> diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
> index d810ca5904bd..ad032cccfb15 100644
> --- a/fs/ntfs/file.c
> +++ b/fs/ntfs/file.c
> @@ -934,23 +934,46 @@ 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;
>
> + if (ni->data_size != old_data_size) {
> + struct ntfs_inode *mrec_ni = ntfs_base_inode(ni);
> +
> + truncate_setsize(vi, old_data_size);
> + /*
> + * ntfs_attr_truncate() would reject a compressed
> + * attribute.
> + */
> mutex_lock(&mrec_ni->mrec_lock);
> - ntfs_attr_set_initialized_size(ni, old_init_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 (ni->data_size != old_data_size) {
> - struct ntfs_inode *mrec_ni =
> - ntfs_base_inode(ni);
> + /*
> + * The shrink above may leave the initialized size above
> + * the old one; ntfs_attr_set_initialized_size() rejects
> + * a resident attribute.
> + */
> + if (ni->initialized_size != old_init_size &&
> + NInoNonResident(ni)) {
> + 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);
> + init_err = ntfs_attr_set_initialized_size(ni,
> + old_init_size);
The shrink above can clamp initialzied_size to old_data_size. If
ntfs_attr_set_initialized_size() fails, the initialized_size remains
instead of being restored to old_init_size. For a compressed file,
this can expose stale data.
Could you restore the initialized_size first and perform the shink only
if that rollback succeeds?
--
Thanks,
Hyunchul
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v4] ntfs: handle a failed size rollback in the write error path
2026-10-21 1:52 ` Hyunchul Lee
@ 2026-10-21 2:52 ` Hongling Zeng
0 siblings, 0 replies; 3+ messages in thread
From: Hongling Zeng @ 2026-10-21 2:52 UTC (permalink / raw)
To: Hyunchul Lee, Hongling Zeng; +Cc: linkinjeon, ntfs, linux-kernel
在 2026年10月21日 09:52, Hyunchul Lee 写道:
>> diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
>> index d810ca5904bd..ad032cccfb15 100644
>> --- a/fs/ntfs/file.c
>> +++ b/fs/ntfs/file.c
>> @@ -934,23 +934,46 @@ 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;
>>
>> + if (ni->data_size != old_data_size) {
>> + struct ntfs_inode *mrec_ni = ntfs_base_inode(ni);
>> +
>> + truncate_setsize(vi, old_data_size);
>> + /*
>> + * ntfs_attr_truncate() would reject a compressed
>> + * attribute.
>> + */
>> mutex_lock(&mrec_ni->mrec_lock);
>> - ntfs_attr_set_initialized_size(ni, old_init_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 (ni->data_size != old_data_size) {
>> - struct ntfs_inode *mrec_ni =
>> - ntfs_base_inode(ni);
>> + /*
>> + * The shrink above may leave the initialized size above
>> + * the old one; ntfs_attr_set_initialized_size() rejects
>> + * a resident attribute.
>> + */
>> + if (ni->initialized_size != old_init_size &&
>> + NInoNonResident(ni)) {
>> + 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);
>> + init_err = ntfs_attr_set_initialized_size(ni,
>> + old_init_size);
> The shrink above can clamp initialzied_size to old_data_size. If
> ntfs_attr_set_initialized_size() fails, the initialized_size remains
> instead of being restored to old_init_size. For a compressed file,
> this can expose stale data.
>
> Could you restore the initialized_size first and perform the shink only
> if that rollback succeeds?
>
ok,you are right, I will roll the initialized size back first, and
skip the data size rollback when it fails. With the initialized size
restored, the clamp keeps it at the old initialized size at most,
and when the restore fails, both sizes stay extended, which keeps the
on-disk initialized size below the data size. Will post v5.
Thanks!
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-21 2:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 7:06 [PATCH v4] ntfs: handle a failed size rollback in the write error path Hongling Zeng
2026-10-21 1:52 ` Hyunchul Lee
2026-10-21 2:52 ` Hongling Zeng
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®