mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 0/3] ntfs: fix volume flag update races and persist the recorded error state
@ 2026-09-07  7:08 Hongling Zeng
  2026-09-07  7:08 ` [PATCH v3 1/3] ntfs: fix volume flag update races Hongling Zeng
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Hongling Zeng @ 2026-09-07  7:08 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, zhongling0719, Hongling Zeng

Hi all,

First of all, my apologies for the many versions of this patch.  I
should have folded the reviewer feedback in and resent a single coherent
series sooner; the split across v1/v2 and follow-ups made the thread
harder to follow than it needed to be.  This v3 consolidates everything
into one series of three patches.

To recap the review discussion:

The original patch (now 1/3) fixed the lost-update race where
ntfs_set_volume_flags() and ntfs_clear_volume_flags() computed the new
value from vol->vol_flags outside any lock.  The review correctly
pointed out that a race remained in ntfs_sync_fs(): it checked
NVolErrors() outside the critical section, so a concurrent error path
could still record an error after the check and before the clear, and
the volume would end up persisted as clean despite the recorded error,
meaning chkdsk would not run on the next mount.

That race is now fixed in 1/3 itself: the NVolErrors() check and the
clearing of VOLUME_IS_DIRTY both happen inside the same mrec_lock
critical section, via a dedicated ntfs_clear_volume_dirty_if_no_errors()
helper.  With that, every interleaving of the sync thread and the error
path leaves the volume dirty on disk.

Patches 2/3 then close the writer-side half of the window: the runtime
metadata-corruption paths in fs/ntfs recorded only the in-memory
NVolErrors() flag and never persisted VOLUME_IS_DIRTY at all, so a
volume could unmount with a clean on-disk flag despite recorded
corruption.  Persisting from the error paths themselves is not an
option: they run under a wide variety of ntfs locks (runlist locks,
vol->lcnbmp_lock, vol->mftbmp_lock, mrec_locks) while the dirty-bit
write needs the $Volume mrec_lock and can take the $MFT runlist lock,
which self-deadlocks or forms ABBA cycles.  Instead, 2/3 makes
persistence a property of the lock-free sync contexts: the new
ntfs_sync_volume_dirty_state() sets VOLUME_IS_DIRTY when NVolErrors()
is recorded and clears it otherwise, evaluating the error flag under
the $Volume mrec_lock.  It is called from ntfs_sync_fs(), the
remount-to-read-only path and ntfs_put_super(), with NV_Hibernated
gating so a hibernated volume is never written by these paths.  The
guarantee is eventual rather than instantaneous (a crash between the
error record and the next persistence point remains a window);
ntfs_put_super() runs on a quiesced filesystem after evict_inodes(),
so a volume that is read-write at unmount time cannot unmount clean.

Patch 3/3 is pure hygiene found during review: the load_system_files()
error unwind leaves a stale vol->vol_ino pointer in place, and carries
a dead iput() inside the IS_ERR(vol->vol_ino) branch; NULL the pointer
like ntfs_put_super() and the ntfs_fill_super() error path already do.

One housekeeping note: this series was rebased onto the current ntfs
tree after the first posting failed to apply there.  Dennis Tighe's
"ntfs: do not mark the volume clean in sync_fs when errors were
recorded" landed in the meantime and fixed part of the same sync_fs
problem this series addresses; patch 2/3 supersedes that fix with the
ntfs_sync_volume_dirty_state() helper, which guards the clear in every
persistence context, not just sync_fs.  No conflict with its intent.

Patch layout:

  1/3 ntfs: fix volume flag update races
      Move the read-modify-write of vol->vol_flags inside the
      $Volume mrec_lock; ntfs_sync_fs() clears the dirty bit under
      the lock with the NVolErrors() check.

  2/3 ntfs: sync the volume dirty bit with the recorded error state
      New ntfs_sync_volume_dirty_state(), called from the lock-free
      sync contexts (sync_fs / remount-ro / put_super); NV_Hibernated
      gating.

  3/3 ntfs: NULL vol->vol_ino in the load_system_files() error
      teardown
      Drop the stale pointer and the dead iput().

The series builds cleanly with W=1.

Thanks,
Hongling

^ permalink raw reply	[flat|nested] 15+ messages in thread
* [PATCH v4 0/4] ntfs: fix volume flag update races and persist the recorded error state
@ 2026-09-09  2:48 Hongling Zeng
  2026-09-09  2:48 ` [PATCH v3 2/3] ntfs: sync the volume dirty bit with " Hongling Zeng
  0 siblings, 1 reply; 15+ messages in thread
