Re: [PATCH] ntfs: fix $MFTMirr write offset for MFT record sizes larger than PAGE_SIZE

From: Hyunchul Lee

Date: Mon Sep 07 2026 - 05:01:04 EST


Hi Tianhao,

2026년 9월 5일 (토) 오후 9:35, 朱 天浩 <zhutianhao75@xxxxxxxxxxx>님이 작성:
>
> When commit 115380f9a2f9 ("ntfs: update mft operations") refactored the
> MFT code to use folios, it assumed $MFTMirr is allocated contiguously,
> which is indeed what mkfs arranges. However, the folio rewrite kept a
> leftover from the old buffer-head/runlist based implementation:
> vol->cluster_size_mask is still applied to the mirror write offset.
> In fact it is no longer needed -- and in some configurations it is
> actively wrong.
>
> For example, on the built-in 4Kn SSD (Apple SSD AP0256J) of a
> MacBookPro14,1 with 4096-byte MFT records, mirror record 3 is still
> written to the location of record 0. $MFTMirr therefore gets out of
> sync with $MFT and the following warning is printed on remount:
>
> ntfs: (device nvme0n1p3): check_mft_mirror(): $MFT and $MFTMirr
> record 0 do not match. Run chkdsk.
>
> Fix it by always adding the folio offset (folio->index << PAGE_SHIFT)
> to the base LCN address instead of applying the cluster_size_mask
> truncation. This is the standard file-offset calculation and is
> correct for every combination of cluster size, page size and MFT
> record size, provided $MFTMirr data is contiguous from mftmirr_lcn
> (which mkfs always arranges).
>
> While here, verify that the folio returned by read_mapping_folio() is
> large enough to hold the whole mirror record (folio_ofs +
> mft_record_size <= folio_size(folio)), guarding against records that
> straddle a folio boundary when mft_record_size exceeds PAGE_SIZE,
> although this cannot happen in practice.
>
> Tested on the volume above: before the change mirror records 1-3 are
> all written to record 0's sector; after the change the write-back
> probe reports:
>
> ntfs: NTFSDBG pre mft_no=0x3 folio_idx=0x3 ofs=0x0 mftmirr_lcn=0x2540c5 clu_bits=12 rec_bits=12 PAGE_SHIFT=12 sect=0x12a0640
> ntfs: NTFSDBG pre mft_no=0x0 folio_idx=0x0 ofs=0x0 mftmirr_lcn=0x2540c5 clu_bits=12 rec_bits=12 PAGE_SHIFT=12 sect=0x12a0628
>
> which matches the expected sectors computed as:
>
> (NTFS_CLU_TO_B(mftmirr_lcn) + (folio->index << PAGE_SHIFT)) >> SECTOR_SHIFT
>
> record 0: (0x2540c5 << 12) + 0x0000 = 0x2540c5000 >> 9 = 0x12a0628
> record 3: (0x2540c5 << 12) + 0x3000 = 0x2540c8000 >> 9 = 0x12a0640
>
> A 13 MB file write succeeds on this volume and the file is byte-for-byte
> identical after a umount/mount cycle.
>
> Fixes: 115380f9a2f9 ("ntfs: update mft operations")
> Assisted-by: UOS-AI
> Assisted-by: CodeBuddy:Hy4 preview
> Signed-off-by: Zhu Tianhao <zhutianhao75@xxxxxxxxxxx>
> ---
> fs/ntfs/mft.c | 26 ++++++++++++++++----------
> 1 file changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
> index 7e58c99f1728..a62b1d41697c 100644
> --- a/fs/ntfs/mft.c
> +++ b/fs/ntfs/mft.c
> @@ -462,7 +462,7 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,
> {
> u8 *kmirr;
> struct folio *folio;
> - unsigned int folio_ofs, lcn_folio_off = 0;
> + unsigned int folio_ofs;
> int err = 0;
> struct bio *bio;
>
> @@ -482,25 +482,31 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,
> goto err_out;
> }
>
> + /* Offset of the mft mirror record inside the folio. */
> + folio_ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no);
> +
> + /* The mirror record must fit entirely within this folio. */
> + if (folio_ofs + vol->mft_record_size > folio_size(folio)) {
> + ntfs_error(vol->sb, "Mft mirror record 0x%llx does not fit in folio.",
> + mft_no);
> + folio_put(folio);
> + err = -EIO;
> + goto err_out;
> + }

vol->mft_record_size < PAGE_SIZE is guaranteed by ntfs_fill_super().
Is this check necessary?

Thanks,
Hyunchul