added special image format support (Per Olofsson)
(Logical change 1.451)edge.strict_endians
parent
3b20fca911
commit
87a2d1bfef
|
@ -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 = ℑ
|
||||
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue