parent
aa568dccc0
commit
ce45772a9c
|
@ -13,7 +13,7 @@ ntfsresize \- resize an NTFS filesystem
|
|||
The
|
||||
.B ntfsresize
|
||||
program non-destructively resizes Windows NT4, 2000, XP or .NET
|
||||
NTFS filesystems. At present it can be used to shrink a
|
||||
NTFS filesystems. At present it can be used to enlarge or shrink a
|
||||
defragmented NTFS filesystem located on an unmounted
|
||||
.I device
|
||||
(usually a disk partition). The new volume will have
|
||||
|
@ -28,10 +28,28 @@ parameter is given in kilo-, mega- or gigabytes respectively.
|
|||
.B ntfsresize
|
||||
conforms to the SI, ATA, IEEE standards and the disk manufacturers
|
||||
by using k=10^3, M=10^6 and G=10^9.
|
||||
The options
|
||||
.B -i
|
||||
and
|
||||
.B -s
|
||||
are mutually exclusive. If both of them is omitted then the
|
||||
NTFS filesystem will be enlarged to the device size.
|
||||
.PP
|
||||
The
|
||||
.B ntfsresize
|
||||
program does not manipulate the size of partitions.
|
||||
program doesn't manipulate the size of partitions.
|
||||
To do that you have to use a disk partitioning tool, for example
|
||||
.BR fdisk (8).
|
||||
.PP
|
||||
If you wish to enlarge an NTFS filesystem,
|
||||
you must first make sure you can expand the size of the
|
||||
underlying partition first. This can be done using
|
||||
.BR fdisk (8)
|
||||
by deleting the partition and recreating it with a larger size.
|
||||
Then you may use
|
||||
.B ntfsresize
|
||||
to shrink the size of the filesystem.
|
||||
.PP
|
||||
If you wish to shrink an NTFS partition, first use
|
||||
.B ntfsresize
|
||||
to shrink the size of the filesystem. Then you may use
|
||||
|
@ -44,7 +62,8 @@ When recreating the partition with
|
|||
.BR fdisk (8)
|
||||
make sure you create it with the same starting
|
||||
disk cylinder and partition type
|
||||
as before and you do not make it smaller than the new size
|
||||
as before. If you shrink a partition also make sure you do not make
|
||||
it smaller than the new size
|
||||
of the NTFS filesystem! Otherwise you may lose your entire filesystem.
|
||||
Also make sure you set the bootable flag for the partition if it
|
||||
existed before. Failing to do so you might not be able to boot your
|
||||
|
@ -53,8 +72,7 @@ computer from the disk!
|
|||
Note,
|
||||
.B ntfsresize
|
||||
schedules 'chkdsk' to make an NTFS consistency check
|
||||
when you will boot Windows. If your partition was a
|
||||
system partition than Windows may intentionally reboot after
|
||||
when you will boot Windows. Windows may force a reboot after
|
||||
the successful consistency check.
|
||||
|
||||
.SH OPTIONS
|
||||
|
@ -80,7 +98,7 @@ Volume will be opened read-only and
|
|||
displays what it would do if it were to resize the filesystem.
|
||||
.TP
|
||||
.B -s \fIsize\fR[\fBk\fR|\fBM\fR|\fBG\fR]
|
||||
Shrink volume to \fIsize\fR[\fBk\fR|\fBM\fR|\fBG\fR] bytes.
|
||||
Resize volume to \fIsize\fR[\fBk\fR|\fBM\fR|\fBG\fR] bytes.
|
||||
The optional modifiers \fBk\fR, \fBM\fR, \fBG\fR mean the
|
||||
.I size
|
||||
parameter is given in kilo-, mega- or gigabytes respectively.
|
||||
|
@ -92,8 +110,7 @@ If you find otherwise, please report it to <linux-ntfs-dev@lists.sourceforge.net
|
|||
.B MAKE SURE YOU HAVE A BACKUP
|
||||
of your important data in case of an unexpected failure.
|
||||
.PP
|
||||
Future work is planned to include support for volume enlargement
|
||||
and resizing fragmented NTFS volumes.
|
||||
Future work is planned to include support resizing fragmented NTFS volumes.
|
||||
Please note, Windows 2000, XP and .NET have built in NTFS defragmenter.
|
||||
.SH AUTHOR
|
||||
.B ntfsresize
|
||||
|
@ -109,6 +126,9 @@ man page formed the basis of this page.
|
|||
.B ntfsresize
|
||||
is part of the linux-ntfs package and is available from
|
||||
http://linux-ntfs.sourceforge.net/ as source and pre-compiled binary.
|
||||
.B ntfsresize
|
||||
related news and FAQ (frequently asked questions) is maintained at
|
||||
http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html
|
||||
.SH SEE ALSO
|
||||
.BR fdisk (8),
|
||||
.BR cfdisk (8),
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
static const char *EXEC_NAME = "ntfsresize";
|
||||
|
||||
static const char *ntfs_report_banner =
|
||||
"\nReport bugs to linux-ntfs-dev@lists.sf.net. "
|
||||
"\nReport bugs to linux-ntfs-dev@lists.sf.net "
|
||||
"Homepage: http://linux-ntfs.sf.net\n";
|
||||
|
||||
static const char *resize_warning_msg =
|
||||
|
@ -59,8 +59,7 @@ static const char *resize_warning_msg =
|
|||
"unexpected failure!\n";
|
||||
|
||||
static const char *resize_important_msg =
|
||||
"NTFS had been successfully resized on device '%s'.\n"
|
||||
"You can go on to resize the device e.g. with 'fdisk'.\n"
|
||||
"You can go on to shrink the device e.g. with 'fdisk'.\n"
|
||||
"IMPORTANT: When recreating the partition, make sure you\n"
|
||||
" 1) create it with the same starting disk cylinder\n"
|
||||
" 2) create it with the same partition type (usually 7, HPFS/NTFS)\n"
|
||||
|
@ -94,6 +93,17 @@ struct progress_bar {
|
|||
float unit;
|
||||
};
|
||||
|
||||
struct __ntfs_resize_t {
|
||||
s64 new_volume_size;
|
||||
ntfs_inode *ni; /* inode being processed */
|
||||
MFT_RECORD *mrec; /* MFT buffer being processed */
|
||||
ntfs_attr_search_ctx *ctx; /* inode attribute being processed */
|
||||
u64 relocations; /* number of clusters to relocate */
|
||||
u64 inuse; /* number of clusters in use */
|
||||
};
|
||||
|
||||
typedef struct __ntfs_resize_t ntfs_resize_t;
|
||||
|
||||
ntfs_volume *vol = NULL;
|
||||
struct bitmap lcn_bitmap;
|
||||
|
||||
|
@ -171,16 +181,18 @@ void usage()
|
|||
{
|
||||
printf("\n");
|
||||
printf ("Usage: %s [-fhin] [-s size[k|M|G]] device\n", EXEC_NAME);
|
||||
printf("Shrink a defragmented NTFS volume.\n");
|
||||
printf("Resize an NTFS volume non-destructively.\n");
|
||||
printf("\n");
|
||||
Dprintf(" -d Show debug information\n");
|
||||
printf (" -f Force to progress (DANGEROUS)\n");
|
||||
printf (" -h This help text\n");
|
||||
printf (" -i Calculate the smallest shrunken size supported (read-only)\n");
|
||||
printf (" -n Make a test run without write operations (read-only)\n");
|
||||
printf (" -s size[k|M|G] Shrink volume to size[k|M|G] bytes (k=10^3, M=10^6, G=10^9)\n");
|
||||
printf (" -s size[k|M|G] Resize volume to size[k|M|G] bytes (k=10^3, M=10^6, G=10^9)\n");
|
||||
/* printf (" -v Verbose operation\n"); */
|
||||
printf (" -V Version information\n");
|
||||
printf("\nThe options -i and -s are mutually exclusive. If both options are "
|
||||
"omitted then\nthe NTFS volume will be enlarged to the device size.\n");
|
||||
printf(ntfs_report_banner);
|
||||
exit(1);
|
||||
}
|
||||
|
@ -202,7 +214,8 @@ void proceed_question(void)
|
|||
buf[0] = 0;
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
if (strchr(short_yes, buf[0]) == 0) {
|
||||
printf("OK quitting. NO CHANGES have been made to your NTFS volume.\n");
|
||||
printf("OK quitting. NO CHANGES have been made to your "
|
||||
"NTFS volume.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -328,20 +341,26 @@ void parse_options(int argc, char **argv)
|
|||
if (!(stderr = fopen("/dev/null", "rw")))
|
||||
perr_exit("Couldn't open /dev/null");
|
||||
|
||||
/* If no '-s size' then estimate smallest shrunken volume size */
|
||||
if (!opt.bytes)
|
||||
opt.info = 1;
|
||||
|
||||
if (opt.info) {
|
||||
if (opt.bytes) {
|
||||
printf(NERR_PREFIX "It makes no sense to use -i and "
|
||||
"-s together.\n");
|
||||
printf(NERR_PREFIX "Options -i and -s can't be used "
|
||||
"together.\n");
|
||||
usage();
|
||||
}
|
||||
opt.ro_flag = MS_RDONLY;
|
||||
}
|
||||
}
|
||||
|
||||
int runlist_extent_number(runlist *rl)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; rl[i].length; i++)
|
||||
;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* nr_clusters_to_bitmap_byte_size
|
||||
*
|
||||
|
@ -370,10 +389,16 @@ s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters)
|
|||
*
|
||||
* This serves as a rudimentary "chkdsk" operation.
|
||||
*/
|
||||
void build_lcn_usage_bitmap(ATTR_RECORD *a)
|
||||
void build_lcn_usage_bitmap(ntfs_resize_t *resize)
|
||||
{
|
||||
s64 new_volume_size, inode;
|
||||
ATTR_RECORD *a;
|
||||
runlist *rl;
|
||||
int i, j;
|
||||
int i, j, runs;
|
||||
|
||||
a = resize->ctx->attr;
|
||||
new_volume_size = resize->new_volume_size;
|
||||
inode = resize->ni->mft_no;
|
||||
|
||||
if (!a->non_resident)
|
||||
return;
|
||||
|
@ -381,15 +406,45 @@ void build_lcn_usage_bitmap(ATTR_RECORD *a)
|
|||
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++) {
|
||||
if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED)
|
||||
s64 lcn = rl[i].lcn;
|
||||
s64 lcn_length = rl[i].length;
|
||||
|
||||
if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED)
|
||||
continue;
|
||||
for (j = 0; j < rl[i].length; j++) {
|
||||
u64 k = (u64)rl[i].lcn + j;
|
||||
|
||||
/* FIXME: ntfs_mapping_pairs_decompress should return error */
|
||||
if (lcn < 0 || lcn_length <= 0)
|
||||
err_exit("Corrupt runlist in inode %lld attr %x LCN "
|
||||
"%llx length %llx\n", inode, a->type, lcn,
|
||||
lcn_length);
|
||||
|
||||
for (j = 0; j < lcn_length; j++) {
|
||||
u64 k = (u64)lcn + j;
|
||||
if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1))
|
||||
err_exit("Cluster %lu referenced twice!\n"
|
||||
"You didn't shutdown your Windows"
|
||||
"properly?", k);
|
||||
"properly?\n", k);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
free(rl);
|
||||
|
@ -401,17 +456,18 @@ void build_lcn_usage_bitmap(ATTR_RECORD *a)
|
|||
* For a given MFT Record, iterate through all its attributes. Any non-resident
|
||||
* data runs will be marked in lcn_bitmap.
|
||||
*/
|
||||
void walk_attributes(MFT_RECORD *mr)
|
||||
void walk_attributes(ntfs_resize_t *resize)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
|
||||
if (!(ctx = ntfs_attr_get_search_ctx(NULL, mr)))
|
||||
if (!(ctx = ntfs_attr_get_search_ctx(resize->ni, NULL)))
|
||||
perr_exit("ntfs_get_attr_search_ctx");
|
||||
|
||||
while (!ntfs_attrs_walk(ctx)) {
|
||||
if (ctx->attr->type == AT_END)
|
||||
break;
|
||||
build_lcn_usage_bitmap(ctx->attr);
|
||||
resize->ctx = ctx;
|
||||
build_lcn_usage_bitmap(resize);
|
||||
}
|
||||
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
@ -436,7 +492,7 @@ void get_bitmap_data(ntfs_volume *vol, struct bitmap *bm)
|
|||
perr_exit ("get_bitmap_data");
|
||||
|
||||
if (ntfs_attr_pread (attr, 0, bm->size, bm->bm) < 0)
|
||||
perr_exit("Couldn't get $Bitmap $DATA\n");
|
||||
perr_exit("Couldn't get $Bitmap $DATA");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -447,17 +503,37 @@ void get_bitmap_data(ntfs_volume *vol, struct bitmap *bm)
|
|||
*/
|
||||
void compare_bitmaps(struct bitmap *a, struct bitmap *b)
|
||||
{
|
||||
int i;
|
||||
u64 i, j, mismatch = 0;
|
||||
char bit;
|
||||
|
||||
if (a->size != b->size)
|
||||
err_exit("$Bitmap file size doesn't match "
|
||||
"calculated size ((%d != %d)\n", a->size, b->size);
|
||||
|
||||
for (i = 0; i < a->size; i++)
|
||||
if (a->bm[i] != b->bm[i])
|
||||
err_exit("Cluster bitmaps differ at %d (%d != %d)\n"
|
||||
"You didn't shutdown your Windows properly?",
|
||||
i, a->bm[i], b->bm[i]);
|
||||
for (i = 0; i < a->size; i++) {
|
||||
|
||||
if (a->bm[i] == b->bm[i])
|
||||
continue;
|
||||
|
||||
for (j = i * 8; j < i * 8 + 8; j++) {
|
||||
|
||||
bit = ntfs_bit_get(a->bm, j);
|
||||
if (bit != ntfs_bit_get(b->bm, j)) {
|
||||
if (++mismatch > 10)
|
||||
continue;
|
||||
|
||||
printf("Cluster accounting failed at %llu "
|
||||
"(0x%Lx): %s cluster in $Bitmap\n",
|
||||
j, j, bit ? "extra" : "miss");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mismatch) {
|
||||
printf("Totally %Lu cluster accounting mismatches.\n"
|
||||
"You didn't shutdown Windows properly?\n", mismatch);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -497,12 +573,11 @@ void progress_update(struct progress_bar *p, u64 current)
|
|||
* Read each record in the MFT, skipping the unused ones, and build up a bitmap
|
||||
* from all the non-resident attributes.
|
||||
*/
|
||||
void walk_inodes()
|
||||
void walk_inodes(ntfs_resize_t *resize)
|
||||
{
|
||||
s32 inode = 0;
|
||||
s64 inode = 0;
|
||||
s64 last_mft_rec;
|
||||
MFT_REF mref;
|
||||
MFT_RECORD *mrec = NULL;
|
||||
ntfs_inode *ni;
|
||||
struct progress_bar progress;
|
||||
|
||||
printf("Scanning volume ...\n");
|
||||
|
@ -513,21 +588,27 @@ void walk_inodes()
|
|||
for (; inode <= last_mft_rec; inode++) {
|
||||
progress_update(&progress, inode);
|
||||
|
||||
mref = (MFT_REF)inode;
|
||||
if (ntfs_file_record_read(vol, mref, &mrec, NULL)) {
|
||||
if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) {
|
||||
/* FIXME: continue only if it make sense, e.g.
|
||||
MFT record not in use based on $MFT bitmap */
|
||||
if (errno == EIO)
|
||||
if (errno == EIO || errno == ENOENT)
|
||||
continue;
|
||||
perr_exit("Reading inode %ld failed", inode);
|
||||
perr_exit("Reading inode %lld failed", inode);
|
||||
}
|
||||
if (!(mrec->flags & MFT_RECORD_IN_USE))
|
||||
|
||||
if (!(ni->mrec->flags & MFT_RECORD_IN_USE))
|
||||
continue;
|
||||
|
||||
walk_attributes(mrec);
|
||||
if ((ni->mrec->base_mft_record) != 0)
|
||||
continue;
|
||||
|
||||
resize->ni = ni;
|
||||
resize->mrec = ni->mrec;
|
||||
walk_attributes(resize);
|
||||
|
||||
if (ntfs_inode_close(ni))
|
||||
perr_exit("ntfs_inode_close for inode %lld", inode);
|
||||
}
|
||||
if (mrec)
|
||||
free(mrec);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -552,7 +633,7 @@ void advise_on_resize()
|
|||
if (fragmanted_end || !opt.info) {
|
||||
printf(fragmented_volume_msg);
|
||||
if (fragmanted_end)
|
||||
exit(1);
|
||||
return;
|
||||
printf("Now ");
|
||||
}
|
||||
|
||||
|
@ -576,7 +657,6 @@ void advise_on_resize()
|
|||
printf("%lld bytes", g_b);
|
||||
|
||||
printf(").\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -630,7 +710,7 @@ void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm)
|
|||
* The metadata file $BadClus needs to be shrunk.
|
||||
*
|
||||
* FIXME: this function should go away and instead using a generalized
|
||||
* "truncate_bitmap_unnamed_attr()"
|
||||
* "truncate_bitmap_data_attr()"
|
||||
*/
|
||||
void truncate_badclust_bad_attr(ATTR_RECORD *a, s64 nr_clusters)
|
||||
{
|
||||
|
@ -656,6 +736,10 @@ void truncate_badclust_bad_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl_bad))
|
||||
exit(1);
|
||||
|
||||
/* We must have at least 8 bytes free to hold the runlist */
|
||||
if (mp_size > 8)
|
||||
perr_exit("ntfs_mapping_pairs_build");
|
||||
|
||||
memcpy((char *)a + a->mapping_pairs_offset, mp, mp_size);
|
||||
a->highest_vcn = cpu_to_le64(nr_clusters - 1LL);
|
||||
a->allocated_size = cpu_to_le64(nr_clusters * vol->cluster_size);
|
||||
|
@ -666,29 +750,17 @@ void truncate_badclust_bad_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
}
|
||||
|
||||
/**
|
||||
* truncate_bitmap_unnamed_attr
|
||||
* shrink_bitmap_data_attr
|
||||
*
|
||||
* Shrink the metadata file $Bitmap. It must be large enough for one bit per
|
||||
* cluster of the shrunken volume. Also it must be a of 8 bytes in size.
|
||||
*/
|
||||
void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters)
|
||||
void shrink_bitmap_data_attr(runlist **rlist, s64 nr_bm_clusters, s64 new_size)
|
||||
{
|
||||
runlist *rl;
|
||||
s64 bm_bsize, size;
|
||||
s64 nr_bm_clusters;
|
||||
int i, j, mp_size;
|
||||
runlist *rl = *rlist;
|
||||
int i, j;
|
||||
u64 k;
|
||||
int trunc_at = -1; /* FIXME: -1 means unset */
|
||||
char *mp;
|
||||
|
||||
if (!a->non_resident)
|
||||
/* FIXME: handle resident attribute value */
|
||||
perr_exit("Resident data attribute in $Bitmap not supported!");
|
||||
|
||||
bm_bsize = nr_clusters_to_bitmap_byte_size(nr_clusters);
|
||||
nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size);
|
||||
|
||||
if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL)))
|
||||
perr_exit("ntfs_mapping_pairs_decompress");
|
||||
|
||||
/* Unallocate truncated clusters in $Bitmap */
|
||||
for (i = 0; rl[i].length; i++) {
|
||||
|
@ -698,19 +770,19 @@ void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
trunc_at = i;
|
||||
if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED)
|
||||
continue;
|
||||
for (j = 0; j < rl[i].length; j++)
|
||||
if (rl[i].vcn + j >= nr_bm_clusters) {
|
||||
u64 k = (u64)rl[i].lcn + j;
|
||||
for (j = 0; j < rl[i].length; j++) {
|
||||
if (rl[i].vcn + j < nr_bm_clusters)
|
||||
continue;
|
||||
|
||||
k = (u64)rl[i].lcn + j;
|
||||
if (k < new_size) {
|
||||
ntfs_bit_set(lcn_bitmap.bm, k, 0);
|
||||
Dprintf("Unallocate cluster: "
|
||||
"%llu (%llx)\n", k, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: realloc lcn_bitmap.bm (if it's worth the risk) */
|
||||
lcn_bitmap.size = bm_bsize;
|
||||
bitmap_file_data_fixup(nr_clusters, &lcn_bitmap);
|
||||
|
||||
if (trunc_at != -1) {
|
||||
/* NOTE: 'i' always > 0 */
|
||||
i = nr_bm_clusters - rl[trunc_at].vcn;
|
||||
|
@ -718,9 +790,101 @@ void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
rl_set(rl + trunc_at + 1, nr_bm_clusters, -1LL, 0LL);
|
||||
|
||||
Dprintf("Runlist truncated at index %d, "
|
||||
"new cluster length %d\n", trunc_at, i);
|
||||
"new cluster length %d\n", trunc_at, i);
|
||||
}
|
||||
}
|
||||
|
||||
void enlarge_bitmap_data_attr(runlist **rlist, s64 nr_bm_clusters, s64 new_size)
|
||||
{
|
||||
runlist *rl = *rlist;
|
||||
s64 i, free_zone = 0;
|
||||
|
||||
for (i = 0; i < rl->length; i++)
|
||||
ntfs_bit_set(lcn_bitmap.bm, rl->lcn + i, 0);
|
||||
free(rl);
|
||||
|
||||
if (!(rl = *rlist = (runlist *)malloc(2 * sizeof(runlist))))
|
||||
perr_exit("Couldn't get memory");
|
||||
|
||||
for (i = vol->nr_clusters; i < new_size; i++)
|
||||
ntfs_bit_set(lcn_bitmap.bm, i, 0);
|
||||
|
||||
for (i = 0; i < new_size; i++) {
|
||||
if (!ntfs_bit_get(lcn_bitmap.bm, i)) {
|
||||
if (++free_zone == nr_bm_clusters)
|
||||
break;
|
||||
} else
|
||||
free_zone = 0;
|
||||
}
|
||||
|
||||
if (free_zone != nr_bm_clusters)
|
||||
err_exit("Couldn't allocate $Bitmap clusters.\n");
|
||||
|
||||
for (; free_zone; free_zone--, i--)
|
||||
ntfs_bit_set(lcn_bitmap.bm, i, 1);
|
||||
|
||||
rl_set(rl, 0LL, i + 1, nr_bm_clusters);
|
||||
rl_set(rl + 1, nr_bm_clusters, -1LL, 0LL);
|
||||
}
|
||||
|
||||
|
||||
void truncate_bitmap_data_attr(ATTR_RECORD *a, s64 nr_clusters)
|
||||
{
|
||||
runlist *rl;
|
||||
s64 bm_bsize, size;
|
||||
s64 nr_bm_clusters;
|
||||
int mp_size;
|
||||
char *mp;
|
||||
u8 *tmp;
|
||||
|
||||
if (!a->non_resident)
|
||||
/* FIXME: handle resident attribute value */
|
||||
perr_exit("Resident data attribute in $Bitmap not supported!");
|
||||
|
||||
bm_bsize = nr_clusters_to_bitmap_byte_size(nr_clusters);
|
||||
nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size);
|
||||
|
||||
if (!(tmp = (u8 *)realloc(lcn_bitmap.bm, bm_bsize)))
|
||||
perr_exit("realloc");
|
||||
lcn_bitmap.bm = tmp;
|
||||
lcn_bitmap.size = bm_bsize;
|
||||
bitmap_file_data_fixup(nr_clusters, &lcn_bitmap);
|
||||
|
||||
if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL)))
|
||||
perr_exit("ntfs_mapping_pairs_decompress");
|
||||
|
||||
if (runlist_extent_number(rl) != 1)
|
||||
err_exit("$Bitmap data has more than one extent, unusual.\n"
|
||||
"Please report it to linux-ntfs-dev@lists.sf.net");
|
||||
|
||||
if (nr_clusters < vol->nr_clusters)
|
||||
shrink_bitmap_data_attr(&rl, nr_bm_clusters, nr_clusters);
|
||||
else
|
||||
enlarge_bitmap_data_attr(&rl, nr_bm_clusters, nr_clusters);
|
||||
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl);
|
||||
|
||||
/* We must have at least 8 bytes free to hold the runlist */
|
||||
if (mp_size > 8)
|
||||
err_exit("Resizing attribute header isn't supported yet.\n");
|
||||
|
||||
if (!(mp = (char *)calloc(1, mp_size)))
|
||||
perr_exit("Couldn't get memory");
|
||||
|
||||
if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl))
|
||||
perr_exit("ntfs_mapping_pairs_build");
|
||||
|
||||
memcpy((char *)a + a->mapping_pairs_offset, mp, mp_size);
|
||||
a->highest_vcn = cpu_to_le64(nr_bm_clusters - 1LL);
|
||||
a->allocated_size = cpu_to_le64(nr_bm_clusters * vol->cluster_size);
|
||||
a->data_size = cpu_to_le64(bm_bsize);
|
||||
a->initialized_size = cpu_to_le64(bm_bsize);
|
||||
|
||||
/*
|
||||
* FIXME: update allocated/data sizes and timestamps in $FILE_NAME
|
||||
* attribute too, for now chkdsk will do this for us.
|
||||
*/
|
||||
|
||||
if (!opt.ro_flag) {
|
||||
size = ntfs_rl_pwrite(vol, rl, 0, bm_bsize, lcn_bitmap.bm);
|
||||
if (bm_bsize != size) {
|
||||
|
@ -732,20 +896,6 @@ void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
}
|
||||
}
|
||||
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl);
|
||||
|
||||
if (!(mp = (char *)calloc(1, mp_size)))
|
||||
perr_exit("Couldn't get memory");
|
||||
|
||||
if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl))
|
||||
exit(1);
|
||||
|
||||
memcpy((char *)a + a->mapping_pairs_offset, mp, mp_size);
|
||||
a->highest_vcn = cpu_to_le64(nr_bm_clusters - 1LL);
|
||||
a->allocated_size = cpu_to_le64(nr_bm_clusters * vol->cluster_size);
|
||||
a->data_size = cpu_to_le64(bm_bsize);
|
||||
a->initialized_size = cpu_to_le64(bm_bsize);
|
||||
|
||||
free(rl);
|
||||
free(mp);
|
||||
}
|
||||
|
@ -814,7 +964,6 @@ void truncate_badclust_file(s64 nr_clusters)
|
|||
lookup_data_attr((MFT_REF)FILE_BadClus, "$Bad", &ctx);
|
||||
look_for_bad_sector(ctx->attr);
|
||||
/* FIXME: sanity_check_attr(ctx->attr); */
|
||||
/* FIXME: should use an "extended" truncate_bitmap_unnamed_attr() */
|
||||
truncate_badclust_bad_attr(ctx->attr, nr_clusters);
|
||||
|
||||
if (write_mft_record(ctx))
|
||||
|
@ -836,7 +985,7 @@ void truncate_bitmap_file(s64 nr_clusters)
|
|||
|
||||
lookup_data_attr((MFT_REF)FILE_Bitmap, NULL, &ctx);
|
||||
/* FIXME: sanity_check_attr(ctx->attr); */
|
||||
truncate_bitmap_unnamed_attr(ctx->attr, nr_clusters);
|
||||
truncate_bitmap_data_attr(ctx->attr, nr_clusters);
|
||||
|
||||
if (write_mft_record(ctx))
|
||||
perr_exit("Couldn't update $Bitmap");
|
||||
|
@ -904,6 +1053,22 @@ void print_volume_size(char *str, ntfs_volume *v, s64 nr_clusters)
|
|||
str, b, rounded_up_division(b, NTFS_MBYTE));
|
||||
}
|
||||
|
||||
void print_disk_usage(ntfs_resize_t *resize)
|
||||
{
|
||||
s64 total, used, free, relocations;
|
||||
|
||||
total = vol->nr_clusters * vol->cluster_size;
|
||||
used = resize->inuse * vol->cluster_size;
|
||||
free = total - used;
|
||||
relocations = resize->relocations * vol->cluster_size;
|
||||
|
||||
printf("Space in use: %lld MB (%.1f%%) ",
|
||||
rounded_up_division(used, NTFS_MBYTE),
|
||||
100.0 * ((float)used / total));
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* mount_volume
|
||||
*
|
||||
|
@ -936,7 +1101,7 @@ void mount_volume()
|
|||
printf("Apparently device '%s' doesn't have a "
|
||||
"valid NTFS. Maybe you selected\nthe whole "
|
||||
"disk instead of a partition (e.g. /dev/hda, "
|
||||
"not /dev/hda8)?\n", opt.volume);
|
||||
"not /dev/hda1)?\n", opt.volume);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
@ -963,21 +1128,25 @@ void mount_volume()
|
|||
*/
|
||||
void prepare_volume_fixup()
|
||||
{
|
||||
if (!opt.ro_flag) {
|
||||
u16 flags;
|
||||
u16 flags;
|
||||
|
||||
flags = vol->flags | VOLUME_IS_DIRTY;
|
||||
if (vol->major_ver >= 2)
|
||||
flags |= VOLUME_MOUNTED_ON_NT4;
|
||||
if (opt.ro_flag)
|
||||
return;
|
||||
|
||||
printf("Schedule chkdsk NTFS consistency check at Windows boot time ...\n");
|
||||
if (ntfs_volume_set_flags(vol, flags))
|
||||
perr_exit("Failed to set $Volume dirty");
|
||||
flags = vol->flags | VOLUME_IS_DIRTY;
|
||||
if (vol->major_ver >= 2)
|
||||
flags |= VOLUME_MOUNTED_ON_NT4;
|
||||
|
||||
printf("Resetting $LogFile ... (this might take a while)\n");
|
||||
if (ntfs_logfile_reset(vol))
|
||||
perr_exit("Failed to reset $LogFile");
|
||||
}
|
||||
printf("Schedule chkdsk for NTFS consistency check at Windows "
|
||||
"boot time ...\n");
|
||||
|
||||
if (ntfs_volume_set_flags(vol, flags))
|
||||
perr_exit("Failed to set $Volume dirty");
|
||||
|
||||
printf("Resetting $LogFile ... (this might take a while)\n");
|
||||
|
||||
if (ntfs_logfile_reset(vol))
|
||||
perr_exit("Failed to reset $LogFile");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -988,7 +1157,9 @@ void prepare_volume_fixup()
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bitmap on_disk_lcn_bitmap;
|
||||
s64 new_volume_size = 0; /* in clusters */
|
||||
ntfs_resize_t resize;
|
||||
s64 new_size = 0; /* in clusters */
|
||||
s64 device_size; /* in bytes */
|
||||
int i;
|
||||
const char *locale;
|
||||
|
||||
|
@ -1002,45 +1173,69 @@ int main(int argc, char **argv)
|
|||
|
||||
mount_volume();
|
||||
|
||||
device_size = ntfs_device_size_get(vol->fd, vol->sector_size);
|
||||
device_size *= vol->sector_size;
|
||||
if (device_size <= 0)
|
||||
err_exit("Couldn't get device size (%Ld)!\n", device_size);
|
||||
|
||||
if (device_size < vol->nr_clusters * vol->cluster_size)
|
||||
err_exit("Current NTFS volume size is bigger than the device "
|
||||
"size (%Ld)!\nCorrupt partition table or incorrect "
|
||||
"device partitioning?\n", device_size);
|
||||
|
||||
if (opt.bytes) {
|
||||
/* Take the integer part: when shrinking we don't want
|
||||
to make the volume to be bigger than requested.
|
||||
Later on we will also decrease this value to save
|
||||
room for the backup boot sector */
|
||||
new_volume_size = opt.bytes / vol->cluster_size;
|
||||
print_volume_size("New volume size ", vol, new_volume_size);
|
||||
if (device_size < opt.bytes)
|
||||
err_exit("New size can't be bigger than the "
|
||||
"device size (%Ld bytes).\n", device_size);
|
||||
} else
|
||||
opt.bytes = device_size;
|
||||
|
||||
/*
|
||||
* Take the integer part: we don't want to make the volume bigger
|
||||
* than requested. Later on we will also decrease this value to save
|
||||
* room for the backup boot sector.
|
||||
*/
|
||||
new_size = opt.bytes / vol->cluster_size;
|
||||
|
||||
if (!opt.info)
|
||||
print_volume_size("New volume size ", vol, new_size);
|
||||
|
||||
/* Backup boot sector at the end of device isn't counted in NTFS
|
||||
volume size thus we have to reserve space for. We don't trust
|
||||
the user does this for us: better to be on the safe side ;) */
|
||||
if (new_size)
|
||||
--new_size;
|
||||
|
||||
if (!opt.info && (new_size == vol->nr_clusters ||
|
||||
(opt.bytes == device_size &&
|
||||
new_size == vol->nr_clusters - 1))) {
|
||||
printf("Nothing to do: NTFS volume size is already OK.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
setup_lcn_bitmap();
|
||||
|
||||
walk_inodes();
|
||||
memset(&resize, 0, sizeof(resize));
|
||||
resize.new_volume_size = new_size;
|
||||
|
||||
walk_inodes(&resize);
|
||||
|
||||
print_disk_usage(&resize);
|
||||
|
||||
get_bitmap_data(vol, &on_disk_lcn_bitmap);
|
||||
compare_bitmaps(&on_disk_lcn_bitmap, &lcn_bitmap);
|
||||
free(on_disk_lcn_bitmap.bm);
|
||||
|
||||
if (opt.info)
|
||||
if (opt.info) {
|
||||
advise_on_resize();
|
||||
|
||||
/* FIXME: check new_volume_size validity */
|
||||
|
||||
/* Backup boot sector at the end of device isn't counted in NTFS
|
||||
volume size thus we have to reserve space for. We don't trust
|
||||
the user does this for us: better to be on the safe side ;) */
|
||||
if (new_volume_size)
|
||||
--new_volume_size;
|
||||
|
||||
if (new_volume_size > vol->nr_clusters)
|
||||
err_exit("Volume enlargement not yet supported\n");
|
||||
else if (new_volume_size == vol->nr_clusters) {
|
||||
printf("Nothing to do: NTFS volume size is already OK.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
for (i = new_volume_size; i < vol->nr_clusters; i++)
|
||||
for (i = new_size; i < vol->nr_clusters; i++)
|
||||
if (ntfs_bit_get(lcn_bitmap.bm, (u64)i)) {
|
||||
/* FIXME: relocate cluster */
|
||||
advise_on_resize();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (opt.force-- <= 0 && !opt.ro_flag) {
|
||||
|
@ -1050,9 +1245,9 @@ int main(int argc, char **argv)
|
|||
|
||||
prepare_volume_fixup();
|
||||
|
||||
truncate_badclust_file(new_volume_size);
|
||||
truncate_bitmap_file(new_volume_size);
|
||||
update_bootsector(new_volume_size);
|
||||
truncate_badclust_file(new_size);
|
||||
truncate_bitmap_file(new_size);
|
||||
update_bootsector(new_size);
|
||||
|
||||
/* We don't create backup boot sector because we don't know where the
|
||||
partition will be split. The scheduled chkdsk will fix it anyway */
|
||||
|
@ -1066,7 +1261,10 @@ int main(int argc, char **argv)
|
|||
if (fsync(vol->fd) == -1)
|
||||
perr_exit("fsync");
|
||||
|
||||
printf(resize_important_msg, vol->dev_name);
|
||||
printf("Successfully resized NTFS on device '%s'.\n", vol->dev_name);
|
||||
if (new_size < vol->nr_clusters)
|
||||
printf(resize_important_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue