added special image format support (Per Olofsson)

(Logical change 1.451)
edge.strict_endians
elisa-laajakaista.fi!szaka 2004-07-10 20:01:17 +00:00
parent 3b20fca911
commit 87a2d1bfef
1 changed files with 256 additions and 49 deletions

View File

@ -3,8 +3,9 @@
*
* Copyright (c) 2003-2004 Szabolcs Szakacsits
* Copyright (c) 2004 Anton Altaparmakov
* Special image format support copyright (c) 2004 Per Olofsson
*
* Clone NTFS data and/or metadata to a sparse file, device or stdout.
* Clone NTFS data and/or metadata to a sparse file, image, device or stdout.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -61,6 +62,8 @@ struct {
int std_out;
int blkdev_out; /* output file is block device */
int metadata_only;
int save_image;
int restore_image;
char *output;
char *volume;
struct statfs stfs;
@ -95,6 +98,7 @@ struct ntfs_walk_cluster {
ntfs_volume *vol = NULL;
struct bitmap lcn_bitmap;
int fd_in;
int fd_out;
FILE *msg_out = NULL;
@ -105,6 +109,19 @@ int wiped_unused_mft = 0;
int wiped_resident_data = 0;
int wiped_timestamp_data = 0;
#define IMAGE_MAGIC "\0ntfsclone-image"
#define IMAGE_MAGIC_SIZE 16
struct {
char magic[IMAGE_MAGIC_SIZE];
u8 major_ver;
u8 minor_ver;
u32 cluster_size;
s64 device_size;
s64 nr_clusters;
s64 inuse;
} __attribute__ ((__packed__)) image_hdr;
#define NTFS_MBYTE (1000 * 1000)
#define ERR_PREFIX "ERROR"
@ -180,12 +197,14 @@ static int perr_exit(const char *fmt, ...)
static void usage(void)
{
Eprintf("\nUsage: %s [options] device\n"
" Efficiently clone NTFS to a sparse file, device or standard output.\n"
Eprintf("\nUsage: %s [options] source\n"
" Efficiently clone NTFS to a sparse file, image, device or standard output.\n"
"\n"
" -o, --output FILE Clone NTFS to the non-existent FILE\n"
" -O, --overwrite FILE Clone NTFS to FILE, overwriting if exists\n"
" -m, --metadata Clone *only* metadata (for NTFS experts)\n"
" -s, --save-image Save to the special image format\n"
" -r, --restore-image Restore from the special image format\n"
" -f, --force Force to progress (DANGEROUS)\n"
" -h, --help Display this help\n"
#ifdef DEBUG
@ -202,16 +221,18 @@ static void usage(void)
static void parse_options(int argc, char **argv)
{
static const char *sopt = "-dfhmo:O:";
static const char *sopt = "-dfhmo:O:rs";
static const struct option lopt[] = {
#ifdef DEBUG
{ "debug", no_argument, NULL, 'd' },
{ "debug", no_argument, NULL, 'd' },
#endif
{ "force", no_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' },
{ "metadata", no_argument, NULL, 'm' },
{ "output", required_argument, NULL, 'o' },
{ "overwrite", required_argument, NULL, 'O' },
{ "force", no_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' },
{ "metadata", no_argument, NULL, 'm' },
{ "output", required_argument, NULL, 'o' },
{ "overwrite", required_argument, NULL, 'O' },
{ "restore-image", no_argument, NULL, 'r' },
{ "save-image", no_argument, NULL, 's' },
{ NULL, 0, NULL, 0 }
};
@ -245,6 +266,12 @@ static void parse_options(int argc, char **argv)
usage();
opt.output = optarg;
break;
case 'r':
opt.restore_image++;
break;
case 's':
opt.save_image++;
break;
default:
err_printf("Unknown option '%s'.\n", argv[optind-1]);
usage();
@ -264,9 +291,21 @@ static void parse_options(int argc, char **argv)
usage();
}
if (opt.metadata_only && opt.save_image)
err_exit("Saving only metadata to an image is not "
"supported!\n");
if (opt.metadata_only && opt.restore_image)
err_exit("Restoring only metadata from an image is not "
"supported!\n");
if (opt.metadata_only && opt.std_out)
err_exit("Cloning only metadata to stdout isn't supported!\n");
if (opt.save_image && opt.restore_image)
err_exit("Saving and restoring an image at the same time "
"is not supported!\n");
if (!opt.std_out) {
struct stat st;
@ -378,6 +417,8 @@ static int io_all(void *fd, void *buf, int count, int do_write)
while (count > 0) {
if (do_write)
i = write(*(int *)fd, buf, count);
else if (opt.restore_image)
i = read(*(int *)fd, buf, count);
else
i = dev->d_ops->read(dev, buf, count);
if (i < 0) {
@ -395,11 +436,20 @@ static int io_all(void *fd, void *buf, int count, int do_write)
static void copy_cluster(void)
{
char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
u32 csize = opt.restore_image ? image_hdr.cluster_size
: vol->cluster_size;
if (read_all(vol->dev, buff, vol->cluster_size) == -1)
if (read_all(opt.restore_image ? (void *)&fd_in : vol->dev, buff,
csize) == -1)
perr_exit("read_all");
if (write_all(&fd_out, buff, vol->cluster_size) == -1) {
if (opt.save_image) {
char cmd = 1;
if (write_all(&fd_out, &cmd, sizeof(cmd)) == -1)
perr_exit("write_all");
}
if (write_all(&fd_out, buff, csize) == -1) {
int err = errno;
perr_printf("Write failed");
if (err == EIO && opt.stfs.f_type == 0x517b)
@ -420,13 +470,26 @@ static void lseek_to_cluster(s64 lcn)
if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1)
perr_exit("lseek input");
if (opt.std_out)
if (opt.std_out || opt.save_image)
return;
if (lseek(fd_out, pos, SEEK_SET) == (off_t)-1)
perr_exit("lseek output");
}
static void image_skip_clusters(s64 count)
{
if (opt.save_image && count > 0) {
char buff[1 + sizeof(count)];
buff[0] = 0;
memcpy(buff + 1, &count, sizeof(count));
if (write_all(&fd_out, buff, sizeof(buff)) == -1)
perr_exit("write_all");
}
}
static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl)
{
s64 i, len; /* number of clusters to copy */
@ -446,45 +509,62 @@ static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl)
static void clone_ntfs(u64 nr_clusters)
{
s64 i, pos, count;
s64 i, pos, count, last_cl;
u8 bm[NTFS_BUF_SIZE];
void *buf;
u32 csize = vol->cluster_size;
u64 p_counter = 0;
struct progress_bar progress;
Printf("Cloning NTFS ...\n");
if (opt.save_image)
Printf("Saving NTFS to image ...\n");
else
Printf("Cloning NTFS ...\n");
if ((buf = calloc(1, csize)) == NULL)
perr_exit("dump_to_stdout");
progress_init(&progress, p_counter, nr_clusters, 100);
if (opt.save_image) {
if (write_all(&fd_out, &image_hdr, sizeof(image_hdr))
== -1)
perr_exit("write_all");
}
pos = 0;
last_cl = 0;
while (1) {
count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm);
if (count == -1)
perr_exit("Couldn't read $Bitmap (pos = %lld)\n", pos);
if (count == 0)
if (count == 0) {
image_skip_clusters(pos * 8 - last_cl - 1);
return;
}
for (i = 0; i < count; i++, pos++) {
s64 cl; /* current cluster */
for (cl = pos * 8; cl < (pos + 1) * 8; cl++) {
if (cl > vol->nr_clusters - 1)
if (cl > vol->nr_clusters - 1) {
image_skip_clusters(cl - last_cl - 1);
return;
}
if (ntfs_bit_get(bm, i * 8 + cl % 8)) {
progress_update(&progress, ++p_counter);
lseek_to_cluster(cl);
image_skip_clusters(cl - last_cl - 1);
copy_cluster();
last_cl = cl;
continue;
}
if (opt.std_out) {
if (opt.std_out && !opt.save_image) {
progress_update(&progress, ++p_counter);
if (write_all(&fd_out, buf, csize) == -1)
perr_exit("write_all");
@ -494,6 +574,59 @@ static void clone_ntfs(u64 nr_clusters)
}
}
static void write_empty_clusters(s32 csize, s64 count,
struct progress_bar *progress, u64 *p_counter)
{
s64 i;
char buff[NTFS_MAX_CLUSTER_SIZE];
memset(buff, 0, csize);
for (i = 0; i < count; i++) {
if (write_all(&fd_out, buff, csize) == -1)
perr_exit("write_all");
progress_update(progress, ++(*p_counter));
}
}
static void restore_image(void)
{
s64 pos = 0, count;
s32 csize = image_hdr.cluster_size;
char cmd;
u64 p_counter = 0;
struct progress_bar progress;
Printf("Restoring NTFS from image ...\n");
progress_init(&progress, p_counter, opt.std_out ?
image_hdr.nr_clusters : image_hdr.inuse, 100);
while (pos < image_hdr.nr_clusters) {
if (read_all(&fd_in, &cmd, sizeof(cmd)) == -1)
perr_exit("read_all");
if (cmd == 0) {
if (read_all(&fd_in, &count, sizeof(count)) == -1)
perr_exit("read_all");
if (opt.std_out)
write_empty_clusters(csize, count,
&progress, &p_counter);
else {
if (lseek(fd_out, count * csize, SEEK_CUR)
== (off_t)-1)
perr_exit("lseek output");
}
pos += count;
} else if (cmd == 1) {
copy_cluster();
pos++;
progress_update(&progress, ++p_counter);
} else
err_exit("Invalid command code in image\n");
}
}
#define WIPE_TIMESTAMPS(atype, attr) \
do { \
atype *ats; \
@ -845,12 +978,12 @@ static void print_volume_size(const char *str, s64 bytes)
}
static void print_disk_usage(ntfs_walk_clusters_ctx *image)
static void print_disk_usage(u32 cluster_size, s64 nr_clusters, s64 inuse)
{
s64 total, used;
total = vol->nr_clusters * vol->cluster_size;
used = image->inuse * vol->cluster_size;
total = nr_clusters * cluster_size;
used = inuse * cluster_size;
Printf("Space in use : %lld MB (%.1f%%) ",
(long long)rounded_up_division(used, NTFS_MBYTE),
@ -859,6 +992,22 @@ static void print_disk_usage(ntfs_walk_clusters_ctx *image)
Printf("\n");
}
static void print_image_info(void)
{
Printf("NTFS volume version: %d.%d\n",
image_hdr.major_ver, image_hdr.minor_ver);
Printf("Cluster size : %u bytes\n",
image_hdr.cluster_size);
print_volume_size("Image volume size ",
image_hdr.cluster_size
* image_hdr.nr_clusters);
Printf("Image device size : %lld bytes\n",
image_hdr.device_size);
print_disk_usage(image_hdr.cluster_size,
image_hdr.nr_clusters,
image_hdr.inuse);
}
static void check_if_mounted(const char *device, unsigned long new_mntflag)
{
unsigned long mntflag;
@ -1019,7 +1168,71 @@ static void set_filesize(s64 filesize)
exit(1);
}
}
static s64 open_image(void)
{
if (strcmp(opt.volume, "-") == 0) {
if ((fd_in = fileno(stdin)) == -1)
perr_exit("fileno for stdout failed");
} else {
if ((fd_in = open(opt.volume, O_RDONLY)) == -1)
perr_exit("failed to open image");
}
if (read_all(&fd_in, &image_hdr, sizeof(image_hdr)) == -1)
perr_exit("read_all");
if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0)
err_exit("Input file is not an image! (invalid magic)\n");
return image_hdr.device_size;
}
static s64 open_volume(void)
{
s64 device_size;
mount_volume(MS_RDONLY);
device_size = ntfs_device_size_get(vol->dev, 1);
if (device_size <= 0)
err_exit("Couldn't get device size (%lld)!\n", device_size);
print_volume_size("Current device size", device_size);
if (device_size < vol->nr_clusters * vol->cluster_size)
err_exit("Current NTFS volume size is bigger than the device "
"size (%lld)!\nCorrupt partition table or incorrect "
"device partitioning?\n", device_size);
return device_size;
}
static void initialise_image_hdr(s64 device_size, s64 inuse)
{
memcpy(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE);
image_hdr.major_ver = vol->major_ver;
image_hdr.minor_ver = vol->minor_ver;
image_hdr.cluster_size = vol->cluster_size;
image_hdr.device_size = device_size;
image_hdr.nr_clusters = vol->nr_clusters;
image_hdr.inuse = inuse;
}
static void check_output_filesize(s64 device_size)
{
if (opt.blkdev_out) {
s64 dest_size = device_size_get(fd_out);
s64 ntfs_size = vol->nr_clusters * vol->cluster_size;
ntfs_size += 512; /* add backup boot sector */
if (dest_size < ntfs_size)
err_exit("Output device size (%lld) is too small"
" to fit the NTFS image.\n", dest_size);
check_if_mounted(opt.output, 0);
} else
set_filesize(device_size);
}
int main(int argc, char **argv)
{
@ -1035,18 +1248,10 @@ int main(int argc, char **argv)
utils_set_locale();
mount_volume(MS_RDONLY);
device_size = ntfs_device_size_get(vol->dev, 1);
if (device_size <= 0)
err_exit("Couldn't get device size (%lld)!\n", device_size);
print_volume_size("Current device size", device_size);
if (device_size < vol->nr_clusters * vol->cluster_size)
err_exit("Current NTFS volume size is bigger than the device "
"size (%lld)!\nCorrupt partition table or incorrect "
"device partitioning?\n", device_size);
if (opt.restore_image)
device_size = open_image();
else
device_size = open_volume();
if (opt.std_out) {
if ((fd_out = fileno(stdout)) == -1)
@ -1063,34 +1268,36 @@ int main(int argc, char **argv)
if ((fd_out = open(opt.output, flags, S_IRWXU)) == -1)
perr_exit("Opening file '%s' failed", opt.output);
if (opt.blkdev_out) {
s64 dest_size = device_size_get(fd_out);
s64 ntfs_size = vol->nr_clusters * vol->cluster_size;
ntfs_size += 512; /* add backup boot sector */
if (dest_size < ntfs_size)
err_exit("Output device size (%lld) is too small"
" to fit the NTFS image.\n", dest_size);
check_if_mounted(opt.output, 0);
} else
set_filesize(device_size);
if (!opt.save_image)
check_output_filesize(device_size);
}
if (opt.restore_image) {
print_image_info();
restore_image();
fsync_clone(fd_out);
exit(0);
}
setup_lcn_bitmap();
memset(&image, 0, sizeof(image));
backup_clusters.image = &image;
walk_clusters(vol, &backup_clusters);
compare_bitmaps(&lcn_bitmap);
print_disk_usage(&image);
print_disk_usage(vol->cluster_size, vol->nr_clusters, image.inuse);
free(lcn_bitmap.bm);
if (opt.save_image)
initialise_image_hdr(device_size, image.inuse);
/* FIXME: save backup boot sector */
if (opt.std_out || !opt.metadata_only) {
s64 nr_clusters = opt.std_out ? vol->nr_clusters : image.inuse;
s64 nr_clusters = (opt.std_out && !opt.save_image)
? vol->nr_clusters : image.inuse;
clone_ntfs(nr_clusters);
fsync_clone(fd_out);