Delayed updating the MFT runlist when resizing in read-only mode

When trying a resize in "no action" (read-only) mode, and the MFT runlist
has to be reorganized to take its new fragmentation into account, the updated
runlist cannot be read from the device while updating the runlist of
normal files. To avoid having to read the updated runlist, the update
is delayed so that the original runlist is used. As a consequence the test
of reorganizing the runlists is only an approximation of what would happen
in a real resize.
pull/2/head
Jean-Pierre André 2018-02-13 08:54:12 +01:00
parent 163635f08f
commit f334c1fdc3
1 changed files with 63 additions and 7 deletions

View File

@ -5,7 +5,7 @@
* Copyright (c) 2002-2005 Anton Altaparmakov
* Copyright (c) 2002-2003 Richard Russon
* Copyright (c) 2007 Yura Pakhuchiy
* Copyright (c) 2011-2016 Jean-Pierre Andre
* Copyright (c) 2011-2018 Jean-Pierre Andre
*
* This utility will resize an NTFS volume without data loss.
*
@ -137,6 +137,8 @@ static const char *many_bad_sectors_msg =
"* other reason. We suggest to get a replacement disk as soon as possible. *\n"
"***************************************************************************\n";
enum mirror_source { MIRR_OLD, MIRR_NEWMFT, MIRR_MFT };
static struct {
int verbose;
int debug;
@ -226,6 +228,7 @@ typedef struct {
struct llcn_t last_compressed;
struct llcn_t last_lcn;
s64 last_unsupp; /* last unsupported cluster */
enum mirror_source mirr_from;
} ntfs_resize_t;
/* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster
@ -1459,10 +1462,13 @@ static int record_mft_in_bitmap(ntfs_resize_t *resize)
static void delayed_updates(ntfs_resize_t *resize)
{
struct DELAYED *delayed;
struct DELAYED *delayed_mft_data;
int nr_extents;
if (ntfs_volume_get_free_space(resize->vol))
err_exit("Failed to determine free space\n");
delayed_mft_data = (struct DELAYED*)NULL;
if (resize->delayed_runlists && reload_mft(resize))
err_exit("Failed to reload the MFT for delayed updates\n");
@ -1476,19 +1482,55 @@ static void delayed_updates(ntfs_resize_t *resize)
* So we update MFT data first, and we record the MFT
* extents again in the MFT bitmap if they were recorded
* in the old location.
*
* However, if we are operating in "no action" mode, the
* MFT records to update are not written to their new location
* and the MFT data runlist has to be updated last in order
* to have the entries read from their old location.
* In this situation the MFT bitmap is never written to
* disk, so the same extents are reallocated repeatedly,
* which is not what would be done in a real resizing.
*/
if (opt.ro_flag
&& resize->delayed_runlists
&& (resize->delayed_runlists->mref == FILE_MFT)
&& (resize->delayed_runlists->type == AT_DATA)) {
/* Update the MFT data runlist later */
delayed_mft_data = resize->delayed_runlists;
resize->delayed_runlists = resize->delayed_runlists->next;
}
while (resize->delayed_runlists) {
delayed = resize->delayed_runlists;
expand_attribute_runlist(resize->vol, delayed);
if ((delayed->mref == FILE_MFT) && (delayed->type == AT_BITMAP))
record_mft_in_bitmap(resize);
if (delayed->mref == FILE_MFT) {
if (delayed->type == AT_BITMAP)
record_mft_in_bitmap(resize);
if (delayed->type == AT_DATA)
resize->mirr_from = MIRR_MFT;
}
resize->delayed_runlists = resize->delayed_runlists->next;
if (delayed->attr_name)
free(delayed->attr_name);
free(delayed->head_rl);
free(delayed);
}
if (opt.ro_flag && delayed_mft_data) {
/* in "no action" mode, check updating the MFT runlist now */
expand_attribute_runlist(resize->vol, delayed_mft_data);
resize->mirr_from = MIRR_MFT;
if (delayed_mft_data->attr_name)
free(delayed_mft_data->attr_name);
free(delayed_mft_data->head_rl);
free(delayed_mft_data);
}
/* Beware of MFT fragmentation when the target size is too small */
nr_extents = resize->vol->mft_ni->nr_extents;
if (nr_extents > 2) {
printf("WARNING: The MFT is now severely fragmented"
" (%d extents)\n", nr_extents);
}
}
/*
@ -2262,6 +2304,7 @@ static void relocate_inodes(ntfs_resize_t *resize)
err_exit("Could not allocate 16 records in"
" the first MFT chunk\n");
}
resize->mirr_from = MIRR_NEWMFT;
}
for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++)
@ -2718,16 +2761,27 @@ static void update_bootsector(ntfs_resize_t *r)
bs->number_of_sectors = cpu_to_sle64(r->new_volume_size *
bs->bpb.sectors_per_cluster);
if (r->mftmir_old) {
if (r->mftmir_old || (r->mirr_from == MIRR_MFT)) {
r->progress.flags |= NTFS_PROGBAR_SUPPRESS;
/* Be sure the MFTMirr holds the updated MFT runlist */
if (r->new_mft_start)
switch (r->mirr_from) {
case MIRR_MFT :
/* The late updates of MFT have not been synced */
ntfs_inode_sync(vol->mft_ni);
copy_clusters(r, r->mftmir_rl.lcn,
vol->mft_na->rl->lcn, r->mftmir_rl.length);
break;
case MIRR_NEWMFT :
copy_clusters(r, r->mftmir_rl.lcn,
r->new_mft_start->lcn, r->mftmir_rl.length);
else
break;
default :
copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old,
r->mftmir_rl.length);
bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn);
break;
}
if (r->mftmir_old)
bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn);
r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS;
}
/* Set the start of the relocated MFT */
@ -3904,6 +3958,7 @@ static int update_runlist(expand_t *expand, s64 inum,
ctx.mrec = mrec;
resize.mref = inum;
resize.delayed_runlists = expand->delayed_runlists;
resize.mirr_from = MIRR_OLD;
must_delay = 1;
replace_later(&resize,rl,head_rl);
expand->delayed_runlists = resize.delayed_runlists;
@ -4577,6 +4632,7 @@ int main(int argc, char **argv)
resize.inuse = fsck.inuse;
resize.lcn_bitmap = fsck.lcn_bitmap;
resize.mirr_from = MIRR_OLD;
set_resize_constraints(&resize);
set_disk_usage_constraint(&resize);