* [PATCH] ntfs: account for MFT records added during allocation [not found] <20260908094416.10534-1-linkinjeon@kernel.org> @ 2026-09-08 9:44 ` Namjae Jeon 2026-09-12 16:24 ` Hyunchul Lee 0 siblings, 1 reply; 2+ messages in thread From: Namjae Jeon @ 2026-09-08 9:44 UTC (permalink / raw) To: hyc.lee; +Cc: ntfs, Namjae Jeon, stable When no free MFT record is available in the initialized $MFT/$BITMAP, ntfs_mft_record_alloc() extends $MFT/$DATA and formats the requested record together with a dynamically sized tail reserve. Those records become visible through the $MFT file size before charging the requested record to the free-record counter. Account for all newly visible records before releasing the MFT allocation lock, then subtract the one record being allocated. Keep MFT counter updates independent of the asynchronous free-cluster scan and update the counter when a record is successfully cleared in the MFT bitmap. Store the clamped result of the MFT bitmap scan and keep statfs from exposing an invalid cached count if an accounting error occurs. Fixes: 115380f9a2f9 ("ntfs: update mft operations") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> --- fs/ntfs/mft.c | 11 ++++++++--- fs/ntfs/super.c | 12 +++++++++--- fs/ntfs/volume.h | 6 ------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index 61d4ee3a57fd..f0656d0bbeeb 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -1539,7 +1539,6 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(struct ntfs_volume *vol) ret = ntfs_attr_set(mftbmp_ni, old_initialized_size, 8, 0); if (likely(!ret)) { ntfs_debug("Done. (Wrote eight initialized bytes to mft bitmap."); - ntfs_inc_free_mft_records(vol, 8 * 8); return 0; } ntfs_error(vol->sb, "Failed to write to mft bitmap."); @@ -2135,6 +2134,7 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode, struct mft_record **ni_mrec, const s64 mft_data_vcn) { s64 ll, bit, old_data_initialized, old_data_size; + s64 nr_new_mft_records = 0; s64 max_mft_no = -1, reserve_start = -1, reserve_end = -1; s64 candidate_reserve_end = -1; s64 *reserve_endp; @@ -2501,8 +2501,13 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode, mft_ni->initialized_size); WARN_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_size); WARN_ON(mft_ni->initialized_size > i_size_read(vol->mft_ino)); + nr_new_mft_records = (i_size_read(vol->mft_ino) - old_data_size) >> + vol->mft_record_size_bits; read_unlock_irqrestore(&mft_ni->size_lock, flags); mft_rec_already_initialized: + /* Account for newly visible MFT records before dropping the lock. */ + if (nr_new_mft_records > 0) + ntfs_inc_free_mft_records(vol, nr_new_mft_records); /* * We can finally drop the mft bitmap lock as the mft data attribute * has been fully updated. The only disparity left is that the @@ -2813,6 +2818,8 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) if (base_ni->mft_no != FILE_MFT) down_write(&vol->mftbmp_lock); err = ntfs_bitmap_clear_bit(vol->mftbmp_ino, mft_no); + if (!err) + ntfs_inc_free_mft_records(vol, 1); if (!err && base_ni->mft_no == FILE_MFT && mft_no + 1 == vol->mft_record_reserve_pos && mft_no < vol->mft_record_reserve_end) @@ -2822,9 +2829,7 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) memalloc_nofs_restore(memalloc_flags); if (err) goto bitmap_rollback; - unmap_mft_record(ni); - ntfs_inc_free_mft_records(vol, 1); return 0; /* Rollback what we did... */ diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 60d43339c590..0867d6a82ebe 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2064,8 +2064,7 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, /* If errors occurred we may well have gone below zero, fix this. */ if (nr_free < 0) nr_free = 0; - else - atomic64_set(&vol->free_mft_records, nr_free); + atomic64_set(&vol->free_mft_records, nr_free); ntfs_debug("Exiting."); return nr_free; @@ -2131,7 +2130,14 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs) read_unlock_irqrestore(&mft_ni->size_lock, flags); /* Free inodes in fs (based on current total count). */ - sfs->f_ffree = atomic64_read(&vol->free_mft_records); + size = atomic64_read(&vol->free_mft_records); + if (unlikely(size < 0 || size > (s64)sfs->f_files)) + ntfs_warning(vol->sb, "Invalid free MFT record count %lld.", size); + if (size < 0) + size = 0; + else if (size > (s64)sfs->f_files) + size = sfs->f_files; + sfs->f_ffree = size; /* * File system id. This is extremely *nix flavour dependent and even diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index 2b60d14fc7ef..bc85a9592245 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -258,17 +258,11 @@ static inline void ntfs_dec_free_clusters(struct ntfs_volume *vol, s64 nr) static inline void ntfs_inc_free_mft_records(struct ntfs_volume *vol, s64 nr) { - if (!NVolFreeClusterKnown(vol)) - return; - atomic64_add(nr, &vol->free_mft_records); } static inline void ntfs_dec_free_mft_records(struct ntfs_volume *vol, s64 nr) { - if (!NVolFreeClusterKnown(vol)) - return; - atomic64_sub(nr, &vol->free_mft_records); } -- 2.25.1 ^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] ntfs: account for MFT records added during allocation 2026-09-08 9:44 ` [PATCH] ntfs: account for MFT records added during allocation Namjae Jeon @ 2026-09-12 16:24 ` Hyunchul Lee 0 siblings, 0 replies; 2+ messages in thread From: Hyunchul Lee @ 2026-09-12 16:24 UTC (permalink / raw) To: Namjae Jeon; +Cc: ntfs, stable 2026년 9월 8일 (화) 오후 6:44, Namjae Jeon <linkinjeon@kernel.org>님이 작성: > > When no free MFT record is available in the initialized $MFT/$BITMAP, > ntfs_mft_record_alloc() extends $MFT/$DATA and formats the requested record > together with a dynamically sized tail reserve. Those records become > visible through the $MFT file size before charging the requested record to > the free-record counter. > > Account for all newly visible records before releasing the MFT allocation > lock, then subtract the one record being allocated. Keep MFT counter > updates independent of the asynchronous free-cluster scan and update the > counter when a record is successfully cleared in the MFT bitmap. > > Store the clamped result of the MFT bitmap scan and keep statfs from > exposing an invalid cached count if an accounting error occurs. > > Fixes: 115380f9a2f9 ("ntfs: update mft operations") > Cc: stable@vger.kernel.org > Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Looks good to me. Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> > --- > fs/ntfs/mft.c | 11 ++++++++--- > fs/ntfs/super.c | 12 +++++++++--- > fs/ntfs/volume.h | 6 ------ > 3 files changed, 17 insertions(+), 12 deletions(-) > > diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c > index 61d4ee3a57fd..f0656d0bbeeb 100644 > --- a/fs/ntfs/mft.c > +++ b/fs/ntfs/mft.c > @@ -1539,7 +1539,6 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(struct ntfs_volume *vol) > ret = ntfs_attr_set(mftbmp_ni, old_initialized_size, 8, 0); > if (likely(!ret)) { > ntfs_debug("Done. (Wrote eight initialized bytes to mft bitmap."); > - ntfs_inc_free_mft_records(vol, 8 * 8); > return 0; > } > ntfs_error(vol->sb, "Failed to write to mft bitmap."); > @@ -2135,6 +2134,7 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode, > struct mft_record **ni_mrec, const s64 mft_data_vcn) > { > s64 ll, bit, old_data_initialized, old_data_size; > + s64 nr_new_mft_records = 0; > s64 max_mft_no = -1, reserve_start = -1, reserve_end = -1; > s64 candidate_reserve_end = -1; > s64 *reserve_endp; > @@ -2501,8 +2501,13 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode, > mft_ni->initialized_size); > WARN_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_size); > WARN_ON(mft_ni->initialized_size > i_size_read(vol->mft_ino)); > + nr_new_mft_records = (i_size_read(vol->mft_ino) - old_data_size) >> > + vol->mft_record_size_bits; > read_unlock_irqrestore(&mft_ni->size_lock, flags); > mft_rec_already_initialized: > + /* Account for newly visible MFT records before dropping the lock. */ > + if (nr_new_mft_records > 0) > + ntfs_inc_free_mft_records(vol, nr_new_mft_records); > /* > * We can finally drop the mft bitmap lock as the mft data attribute > * has been fully updated. The only disparity left is that the > @@ -2813,6 +2818,8 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) > if (base_ni->mft_no != FILE_MFT) > down_write(&vol->mftbmp_lock); > err = ntfs_bitmap_clear_bit(vol->mftbmp_ino, mft_no); > + if (!err) > + ntfs_inc_free_mft_records(vol, 1); > if (!err && base_ni->mft_no == FILE_MFT && > mft_no + 1 == vol->mft_record_reserve_pos && > mft_no < vol->mft_record_reserve_end) > @@ -2822,9 +2829,7 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) > memalloc_nofs_restore(memalloc_flags); > if (err) > goto bitmap_rollback; > - > unmap_mft_record(ni); > - ntfs_inc_free_mft_records(vol, 1); > return 0; > > /* Rollback what we did... */ > diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c > index 60d43339c590..0867d6a82ebe 100644 > --- a/fs/ntfs/super.c > +++ b/fs/ntfs/super.c > @@ -2064,8 +2064,7 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, > /* If errors occurred we may well have gone below zero, fix this. */ > if (nr_free < 0) > nr_free = 0; > - else > - atomic64_set(&vol->free_mft_records, nr_free); > + atomic64_set(&vol->free_mft_records, nr_free); > > ntfs_debug("Exiting."); > return nr_free; > @@ -2131,7 +2130,14 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs) > read_unlock_irqrestore(&mft_ni->size_lock, flags); > > /* Free inodes in fs (based on current total count). */ > - sfs->f_ffree = atomic64_read(&vol->free_mft_records); > + size = atomic64_read(&vol->free_mft_records); > + if (unlikely(size < 0 || size > (s64)sfs->f_files)) > + ntfs_warning(vol->sb, "Invalid free MFT record count %lld.", size); > + if (size < 0) > + size = 0; > + else if (size > (s64)sfs->f_files) > + size = sfs->f_files; > + sfs->f_ffree = size; > > /* > * File system id. This is extremely *nix flavour dependent and even > diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h > index 2b60d14fc7ef..bc85a9592245 100644 > --- a/fs/ntfs/volume.h > +++ b/fs/ntfs/volume.h > @@ -258,17 +258,11 @@ static inline void ntfs_dec_free_clusters(struct ntfs_volume *vol, s64 nr) > > static inline void ntfs_inc_free_mft_records(struct ntfs_volume *vol, s64 nr) > { > - if (!NVolFreeClusterKnown(vol)) > - return; > - > atomic64_add(nr, &vol->free_mft_records); > } > > static inline void ntfs_dec_free_mft_records(struct ntfs_volume *vol, s64 nr) > { > - if (!NVolFreeClusterKnown(vol)) > - return; > - > atomic64_sub(nr, &vol->free_mft_records); > } > > -- > 2.25.1 > -- Thanks, Hyunchul ^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-12 16:24 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260908094416.10534-1-linkinjeon@kernel.org>
2026-09-08 9:44 ` [PATCH] ntfs: account for MFT records added during allocation Namjae Jeon
2026-09-12 16:24 ` Hyunchul Lee
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.