From: Hongling Zeng @ 2026-09-09  2:48 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, zhongling0719, Hongling Zeng

Hi all,

v4 of the series, incorporating Baolin Wang's review of v3.  The 3/3
hygiene patch ("ntfs: NULL vol->vol_ino in the load_system_files()
error teardown") has been applied separately, so this respin carries
the remaining work as four patches.  1/4 and 3/4 are unchanged from v3
(formerly 1/3 and 2/3); 2/4 and 4/4 are new and close the two races
Baolin spotted.

Changes since v3:

  2/4 (new): the callers in file.c and namei.c checked VOLUME_IS_DIRTY
      without any lock before calling ntfs_set_volume_flags(), so the
      check could race with the ntfs_sync_fs() clear and leave the
      volume clean on disk despite a metadata modification.  They now
      call ntfs_set_volume_flags() unconditionally; with the
      unchanged-value check under the mrec_lock from 1/4 this is a
      cheap no-op in the write itself when the bit is already set, at
      the cost of taking the lock on every call - see 2/4's commit
      message for why that cannot be avoided.

  4/4 (new): ntfs_put_super() persisted the dirty state before the
      just-in-case mftmirr/mft commits and the final write_inode_now(),
      which can themselves record NVolErrors(); errors from those
      points would leave the volume unmounted with a clean on-disk
      dirty bit, contradicting the "cannot unmount clean" guarantee.
      The persistence now happens after the last of them, with the
      iput(vol->vol_ino) moved to the end of ntfs_put_super() so
      vol->vol_ino is still available.

To recap the discussion that shaped v3:

The original patch (now 1/4) fixed the lost-update race where
ntfs_set_volume_flags() and ntfs_clear_volume_flags() computed the new
value from vol->vol_flags outside any lock.  The review correctly
pointed out that a race remained in ntfs_sync_fs(): it checked
NVolErrors() outside the critical section, so a concurrent error path
could still record an error after the check and before the clear, and
the volume would end up persisted as clean despite the recorded error.
1/4 fixes both: the read-modify-write and the NVolErrors()-checked
clearing of VOLUME_IS_DIRTY happen inside the same mrec_lock critical
section.

3/4 closes the writer-side half of the window: the runtime
metadata-corruption paths in fs/ntfs recorded only the in-memory
NVolErrors() flag and never persisted VOLUME_IS_DIRTY at all, so a
volume could unmount with a clean on-disk flag despite recorded
corruption.  Persisting from the error paths themselves is not an
option: they run under a wide variety of ntfs locks (runlist locks,
vol->lcnbmp_lock, vol->mftbmp_lock, mrec_locks) while the dirty-bit
write needs the $Volume mrec_lock and can take the $MFT runlist lock,
which self-deadlocks or forms ABBA cycles.  Instead, 3/4 makes
persistence a property of the lock-free sync contexts: the new
ntfs_sync_volume_dirty_state() sets VOLUME_IS_DIRTY when NVolErrors()
is recorded and clears it otherwise, evaluating the error flag under
the $Volume mrec_lock.  It is called from ntfs_sync_fs(), the
remount-to-read-only path and ntfs_put_super(), with NV_Hibernated
gating so a hibernated volume is never written by these paths.  The
guarantee is eventual rather than instantaneous (a crash between the
error record and the next persistence point remains a window); with
4/4, ntfs_put_super() persists after the last commit that can record
errors, so a volume that is read-write at unmount time cannot unmount
clean.

One housekeeping note: this series was rebased onto the current ntfs
tree after the first posting failed to apply there.  Dennis Tighe's
"ntfs: do not mark the volume clean in sync_fs when errors were
recorded" landed in the meantime and fixed part of the same sync_fs
problem this series addresses; 3/4 supersedes that fix with the
ntfs_sync_volume_dirty_state() helper, which guards the clear in every
persistence context, not just sync_fs.  No conflict with its intent.

Patch layout:

  1/4 ntfs: fix volume flag update races
      Move the read-modify-write of vol->vol_flags inside the
      $Volume mrec_lock; ntfs_sync_fs() clears the dirty bit under
      the lock with the NVolErrors() check.

  2/4 ntfs: set the volume dirty bit unconditionally on metadata
      changes
      Drop the racy caller-side VOLUME_IS_DIRTY checks in file.c and
      namei.c; the locked unchanged-value check makes the unconditional
      call skip the write when the bit is already set.

  3/4 ntfs: sync the volume dirty bit with the recorded error state
      New ntfs_sync_volume_dirty_state(), called from the lock-free
      sync contexts (sync_fs / remount-ro / put_super); NV_Hibernated
      gating.

  4/4 ntfs: persist the dirty state after the final put_super()
      commits
      Move the persistence after the last commits that can record
      errors, with the iput(vol->vol_ino) moved to the end.

Thanks,
Hongling Zeng (4):
  ntfs: fix volume flag update races
  ntfs: set the volume dirty bit unconditionally on metadata changes
  ntfs: sync the volume dirty bit with the recorded error state
  ntfs: persist the dirty state after the final put_super() commits

 fs/ntfs/file.c   |  14 ++--
 fs/ntfs/namei.c  |  24 +++----
 fs/ntfs/super.c  | 140 +++++++++++++++++++++++++++++++------------
 fs/ntfs/volume.h |   4 ++
 4 files changed, 130 insertions(+), 52 deletions(-)

^ permalink raw reply	[flat|nested] 15+ messages in thread
* [PATCH v3 0/3] ntfs: fix volume flag update races and persist the recorded error state
@ 2026-09-07  6:22 Hongling Zeng
  2026-09-07  6:22 ` [PATCH v3 2/3] ntfs: sync the volume dirty bit with " Hongling Zeng
  0 siblings, 1 reply; 15+ messages in thread
From: Hongling Zeng @ 2026-09-07  6:22 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, zhongling0719, Hongling Zeng

Hi all,

First of all, my apologies for the many versions of this patch.  I
should have folded the reviewer feedback in and resent a single coherent
series sooner; the split across v1/v2 and follow-ups made the thread
harder to follow than it needed to be.  This v3 consolidates everything
into one series of three patches.

To recap the review discussion:

The original patch (now 1/3) fixed the lost-update race where
ntfs_set_volume_flags() and ntfs_clear_volume_flags() computed the new
value from vol->vol_flags outside any lock.  The review correctly
pointed out that a race remained in ntfs_sync_fs(): it checked
NVolErrors() outside the critical section, so a concurrent error path
could still record an error after the check and before the clear, and
the volume would end up persisted as clean despite the recorded error,
meaning chkdsk would not run on the next mount.

That race is now fixed in 1/3 itself: the NVolErrors() check and the
clearing of VOLUME_IS_DIRTY both happen inside the same mrec_lock
critical section, via a dedicated ntfs_clear_volume_dirty_if_no_errors()
helper.  With that, every interleaving of the sync thread and the error
path leaves the volume dirty on disk.

Patches 2/3 then close the writer-side half of the window: the runtime
metadata-corruption paths in fs/ntfs recorded only the in-memory
NVolErrors() flag and never persisted VOLUME_IS_DIRTY at all, so a
volume could unmount with a clean on-disk flag despite recorded
corruption.  Persisting from the error paths themselves is not an
option: they run under a wide variety of ntfs locks (runlist locks,
vol->lcnbmp_lock, vol->mftbmp_lock, mrec_locks) while the dirty-bit
write needs the $Volume mrec_lock and can take the $MFT runlist lock,
which self-deadlocks or forms ABBA cycles.  Instead, 2/3 makes
persistence a property of the lock-free sync contexts: the new
ntfs_sync_volume_dirty_state() sets VOLUME_IS_DIRTY when NVolErrors()
is recorded and clears it otherwise, evaluating the error flag under
the $Volume mrec_lock.  It is called from ntfs_sync_fs(), the
remount-to-read-only path and ntfs_put_super(), with NV_Hibernated
gating so a hibernated volume is never written by these paths.  The
guarantee is eventual rather than instantaneous (a crash between the
error record and the next persistence point remains a window);
ntfs_put_super() runs on a quiesced filesystem after evict_inodes(),
so a volume that is read-write at unmount time cannot unmount clean.

Patch 3/3 is pure hygiene found during review: the load_system_files()
error unwind leaves a stale vol->vol_ino pointer in place, and carries
a dead iput() inside the IS_ERR(vol->vol_ino) branch; NULL the pointer
like ntfs_put_super() and the ntfs_fill_super() error path already do.

Patch layout:

  1/3 ntfs: fix volume flag update races
      Move the read-modify-write of vol->vol_flags inside the
      $Volume mrec_lock; ntfs_sync_fs() clears the dirty bit under
      the lock with the NVolErrors() check.

  2/3 ntfs: sync the volume dirty bit with the recorded error state
      New ntfs_sync_volume_dirty_state(), called from the lock-free
      sync contexts (sync_fs / remount-ro / put_super); NV_Hibernated
      gating.

  3/3 ntfs: NULL vol->vol_ino in the load_system_files() error
      teardown
      Drop the stale pointer and the dead iput().

The series builds cleanly with W=1.

Thanks,
Hongling

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

end of thread, other threads:[~2026-09-09  2:48 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-07  7:08 [PATCH v3 0/3] ntfs: fix volume flag update races and persist the recorded error state Hongling Zeng
2026-09-07  7:08 ` [PATCH v3 1/3] ntfs: fix volume flag update races Hongling Zeng
2026-09-08  5:29   ` liubaolin
2026-09-08  9:36     ` Hongling Zeng
2026-09-07  7:08 ` [PATCH v3 2/3] ntfs: sync the volume dirty bit with the recorded error state Hongling Zeng
2026-09-08  4:10   ` Namjae Jeon
2026-09-08  7:20     ` Hongling Zeng
2026-09-08  5:23   ` liubaolin
2026-09-08  9:22     ` Hongling Zeng
2026-09-07  7:08 ` [PATCH v3 3/3] ntfs: NULL vol->vol_ino in the load_system_files() error teardown Hongling Zeng
2026-09-08  4:38   ` liubaolin
2026-09-08  9:42   ` Namjae Jeon
2026-09-08  4:32 ` [PATCH v3 0/3] ntfs: fix volume flag update races and persist the recorded error state liubaolin
  -- strict thread matches above, loose matches on Subject: below --
2026-09-09  2:48 [PATCH v4 0/4] " Hongling Zeng
2026-09-09  2:48 ` [PATCH v3 2/3] ntfs: sync the volume dirty bit with " Hongling Zeng
2026-09-07  6:22 [PATCH v3 0/3] ntfs: fix volume flag update races and persist " Hongling Zeng
2026-09-07  6:22 ` [PATCH v3 2/3] ntfs: sync the volume dirty bit with " 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®