7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Hyunchul Lee [ Upstream commit b9fa884a39874091a10706fca69483ca957138f3 ] Replace the record-specific mirror writer with a unit-based helper and use a stack-allocated BIO for synchronous mirror writes. Route write_mft_record_nolock() and ntfs_write_mft_block() through the common mirror primitive. The helper only returns errors, So callers handle volume error reporting and retry policy. Fixes: 115380f9a2f9 ("ntfs: update mft operations") Cc: stable@vger.kernel.org Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/mft.c | 142 ++++++++++++++++++++++++++------------------------ fs/ntfs/mft.h | 2 - 2 files changed, 73 insertions(+), 71 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index c9ea11198aa64..b102e0204eba9 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -429,6 +429,12 @@ void __mark_mft_record_dirty(struct ntfs_inode *ni) __mark_inode_dirty(VFS_I(base_ni), I_DIRTY_DATASYNC); } +struct ntfs_mft_io_unit { + sector_t sector; + unsigned int folio_ofs; + unsigned int len; +}; + /* * ntfs_bio_end_io - bio completion callback for MFT record writes * @@ -445,65 +451,60 @@ static void ntfs_bio_end_io(struct bio *bio) } /* - * ntfs_sync_mft_mirror - synchronize an mft record to the mft mirror - * @vol: ntfs volume on which the mft record to synchronize resides - * @mft_no: mft record number of mft record to synchronize - * @m: mapped, mst protected (extent) mft record to synchronize - * - * Write the mapped, mst protected (extent) mft record @m with mft record - * number @mft_no to the mft mirror ($MFTMirr) of the ntfs volume @vol. - * - * On success return 0. On error return -errno and set the volume errors flag - * in the ntfs volume @vol. - * - * NOTE: We always perform synchronous i/o. + * Write one MFT I/O unit to $MFTMirr. The source folio contains the MST + * protected image that will be written to $MFT. */ -int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no, - struct mft_record *m) +static int ntfs_sync_mft_mirror_unit(struct ntfs_volume *vol, + struct folio *source, u64 mirror_file_ofs, + const struct ntfs_mft_io_unit *unit) { - u8 *kmirr; - struct folio *folio; - unsigned int folio_ofs; - int err = 0; - struct bio *bio; + struct folio *mirror; + struct bio_vec bvec; + struct bio bio; + u64 mirror_size; + unsigned int mirror_ofs; + u8 *src, *dst; + int err; - ntfs_debug("Entering for inode 0x%llx.", mft_no); + if (unlikely(!vol->mftmirr_ino)) + return -EIO; + + mirror_size = (u64)vol->mftmirr_size * vol->mft_record_size; + if (mirror_file_ofs >= mirror_size || + unit->len > mirror_size - mirror_file_ofs) + return -EIO; + if (unit->folio_ofs + unit->len > folio_size(source)) + return -EIO; - if (unlikely(!vol->mftmirr_ino)) { - /* This could happen during umount... */ + mirror = read_mapping_folio(vol->mftmirr_ino->i_mapping, + mirror_file_ofs >> PAGE_SHIFT, NULL); + if (IS_ERR(mirror)) + return PTR_ERR(mirror); + + folio_lock(mirror); + if (folio_test_writeback(mirror)) + folio_wait_writeback(mirror); + mirror_ofs = mirror_file_ofs - folio_pos(mirror); + if (mirror_ofs + unit->len > folio_size(mirror)) { err = -EIO; - goto err_out; - } - /* Get the page containing the mirror copy of the mft record @m. */ - folio = read_mapping_folio(vol->mftmirr_ino->i_mapping, - NTFS_MFT_NR_TO_PIDX(vol, mft_no), NULL); - if (IS_ERR(folio)) { - ntfs_error(vol->sb, "Failed to map mft mirror page."); - err = PTR_ERR(folio); - goto err_out; + goto out_unlock; } - folio_lock(folio); - folio_clear_uptodate(folio); - /* Offset of the mft mirror record inside the page. */ - folio_ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no); - /* The address in the page of the mirror copy of the mft record @m. */ - kmirr = kmap_local_folio(folio, 0) + folio_ofs; - /* Copy the mst protected mft record to the mirror. */ - memcpy(kmirr, m, vol->mft_record_size); - kunmap_local(kmirr); - - bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); - bio->bi_iter.bi_sector = - ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) + - ((u64)folio->index << PAGE_SHIFT) + - folio_ofs); - - if (bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs)) - err = submit_bio_wait(bio); - else + folio_clear_uptodate(mirror); + src = kmap_local_folio(source, unit->folio_ofs); + dst = kmap_local_folio(mirror, mirror_ofs); + memcpy(dst, src, unit->len); + kunmap_local(dst); + kunmap_local(src); + + bio_init(&bio, vol->sb->s_bdev, &bvec, 1, REQ_OP_WRITE); + bio.bi_iter.bi_sector = ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) + + mirror_file_ofs); + if (!bio_add_folio(&bio, mirror, unit->len, mirror_ofs)) err = -EIO; - bio_put(bio); + else + err = submit_bio_wait(&bio); + bio_uninit(&bio); /* * The in-memory mirror is now valid because we just memcpy()'d the @@ -512,23 +513,26 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no, * the stale on-disk mirror and overwrite this copy. The error is * propagated to the caller via @err. */ - folio_mark_uptodate(folio); + folio_mark_uptodate(mirror); - folio_unlock(folio); - folio_put(folio); - if (likely(!err)) { - ntfs_debug("Done."); - } else { - ntfs_error(vol->sb, "I/O error while writing mft mirror record 0x%llx!", mft_no); -err_out: - ntfs_error(vol->sb, - "Failed to synchronize $MFTMirr (error code %i). Volume will be left marked dirty on umount. Run chkdsk on the partition after umounting to correct this.", - err); - NVolSetErrors(vol); - } +out_unlock: + folio_unlock(mirror); + folio_put(mirror); return err; } +static int ntfs_sync_mft_mirror_record(struct ntfs_volume *vol, + struct folio *source, const u64 mft_no) +{ + struct ntfs_mft_io_unit unit = { + .folio_ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no), + .len = vol->mft_record_size, + }; + + return ntfs_sync_mft_mirror_unit(vol, source, + (u64)mft_no * vol->mft_record_size, &unit); +} + /* * write_mft_record_nolock - write out a mapped (extent) mft record * @ni: ntfs inode describing the mapped (extent) mft record @@ -600,8 +604,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn /* Synchronize the mft mirror now if not @sync. */ if (!sync && ni->mft_no < vol->mftmirr_size) { - int sub_err = ntfs_sync_mft_mirror(vol, ni->mft_no, - fixup_m); + int sub_err = ntfs_sync_mft_mirror_record(vol, folio, + ni->mft_no); if (unlikely(sub_err) && !err) err = sub_err; } @@ -624,7 +628,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn /* If @sync, now synchronize the mft mirror. */ if (sync && ni->mft_no < vol->mftmirr_size) { - int sub_err = ntfs_sync_mft_mirror(vol, ni->mft_no, fixup_m); + int sub_err = + ntfs_sync_mft_mirror_record(vol, folio, ni->mft_no); if (unlikely(sub_err) && !err) err = sub_err; @@ -3031,8 +3036,7 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w prev_mft_ofs = mft_ofs; if (mft_no < vol->mftmirr_size) { - int sub_err = ntfs_sync_mft_mirror(vol, mft_no, - (struct mft_record *)(kaddr + mft_ofs)); + int sub_err = ntfs_sync_mft_mirror_record(vol, folio, mft_no); if (unlikely(sub_err) && !err) err = sub_err; diff --git a/fs/ntfs/mft.h b/fs/ntfs/mft.h index ed5c1d595c0d3..eb8a6f0d7944f 100644 --- a/fs/ntfs/mft.h +++ b/fs/ntfs/mft.h @@ -42,8 +42,6 @@ static inline void mark_mft_record_dirty(struct ntfs_inode *ni) __mark_mft_record_dirty(ni); } -int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no, - struct mft_record *m); int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int sync); /* -- 2.53.0