Developped creating a metadata image in ntfsclone

With this patch a metadata image can be created without creating
an intermediate file. Use both option -m and -s.
edge.strict_endians
Jean-Pierre André 2011-10-07 11:42:08 +02:00
parent c42c502e92
commit 42597c830d
2 changed files with 122 additions and 37 deletions

View File

@ -157,6 +157,8 @@ embedded into metadata. All is filled with zeros.
Moreover all the file timestamps, deleted and unused spaces inside
the metadata are filled with zeros. Thus this mode is inappropriate
for example for forensic analyses.
This mode may be combined with \fB\-\-save\-image\fP to create a
special image format file instead of a sparse file.
Please note, filenames are not wiped out. They might contain
sensitive information, so think twice before sending such an
@ -211,8 +213,9 @@ beginning of such sectors are marked by "BadSectoR\\0".
\fB\-m\fR, \fB\-\-metadata\fR
Clone
.B ONLY METADATA
(for NTFS experts). Moreover only cloning to a file is allowed.
You can't metadata\-only clone to a device, image or standard output.
(for NTFS experts). Only cloning to a (sparse) file is allowed, unless used
the option \fB\-\-save\-image\fP is also used.
You can't metadata\-only clone to a device.
.TP
\fB\-\-ignore\-fs\-check\fR
Ignore the result of the filesystem consistency check. This option is allowed
@ -301,13 +304,17 @@ Clone an NTFS volume to a non\-existent file:
.RE
Pack NTFS metadata for NTFS experts. Please note that bzip2 runs
very long but results usually at least 10 times smaller archives
than gzip.
than gzip on a sparse file.
.RS
.sp
.B ntfsclone \-\-metadata \-\-output ntfsmeta.img /dev/hda1
.br
.B bzip2 ntfsmeta.img
.sp
Or, outputting to a compressed image :
.br
.B ntfsclone \-mst \-\-output - /dev/hda1 | bzip2 > ntfsmeta.bz2
.sp
.RE
Unpacking NTFS metadata into a sparse file:
.RS

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 2003-2006 Szabolcs Szakacsits
* Copyright (c) 2004-2006 Anton Altaparmakov
* Copyright (c) 2010 Jean-Pierre Andre
* Copyright (c) 2010-2011 Jean-Pierre Andre
* Special image format support copyright (c) 2004 Per Olofsson
*
* Clone NTFS data and/or metadata to a sparse file, image, device or stdout.
@ -119,6 +119,7 @@ static struct {
int ignore_fs_check;
int rescue;
int save_image;
int metadata_image;
int preserve_timestamps;
int restore_image;
char *output;
@ -144,6 +145,7 @@ typedef struct {
ntfs_inode *ni; /* inode being processed */
ntfs_attr_search_ctx *ctx; /* inode attribute being processed */
s64 inuse; /* number of clusters in use */
LCN current_lcn;
} ntfs_walk_clusters_ctx;
typedef int (ntfs_walk_op)(ntfs_inode *ni, void *data);
@ -328,7 +330,6 @@ static void usage(void)
exit(1);
}
static void parse_options(int argc, char **argv)
{
static const char *sopt = "-dfhmo:O:rst";
@ -413,15 +414,16 @@ static void parse_options(int argc, char **argv)
usage();
}
if (opt.metadata && opt.save_image)
err_exit("Saving only metadata to an image is not "
"supported!\n");
if (opt.metadata && opt.save_image) {
opt.metadata_image++;
opt.save_image = 0;
}
if (opt.metadata && opt.restore_image)
err_exit("Restoring only metadata from an image is not "
"supported!\n");
if (opt.metadata && opt.std_out)
if (opt.metadata && !opt.metadata_image && opt.std_out)
err_exit("Cloning only metadata to stdout isn't supported!\n");
if (opt.ignore_fs_check && !opt.metadata)
@ -547,7 +549,7 @@ static int io_all(void *fd, void *buf, int count, int do_write)
while (count > 0) {
if (do_write) {
if (opt.save_image)
if (opt.save_image || opt.metadata_image)
i = fwrite(buf, 1, count, stream_out);
else
i = write(*(int *)fd, buf, count);
@ -628,13 +630,14 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn)
}
}
if (opt.save_image) {
if (opt.save_image || (opt.metadata_image && !wipe)) {
char cmd = 1;
if (write_all(&fd_out, &cmd, sizeof(cmd)) == -1)
perr_exit("write_all");
}
if (write_all(&fd_out, buff, csize) == -1) {
if ((!opt.metadata_image || !wipe)
&& (write_all(&fd_out, buff, csize) == -1)) {
#ifndef NO_STATFS
int err = errno;
perr_printf("Write failed");
@ -659,13 +662,27 @@ 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 || opt.save_image)
if (opt.std_out || opt.save_image || opt.metadata_image)
return;
if (lseek(fd_out, pos, SEEK_SET) == (off_t)-1)
perr_exit("lseek output");
}
static void gap_to_cluster(s64 gap)
{
sle64 count;
char buf[1 + sizeof(count)];
if (gap) {
count = cpu_to_sle64(gap);
buf[0] = 0;
memcpy(&buf[1], &count, sizeof(count));
if (write_all(&fd_out, buf, sizeof(buf)) == -1)
perr_exit("write_all");
}
}
static void image_skip_clusters(s64 count)
{
if (opt.save_image && count > 0) {
@ -685,17 +702,38 @@ static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl)
{
s64 i, len; /* number of clusters to copy */
if (opt.std_out || !opt.metadata)
if ((opt.std_out && !opt.metadata_image) || !opt.metadata)
return;
if (!(len = is_critical_metadata(image, rl)))
return;
lseek_to_cluster(rl->lcn);
if (opt.metadata_image && !wipe)
gap_to_cluster(rl->lcn - image->current_lcn);
if (!wipe) {
/* FIXME: this could give pretty suboptimal performance */
for (i = 0; i < len; i++)
copy_cluster(opt.rescue, rl->lcn + i, rl->lcn + i);
image->current_lcn = rl->lcn + len;
}
if (opt.metadata_image)
image->inuse += len;
}
/* FIXME: this could give pretty suboptimal performance */
for (i = 0; i < len; i++)
copy_cluster(opt.rescue, rl->lcn + i, rl->lcn + i);
static void write_image_hdr(void)
{
char alignment[IMAGE_HDR_ALIGN];
if (opt.save_image || opt.metadata_image) {
int alignsize = le32_to_cpu(image_hdr.offset_to_image_data)
- sizeof(image_hdr);
memset(alignment,0,IMAGE_HDR_ALIGN);
if ((alignsize < 0)
|| write_all(&fd_out, &image_hdr, sizeof(image_hdr))
|| write_all(&fd_out, alignment, alignsize))
perr_exit("write_all");
}
}
static void clone_ntfs(u64 nr_clusters)
@ -808,13 +846,22 @@ static void restore_image(void)
sizeof(count)) == -1)
perr_exit("read_all");
}
if (opt.std_out)
write_empty_clusters(csize, count,
if (opt.std_out) {
if ((!p_counter && count) || (count < 0))
err_exit("Cannot restore a metadata"
" image to stdout\n");
else
write_empty_clusters(csize, count,
&progress, &p_counter);
else {
if (lseek(fd_out, count * csize, SEEK_CUR) ==
(off_t)-1)
perr_exit("restore_image: lseek");
} else {
if (((pos + count) < 0)
|| ((pos + count)
> sle64_to_cpu(image_hdr.nr_clusters)))
err_exit("restore_image: corrupt image\n");
else
if (lseek(fd_out, count * csize,
SEEK_CUR) == (off_t)-1)
perr_exit("restore_image: lseek");
}
pos += count;
} else if (cmd == 1) {
@ -1052,7 +1099,14 @@ static void clone_logfile_parts(ntfs_walk_clusters_ctx *image, runlist *rl)
break;
lseek_to_cluster(lcn);
if (opt.metadata_image && !wipe)
gap_to_cluster(lcn - image->current_lcn);
copy_cluster(opt.rescue, lcn, lcn);
image->current_lcn = lcn + 1;
if (opt.metadata_image)
image->inuse++;
if (offset == 0)
offset = NTFS_BLOCK_SIZE >> 1;
@ -1103,7 +1157,7 @@ static void walk_runs(struct ntfs_walk_cluster *walk)
(unsigned int)le32_to_cpu(a->type),
(long long)lcn, (long long)lcn_length);
if (!wipe)
if (!wipe || opt.metadata_image)
dump_clusters(walk->image, rl + i);
for (j = 0; j < lcn_length; j++) {
@ -1114,9 +1168,10 @@ static void walk_runs(struct ntfs_walk_cluster *walk)
"properly?\n", (unsigned long long)k);
}
walk->image->inuse += lcn_length;
if (!opt.metadata_image)
walk->image->inuse += lcn_length;
}
if (!wipe && !opt.std_out && opt.metadata &&
if (((!wipe && !opt.std_out) || opt.metadata_image) && opt.metadata &&
walk->image->ni->mft_no == FILE_LogFile &&
walk->image->ctx->attr->type == AT_DATA)
clone_logfile_parts(walk->image, rl);
@ -1283,6 +1338,7 @@ static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk)
{
s64 inode = 0;
s64 last_mft_rec;
u64 nr_clusters;
ntfs_inode *ni;
struct progress_bar progress;
@ -1290,6 +1346,7 @@ static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk)
last_mft_rec = (volume->mft_na->initialized_size >>
volume->mft_record_size_bits) - 1;
walk->image->current_lcn = 0;
progress_init(&progress, inode, last_mft_rec, 100);
NVolSetNoFixupWarn(volume);
@ -1359,7 +1416,17 @@ out:
perr_exit("ntfs_inode_close for inode %lld",
(long long)inode);
}
if (opt.metadata) {
/* also get the backup bootsector */
nr_clusters = vol->nr_clusters;
lseek_to_cluster(nr_clusters);
if (opt.metadata_image && !wipe)
gap_to_cluster(nr_clusters - walk->image->current_lcn);
if (!wipe)
copy_cluster(opt.rescue, nr_clusters, nr_clusters);
walk->image->current_lcn = nr_clusters;
walk->image->inuse++;
}
return 0;
}
@ -1912,7 +1979,7 @@ int main(int argc, char **argv)
flags |= O_EXCL;
}
if (opt.save_image) {
if (opt.save_image || opt.metadata_image) {
stream_out = fopen(opt.output,"w");
if (!stream_out)
perr_exit("Opening file '%s' failed",
@ -1924,7 +1991,7 @@ int main(int argc, char **argv)
perr_exit("Opening file '%s' failed",
opt.output);
if (!opt.save_image)
if (!opt.save_image && !opt.metadata_image)
check_output_device(ntfs_size);
}
@ -1939,6 +2006,8 @@ int main(int argc, char **argv)
memset(&image, 0, sizeof(image));
backup_clusters.image = &image;
if (opt.metadata_image)
wipe = 1;
walk_clusters(vol, &backup_clusters);
compare_bitmaps(&lcn_bitmap);
print_disk_usage("", vol->cluster_size, vol->nr_clusters, image.inuse);
@ -1950,7 +2019,7 @@ int main(int argc, char **argv)
if (opt.save_image)
initialise_image_hdr(device_size, image.inuse);
if (opt.std_out || !opt.metadata) {
if ((opt.std_out && !opt.metadata_image) || !opt.metadata) {
s64 nr_clusters_to_save = image.inuse;
if (opt.std_out && !opt.save_image)
nr_clusters_to_save = vol->nr_clusters;
@ -1965,14 +2034,20 @@ int main(int argc, char **argv)
exit(0);
}
wipe = 1;
fsync_clone(fd_out); /* sync copy before mounting */
opt.volume = opt.output;
if (opt.metadata_image) {
wipe = 0;
initialise_image_hdr(device_size, image.inuse);
write_image_hdr();
} else {
wipe = 1;
fsync_clone(fd_out); /* sync copy before mounting */
opt.volume = opt.output;
/* 'force' again mount for dirty volumes (e.g. after resize).
FIXME: use mount flags to avoid potential side-effects in future */
opt.force++;
ntfs_umount(vol,FALSE);
mount_volume(0 /*MS_NOATIME*/);
opt.force++;
ntfs_umount(vol,FALSE);
mount_volume(0 /*MS_NOATIME*/);
}
free(lcn_bitmap.bm);
setup_lcn_bitmap();
@ -1997,7 +2072,10 @@ int main(int argc, char **argv)
wiped_total += wiped_timestamp_data;
Printf("Wiped totally = %10u\n", wiped_total);
fsync_clone(fd_out);
if (opt.metadata_image)
fclose(stream_out);
else
fsync_clone(fd_out);
ntfs_umount(vol,FALSE);
free(lcn_bitmap.bm);
return (0);