mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
ntfs: write MFT mirror data in mapped I/O units
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: 115380f9a2 ("ntfs: update mft operations")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
dfe7ed335b
commit
b9fa884a39
140
fs/ntfs/mft.c
140
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;
|
||||
|
||||
if (unlikely(!vol->mftmirr_ino)) {
|
||||
/* This could happen during umount... */
|
||||
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;
|
||||
|
||||
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);
|
||||
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 = 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);
|
||||
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;
|
||||
else
|
||||
err = -EIO;
|
||||
bio_put(bio);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user