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
parent
c42c502e92
commit
42597c830d
|
@ -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
|
||||
|
|
|
@ -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 = ℑ
|
||||
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue