From 42597c830dbe91ae504b7f0c40237e133387dfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 7 Oct 2011 11:42:08 +0200 Subject: [PATCH] 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. --- ntfsprogs/ntfsclone.8.in | 13 +++- ntfsprogs/ntfsclone.c | 146 ++++++++++++++++++++++++++++++--------- 2 files changed, 122 insertions(+), 37 deletions(-) diff --git a/ntfsprogs/ntfsclone.8.in b/ntfsprogs/ntfsclone.8.in index ca7f7829..031578fe 100644 --- a/ntfsprogs/ntfsclone.8.in +++ b/ntfsprogs/ntfsclone.8.in @@ -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 diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index 72d8d390..7d56cc6a 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -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);