added a version option
simplified get_bitmap_data added comment blocks (Logical change 1.79)edge.strict_endians
parent
60ac38315c
commit
ed1149f0a0
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/**
|
||||
* ntfsresize - Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002 Szabolcs Szakacsits
|
||||
|
@ -31,6 +31,8 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "types.h"
|
||||
|
@ -45,7 +47,7 @@
|
|||
#include "inode.h"
|
||||
#include "runlist.h"
|
||||
|
||||
const char *EXEC_NAME = "ntfsresize";
|
||||
static const char *EXEC_NAME = "ntfsresize";
|
||||
|
||||
static const char *ntfs_report_banner =
|
||||
"\nReport bugs to linux-ntfs-dev@lists.sf.net. "
|
||||
|
@ -103,7 +105,11 @@ struct bitmap lcn_bitmap;
|
|||
|
||||
#define rounded_up_division(a, b) (((a) + (b - 1)) / (b))
|
||||
|
||||
|
||||
/**
|
||||
* perr_printf
|
||||
*
|
||||
* Print an error message.
|
||||
*/
|
||||
void perr_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -118,7 +124,11 @@ void perr_printf(const char *fmt, ...)
|
|||
fflush(stderr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* err_exit
|
||||
*
|
||||
* Print and error message and exit the program.
|
||||
*/
|
||||
int err_exit(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -132,7 +142,11 @@ int err_exit(const char *fmt, ...)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* perr_exit
|
||||
*
|
||||
* Print and error message and exit the program
|
||||
*/
|
||||
int perr_exit(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -148,7 +162,11 @@ int perr_exit(const char *fmt, ...)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usage
|
||||
*
|
||||
* Print a brief list of program options.
|
||||
*/
|
||||
void usage()
|
||||
{
|
||||
printf("\n");
|
||||
|
@ -162,12 +180,17 @@ void usage()
|
|||
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 (" -v Verbose operation\n"); */
|
||||
printf (" -V Version information\n");
|
||||
printf(ntfs_report_banner);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* Copy-paste from e2fsprogs */
|
||||
/**
|
||||
* proceed_question
|
||||
*
|
||||
* Force the user to confirm an action before performing it.
|
||||
* Copy-paste from e2fsprogs
|
||||
*/
|
||||
void proceed_question(void)
|
||||
{
|
||||
char buf[256];
|
||||
|
@ -179,12 +202,38 @@ void proceed_question(void)
|
|||
buf[0] = 0;
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
if (strchr(short_yes, buf[0]) == 0) {
|
||||
printf("OK quitting. NO CHANGES has been made to your NTFS volume.\n");
|
||||
printf("OK quitting. NO CHANGES have been made to your NTFS volume.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* version - Print version information about the program
|
||||
*
|
||||
* Print a copyright statement and a brief description of the program.
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void version (void)
|
||||
{
|
||||
printf ("Resize an NTFS Volume, without data loss.\n\n"
|
||||
"%s is free software, released under the GNU "
|
||||
"General Public License\nand you are welcome to redistribute "
|
||||
"it under certain conditions.\n%s comes with ABSOLUTELY NO "
|
||||
"WARRANTY; for details read the GNU\nGeneral Public License "
|
||||
"to be found in the file COPYING in the main\nLinux-NTFS "
|
||||
"distribution directory.\n\n",
|
||||
EXEC_NAME, EXEC_NAME);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_new_volume_size
|
||||
*
|
||||
* Convert a user-supplied string into a size. Without any suffix the number
|
||||
* will be assumed to be in bytes. If the number has a suffix of k, M or G it
|
||||
* will be scaled up by 1000, 1000000, or 1000000000.
|
||||
*/
|
||||
s64 get_new_volume_size(char *s)
|
||||
{
|
||||
s64 size;
|
||||
|
@ -228,7 +277,11 @@ s64 get_new_volume_size(char *s)
|
|||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* parse_options
|
||||
*
|
||||
* Parse the command line options
|
||||
*/
|
||||
void parse_options(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
@ -237,7 +290,7 @@ void parse_options(int argc, char **argv)
|
|||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
|
||||
while ((i = getopt(argc, argv, "dfhins:")) != EOF)
|
||||
while ((i = getopt(argc, argv, "dfhins:V")) != EOF)
|
||||
switch (i) {
|
||||
case 'd':
|
||||
opt.debug = 1;
|
||||
|
@ -259,6 +312,8 @@ void parse_options(int argc, char **argv)
|
|||
case 'v':
|
||||
opt.verbose++;
|
||||
break;
|
||||
case 'V':
|
||||
version();
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
@ -273,7 +328,6 @@ 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;
|
||||
|
@ -288,14 +342,18 @@ void parse_options(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nr_clusters_to_bitmap_byte_size
|
||||
*
|
||||
* Take the number of clusters in the volume and calculate the size of $Bitmap.
|
||||
* The size will always be a multiple of 8 bytes.
|
||||
*/
|
||||
s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters)
|
||||
{
|
||||
s64 bm_bsize;
|
||||
|
||||
bm_bsize = rounded_up_division(nr_clusters, 8);
|
||||
|
||||
/* Needs to be multiple of 8 bytes */
|
||||
bm_bsize = (bm_bsize + 7) & ~7;
|
||||
Dprintf("Bitmap byte size : %lld (%lld clusters)\n",
|
||||
bm_bsize, rounded_up_division(bm_bsize, vol->cluster_size));
|
||||
|
@ -303,7 +361,15 @@ s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters)
|
|||
return bm_bsize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* build_lcn_usage_bitmap
|
||||
*
|
||||
* lcn_bitmap has one bit for each cluster on the disk. Initially, lcn_bitmap
|
||||
* has no bits set. As each attribute record is read the bits in lcn_bitmap are
|
||||
* checked to ensure that no other file already references that cluster.
|
||||
*
|
||||
* This serves as a rudimentary "chkdsk" operation.
|
||||
*/
|
||||
void build_lcn_usage_bitmap(ATTR_RECORD *a)
|
||||
{
|
||||
runlist *rl;
|
||||
|
@ -329,7 +395,12 @@ void build_lcn_usage_bitmap(ATTR_RECORD *a)
|
|||
free(rl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* walk_attributes
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
|
@ -346,35 +417,34 @@ void walk_attributes(MFT_RECORD *mr)
|
|||
ntfs_attr_put_search_ctx(ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get_bitmap_data
|
||||
*
|
||||
* Read the metadata file $Bitmap into a bitmap struct.
|
||||
* Each cluster on disk is represented by a bit in this file.
|
||||
*/
|
||||
void get_bitmap_data(ntfs_volume *vol, struct bitmap *bm)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
ntfs_attr *attr;
|
||||
|
||||
if (!(ni = ntfs_inode_open(vol, (MFT_REF)FILE_Bitmap)))
|
||||
perr_exit("ntfs_open_inode");
|
||||
attr = vol->lcnbmp_na;
|
||||
|
||||
if (!(ctx = ntfs_attr_get_search_ctx(ni, NULL)))
|
||||
perr_exit("ntfs_get_attr_search_ctx");
|
||||
bm->size = attr->initialized_size;
|
||||
|
||||
if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx))
|
||||
perr_exit("ntfs_lookup_attr");
|
||||
bm->bm = malloc (bm->size);
|
||||
if (!bm->bm)
|
||||
perr_exit ("get_bitmap_data");
|
||||
|
||||
/* FIXME: get_attribute_value_length() can't handle extents */
|
||||
bm->size = ntfs_get_attribute_value_length(ctx->attr);
|
||||
|
||||
if (!(bm->bm = (u8 *)malloc(bm->size)))
|
||||
perr_exit("get_bitmap_data");
|
||||
|
||||
if (ntfs_get_attribute_value(vol, ni->mrec, ctx->attr, bm->bm) != bm->size)
|
||||
if (ntfs_attr_pread (attr, 0, bm->size, bm->bm) < 0)
|
||||
perr_exit("Couldn't get $Bitmap $DATA\n");
|
||||
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ntfs_inode_close(ni);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* compare_bitmaps
|
||||
*
|
||||
* Compare two bitmaps. In this case, $Bitmap as read from the disk and
|
||||
* lcn_bitmap which we built from the MFT Records.
|
||||
*/
|
||||
void compare_bitmaps(struct bitmap *a, struct bitmap *b)
|
||||
{
|
||||
int i;
|
||||
|
@ -390,7 +460,11 @@ void compare_bitmaps(struct bitmap *a, struct bitmap *b)
|
|||
i, a->bm[i], b->bm[i]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* progress_init
|
||||
*
|
||||
* Create and scale our progress bar.
|
||||
*/
|
||||
void progress_init(struct progress_bar *p, u64 start, u64 stop, int res)
|
||||
{
|
||||
p->start = start;
|
||||
|
@ -399,7 +473,11 @@ void progress_init(struct progress_bar *p, u64 start, u64 stop, int res)
|
|||
p->resolution = res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* progress_update
|
||||
*
|
||||
* Update the progress bar and tell the user.
|
||||
*/
|
||||
void progress_update(struct progress_bar *p, u64 current)
|
||||
{
|
||||
float percent = p->unit * current;
|
||||
|
@ -413,6 +491,12 @@ void progress_update(struct progress_bar *p, u64 current)
|
|||
fflush(stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
* walk_inodes
|
||||
*
|
||||
* Read each record in the MFT, skipping the unused ones, and build up a bitmap
|
||||
* from all the non-resident attributes.
|
||||
*/
|
||||
void walk_inodes()
|
||||
{
|
||||
s32 inode = 0;
|
||||
|
@ -446,7 +530,13 @@ void walk_inodes()
|
|||
free(mrec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* advise_on_resize
|
||||
*
|
||||
* The metadata file $Bitmap has one bit for each cluster on disk. This has
|
||||
* 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()
|
||||
{
|
||||
u64 i, old_b, new_b, g_b, old_mb, new_mb, g_mb;
|
||||
|
@ -489,7 +579,11 @@ void advise_on_resize()
|
|||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* look_for_bad_sector
|
||||
*
|
||||
* Read through the metadata file $BadClus looking for bad sectors on the disk.
|
||||
*/
|
||||
void look_for_bad_sector(ATTR_RECORD *a)
|
||||
{
|
||||
runlist *rl;
|
||||
|
@ -506,7 +600,11 @@ void look_for_bad_sector(ATTR_RECORD *a)
|
|||
free(rl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rl_set
|
||||
*
|
||||
* Helper to set up a runlist object
|
||||
*/
|
||||
void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len)
|
||||
{
|
||||
rl->vcn = vcn;
|
||||
|
@ -514,8 +612,9 @@ void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len)
|
|||
rl->length = len;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* bitmap_file_data_fixup
|
||||
*
|
||||
* $Bitmap can overlap the end of the volume. Any bits in this region
|
||||
* must be set. This region also encompasses the backup boot sector.
|
||||
*/
|
||||
|
@ -525,8 +624,11 @@ void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm)
|
|||
ntfs_bit_set(bm->bm, (u64)cluster, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* truncate_badclust_bad_attr
|
||||
*
|
||||
* The metadata file $BadClus needs to be shrunk.
|
||||
*
|
||||
* FIXME: this function should go away and instead using a generalized
|
||||
* "truncate_bitmap_unnamed_attr()"
|
||||
*/
|
||||
|
@ -563,7 +665,12 @@ void truncate_badclust_bad_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
free(mp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* truncate_bitmap_unnamed_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)
|
||||
{
|
||||
runlist *rl;
|
||||
|
@ -573,7 +680,6 @@ void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
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!");
|
||||
|
@ -644,7 +750,12 @@ void truncate_bitmap_unnamed_attr(ATTR_RECORD *a, s64 nr_clusters)
|
|||
free(mp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lookup_data_attr
|
||||
*
|
||||
* Find the $DATA attribute (with or without a name) for the given MFT reference
|
||||
* (inode number).
|
||||
*/
|
||||
void lookup_data_attr(MFT_REF mref, char *aname, ntfs_attr_search_ctx **ctx)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
|
@ -675,7 +786,12 @@ void lookup_data_attr(MFT_REF mref, char *aname, ntfs_attr_search_ctx **ctx)
|
|||
free(ustr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* write_mft_record
|
||||
*
|
||||
* Write an MFT Record back to the disk. If the read-only command line option
|
||||
* was given, this function will do nothing.
|
||||
*/
|
||||
int write_mft_record(ntfs_attr_search_ctx *ctx)
|
||||
{
|
||||
if (opt.ro_flag)
|
||||
|
@ -684,7 +800,11 @@ int write_mft_record(ntfs_attr_search_ctx *ctx)
|
|||
return ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* truncate_badclust_file
|
||||
*
|
||||
* Shrink the $BadClus file to match the new volume size.
|
||||
*/
|
||||
void truncate_badclust_file(s64 nr_clusters)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
|
@ -703,7 +823,11 @@ void truncate_badclust_file(s64 nr_clusters)
|
|||
ntfs_attr_put_search_ctx(ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* truncate_bitmap_file
|
||||
*
|
||||
* Shrink the $Bitmap file to match the new volume size.
|
||||
*/
|
||||
void truncate_bitmap_file(s64 nr_clusters)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
|
@ -720,7 +844,13 @@ void truncate_bitmap_file(s64 nr_clusters)
|
|||
ntfs_attr_put_search_ctx(ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setup_lcn_bitmap
|
||||
*
|
||||
* Allocate a block of memory with one bit for each cluster of the disk.
|
||||
* All the bits are set to 0, except those representing the region beyond the
|
||||
* end of the disk.
|
||||
*/
|
||||
void setup_lcn_bitmap()
|
||||
{
|
||||
/* Determine lcn bitmap byte size and allocate it. */
|
||||
|
@ -732,8 +862,11 @@ void setup_lcn_bitmap()
|
|||
bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap);
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: should be done using ntfs_* functions */
|
||||
/**
|
||||
* update_bootsector
|
||||
*
|
||||
* FIXME: should be done using ntfs_* functions
|
||||
*/
|
||||
void update_bootsector(s64 nr_clusters)
|
||||
{
|
||||
NTFS_BOOT_SECTOR bs;
|
||||
|
@ -757,7 +890,11 @@ void update_bootsector(s64 nr_clusters)
|
|||
perr_exit("write() error");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* print_volume_size
|
||||
*
|
||||
* Print the volume size in bytes and decimal megabytes.
|
||||
*/
|
||||
void print_volume_size(char *str, ntfs_volume *v, s64 nr_clusters)
|
||||
{
|
||||
s64 b; /* volume size in bytes */
|
||||
|
@ -767,7 +904,13 @@ void print_volume_size(char *str, ntfs_volume *v, s64 nr_clusters)
|
|||
str, b, rounded_up_division(b, NTFS_MBYTE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mount_volume
|
||||
*
|
||||
* First perform some checks to determine if the volume is already mounted, or
|
||||
* is dirty (Windows wasn't shutdown properly). If everything is OK, then mount
|
||||
* the volume (load the metadata into memory).
|
||||
*/
|
||||
void mount_volume()
|
||||
{
|
||||
unsigned long mntflag;
|
||||
|
@ -811,7 +954,13 @@ void mount_volume()
|
|||
print_volume_size("Current volume size", vol, vol->nr_clusters);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* prepare_volume_fixup
|
||||
*
|
||||
* Set the volume's dirty flag and wipe the filesystem journal. When Windows
|
||||
* boots it will automatically run chkdsk to check for any problems. If the
|
||||
* read-only command line option was given, this function will do nothing.
|
||||
*/
|
||||
void prepare_volume_fixup()
|
||||
{
|
||||
if (!opt.ro_flag) {
|
||||
|
@ -825,19 +974,29 @@ void prepare_volume_fixup()
|
|||
if (ntfs_volume_set_flags(vol, flags))
|
||||
perr_exit("Failed to set $Volume dirty");
|
||||
|
||||
printf("Resetting $LogFile ... "
|
||||
"(this might take a while)\n");
|
||||
printf("Resetting $LogFile ... (this might take a while)\n");
|
||||
if (ntfs_logfile_reset(vol))
|
||||
perr_exit("Failed to reset $LogFile");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* main
|
||||
*
|
||||
* Start here
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bitmap on_disk_lcn_bitmap;
|
||||
s64 new_volume_size = 0; /* in clusters */
|
||||
int i;
|
||||
const char *locale;
|
||||
|
||||
locale = setlocale (LC_ALL, "");
|
||||
if (!locale) {
|
||||
locale = setlocale (LC_ALL, NULL);
|
||||
printf ("Failed to set locale, using default (%s).\n", locale);
|
||||
}
|
||||
|
||||
parse_options(argc, argv);
|
||||
|
||||
|
|
Loading…
Reference in New Issue