From baf13fe5556b47582ba7d39da3045769d9fa3657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 22 Apr 2013 18:55:26 +0200 Subject: [PATCH] Fixed updating all the MFT runs in a relocated MFT in ntfsresize When the MFT is partially located beyond the end of a resized partition, it has to be relocated after the other files have been processed because the MFT data is needed to do the relocations. When the MFT runlist is split over extents, all of them have to be processed. --- ntfsprogs/ntfsresize.c | 65 +++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index 09b9ee7f..f6560677 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -1920,6 +1920,12 @@ static void relocate_attribute(ntfs_resize_t *resize) static int is_mftdata(ntfs_resize_t *resize) { + /* + * We must update the MFT own DATA record at the end of the second + * step, because the old MFT must be kept available for processing + * the other files. + */ + if (resize->ctx->attr->type != AT_DATA) return 0; @@ -2017,24 +2023,24 @@ static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata) relocate_attributes(resize, do_mftdata); - if (resize->dirty_inode == DIRTY_INODE) { // if (vol->dev->d_ops->sync(vol->dev) == -1) // perr_exit("Failed to sync device"); - if ((mref == FILE_MFT) && resize->new_mft_start) { - s64 pos; + /* relocate MFT during second step, even if not dirty */ + if ((mref == FILE_MFT) && do_mftdata && resize->new_mft_start) { + s64 pos; /* write the MFT own record at its new location */ - pos = (resize->new_mft_start->lcn - << vol->cluster_size_bits) - + (FILE_MFT << vol->mft_record_size_bits); - if (!opt.ro_flag - && (ntfs_mst_pwrite(vol->dev, pos, 1, - vol->mft_record_size, - resize->mrec) != 1)) - perr_exit("Couldn't update MFT own record"); - } else { - if (write_mft_record(vol, mref, resize->mrec)) - perr_exit("Couldn't update record %llu", + pos = (resize->new_mft_start->lcn + << vol->cluster_size_bits) + + (FILE_MFT << vol->mft_record_size_bits); + if (!opt.ro_flag + && (ntfs_mst_pwrite(vol->dev, pos, 1, + vol->mft_record_size, resize->mrec) != 1)) + perr_exit("Couldn't update MFT own record"); + } else { + if ((resize->dirty_inode == DIRTY_INODE) + && write_mft_record(vol, mref, resize->mrec)) { + perr_exit("Couldn't update record %llu", (unsigned long long)mref); } } @@ -2045,6 +2051,7 @@ static void relocate_inodes(ntfs_resize_t *resize) s64 nr_mft_records; MFT_REF mref; VCN highest_vcn; + u64 length; printf("Relocating needed data ...\n"); @@ -2071,9 +2078,35 @@ static void relocate_inodes(ntfs_resize_t *resize) if ((resize->vol->mft_na->rl->lcn + resize->vol->mft_na->rl->length) >= resize->new_volume_size) { + /* + * The length of the first run is normally found in + * mft_na. However in some rare circumstance, this is + * merged with the first run of an extent of MFT, + * which implies there is a single run in the base record. + * So we have to make sure not to overflow from the + * runs present in the base extent. + */ + length = resize->vol->mft_na->rl->length; + if (ntfs_file_record_read(resize->vol, FILE_MFT, + &resize->mrec, NULL) + || !(resize->ctx = attr_get_search_ctx(NULL, + resize->mrec))) { + err_exit("Could not read the base record of MFT\n"); + } + while (!ntfs_attrs_walk(resize->ctx) + && (resize->ctx->attr->type != AT_DATA)) { } + if (resize->ctx->attr->type == AT_DATA) { + le64 high_le; + + high_le = resize->ctx->attr->highest_vcn; + if (le64_to_cpu(high_le) < length) + length = le64_to_cpu(high_le) + 1; + } else { + err_exit("Could not find the DATA of MFT\n"); + } + ntfs_attr_put_search_ctx(resize->ctx); resize->new_mft_start = alloc_cluster(&resize->lcn_bitmap, - resize->vol->mft_na->rl->length, - resize->new_volume_size, 0); + length, resize->new_volume_size, 0); if (!resize->new_mft_start || (((resize->new_mft_start->length << resize->vol->cluster_size_bits)