Enabled relocating the MFT when shrinking a volume

This patch makes it possible to shrink a volume to a small size such that
the existing $MFT is beyond the new size.
edge.strict_endians
Jean-Pierre André 2013-02-09 12:00:02 +01:00
parent b2e1dbbf38
commit aebfcf3d27
1 changed files with 94 additions and 24 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 Jean-Pierre Andre
* Copyright (c) 2011-2012 Jean-Pierre Andre
*
* This utility will resize an NTFS volume without data loss.
*
@ -213,6 +213,7 @@ typedef struct {
int shrink; /* shrink = 1, enlarge = 0 */
s64 badclusters; /* num of physically dead clusters */
VCN mft_highest_vcn; /* used for relocating the $MFT */
runlist_element *new_mft_start; /* new first run for $MFT:$DATA */
struct DELAYED *delayed_runlists; /* runlists to process later */
struct progress_bar progress;
struct bitmap lcn_bitmap;
@ -402,7 +403,7 @@ static void version(void)
printf("Copyright (c) 2002-2005 Anton Altaparmakov\n");
printf("Copyright (c) 2002-2003 Richard Russon\n");
printf("Copyright (c) 2007 Yura Pakhuchiy\n");
printf("Copyright (c) 2011 Jean-Pierre Andre\n");
printf("Copyright (c) 2011-2012 Jean-Pierre Andre\n");
printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home);
}
@ -724,14 +725,6 @@ static void collect_resize_constraints(ntfs_resize_t *resize, runlist *rl)
supported = 1;
} else if (inode == FILE_MFT) {
llcn = &resize->last_mft;
/*
* First run of $MFT AT_DATA isn't supported yet.
*/
if (atype != AT_DATA || rl->vcn)
supported = 1;
} else if (NInoAttrList(resize->ni)) {
llcn = &resize->last_multi_mft;
@ -1848,11 +1841,17 @@ static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run)
}
hint = (resize->mref == FILE_MFTMirr) ? 1 : 0;
if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap, lcn_length,
new_vol_size, hint)))
perr_exit("Cluster allocation failed for %llu:%lld",
(unsigned long long)resize->mref,
(long long)lcn_length);
if ((resize->mref == FILE_MFT)
&& (resize->ctx->attr->type == AT_DATA)
&& !run
&& resize->new_mft_start) {
relocate_rl = resize->new_mft_start;
} else
if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap,
lcn_length, new_vol_size, hint)))
perr_exit("Cluster allocation failed for %llu:%lld",
(unsigned long long)resize->mref,
(long long)lcn_length);
/* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */
ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx "
@ -1868,7 +1867,13 @@ static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run)
/* We don't release old clusters in the bitmap, that area isn't
used by the allocator and will be truncated later on */
free(relocate_rl);
/* Do not free the relocated MFT start */
if ((resize->mref != FILE_MFT)
|| (resize->ctx->attr->type != AT_DATA)
|| run
|| !resize->new_mft_start)
free(relocate_rl);
resize->dirty_inode = DIRTY_ATTRIB;
}
@ -1994,7 +1999,9 @@ static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata)
static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata)
{
if (ntfs_file_record_read(resize->vol, mref, &resize->mrec, NULL)) {
ntfs_volume *vol = resize->vol;
if (ntfs_file_record_read(vol, mref, &resize->mrec, NULL)) {
/* FIXME: continue only if it make sense, e.g.
MFT record not in use based on $MFT bitmap */
if (errno == EIO || errno == ENOENT)
@ -2013,9 +2020,22 @@ static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata)
if (resize->dirty_inode == DIRTY_INODE) {
// if (vol->dev->d_ops->sync(vol->dev) == -1)
// perr_exit("Failed to sync device");
if (write_mft_record(resize->vol, mref, resize->mrec))
perr_exit("Couldn't update record %llu",
(unsigned long long)mref);
if ((mref == FILE_MFT) && 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 (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",
(unsigned long long)mref);
}
}
}
@ -2037,6 +2057,31 @@ static void relocate_inodes(ntfs_resize_t *resize)
nr_mft_records = resize->vol->mft_na->initialized_size >>
resize->vol->mft_record_size_bits;
/*
* If we need to relocate the first run of the MFT DATA,
* do it now, to have a better chance of getting at least
* 16 records in the first chunk. This is mandatory to be
* later able to read an MFT extent in record 15.
* Should this fail, we can stop with no damage, the volume
* is still in its initial state.
*/
if (!resize->vol->mft_na->rl)
err_exit("Internal error : no runlist for $MFT\n");
if ((resize->vol->mft_na->rl->lcn + resize->vol->mft_na->rl->length)
>= resize->new_volume_size) {
resize->new_mft_start = alloc_cluster(&resize->lcn_bitmap,
resize->vol->mft_na->rl->length,
resize->new_volume_size, 0);
if (!resize->new_mft_start
|| (((resize->new_mft_start->length
<< resize->vol->cluster_size_bits)
>> resize->vol->mft_record_size_bits) < 16)) {
err_exit("Could not allocate 16 records in"
" the first MFT chunk\n");
}
}
for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++)
relocate_inode(resize, mref, 0);
@ -2434,14 +2479,27 @@ static void truncate_badclust_file(ntfs_resize_t *resize)
*/
static void truncate_bitmap_file(ntfs_resize_t *resize)
{
ntfs_volume *vol = resize->vol;
printf("Updating $Bitmap file ...\n");
lookup_data_attr(resize->vol, FILE_Bitmap, NULL, &resize->ctx);
truncate_bitmap_data_attr(resize);
if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no,
if (resize->new_mft_start) {
s64 pos;
/* write the MFT record at its new location */
pos = (resize->new_mft_start->lcn << vol->cluster_size_bits)
+ (FILE_Bitmap << vol->mft_record_size_bits);
if (ntfs_mst_pwrite(vol->dev, pos, 1,
vol->mft_record_size, resize->ctx->mrec) != 1)
perr_exit("Couldn't update $Bitmap at new location");
} else {
if (write_mft_record(vol, resize->ctx->ntfs_ino->mft_no,
resize->ctx->mrec))
perr_exit("Couldn't update $Bitmap");
perr_exit("Couldn't update $Bitmap");
}
#if CLEAN_EXIT
close_inode_and_context(resize->ctx);
@ -2494,11 +2552,23 @@ static void update_bootsector(ntfs_resize_t *r)
if (r->mftmir_old) {
r->progress.flags |= NTFS_PROGBAR_SUPPRESS;
copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old,
r->mftmir_rl.length);
/* Be sure the MFTMirr holds the updated MFT runlist */
if (r->new_mft_start)
copy_clusters(r, r->mftmir_rl.lcn,
r->new_mft_start->lcn, r->mftmir_rl.length);
else
copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old,
r->mftmir_rl.length);
bs.mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn);
r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS;
}
/* Set the start of the relocated MFT */
if (r->new_mft_start) {
bs.mft_lcn = cpu_to_sle64(r->new_mft_start->lcn);
/* no more need for the new MFT start */
free(r->new_mft_start);
r->new_mft_start = (runlist_element*)NULL;
}
if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1)
perr_exit("lseek");