From 5f681b6ca02e756e80eb9ecbc076c59d5efd0353 Mon Sep 17 00:00:00 2001 From: "cantab.net!aia21" Date: Mon, 24 Mar 2003 13:36:19 +0000 Subject: [PATCH] ntfsresize patch from Szaka: - option -i collects info about resizing conditions - if options -i and -s are used together then show needed relocations - option -P added to disable progress bars - walk_inodes(): delete redundant MFT_RECORD_IN_USE check - code refactorings, preparations to move relevant codes to library (Logical change 1.131) --- ntfsprogs/ntfsresize.c | 270 ++++++++++++++++++++++++++++++----------- 1 file changed, 198 insertions(+), 72 deletions(-) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index ea394897..971dfa4a 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -74,6 +74,7 @@ struct { int ro_flag; int force; int info; + int show_progress; s64 bytes; char *volume; } opt; @@ -90,13 +91,30 @@ struct progress_bar { float unit; }; +struct llcn_t { + s64 lcn; /* last used LCN for a "special" file/attr type */ + s64 inode; /* inode using it */ + int total; /* total number of such inodes */ +}; + struct __ntfs_resize_t { - s64 new_volume_size; + s64 new_volume_size; /* in clusters */ + int shrink; /* shrink = 1, enlarge = 0 */ ntfs_inode *ni; /* inode being processed */ ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ u64 relocations; /* num of clusters to relocate */ u64 inuse; /* num of clusters in use */ int multi_ref; /* num of clusters ref'd many times */ + /* Temporary statistics until all case is supported */ + struct llcn_t last_mft; + struct llcn_t last_mftmir; + struct llcn_t last_multi_mft; + struct llcn_t last_compressed_sparse; + struct llcn_t last_sparse; + struct llcn_t last_compressed; + struct llcn_t last_lcn; + int inode_seen; + int inode_seen_special; }; typedef struct __ntfs_resize_t ntfs_resize_t; @@ -186,21 +204,22 @@ void usage() printf ("\nUsage: %s [options] device\n" " Resize an NTFS volume non-destructively.\n" "\n" - " -i --info Calculate the smallest shrunken size supported\n" - " -s num --size num Resize volume to num[k|M|G] bytes\n" + " -i --info Calculate the smallest shrunken size supported\n" + " -s num --size num Resize volume to num[k|M|G] bytes\n" "\n" - " -n --no-action Do not write to disk\n" - " -f --force Force to progress (DANGEROUS)\n" - /* " -q --quiet Less output\n"*/ - /* " -v --verbose More output\n"*/ - " -V --version Display version information\n" - " -h --help Display this help\n" + " -n --no-action Do not write to disk\n" + " -f --force Force to progress (DANGEROUS)\n" + " -P --no-progress-bar Don't show progress bar\n" + /* " -q --quiet Less output\n"*/ + /* " -v --verbose More output\n"*/ + " -V --version Display version information\n" + " -h --help Display this help\n" #ifdef DEBUG - " -d --debug Show debug information\n" + " -d --debug Show debug information\n" #endif "\n" - " The options -i and -s are mutually exclusive. If both options are\n" - " omitted then the NTFS volume will be enlarged to the device size.\n" + " If -i and -s are used together then print information about relocations.\n" + " If both are omitted then the volume will be enlarged to the device size.\n" "\n", EXEC_NAME); printf ("%s%s\n", ntfs_bugs, ntfs_home); exit(1); @@ -239,10 +258,9 @@ void proceed_question(void) void version (void) { printf ("\nResize an NTFS Volume, without data loss.\n\n"); - printf ("Copyright (c)\n"); - printf (" 2002-2003 Szabolcs Szakacsits\n"); - printf (" 2002-2003 Anton Altaparmakov\n"); - printf (" 2002-2003 Richard Russon\n"); + printf ("Copyright (c) 2002-2003 Szabolcs Szakacsits\n"); + printf ("Copyright (c) 2002-2003 Anton Altaparmakov\n"); + printf ("Copyright (c) 2002-2003 Richard Russon\n"); printf ("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -310,7 +328,7 @@ s64 get_new_volume_size(char *s) */ int parse_options(int argc, char **argv) { - static const char *sopt = "-dfhins:vV"; + static const char *sopt = "-dfhinPs:vV"; static const struct option lopt[] = { #ifdef DEBUG { "debug", no_argument, NULL, 'd' }, @@ -321,6 +339,7 @@ int parse_options(int argc, char **argv) { "no-action", no_argument, NULL, 'n' }, /* { "quiet", no_argument, NULL, 'q' },*/ { "size", required_argument, NULL, 's' }, + { "no-progress-bar", no_argument, NULL, 'P' }, /* { "verbose", no_argument, NULL, 'v' },*/ { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } @@ -332,6 +351,7 @@ int parse_options(int argc, char **argv) int help = 0; memset(&opt, 0, sizeof(opt)); + opt.show_progress = 1; while ((c = getopt_long (argc, argv, sopt, lopt, NULL)) != -1) { switch (c) { @@ -357,6 +377,9 @@ int parse_options(int argc, char **argv) case 'n': opt.ro_flag = MS_RDONLY; break; + case 'P': + opt.show_progress = 0; + break; case 'q': opt.quiet++; break; @@ -399,14 +422,8 @@ int parse_options(int argc, char **argv) } */ - if (opt.info) { + if (opt.info) opt.ro_flag = MS_RDONLY; - if (opt.bytes > 0) { - Eprintf (NERR_PREFIX "Options --info and --size" - " can't be used together.\n"); - err++; - } - } } stderr = stdout; @@ -459,6 +476,89 @@ s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) return bm_bsize; } +void set_last_lcn(ntfs_resize_t *r, struct llcn_t *llcn, s64 lcn, int special) +{ + if (special) { + if (!r->inode_seen_special) { + r->inode_seen_special = 1; + llcn->total++; + } + } else + if (!r->inode_seen) { + r->inode_seen = 1; + llcn->total++; + } + + if (llcn->lcn < lcn) { + llcn->lcn = lcn; + llcn->inode = r->ni->mft_no; + } +} + +void collect_shrink_constraints(ntfs_resize_t *resize, s64 last_lcn) +{ + s64 inode; + ATTR_FLAGS flags; + struct llcn_t *llcn; + + inode = resize->ni->mft_no; + flags = resize->ctx->attr->flags; + + set_last_lcn(resize, &resize->last_lcn, last_lcn, 0); + + if (inode == 0) + llcn = &resize->last_mft; + + else if (inode == 1) + llcn = &resize->last_mftmir; + + else if (NInoAttrList(resize->ni)) + llcn = &resize->last_multi_mft; + + else if ((flags & ATTR_IS_SPARSE) && (flags & ATTR_IS_COMPRESSED)) + llcn = &resize->last_compressed_sparse; + + else if (flags & ATTR_IS_SPARSE) + llcn = &resize->last_sparse; + + else if (flags & ATTR_IS_COMPRESSED) + llcn = &resize->last_compressed; + else + return; + + set_last_lcn(resize, llcn, last_lcn, 1); +} + + +void collect_shrink_info(ntfs_resize_t *resize, runlist *rl) +{ + s64 new_volume_size, lcn, lcn_length; + + lcn = rl->lcn; + lcn_length = rl->length; + new_volume_size = resize->new_volume_size; + + if (lcn + (lcn_length - 1) > new_volume_size) { + + s64 start = lcn; + s64 len = lcn_length; + + if (start <= new_volume_size) { + start = new_volume_size + 1; + len = lcn_length - (start - lcn); + } + + resize->relocations += len; + + if (opt.info && !resize->new_volume_size) + return; + + printf("Relocation needed for inode %8Ld attr 0x%x LCN 0x%08Lx " + "length %6Ld\n", resize->ni->mft_no, + resize->ctx->attr->type, start, len); + } +} + /** * build_lcn_usage_bitmap * @@ -470,13 +570,12 @@ s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) */ void build_lcn_usage_bitmap(ntfs_resize_t *resize) { - s64 new_volume_size, inode; + s64 inode; ATTR_RECORD *a; runlist *rl; - int i, j;//, runs; + int i, j; a = resize->ctx->attr; - new_volume_size = resize->new_volume_size; inode = resize->ni->mft_no; if (!a->non_resident) @@ -485,12 +584,11 @@ void build_lcn_usage_bitmap(ntfs_resize_t *resize) if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) perr_exit("ntfs_decompress_mapping_pairs"); - //runs = runlist_extent_number(rl); - for (i = 0; rl[i].length; i++) { s64 lcn = rl[i].lcn; s64 lcn_length = rl[i].length; + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) continue; @@ -513,22 +611,11 @@ void build_lcn_usage_bitmap(ntfs_resize_t *resize) } resize->inuse += lcn_length; - - if (opt.info) - continue; - - if (lcn + (lcn_length - 1) > new_volume_size) { - - s64 start = lcn; - s64 len = lcn_length; - - if (start <= new_volume_size) { - start = new_volume_size + 1; - len = lcn_length - (start - lcn); - } - - resize->relocations += len; - } + + collect_shrink_constraints(resize, lcn + (lcn_length - 1)); + + if (resize->shrink) + collect_shrink_info(resize, rl + i); } free(rl); } @@ -636,8 +723,12 @@ void progress_init(struct progress_bar *p, u64 start, u64 stop, int res) */ void progress_update(struct progress_bar *p, u64 current) { - float percent = p->unit * current; - + float percent; + + if (!opt.show_progress) + return; + + percent = p->unit * current; if (current != p->stop) { if ((current - p->start) % p->resolution) return; @@ -676,13 +767,12 @@ void walk_inodes(ntfs_resize_t *resize) perr_exit("Reading inode %lld failed", inode); } - if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) - goto close_inode; - if ((ni->mrec->base_mft_record) != 0) goto close_inode; resize->ni = ni; + resize->inode_seen = 0; + resize->inode_seen_special = 0; walk_attributes(resize); close_inode: if (ntfs_inode_close(ni)) @@ -690,6 +780,19 @@ close_inode: } } +void print_hint(const char *s, struct llcn_t llcn) +{ + s64 runs_b, runs_mb; + + if (llcn.lcn == 0) + return; + + runs_b = llcn.lcn * vol->cluster_size; + runs_mb = rounded_up_division(runs_b, NTFS_MBYTE); + printf("%-19s: %6Ld MB %8Ld %11d\n", + s, runs_mb, llcn.inode, llcn.total); +} + /** * advise_on_resize * @@ -697,13 +800,26 @@ close_inode: * already been read into lcn_bitmap. By looking for the last used cluster on * the disk, we can work out by how much we can shrink the volume. */ -void advise_on_resize() +void advise_on_resize(ntfs_resize_t *resize) { s64 i, old_b, new_b, g_b, old_mb, new_mb, g_mb; int fragmanted_end; printf("Calculating smallest shrunken size supported ...\n"); + old_b = vol->nr_clusters * vol->cluster_size; + old_mb = rounded_up_division(old_b, NTFS_MBYTE); + + printf("File feature Last used Last inode " + "Total inodes\n"); + print_hint("$MFT", resize->last_mft); + print_hint("$MFTMirr", resize->last_mftmir); + print_hint("Compressed", resize->last_compressed); + print_hint("Sparse", resize->last_sparse); + print_hint("Compressed&Sparse", resize->last_compressed_sparse); + print_hint("Multi-Record", resize->last_multi_mft); + print_hint("Ordinary&Special", resize->last_lcn); + for (i = vol->nr_clusters - 1; i > 0 && (i % 8); i--) if (ntfs_bit_get(lcn_bitmap.bm, i)) goto found_used_cluster; @@ -723,6 +839,10 @@ void advise_on_resize() break; found_used_cluster: + if (i != resize->last_lcn.lcn) + err_exit("Last used cluster calculations don't match! " + "%Ld != %Ld\n", i, resize->last_lcn); + i += 2; /* first free + we reserve one for the backup boot sector */ fragmanted_end = (i >= vol->nr_clusters) ? 1 : 0; @@ -733,8 +853,6 @@ found_used_cluster: printf("Now "); } - old_b = vol->nr_clusters * vol->cluster_size; - old_mb = rounded_up_division(old_b, NTFS_MBYTE); new_b = i * vol->cluster_size; new_mb = rounded_up_division(new_b, NTFS_MBYTE); g_b = (vol->nr_clusters - i) * vol->cluster_size; @@ -934,19 +1052,23 @@ void enlarge_bitmap_data_attr(runlist **rlist, s64 nr_bm_clusters, s64 new_size) /** * truncate_bitmap_data_attr */ -void truncate_bitmap_data_attr(ATTR_RECORD *a, s64 nr_clusters) +void truncate_bitmap_data_attr(ntfs_resize_t *resize) { + ATTR_RECORD *a; runlist *rl; s64 bm_bsize, size; s64 nr_bm_clusters; + s64 nr_clusters; int mp_size; char *mp; u8 *tmp; + a = resize->ctx->attr; if (!a->non_resident) /* FIXME: handle resident attribute value */ perr_exit("Resident data attribute in $Bitmap not supported!"); + nr_clusters = resize->new_volume_size; bm_bsize = nr_clusters_to_bitmap_byte_size(nr_clusters); nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size); @@ -959,7 +1081,9 @@ void truncate_bitmap_data_attr(ATTR_RECORD *a, s64 nr_clusters) if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) perr_exit("ntfs_mapping_pairs_decompress"); - if (nr_clusters < vol->nr_clusters) + /* NOTE: shrink could use enlarge_bitmap_data_attr() also. Advantages: + less code, better code coverage. "Drawback": could be relocated */ + if (resize->shrink) shrink_bitmap_data_attr(&rl, nr_bm_clusters, nr_clusters); else enlarge_bitmap_data_attr(&rl, nr_bm_clusters, nr_clusters); @@ -1081,20 +1205,17 @@ void truncate_badclust_file(s64 nr_clusters) * * Shrink the $Bitmap file to match the new volume size. */ -void truncate_bitmap_file(s64 nr_clusters) +void truncate_bitmap_file(ntfs_resize_t *resize) { - ntfs_attr_search_ctx *ctx = NULL; - printf("Updating $Bitmap file ...\n"); - lookup_data_attr((MFT_REF)FILE_Bitmap, NULL, &ctx); - /* FIXME: sanity_check_attr(ctx->attr); */ - truncate_bitmap_data_attr(ctx->attr, nr_clusters); + lookup_data_attr((MFT_REF)FILE_Bitmap, NULL, &resize->ctx); + truncate_bitmap_data_attr(resize); - if (write_mft_record(ctx)) + if (write_mft_record(resize->ctx)) perr_exit("Couldn't update $Bitmap"); - ntfs_attr_put_search_ctx(ctx); + ntfs_attr_put_search_ctx(resize->ctx); } /** @@ -1176,11 +1297,14 @@ void print_disk_usage(ntfs_resize_t *resize) free = total - used; relocations = resize->relocations * vol->cluster_size; - printf("Space in use : %lld MB (%.1f%%) ", + printf("Space in use : %lld MB (%.1f%%)\n", rounded_up_division(used, NTFS_MBYTE), 100.0 * ((float)used / total)); - - printf("\n"); + + if (opt.bytes) + printf("Needed relocations : %Ld (%Ld MB)\n", + resize->relocations, + rounded_up_division(relocations, NTFS_MBYTE)); } /** @@ -1281,7 +1405,7 @@ int main(int argc, char **argv) printf("%s v%s\n", EXEC_NAME, VERSION); - if (!parse_options (argc, argv)) + if (!parse_options(argc, argv)) return 1; utils_set_locale(); @@ -1304,7 +1428,7 @@ int main(int argc, char **argv) if (device_size < opt.bytes) err_exit("New size can't be bigger than the " "device size (%Ld bytes).\n", device_size); - } else + } else if (!opt.info) opt.bytes = device_size; /* @@ -1335,7 +1459,9 @@ int main(int argc, char **argv) memset(&resize, 0, sizeof(resize)); resize.new_volume_size = new_size; - + if (new_size < vol->nr_clusters) + resize.shrink = 1; + walk_inodes(&resize); if (resize.multi_ref) { printf("Totally %d clusters referenced multiply times.\n", @@ -1349,14 +1475,14 @@ int main(int argc, char **argv) print_disk_usage(&resize); if (opt.info) { - advise_on_resize(); + advise_on_resize(&resize); exit(0); } for (i = new_size; i < vol->nr_clusters; i++) if (ntfs_bit_get(lcn_bitmap.bm, (u64)i)) { /* FIXME: relocate cluster */ - advise_on_resize(); + advise_on_resize(&resize); exit(1); } @@ -1368,7 +1494,7 @@ int main(int argc, char **argv) prepare_volume_fixup(); truncate_badclust_file(new_size); - truncate_bitmap_file(new_size); + truncate_bitmap_file(&resize); update_bootsector(new_size); /* We don't create backup boot sector because we don't know where the