1N/A/**
1N/A * ntfsclone - Part of the Linux-NTFS project.
1N/A *
1N/A * Copyright (c) 2003-2006 Szabolcs Szakacsits
1N/A * Copyright (c) 2004-2006 Anton Altaparmakov
1N/A * Special image format support copyright (c) 2004 Per Olofsson
1N/A *
1N/A * Clone NTFS data and/or metadata to a sparse file, image, device or stdout.
1N/A *
1N/A * This program is free software; you can redistribute it and/or modify
1N/A * it under the terms of the GNU General Public License as published by
1N/A * the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A */
1N/A
1N/A#include "config.h"
1N/A
1N/A#ifdef HAVE_UNISTD_H
1N/A#include <unistd.h>
1N/A#endif
1N/A#ifdef HAVE_STDLIB_H
1N/A#include <stdlib.h>
1N/A#endif
1N/A#ifdef HAVE_STDIO_H
1N/A#include <stdio.h>
1N/A#endif
1N/A#ifdef HAVE_SYS_TYPES_H
1N/A#include <sys/types.h>
1N/A#endif
1N/A#ifdef HAVE_SYS_STAT_H
1N/A#include <sys/stat.h>
1N/A#endif
1N/A#ifdef HAVE_SYS_IOCTL_H
1N/A#include <sys/ioctl.h>
1N/A#endif
1N/A#ifdef HAVE_SYS_VFS_H
1N/A#include <sys/vfs.h>
1N/A#endif
1N/A#ifdef HAVE_SYS_STATVFS_H
1N/A#include <sys/statvfs.h>
1N/A#endif
1N/A#ifdef HAVE_FCNTL_H
1N/A#include <fcntl.h>
1N/A#endif
1N/A#ifdef HAVE_STDARG_H
1N/A#include <stdarg.h>
1N/A#endif
1N/A#ifdef HAVE_STRING_H
1N/A#include <string.h>
1N/A#endif
1N/A#ifdef HAVE_ERRNO_H
1N/A#include <errno.h>
1N/A#endif
1N/A#ifdef HAVE_GETOPT_H
1N/A#include <getopt.h>
1N/A#endif
1N/A
1N/A/*
1N/A * FIXME: ntfsclone do bad things about endians handling. Fix it and remove
1N/A * this note and define.
1N/A */
1N/A#define NTFS_DO_NOT_CHECK_ENDIANS
1N/A
1N/A#include "compat.h"
1N/A#include "debug.h"
1N/A#include "types.h"
1N/A#include "support.h"
1N/A#include "endians.h"
1N/A#include "bootsect.h"
1N/A#include "device.h"
1N/A#include "attrib.h"
1N/A#include "mst.h"
1N/A#include "volume.h"
1N/A#include "mft.h"
1N/A#include "bitmap.h"
1N/A#include "inode.h"
1N/A#include "index.h"
1N/A#include "dir.h"
1N/A#include "runlist.h"
1N/A#include "ntfstime.h"
1N/A#include "utils.h"
1N/A#include "version.h"
1N/A
1N/A#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
1N/A#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */
1N/A#endif
1N/A#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
1N/A#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */
1N/A#endif
1N/A
1N/Astatic const char *EXEC_NAME = "ntfsclone";
1N/A
1N/Astatic const char *bad_sectors_warning_msg =
1N/A"*************************************************************************\n"
1N/A"* WARNING: The disk has bad sector. This means physical damage on the *\n"
1N/A"* disk surface caused by deterioration, manufacturing faults or other *\n"
1N/A"* reason. The reliability of the disk may stay stable or degrade fast. *\n"
1N/A"* Use the --rescue option to efficiently save as much data as possible! *\n"
1N/A"*************************************************************************\n";
1N/A
1N/Astatic const char *dirty_volume_msg =
1N/A"Volume '%s' is scheduled for a check or it was shutdown \n"
1N/A"uncleanly. Please boot Windows or use the --force option to progress.\n";
1N/A
1N/Astatic struct {
1N/A int verbose;
1N/A int quiet;
1N/A int debug;
1N/A int force;
1N/A int overwrite;
1N/A int std_out;
1N/A int blkdev_out; /* output file is block device */
1N/A int metadata; /* metadata only cloning */
1N/A int ignore_fs_check;
1N/A int rescue;
1N/A int save_image;
1N/A int restore_image;
1N/A char *output;
1N/A char *volume;
1N/A#ifdef __sun
1N/A struct statvfs stfs;
1N/A#else
1N/A struct statfs stfs;
1N/A#endif
1N/A} opt;
1N/A
1N/Astruct bitmap {
1N/A s64 size;
1N/A u8 *bm;
1N/A};
1N/A
1N/Astruct progress_bar {
1N/A u64 start;
1N/A u64 stop;
1N/A int resolution;
1N/A float unit;
1N/A};
1N/A
1N/Atypedef struct {
1N/A ntfs_inode *ni; /* inode being processed */
1N/A ntfs_attr_search_ctx *ctx; /* inode attribute being processed */
1N/A s64 inuse; /* number of clusters in use */
1N/A} ntfs_walk_clusters_ctx;
1N/A
1N/Atypedef int (ntfs_walk_op)(ntfs_inode *ni, void *data);
1N/A
1N/Astruct ntfs_walk_cluster {
1N/A ntfs_walk_op *inode_op; /* not implemented yet */
1N/A ntfs_walk_clusters_ctx *image;
1N/A};
1N/A
1N/A
1N/Astatic ntfs_volume *vol = NULL;
1N/Astatic struct bitmap lcn_bitmap;
1N/A
1N/Astatic int fd_in;
1N/Astatic int fd_out;
1N/Astatic FILE *msg_out = NULL;
1N/A
1N/Astatic int wipe = 0;
1N/Astatic unsigned int nr_used_mft_records = 0;
1N/Astatic unsigned int wiped_unused_mft_data = 0;
1N/Astatic unsigned int wiped_unused_mft = 0;
1N/Astatic unsigned int wiped_resident_data = 0;
1N/Astatic unsigned int wiped_timestamp_data = 0;
1N/A
1N/Astatic BOOL image_is_host_endian = FALSE;
1N/A
1N/A#define IMAGE_MAGIC "\0ntfsclone-image"
1N/A#define IMAGE_MAGIC_SIZE 16
1N/A
1N/A/* This is the first endianness safe format version. */
1N/A#define NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE 10
1N/A#define NTFSCLONE_IMG_VER_MINOR_ENDIANNESS_SAFE 0
1N/A
1N/A/*
1N/A * Set the version to 10.0 to avoid colisions with old ntfsclone which
1N/A * stupidly used the volume version as the image version... )-: I hope NTFS
1N/A * never reaches version 10.0 and if it does one day I hope no-one is using
1N/A * such an old ntfsclone by then...
1N/A *
1N/A * NOTE: Only bump the minor version if the image format and header are still
1N/A * backwards compatible. Otherwise always bump the major version. If in
1N/A * doubt, bump the major version.
1N/A */
1N/A#define NTFSCLONE_IMG_VER_MAJOR 10
1N/A#define NTFSCLONE_IMG_VER_MINOR 0
1N/A
1N/A/* All values are in little endian. */
1N/A#ifdef __sun
1N/A#pragma pack(1)
1N/A#endif
1N/Astatic struct image_hdr {
1N/A char magic[IMAGE_MAGIC_SIZE];
1N/A u8 major_ver;
1N/A u8 minor_ver;
1N/A u32 cluster_size;
1N/A s64 device_size;
1N/A s64 nr_clusters;
1N/A s64 inuse;
1N/A u32 offset_to_image_data; /* From start of image_hdr. */
1N/A} __attribute__((__packed__)) image_hdr;
1N/A#ifdef __sun
1N/A#pragma pack()
1N/A#endif
1N/A
1N/A#ifdef __sun
1N/A#define NTFSCLONE_IMG_HEADER_SIZE_OLD \
1N/A (offsetof(struct image_hdr, offset_to_image_data))
1N/A#else
1N/A#define NTFSCLONE_IMG_HEADER_SIZE_OLD \
1N/A (offsetof(typeof(image_hdr), offset_to_image_data))
1N/A#endif
1N/A
1N/A#define NTFS_MBYTE (1000 * 1000)
1N/A
1N/A#define ERR_PREFIX "ERROR"
1N/A#define PERR_PREFIX ERR_PREFIX "(%d): "
1N/A#define NERR_PREFIX ERR_PREFIX ": "
1N/A
1N/A#define LAST_METADATA_INODE 11
1N/A
1N/A#define NTFS_MAX_CLUSTER_SIZE 65536
1N/A#define NTFS_SECTOR_SIZE 512
1N/A
1N/A#define rounded_up_division(a, b) (((a) + (b - 1)) / (b))
1N/A
1N/A#define read_all(f, p, n) io_all((f), (p), (n), 0)
1N/A#define write_all(f, p, n) io_all((f), (p), (n), 1)
1N/A
1N/A__attribute__((format(printf, 1, 2)))
1N/Astatic void Printf(const char *fmt, ...)
1N/A{
1N/A va_list ap;
1N/A
1N/A va_start(ap, fmt);
1N/A vfprintf(msg_out, fmt, ap);
1N/A va_end(ap);
1N/A fflush(msg_out);
1N/A}
1N/A
1N/A__attribute__((format(printf, 1, 2)))
1N/Astatic void perr_printf(const char *fmt, ...)
1N/A{
1N/A va_list ap;
1N/A int eo = errno;
1N/A
1N/A Printf(PERR_PREFIX, eo);
1N/A va_start(ap, fmt);
1N/A vfprintf(msg_out, fmt, ap);
1N/A va_end(ap);
1N/A Printf(": %s\n", strerror(eo));
1N/A fflush(msg_out);
1N/A}
1N/A
1N/A__attribute__((format(printf, 1, 2)))
1N/Astatic void err_printf(const char *fmt, ...)
1N/A{
1N/A va_list ap;
1N/A
1N/A Printf(NERR_PREFIX);
1N/A va_start(ap, fmt);
1N/A vfprintf(msg_out, fmt, ap);
1N/A va_end(ap);
1N/A fflush(msg_out);
1N/A}
1N/A
1N/A__attribute__((noreturn))
1N/A__attribute__((format(printf, 1, 2)))
1N/Astatic int err_exit(const char *fmt, ...)
1N/A{
1N/A va_list ap;
1N/A
1N/A Printf(NERR_PREFIX);
1N/A va_start(ap, fmt);
1N/A vfprintf(msg_out, fmt, ap);
1N/A va_end(ap);
1N/A fflush(msg_out);
1N/A exit(1);
1N/A}
1N/A
1N/A__attribute__((noreturn))
1N/A__attribute__((format(printf, 1, 2)))
1N/Astatic int perr_exit(const char *fmt, ...)
1N/A{
1N/A va_list ap;
1N/A int eo = errno;
1N/A
1N/A Printf(PERR_PREFIX, eo);
1N/A va_start(ap, fmt);
1N/A vfprintf(msg_out, fmt, ap);
1N/A va_end(ap);
1N/A Printf(": %s\n", strerror(eo));
1N/A fflush(msg_out);
1N/A exit(1);
1N/A}
1N/A
1N/A
1N/A__attribute__((noreturn))
1N/Astatic void usage(void)
1N/A{
1N/A fprintf(stderr, "\nUsage: %s [OPTIONS] SOURCE\n"
1N/A " Efficiently clone NTFS to a sparse file, image, device or standard output.\n"
1N/A "\n"
1N/A " -o, --output FILE Clone NTFS to the non-existent FILE\n"
1N/A " -O, --overwrite FILE Clone NTFS to FILE, overwriting if exists\n"
1N/A " -s, --save-image Save to the special image format\n"
1N/A " -r, --restore-image Restore from the special image format\n"
1N/A " --rescue Continue after disk read errors\n"
1N/A " -m, --metadata Clone *only* metadata (for NTFS experts)\n"
1N/A " --ignore-fs-check Ignore the filesystem check result\n"
1N/A " -f, --force Force to progress (DANGEROUS)\n"
1N/A " -h, --help Display this help\n"
1N/A#ifdef DEBUG
1N/A " -d, --debug Show debug information\n"
1N/A#endif
1N/A "\n"
1N/A " If FILE is '-' then send the image to the standard output. If SOURCE is '-'\n"
1N/A " and --restore-image is used then read the image from the standard input.\n"
1N/A "\n", EXEC_NAME);
1N/A fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home);
1N/A exit(1);
1N/A}
1N/A
1N/A
1N/Astatic void parse_options(int argc, char **argv)
1N/A{
1N/A static const char *sopt = "-dfhmo:O:rs";
1N/A static const struct option lopt[] = {
1N/A#ifdef DEBUG
1N/A { "debug", no_argument, NULL, 'd' },
1N/A#endif
1N/A { "force", no_argument, NULL, 'f' },
1N/A { "help", no_argument, NULL, 'h' },
1N/A { "metadata", no_argument, NULL, 'm' },
1N/A { "output", required_argument, NULL, 'o' },
1N/A { "overwrite", required_argument, NULL, 'O' },
1N/A { "restore-image", no_argument, NULL, 'r' },
1N/A { "ignore-fs-check", no_argument, NULL, 'C' },
1N/A { "rescue", no_argument, NULL, 'R' },
1N/A { "save-image", no_argument, NULL, 's' },
1N/A { NULL, 0, NULL, 0 }
1N/A };
1N/A
1N/A int c;
1N/A
1N/A memset(&opt, 0, sizeof(opt));
1N/A
1N/A while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
1N/A switch (c) {
1N/A case 1: /* A non-option argument */
1N/A if (opt.volume)
1N/A usage();
1N/A opt.volume = argv[optind-1];
1N/A break;
1N/A case 'd':
1N/A opt.debug++;
1N/A break;
1N/A case 'f':
1N/A opt.force++;
1N/A break;
1N/A case 'h':
1N/A case '?':
1N/A usage();
1N/A case 'm':
1N/A opt.metadata++;
1N/A break;
1N/A case 'O':
1N/A opt.overwrite++;
1N/A case 'o':
1N/A if (opt.output)
1N/A usage();
1N/A opt.output = optarg;
1N/A break;
1N/A case 'r':
1N/A opt.restore_image++;
1N/A break;
1N/A case 'C':
1N/A opt.ignore_fs_check++;
1N/A break;
1N/A case 'R':
1N/A opt.rescue++;
1N/A break;
1N/A case 's':
1N/A opt.save_image++;
1N/A break;
1N/A default:
1N/A err_printf("Unknown option '%s'.\n", argv[optind-1]);
1N/A usage();
1N/A }
1N/A }
1N/A
1N/A if (opt.output == NULL) {
1N/A err_printf("You must specify an output file.\n");
1N/A usage();
1N/A }
1N/A
1N/A if (strcmp(opt.output, "-") == 0)
1N/A opt.std_out++;
1N/A
1N/A if (opt.volume == NULL) {
1N/A err_printf("You must specify a device file.\n");
1N/A usage();
1N/A }
1N/A
1N/A if (opt.metadata && opt.save_image)
1N/A err_exit("Saving only metadata to an image is not "
1N/A "supported!\n");
1N/A
1N/A if (opt.metadata && opt.restore_image)
1N/A err_exit("Restoring only metadata from an image is not "
1N/A "supported!\n");
1N/A
1N/A if (opt.metadata && opt.std_out)
1N/A err_exit("Cloning only metadata to stdout isn't supported!\n");
1N/A
1N/A if (opt.ignore_fs_check && !opt.metadata)
1N/A err_exit("Filesystem check can be ignored only for metadata "
1N/A "cloning!\n");
1N/A
1N/A if (opt.save_image && opt.restore_image)
1N/A err_exit("Saving and restoring an image at the same time "
1N/A "is not supported!\n");
1N/A
1N/A if (!opt.std_out) {
1N/A struct stat st;
1N/A
1N/A if (stat(opt.output, &st) == -1) {
1N/A if (errno != ENOENT)
1N/A perr_exit("Couldn't access '%s'", opt.output);
1N/A } else {
1N/A if (!opt.overwrite)
1N/A err_exit("Output file '%s' already exists.\n"
1N/A "Use option --overwrite if you want to"
1N/A " replace its content.\n", opt.output);
1N/A
1N/A if (S_ISBLK(st.st_mode)) {
1N/A opt.blkdev_out = 1;
1N/A if (opt.metadata)
1N/A err_exit("Cloning only metadata to a "
1N/A "block device isn't supported!\n");
1N/A }
1N/A }
1N/A }
1N/A
1N/A msg_out = stdout;
1N/A
1N/A /* FIXME: this is a workaround for losing debug info if stdout != stderr
1N/A and for the uncontrollable verbose messages in libntfs. Ughhh. */
1N/A if (opt.std_out)
1N/A msg_out = stderr;
1N/A else if (opt.debug) {
1N/A /* Redirect stderr to stdout, note fflush()es are essential! */
1N/A fflush(stdout);
1N/A fflush(stderr);
1N/A if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
1N/A perror("Failed to redirect stderr to stdout");
1N/A exit(1);
1N/A }
1N/A fflush(stdout);
1N/A fflush(stderr);
1N/A } else {
1N/A fflush(stderr);
1N/A if (!freopen("/dev/null", "w", stderr))
1N/A perr_exit("Failed to redirect stderr to /dev/null");
1N/A }
1N/A}
1N/A
1N/Astatic void progress_init(struct progress_bar *p, u64 start, u64 stop, int res)
1N/A{
1N/A p->start = start;
1N/A p->stop = stop;
1N/A p->unit = 100.0 / (stop - start);
1N/A p->resolution = res;
1N/A}
1N/A
1N/A
1N/Astatic void progress_update(struct progress_bar *p, u64 current)
1N/A{
1N/A float percent = p->unit * current;
1N/A
1N/A if (current != p->stop) {
1N/A if ((current - p->start) % p->resolution)
1N/A return;
1N/A Printf("%6.2f percent completed\r", percent);
1N/A } else
1N/A Printf("100.00 percent completed\n");
1N/A fflush(msg_out);
1N/A}
1N/A
1N/Astatic s64 is_critical_metadata(ntfs_walk_clusters_ctx *image, runlist *rl)
1N/A{
1N/A s64 inode = image->ni->mft_no;
1N/A
1N/A if (inode <= LAST_METADATA_INODE) {
1N/A
1N/A /* Don't save bad sectors (both $Bad and unnamed are ignored */
1N/A if (inode == FILE_BadClus && image->ctx->attr->type == AT_DATA)
1N/A return 0;
1N/A
1N/A if (inode != FILE_LogFile)
1N/A return rl->length;
1N/A
1N/A if (image->ctx->attr->type == AT_DATA) {
1N/A
1N/A /* Save at least the first 16 KiB of FILE_LogFile */
1N/A s64 s = (s64)16384 - rl->vcn * vol->cluster_size;
1N/A if (s > 0) {
1N/A s = rounded_up_division(s, vol->cluster_size);
1N/A if (rl->length < s)
1N/A s = rl->length;
1N/A return s;
1N/A }
1N/A return 0;
1N/A }
1N/A }
1N/A
1N/A if (image->ctx->attr->type != AT_DATA)
1N/A return rl->length;
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A
1N/Astatic int io_all(void *fd, void *buf, int count, int do_write)
1N/A{
1N/A int i;
1N/A struct ntfs_device *dev = fd;
1N/A
1N/A while (count > 0) {
1N/A if (do_write)
1N/A i = write(*(int *)fd, buf, count);
1N/A else if (opt.restore_image)
1N/A i = read(*(int *)fd, buf, count);
1N/A else
1N/A i = dev->d_ops->read(dev, buf, count);
1N/A if (i < 0) {
1N/A if (errno != EAGAIN && errno != EINTR)
1N/A return -1;
1N/A } else {
1N/A count -= i;
1N/A buf = i + (char *) buf;
1N/A }
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A
1N/Astatic void rescue_sector(void *fd, off_t pos, void *buff)
1N/A{
1N/A const char *badsector_magic = "BadSectoR\0";
1N/A struct ntfs_device *dev = fd;
1N/A
1N/A if (opt.restore_image) {
1N/A if (lseek(*(int *)fd, pos, SEEK_SET) == (off_t)-1)
1N/A perr_exit("lseek");
1N/A } else {
1N/A if (vol->u.dev->d_ops->seek(dev, pos, SEEK_SET) == (off_t)-1)
1N/A perr_exit("seek input");
1N/A }
1N/A
1N/A if (read_all(fd, buff, NTFS_SECTOR_SIZE) == -1) {
1N/A Printf("WARNING: Can't read sector at %llu, lost data.\n",
1N/A (unsigned long long)pos);
1N/A memset(buff, '?', NTFS_SECTOR_SIZE);
1N/A memmove(buff, badsector_magic, sizeof(badsector_magic));
1N/A }
1N/A}
1N/A
1N/A
1N/Astatic void copy_cluster(int rescue, u64 rescue_lcn)
1N/A{
1N/A char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */
1N/A /* vol is NULL if opt.restore_image is set */
1N/A u32 csize = le32_to_cpu(image_hdr.cluster_size);
1N/A void *fd = (void *)&fd_in;
1N/A off_t rescue_pos;
1N/A
1N/A if (!opt.restore_image) {
1N/A csize = vol->cluster_size;
1N/A fd = vol->u.dev;
1N/A }
1N/A
1N/A rescue_pos = (off_t)(rescue_lcn * csize);
1N/A
1N/A if (read_all(fd, buff, csize) == -1) {
1N/A
1N/A if (errno != EIO)
1N/A perr_exit("read_all");
1N/A else if (rescue){
1N/A u32 i;
1N/A for (i = 0; i < csize; i += NTFS_SECTOR_SIZE)
1N/A rescue_sector(fd, rescue_pos + i, buff + i);
1N/A } else {
1N/A Printf("%s", bad_sectors_warning_msg);
1N/A err_exit("Disk is faulty, can't make full backup!");
1N/A }
1N/A }
1N/A
1N/A if (opt.save_image) {
1N/A char cmd = 1;
1N/A if (write_all(&fd_out, &cmd, sizeof(cmd)) == -1)
1N/A perr_exit("write_all");
1N/A }
1N/A
1N/A if (write_all(&fd_out, buff, csize) == -1) {
1N/A perr_printf("Write failed");
1N/A#ifndef __sun
1N/A int err = errno;
1N/A if (errno == EIO && opt.stfs.f_type == 0x517b)
1N/A Printf("Apparently you tried to clone to a remote "
1N/A "Windows computer but they don't\nhave "
1N/A "efficient sparse file handling by default. "
1N/A "Please try a different method.\n");
1N/A#endif /* !defined(__sun) */
1N/A exit(1);
1N/A }
1N/A}
1N/A
1N/Astatic void lseek_to_cluster(s64 lcn)
1N/A{
1N/A off_t pos;
1N/A
1N/A pos = (off_t)(lcn * vol->cluster_size);
1N/A
1N/A if (vol->u.dev->d_ops->seek(vol->u.dev, pos, SEEK_SET) == (off_t)-1)
1N/A perr_exit("lseek input");
1N/A
1N/A if (opt.std_out || opt.save_image)
1N/A return;
1N/A
1N/A if (lseek(fd_out, pos, SEEK_SET) == (off_t)-1)
1N/A perr_exit("lseek output");
1N/A}
1N/A
1N/Astatic void image_skip_clusters(s64 count)
1N/A{
1N/A if (opt.save_image && count > 0) {
1N/A#ifdef __sun
1N/A s64 count_buf;
1N/A#else
1N/A typeof(count) count_buf;
1N/A#endif
1N/A char buff[1 + sizeof(count)];
1N/A
1N/A buff[0] = 0;
1N/A count_buf = cpu_to_sle64(count);
1N/A memcpy(buff + 1, &count_buf, sizeof(count_buf));
1N/A
1N/A if (write_all(&fd_out, buff, sizeof(buff)) == -1)
1N/A perr_exit("write_all");
1N/A }
1N/A}
1N/A
1N/Astatic void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl)
1N/A{
1N/A s64 i, len; /* number of clusters to copy */
1N/A
1N/A if (opt.std_out || !opt.metadata)
1N/A return;
1N/A
1N/A if (!(len = is_critical_metadata(image, rl)))
1N/A return;
1N/A
1N/A lseek_to_cluster(rl->lcn);
1N/A
1N/A /* FIXME: this could give pretty suboptimal performance */
1N/A for (i = 0; i < len; i++)
1N/A copy_cluster(opt.rescue, rl->lcn + i);
1N/A}
1N/A
1N/Astatic void clone_ntfs(u64 nr_clusters)
1N/A{
1N/A u64 cl, last_cl; /* current and last used cluster */
1N/A void *buf;
1N/A u32 csize = vol->cluster_size;
1N/A u64 p_counter = 0;
1N/A struct progress_bar progress;
1N/A
1N/A if (opt.save_image)
1N/A Printf("Saving NTFS to image ...\n");
1N/A else
1N/A Printf("Cloning NTFS ...\n");
1N/A
1N/A buf = ntfs_calloc(csize);
1N/A if (!buf)
1N/A perr_exit("clone_ntfs");
1N/A
1N/A progress_init(&progress, p_counter, nr_clusters, 100);
1N/A
1N/A if (opt.save_image) {
1N/A if (write_all(&fd_out, &image_hdr,
1N/A image_hdr.offset_to_image_data) == -1)
1N/A perr_exit("write_all");
1N/A }
1N/A
1N/A for (last_cl = cl = 0; cl < (u64)vol->nr_clusters; cl++) {
1N/A
1N/A if (ntfs_bit_get(lcn_bitmap.bm, cl)) {
1N/A progress_update(&progress, ++p_counter);
1N/A lseek_to_cluster(cl);
1N/A image_skip_clusters(cl - last_cl - 1);
1N/A
1N/A copy_cluster(opt.rescue, cl);
1N/A last_cl = cl;
1N/A continue;
1N/A }
1N/A
1N/A if (opt.std_out && !opt.save_image) {
1N/A progress_update(&progress, ++p_counter);
1N/A if (write_all(&fd_out, buf, csize) == -1)
1N/A perr_exit("write_all");
1N/A }
1N/A }
1N/A image_skip_clusters(cl - last_cl - 1);
1N/A}
1N/A
1N/Astatic void write_empty_clusters(s32 csize, s64 count,
1N/A struct progress_bar *progress, u64 *p_counter)
1N/A{
1N/A s64 i;
1N/A char buff[NTFS_MAX_CLUSTER_SIZE];
1N/A
1N/A memset(buff, 0, csize);
1N/A
1N/A for (i = 0; i < count; i++) {
1N/A if (write_all(&fd_out, buff, csize) == -1)
1N/A perr_exit("write_all");
1N/A progress_update(progress, ++(*p_counter));
1N/A }
1N/A}
1N/A
1N/Astatic void restore_image(void)
1N/A{
1N/A s64 pos = 0, count;
1N/A s32 csize = le32_to_cpu(image_hdr.cluster_size);
1N/A char cmd;
1N/A u64 p_counter = 0;
1N/A struct progress_bar progress;
1N/A
1N/A Printf("Restoring NTFS from image ...\n");
1N/A
1N/A progress_init(&progress, p_counter, opt.std_out ?
1N/A sle64_to_cpu(image_hdr.nr_clusters) :
1N/A sle64_to_cpu(image_hdr.inuse),
1N/A 100);
1N/A
1N/A while (pos < sle64_to_cpu(image_hdr.nr_clusters)) {
1N/A if (read_all(&fd_in, &cmd, sizeof(cmd)) == -1)
1N/A perr_exit("read_all");
1N/A
1N/A if (cmd == 0) {
1N/A if (read_all(&fd_in, &count, sizeof(count)) == -1)
1N/A perr_exit("read_all");
1N/A if (!image_is_host_endian)
1N/A count = sle64_to_cpu(count);
1N/A if (opt.std_out)
1N/A write_empty_clusters(csize, count,
1N/A &progress, &p_counter);
1N/A else {
1N/A if (lseek(fd_out, count * csize, SEEK_CUR) ==
1N/A (off_t)-1)
1N/A perr_exit("restore_image: lseek");
1N/A }
1N/A pos += count;
1N/A } else if (cmd == 1) {
1N/A copy_cluster(0, 0);
1N/A pos++;
1N/A progress_update(&progress, ++p_counter);
1N/A } else
1N/A err_exit("Invalid command code in image\n");
1N/A }
1N/A}
1N/A
1N/Astatic void wipe_index_entry_timestams(INDEX_ENTRY *e)
1N/A{
1N/A s64 timestamp = utc2ntfs(0);
1N/A
1N/A /* FIXME: can fall into infinite loop if corrupted */
1N/A while (!(e->flags & INDEX_ENTRY_END)) {
1N/A
1N/A e->key.file_name.creation_time = timestamp;
1N/A e->key.file_name.last_data_change_time = timestamp;
1N/A e->key.file_name.last_mft_change_time = timestamp;
1N/A e->key.file_name.last_access_time = timestamp;
1N/A
1N/A wiped_timestamp_data += 32;
1N/A
1N/A e = (INDEX_ENTRY *)((u8 *)e + le16_to_cpu(e->length));
1N/A }
1N/A}
1N/A
1N/Astatic void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr)
1N/A{
1N/A INDEX_ALLOCATION *indexa, *tmp_indexa;
1N/A INDEX_ENTRY *entry;
1N/A INDEX_ROOT *indexr;
1N/A u8 *bitmap, *byte;
1N/A int bit;
1N/A ntfs_attr *na;
1N/A ntfschar *name;
1N/A u32 name_len;
1N/A
1N/A indexr = ntfs_index_root_get(ni, attr);
1N/A if (!indexr) {
1N/A perr_printf("Failed to read $INDEX_ROOT attribute of inode "
1N/A "%lld", ni->mft_no);
1N/A return;
1N/A }
1N/A
1N/A if (indexr->type != AT_FILE_NAME)
1N/A goto out_indexr;
1N/A
1N/A name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset));
1N/A name_len = attr->name_length;
1N/A
1N/A byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL);
1N/A if (!byte) {
1N/A perr_printf("Failed to read $BITMAP attribute");
1N/A goto out_indexr;
1N/A }
1N/A
1N/A na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, name, name_len);
1N/A if (!na) {
1N/A perr_printf("Failed to open $INDEX_ALLOCATION attribute");
1N/A goto out_bitmap;
1N/A }
1N/A
1N/A if (!na->data_size)
1N/A goto out_na;
1N/A
1N/A tmp_indexa = indexa = ntfs_malloc(na->data_size);
1N/A if (!tmp_indexa)
1N/A goto out_na;
1N/A
1N/A if (ntfs_attr_pread(na, 0, na->data_size, indexa) != na->data_size) {
1N/A perr_printf("Failed to read $INDEX_ALLOCATION attribute");
1N/A goto out_indexa;
1N/A }
1N/A
1N/A bit = 0;
1N/A while ((u8 *)tmp_indexa < (u8 *)indexa + na->data_size) {
1N/A if (*byte & (1 << bit)) {
1N/A if (ntfs_mst_post_read_fixup((NTFS_RECORD *)tmp_indexa,
1N/A le32_to_cpu(
1N/A indexr->index_block_size))) {
1N/A perr_printf("Damaged INDX record");
1N/A goto out_indexa;
1N/A }
1N/A entry = (INDEX_ENTRY *)((u8 *)tmp_indexa + le32_to_cpu(
1N/A tmp_indexa->index.entries_offset) + 0x18);
1N/A
1N/A wipe_index_entry_timestams(entry);
1N/A
1N/A if (ntfs_mft_usn_dec((MFT_RECORD *)tmp_indexa))
1N/A perr_exit("ntfs_mft_usn_dec");
1N/A
1N/A if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)tmp_indexa,
1N/A le32_to_cpu(
1N/A indexr->index_block_size))) {
1N/A perr_printf("INDX write fixup failed");
1N/A goto out_indexa;
1N/A }
1N/A }
1N/A tmp_indexa = (INDEX_ALLOCATION *)((u8 *)tmp_indexa +
1N/A le32_to_cpu(indexr->index_block_size));
1N/A bit++;
1N/A if (bit > 7) {
1N/A bit = 0;
1N/A byte++;
1N/A }
1N/A }
1N/A
1N/A if (ntfs_rl_pwrite(vol, na->rl, 0, na->data_size, indexa) != na->data_size)
1N/A perr_printf("ntfs_rl_pwrite failed for inode %lld", ni->mft_no);
1N/Aout_indexa:
1N/A free(indexa);
1N/Aout_na:
1N/A ntfs_attr_close(na);
1N/Aout_bitmap:
1N/A free(bitmap);
1N/Aout_indexr:
1N/A free(indexr);
1N/A}
1N/A
1N/Astatic void wipe_index_root_timestamps(ATTR_RECORD *attr, s64 timestamp)
1N/A{
1N/A INDEX_ENTRY *entry;
1N/A INDEX_ROOT *iroot;
1N/A
1N/A iroot = (INDEX_ROOT *)((u8 *)attr + le16_to_cpu(attr->u.res.value_offset));
1N/A entry = (INDEX_ENTRY *)((u8 *)iroot +
1N/A le32_to_cpu(iroot->index.entries_offset) + 0x10);
1N/A
1N/A while (!(entry->flags & INDEX_ENTRY_END)) {
1N/A
1N/A if (iroot->type == AT_FILE_NAME) {
1N/A
1N/A entry->key.file_name.creation_time = timestamp;
1N/A entry->key.file_name.last_access_time = timestamp;
1N/A entry->key.file_name.last_data_change_time = timestamp;
1N/A entry->key.file_name.last_mft_change_time = timestamp;
1N/A
1N/A wiped_timestamp_data += 32;
1N/A
1N/A } else if (ntfs_names_are_equal(NTFS_INDEX_Q,
1N/A sizeof(NTFS_INDEX_Q) / 2 - 1,
1N/A (ntfschar *)((char *)attr +
1N/A le16_to_cpu(attr->name_offset)),
1N/A attr->name_length, 0, NULL, 0)) {
1N/A
1N/A QUOTA_CONTROL_ENTRY *quota_q;
1N/A
1N/A quota_q = (QUOTA_CONTROL_ENTRY *)((u8 *)entry +
1N/A le16_to_cpu(entry->u.s.data_offset));
1N/A /*
1N/A * FIXME: no guarantee it's indeed /$Extend/$Quota:$Q.
1N/A * For now, as a minimal safeguard, we check only for
1N/A * quota version 2 ...
1N/A */
1N/A if (le32_to_cpu(quota_q->version) == 2) {
1N/A quota_q->change_time = timestamp;
1N/A wiped_timestamp_data += 4;
1N/A }
1N/A }
1N/A
1N/A entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length));
1N/A }
1N/A}
1N/A
1N/A#define WIPE_TIMESTAMPS(atype, attr, timestamp) \
1N/Ado { \
1N/A atype *ats; \
1N/A ats = (atype *)((char *)(attr) + le16_to_cpu((attr)->u.res.value_offset)); \
1N/A \
1N/A ats->creation_time = (timestamp); \
1N/A ats->last_data_change_time = (timestamp); \
1N/A ats->last_mft_change_time= (timestamp); \
1N/A ats->last_access_time = (timestamp); \
1N/A \
1N/A wiped_timestamp_data += 32; \
1N/A \
1N/A} while (0)
1N/A
1N/Astatic void wipe_timestamps(ntfs_walk_clusters_ctx *image)
1N/A{
1N/A ATTR_RECORD *a = image->ctx->attr;
1N/A s64 timestamp = utc2ntfs(0);
1N/A
1N/A if (a->type == AT_FILE_NAME)
1N/A WIPE_TIMESTAMPS(FILE_NAME_ATTR, a, timestamp);
1N/A
1N/A else if (a->type == AT_STANDARD_INFORMATION)
1N/A WIPE_TIMESTAMPS(STANDARD_INFORMATION, a, timestamp);
1N/A
1N/A else if (a->type == AT_INDEX_ROOT)
1N/A wipe_index_root_timestamps(a, timestamp);
1N/A}
1N/A
1N/Astatic void wipe_resident_data(ntfs_walk_clusters_ctx *image)
1N/A{
1N/A ATTR_RECORD *a;
1N/A u32 i;
1N/A int n = 0;
1N/A u8 *p;
1N/A
1N/A a = image->ctx->attr;
1N/A p = (u8*)a + le16_to_cpu(a->u.res.value_offset);
1N/A
1N/A if (image->ni->mft_no <= LAST_METADATA_INODE)
1N/A return;
1N/A
1N/A if (a->type != AT_DATA)
1N/A return;
1N/A
1N/A for (i = 0; i < le32_to_cpu(a->u.res.value_length); i++) {
1N/A if (p[i]) {
1N/A p[i] = 0;
1N/A n++;
1N/A }
1N/A }
1N/A
1N/A wiped_resident_data += n;
1N/A}
1N/A
1N/Astatic void clone_logfile_parts(ntfs_walk_clusters_ctx *image, runlist *rl)
1N/A{
1N/A s64 offset = 0, lcn, vcn;
1N/A
1N/A while (1) {
1N/A
1N/A vcn = offset / image->ni->vol->cluster_size;
1N/A lcn = ntfs_rl_vcn_to_lcn(rl, vcn);
1N/A if (lcn < 0)
1N/A break;
1N/A
1N/A lseek_to_cluster(lcn);
1N/A copy_cluster(opt.rescue, lcn);
1N/A
1N/A if (offset == 0)
1N/A offset = NTFS_BLOCK_SIZE >> 1;
1N/A else
1N/A offset <<= 1;
1N/A }
1N/A}
1N/A
1N/Astatic void walk_runs(struct ntfs_walk_cluster *walk)
1N/A{
1N/A int i, j;
1N/A runlist *rl;
1N/A ATTR_RECORD *a;
1N/A ntfs_attr_search_ctx *ctx;
1N/A
1N/A ctx = walk->image->ctx;
1N/A a = ctx->attr;
1N/A
1N/A if (!a->non_resident) {
1N/A if (wipe) {
1N/A wipe_resident_data(walk->image);
1N/A wipe_timestamps(walk->image);
1N/A }
1N/A return;
1N/A }
1N/A
1N/A if (wipe && walk->image->ctx->attr->type == AT_INDEX_ALLOCATION)
1N/A wipe_index_allocation_timestamps(walk->image->ni, a);
1N/A
1N/A if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL)))
1N/A perr_exit("ntfs_decompress_mapping_pairs");
1N/A
1N/A for (i = 0; rl[i].length; i++) {
1N/A s64 lcn = rl[i].lcn;
1N/A s64 lcn_length = rl[i].length;
1N/A
1N/A if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED)
1N/A continue;
1N/A
1N/A /* FIXME: ntfs_mapping_pairs_decompress should return error */
1N/A if (lcn < 0 || lcn_length < 0)
1N/A err_exit("Corrupt runlist in inode %lld attr %x LCN "
1N/A "%llx length %llx\n", ctx->ntfs_ino->mft_no,
1N/A (unsigned int)le32_to_cpu(a->type), lcn,
1N/A lcn_length);
1N/A
1N/A if (!wipe)
1N/A dump_clusters(walk->image, rl + i);
1N/A
1N/A for (j = 0; j < lcn_length; j++) {
1N/A u64 k = (u64)lcn + j;
1N/A if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1))
1N/A err_exit("Cluster %llu referenced twice!\n"
1N/A "You didn't shutdown your Windows"
1N/A "properly?\n", (unsigned long long)k);
1N/A }
1N/A
1N/A walk->image->inuse += lcn_length;
1N/A }
1N/A if (!wipe && !opt.std_out && opt.metadata &&
1N/A walk->image->ni->mft_no == FILE_LogFile &&
1N/A walk->image->ctx->attr->type == AT_DATA)
1N/A clone_logfile_parts(walk->image, rl);
1N/A
1N/A free(rl);
1N/A}
1N/A
1N/A
1N/Astatic void walk_attributes(struct ntfs_walk_cluster *walk)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A
1N/A if (!(ctx = ntfs_attr_get_search_ctx(walk->image->ni, NULL)))
1N/A perr_exit("ntfs_get_attr_search_ctx");
1N/A
1N/A while (!ntfs_attrs_walk(ctx)) {
1N/A if (ctx->attr->type == AT_END)
1N/A break;
1N/A
1N/A walk->image->ctx = ctx;
1N/A walk_runs(walk);
1N/A }
1N/A
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A}
1N/A
1N/A
1N/A
1N/Astatic void compare_bitmaps(struct bitmap *a)
1N/A{
1N/A s64 i, pos, count;
1N/A int mismatch = 0;
1N/A u8 bm[NTFS_BUF_SIZE];
1N/A
1N/A Printf("Accounting clusters ...\n");
1N/A
1N/A pos = 0;
1N/A while (1) {
1N/A count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm);
1N/A if (count == -1)
1N/A perr_exit("Couldn't get $Bitmap $DATA");
1N/A
1N/A if (count == 0) {
1N/A if (a->size > pos)
1N/A err_exit("$Bitmap size is smaller than expected"
1N/A " (%lld != %lld)\n", a->size, pos);
1N/A break;
1N/A }
1N/A
1N/A for (i = 0; i < count; i++, pos++) {
1N/A s64 cl; /* current cluster */
1N/A
1N/A if (a->size <= pos)
1N/A goto done;
1N/A
1N/A if (a->bm[pos] == bm[i])
1N/A continue;
1N/A
1N/A for (cl = pos * 8; cl < (pos + 1) * 8; cl++) {
1N/A char bit;
1N/A
1N/A bit = ntfs_bit_get(a->bm, cl);
1N/A if (bit == ntfs_bit_get(bm, i * 8 + cl % 8))
1N/A continue;
1N/A
1N/A if (opt.ignore_fs_check) {
1N/A lseek_to_cluster(cl);
1N/A copy_cluster(opt.rescue, cl);
1N/A }
1N/A
1N/A if (++mismatch > 10)
1N/A continue;
1N/A
1N/A Printf("Cluster accounting failed at %lld "
1N/A "(0x%llx): %s cluster in $Bitmap\n",
1N/A (long long)cl, (unsigned long long)cl,
1N/A bit ? "missing" : "extra");
1N/A }
1N/A }
1N/A }
1N/Adone:
1N/A if (mismatch) {
1N/A Printf("Totally %d cluster accounting mismatches.\n", mismatch);
1N/A if (opt.ignore_fs_check) {
1N/A Printf("WARNING: The NTFS inconsistency was overruled "
1N/A "by the --ignore-fs-check option.\n");
1N/A return;
1N/A }
1N/A err_exit("Filesystem check failed! Windows wasn't shutdown "
1N/A "properly or inconsistent\nfilesystem. Please run "
1N/A "chkdsk /f on Windows then reboot it TWICE.\n");
1N/A }
1N/A}
1N/A
1N/A
1N/Astatic int wipe_data(char *p, int pos, int len)
1N/A{
1N/A int wiped = 0;
1N/A
1N/A for (p += pos; --len >= 0;) {
1N/A if (p[len]) {
1N/A p[len] = 0;
1N/A wiped++;
1N/A }
1N/A }
1N/A
1N/A return wiped;
1N/A}
1N/A
1N/Astatic void wipe_unused_mft_data(ntfs_inode *ni)
1N/A{
1N/A int unused;
1N/A MFT_RECORD *m = ni->mrec;
1N/A
1N/A /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */
1N/A if (ni->mft_no <= LAST_METADATA_INODE)
1N/A return;
1N/A
1N/A unused = le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use);
1N/A wiped_unused_mft_data += wipe_data((char *)m,
1N/A le32_to_cpu(m->bytes_in_use), unused);
1N/A}
1N/A
1N/Astatic void wipe_unused_mft(ntfs_inode *ni)
1N/A{
1N/A int unused;
1N/A MFT_RECORD *m = ni->mrec;
1N/A
1N/A /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */
1N/A if (ni->mft_no <= LAST_METADATA_INODE)
1N/A return;
1N/A
1N/A unused = le32_to_cpu(m->bytes_in_use) - sizeof(MFT_RECORD);
1N/A wiped_unused_mft += wipe_data((char *)m, sizeof(MFT_RECORD), unused);
1N/A}
1N/A
1N/Astatic void mft_record_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni)
1N/A{
1N/A if (ntfs_mft_usn_dec(ni->mrec))
1N/A perr_exit("ntfs_mft_usn_dec");
1N/A
1N/A if (ntfs_mft_record_write(volume, ni->mft_no, ni->mrec))
1N/A perr_exit("ntfs_mft_record_write");
1N/A}
1N/A
1N/Astatic void mft_inode_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni)
1N/A{
1N/A s32 i;
1N/A
1N/A mft_record_write_with_same_usn(volume, ni);
1N/A
1N/A if (ni->nr_extents <= 0)
1N/A return;
1N/A
1N/A for (i = 0; i < ni->nr_extents; ++i) {
1N/A ntfs_inode *eni = ni->u.extent_nis[i];
1N/A mft_record_write_with_same_usn(volume, eni);
1N/A }
1N/A}
1N/A
1N/Astatic int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk)
1N/A{
1N/A s64 inode = 0;
1N/A s64 last_mft_rec;
1N/A ntfs_inode *ni;
1N/A struct progress_bar progress;
1N/A
1N/A Printf("Scanning volume ...\n");
1N/A
1N/A last_mft_rec = (volume->mft_na->initialized_size >>
1N/A volume->mft_record_size_bits) - 1;
1N/A progress_init(&progress, inode, last_mft_rec, 100);
1N/A
1N/A for (; inode <= last_mft_rec; inode++) {
1N/A
1N/A int err, deleted_inode;
1N/A MFT_REF mref = (MFT_REF)inode;
1N/A
1N/A progress_update(&progress, inode);
1N/A
1N/A /* FIXME: Terrible kludge for libntfs not being able to return
1N/A a deleted MFT record as inode */
1N/A ni = ntfs_calloc(sizeof(ntfs_inode));
1N/A if (!ni)
1N/A perr_exit("walk_clusters");
1N/A
1N/A ni->vol = volume;
1N/A
1N/A err = ntfs_file_record_read(volume, mref, &ni->mrec, NULL);
1N/A if (err == -1) {
1N/A free(ni);
1N/A continue;
1N/A }
1N/A
1N/A deleted_inode = !(ni->mrec->flags & MFT_RECORD_IN_USE);
1N/A
1N/A if (deleted_inode) {
1N/A
1N/A ni->mft_no = MREF(mref);
1N/A if (wipe) {
1N/A wipe_unused_mft(ni);
1N/A wipe_unused_mft_data(ni);
1N/A mft_record_write_with_same_usn(volume, ni);
1N/A }
1N/A }
1N/A
1N/A free(ni->mrec);
1N/A free(ni);
1N/A
1N/A if (deleted_inode)
1N/A continue;
1N/A
1N/A if ((ni = ntfs_inode_open(volume, mref)) == NULL) {
1N/A /* FIXME: continue only if it make sense, e.g.
1N/A MFT record not in use based on $MFT bitmap */
1N/A if (errno == EIO || errno == ENOENT)
1N/A continue;
1N/A perr_exit("Reading inode %lld failed", inode);
1N/A }
1N/A
1N/A if (wipe)
1N/A nr_used_mft_records++;
1N/A
1N/A if (ni->mrec->base_mft_record)
1N/A goto out;
1N/A
1N/A walk->image->ni = ni;
1N/A walk_attributes(walk);
1N/Aout:
1N/A if (wipe) {
1N/A wipe_unused_mft_data(ni);
1N/A mft_inode_write_with_same_usn(volume, ni);
1N/A }
1N/A
1N/A if (ntfs_inode_close(ni))
1N/A perr_exit("ntfs_inode_close for inode %lld", inode);
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A
1N/A/*
1N/A * $Bitmap can overlap the end of the volume. Any bits in this region
1N/A * must be set. This region also encompasses the backup boot sector.
1N/A */
1N/Astatic void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm)
1N/A{
1N/A for (; cluster < bm->size << 3; cluster++)
1N/A ntfs_bit_set(bm->bm, (u64)cluster, 1);
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Allocate a block of memory with one bit for each cluster of the disk.
1N/A * All the bits are set to 0, except those representing the region beyond the
1N/A * end of the disk.
1N/A */
1N/Astatic void setup_lcn_bitmap(void)
1N/A{
1N/A /* Determine lcn bitmap byte size and allocate it. */
1N/A lcn_bitmap.size = rounded_up_division(vol->nr_clusters, 8);
1N/A
1N/A lcn_bitmap.bm = ntfs_calloc(lcn_bitmap.size);
1N/A if (!lcn_bitmap.bm)
1N/A perr_exit("Failed to allocate internal buffer");
1N/A
1N/A bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap);
1N/A}
1N/A
1N/A
1N/Astatic s64 volume_size(ntfs_volume *volume, s64 nr_clusters)
1N/A{
1N/A return nr_clusters * volume->cluster_size;
1N/A}
1N/A
1N/A
1N/Astatic void print_volume_size(const char *str, s64 bytes)
1N/A{
1N/A Printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes,
1N/A (long long)rounded_up_division(bytes, NTFS_MBYTE));
1N/A}
1N/A
1N/A
1N/Astatic void print_disk_usage(const char *spacer, u32 cluster_size,
1N/A s64 nr_clusters, s64 inuse)
1N/A{
1N/A s64 total, used;
1N/A
1N/A total = nr_clusters * cluster_size;
1N/A used = inuse * cluster_size;
1N/A
1N/A Printf("Space in use %s: %lld MB (%.1f%%) ", spacer,
1N/A (long long)rounded_up_division(used, NTFS_MBYTE),
1N/A 100.0 * ((float)used / total));
1N/A
1N/A Printf("\n");
1N/A}
1N/A
1N/Astatic void print_image_info(void)
1N/A{
1N/A Printf("Ntfsclone image version: %d.%d\n",
1N/A image_hdr.major_ver, image_hdr.minor_ver);
1N/A Printf("Cluster size : %u bytes\n",
1N/A (unsigned)le32_to_cpu(image_hdr.cluster_size));
1N/A print_volume_size("Image volume size ",
1N/A sle64_to_cpu(image_hdr.nr_clusters) *
1N/A le32_to_cpu(image_hdr.cluster_size));
1N/A Printf("Image device size : %lld bytes\n",
1N/A sle64_to_cpu(image_hdr.device_size));
1N/A print_disk_usage(" ", le32_to_cpu(image_hdr.cluster_size),
1N/A sle64_to_cpu(image_hdr.nr_clusters),
1N/A sle64_to_cpu(image_hdr.inuse));
1N/A Printf("Offset to image data : %u (0x%x) bytes\n",
1N/A (unsigned)le32_to_cpu(image_hdr.offset_to_image_data),
1N/A (unsigned)le32_to_cpu(image_hdr.offset_to_image_data));
1N/A}
1N/A
1N/Astatic void check_if_mounted(const char *device, unsigned long new_mntflag)
1N/A{
1N/A unsigned long mntflag;
1N/A
1N/A if (ntfs_check_if_mounted(device, &mntflag))
1N/A perr_exit("Failed to check '%s' mount state", device);
1N/A
1N/A if (mntflag & NTFS_MF_MOUNTED) {
1N/A if (!(mntflag & NTFS_MF_READONLY))
1N/A err_exit("Device '%s' is mounted read-write. "
1N/A "You must 'umount' it first.\n", device);
1N/A if (!new_mntflag)
1N/A err_exit("Device '%s' is mounted. "
1N/A "You must 'umount' it first.\n", device);
1N/A }
1N/A}
1N/A
1N/A/**
1N/A * mount_volume -
1N/A *
1N/A * First perform some checks to determine if the volume is already mounted, or
1N/A * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount
1N/A * the volume (load the metadata into memory).
1N/A */
1N/Astatic void mount_volume(unsigned long new_mntflag)
1N/A{
1N/A check_if_mounted(opt.volume, new_mntflag);
1N/A
1N/A if (!(vol = ntfs_mount(opt.volume, new_mntflag))) {
1N/A
1N/A int err = errno;
1N/A
1N/A perr_printf("Opening '%s' as NTFS failed", opt.volume);
1N/A if (err == EINVAL) {
1N/A Printf("Apparently device '%s' doesn't have a "
1N/A "valid NTFS. Maybe you selected\nthe whole "
1N/A "disk instead of a partition (e.g. /dev/hda, "
1N/A "not /dev/hda1)?\n", opt.volume);
1N/A }
1N/A exit(1);
1N/A }
1N/A
1N/A if (NVolWasDirty(vol))
1N/A if (opt.force-- <= 0)
1N/A err_exit(dirty_volume_msg, opt.volume);
1N/A
1N/A if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size)
1N/A err_exit("Cluster size %u is too large!\n",
1N/A (unsigned int)vol->cluster_size);
1N/A
1N/A Printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver);
1N/A if (ntfs_version_is_supported(vol))
1N/A perr_exit("Unknown NTFS version");
1N/A
1N/A Printf("Cluster size : %u bytes\n",
1N/A (unsigned int)vol->cluster_size);
1N/A print_volume_size("Current volume size",
1N/A volume_size(vol, vol->nr_clusters));
1N/A}
1N/A
1N/Astatic struct ntfs_walk_cluster backup_clusters = { NULL, NULL };
1N/A
1N/Astatic int device_offset_valid(int fd, s64 ofs)
1N/A{
1N/A char ch;
1N/A
1N/A if (lseek(fd, ofs, SEEK_SET) >= 0 && read(fd, &ch, 1) == 1)
1N/A return 0;
1N/A return -1;
1N/A}
1N/A
1N/Astatic s64 device_size_get(int fd)
1N/A{
1N/A s64 high, low;
1N/A#ifdef BLKGETSIZE64
1N/A { u64 size;
1N/A
1N/A if (ioctl(fd, BLKGETSIZE64, &size) >= 0) {
1N/A ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu "
1N/A "(0x%llx).\n", (unsigned long long)size,
1N/A (unsigned long long)size);
1N/A return (s64)size;
1N/A }
1N/A }
1N/A#endif
1N/A#ifdef BLKGETSIZE
1N/A { unsigned long size;
1N/A
1N/A if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
1N/A ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu "
1N/A "(0x%lx).\n", size, size);
1N/A return (s64)size * 512;
1N/A }
1N/A }
1N/A#endif
1N/A#ifdef FDGETPRM
1N/A { struct floppy_struct this_floppy;
1N/A
1N/A if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
1N/A ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu "
1N/A "(0x%lx).\n", this_floppy.size,
1N/A this_floppy.size);
1N/A return (s64)this_floppy.size * 512;
1N/A }
1N/A }
1N/A#endif
1N/A /*
1N/A * We couldn't figure it out by using a specialized ioctl,
1N/A * so do binary search to find the size of the device.
1N/A */
1N/A low = 0LL;
1N/A for (high = 1024LL; !device_offset_valid(fd, high); high <<= 1)
1N/A low = high;
1N/A while (low < high - 1LL) {
1N/A const s64 mid = (low + high) / 2;
1N/A
1N/A if (!device_offset_valid(fd, mid))
1N/A low = mid;
1N/A else
1N/A high = mid;
1N/A }
1N/A lseek(fd, 0LL, SEEK_SET);
1N/A return (low + 1LL);
1N/A}
1N/A
1N/Astatic void fsync_clone(int fd)
1N/A{
1N/A Printf("Syncing ...\n");
1N/A if (fsync(fd) && errno != EINVAL)
1N/A perr_exit("fsync");
1N/A}
1N/A
1N/Astatic void set_filesize(s64 filesize)
1N/A{
1N/A
1N/A#ifdef __sun
1N/A
1N/A if (fstatvfs(fd_out, &opt.stfs) == -1)
1N/A Printf("WARNING: Couldn't get filesystem type: "
1N/A "%s\n", strerror(errno));
1N/A
1N/A if (strcmp(opt.stfs.f_basetype, "pcfs") == 0)
1N/A Printf("WARNING: You're using PCFS, it does not support "
1N/A "sparse files so the next operation might take "
1N/A "a while. You should consider using the more "
1N/A "efficient --save-image option of ntfsclone. Use "
1N/A "the --restore-image option to restore the image.\n");
1N/A
1N/A if (ftruncate(fd_out, filesize) == -1) {
1N/A int err = errno;
1N/A perr_printf("ftruncate failed for file '%s'", opt.output);
1N/A Printf("Destination filesystem type is %s\n",
1N/A opt.stfs.f_basetype);
1N/A if (err == EFBIG || (err == EINVAL && filesize > 0))
1N/A Printf("Your system or the destination filesystem "
1N/A "doesn't support large files.\n");
1N/A exit(1);
1N/A }
1N/A
1N/A#else /* !defined(__sun) */
1N/A
1N/A long fs_type = 0; /* Unknown filesystem type */
1N/A
1N/A if (fstatfs(fd_out, &opt.stfs) == -1)
1N/A Printf("WARNING: Couldn't get filesystem type: "
1N/A "%s\n", strerror(errno));
1N/A else
1N/A fs_type = opt.stfs.f_type;
1N/A
1N/A if (fs_type == 0x52654973)
1N/A Printf("WARNING: You're using ReiserFS, it has very poor "
1N/A "performance creating\nlarge sparse files. The next "
1N/A "operation might take a very long time!\n"
1N/A "Creating sparse output file ...\n");
1N/A else if (fs_type == 0x517b)
1N/A Printf("WARNING: You're using SMBFS and if the remote share "
1N/A "isn't Samba but a Windows\ncomputer then the clone "
1N/A "operation will be very inefficient and may fail!\n");
1N/A
1N/A if (ftruncate(fd_out, filesize) == -1) {
1N/A int err = errno;
1N/A perr_printf("ftruncate failed for file '%s'", opt.output);
1N/A if (fs_type)
1N/A Printf("Destination filesystem type is 0x%lx.\n",
1N/A (unsigned long)fs_type);
1N/A if (err == E2BIG) {
1N/A Printf("Your system or the destination filesystem "
1N/A "doesn't support large files.\n");
1N/A if (fs_type == 0x517b) {
1N/A Printf("SMBFS needs minimum Linux kernel "
1N/A "version 2.4.25 and\n the 'lfs' option"
1N/A "\nfor smbmount to have large "
1N/A "file support.\n");
1N/A }
1N/A } else if (err == EPERM) {
1N/A Printf("Apparently the destination filesystem doesn't "
1N/A "support sparse files.\nYou can overcome this "
1N/A "by using the more efficient --save-image "
1N/A "option\nof ntfsclone. Use the --restore-image "
1N/A "option to restore the image.\n");
1N/A }
1N/A exit(1);
1N/A }
1N/A
1N/A#endif /* defined(__sun) */
1N/A}
1N/A
1N/Astatic s64 open_image(void)
1N/A{
1N/A if (strcmp(opt.volume, "-") == 0) {
1N/A if ((fd_in = fileno(stdin)) == -1)
1N/A perr_exit("fileno for stdout failed");
1N/A } else {
1N/A if ((fd_in = open(opt.volume, O_RDONLY)) == -1)
1N/A perr_exit("failed to open image");
1N/A }
1N/A if (read_all(&fd_in, &image_hdr, NTFSCLONE_IMG_HEADER_SIZE_OLD) == -1)
1N/A perr_exit("read_all");
1N/A if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0)
1N/A err_exit("Input file is not an image! (invalid magic)\n");
1N/A if (image_hdr.major_ver < NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE) {
1N/A image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR;
1N/A image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR;
1N/A#if (__BYTE_ORDER == __BIG_ENDIAN)
1N/A Printf("Old image format detected. If the image was created "
1N/A "on a little endian architecture it will not "
1N/A "work. Use a more recent version of "
1N/A "ntfsclone to recreate the image.\n");
1N/A image_hdr.cluster_size = cpu_to_le32(image_hdr.cluster_size);
1N/A image_hdr.device_size = cpu_to_sle64(image_hdr.device_size);
1N/A image_hdr.nr_clusters = cpu_to_sle64(image_hdr.nr_clusters);
1N/A image_hdr.inuse = cpu_to_sle64(image_hdr.inuse);
1N/A#endif
1N/A image_hdr.offset_to_image_data =
1N/A const_cpu_to_le32((sizeof(image_hdr) + 7) & ~7);
1N/A image_is_host_endian = TRUE;
1N/A } else {
1N/A#ifdef __sun
1N/A u32 offset_to_image_data;
1N/A#else
1N/A typeof(image_hdr.offset_to_image_data) offset_to_image_data;
1N/A#endif
1N/A int delta;
1N/A
1N/A if (image_hdr.major_ver > NTFSCLONE_IMG_VER_MAJOR)
1N/A err_exit("Do not know how to handle image format "
1N/A "version %d.%d. Please obtain a "
1N/A "newer version of ntfsclone.\n",
1N/A image_hdr.major_ver,
1N/A image_hdr.minor_ver);
1N/A /* Read the image header data offset. */
1N/A if (read_all(&fd_in, &offset_to_image_data,
1N/A sizeof(offset_to_image_data)) == -1)
1N/A perr_exit("read_all");
1N/A image_hdr.offset_to_image_data =
1N/A le32_to_cpu(offset_to_image_data);
1N/A /*
1N/A * Read any fields from the header that we have not read yet so
1N/A * that the input stream is positioned correctly. This means
1N/A * we can support future minor versions that just extend the
1N/A * header in a backwards compatible way.
1N/A */
1N/A delta = offset_to_image_data - (NTFSCLONE_IMG_HEADER_SIZE_OLD +
1N/A sizeof(image_hdr.offset_to_image_data));
1N/A if (delta > 0) {
1N/A char *dummy_buf;
1N/A
1N/A dummy_buf = malloc(delta);
1N/A if (!dummy_buf)
1N/A perr_exit("malloc dummy_buffer");
1N/A if (read_all(&fd_in, dummy_buf, delta) == -1)
1N/A perr_exit("read_all");
1N/A }
1N/A }
1N/A return sle64_to_cpu(image_hdr.device_size);
1N/A}
1N/A
1N/Astatic s64 open_volume(void)
1N/A{
1N/A s64 device_size;
1N/A
1N/A mount_volume(NTFS_MNT_RDONLY);
1N/A
1N/A device_size = ntfs_device_size_get(vol->u.dev, 1);
1N/A if (device_size <= 0)
1N/A err_exit("Couldn't get device size (%lld)!\n", device_size);
1N/A
1N/A print_volume_size("Current device size", device_size);
1N/A
1N/A if (device_size < vol->nr_clusters * vol->cluster_size)
1N/A err_exit("Current NTFS volume size is bigger than the device "
1N/A "size (%lld)!\nCorrupt partition table or incorrect "
1N/A "device partitioning?\n", device_size);
1N/A
1N/A return device_size;
1N/A}
1N/A
1N/Astatic void initialise_image_hdr(s64 device_size, s64 inuse)
1N/A{
1N/A memcpy(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE);
1N/A image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR;
1N/A image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR;
1N/A image_hdr.cluster_size = cpu_to_le32(vol->cluster_size);
1N/A image_hdr.device_size = cpu_to_sle64(device_size);
1N/A image_hdr.nr_clusters = cpu_to_sle64(vol->nr_clusters);
1N/A image_hdr.inuse = cpu_to_sle64(inuse);
1N/A image_hdr.offset_to_image_data = cpu_to_le32((sizeof(image_hdr) + 7) &
1N/A ~7);
1N/A}
1N/A
1N/Astatic void check_output_device(s64 input_size)
1N/A{
1N/A if (opt.blkdev_out) {
1N/A s64 dest_size = device_size_get(fd_out);
1N/A if (dest_size < input_size)
1N/A err_exit("Output device is too small (%lld) to fit the "
1N/A "NTFS image (%lld).\n", dest_size, input_size);
1N/A
1N/A check_if_mounted(opt.output, 0);
1N/A } else
1N/A set_filesize(input_size);
1N/A}
1N/A
1N/Astatic ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni)
1N/A{
1N/A ntfs_attr_search_ctx *ret;
1N/A
1N/A if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL)
1N/A perr_printf("ntfs_attr_get_search_ctx");
1N/A
1N/A return ret;
1N/A}
1N/A
1N/A/**
1N/A * lookup_data_attr
1N/A *
1N/A * Find the $DATA attribute (with or without a name) for the given ntfs inode.
1N/A */
1N/Astatic ntfs_attr_search_ctx *lookup_data_attr(ntfs_inode *ni, const char *aname)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A ntfschar *ustr;
1N/A int len = 0;
1N/A
1N/A if ((ctx = attr_get_search_ctx(ni)) == NULL)
1N/A return NULL;
1N/A
1N/A if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) {
1N/A perr_printf("Couldn't convert '%s' to Unicode", aname);
1N/A goto error_out;
1N/A }
1N/A
1N/A if (ntfs_attr_lookup(AT_DATA, ustr, len, 0, 0, NULL, 0, ctx)) {
1N/A perr_printf("ntfs_attr_lookup");
1N/A goto error_out;
1N/A }
1N/A ntfs_ucsfree(ustr);
1N/A return ctx;
1N/Aerror_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return NULL;
1N/A}
1N/A
1N/Astatic void ignore_bad_clusters(ntfs_walk_clusters_ctx *image)
1N/A{
1N/A ntfs_inode *ni;
1N/A ntfs_attr_search_ctx *ctx = NULL;
1N/A runlist *rl, *rl_bad;
1N/A s64 nr_bad_clusters = 0;
1N/A
1N/A if (!(ni = ntfs_inode_open(vol, FILE_BadClus)))
1N/A perr_exit("ntfs_open_inode");
1N/A
1N/A if ((ctx = lookup_data_attr(ni, "$Bad")) == NULL)
1N/A exit(1);
1N/A
1N/A if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL)))
1N/A perr_exit("ntfs_mapping_pairs_decompress");
1N/A
1N/A for (rl = rl_bad; rl->length; rl++) {
1N/A s64 lcn = rl->lcn;
1N/A
1N/A if (lcn == LCN_HOLE || lcn < 0)
1N/A continue;
1N/A
1N/A for (; lcn < rl->lcn + rl->length; lcn++, nr_bad_clusters++) {
1N/A if (ntfs_bit_get_and_set(lcn_bitmap.bm, lcn, 0))
1N/A image->inuse--;
1N/A }
1N/A }
1N/A if (nr_bad_clusters)
1N/A Printf("WARNING: The disk has %lld or more bad sectors"
1N/A " (hardware faults).\n", nr_bad_clusters);
1N/A free(rl_bad);
1N/A
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (ntfs_inode_close(ni))
1N/A perr_exit("ntfs_inode_close failed for $BadClus");
1N/A}
1N/A
1N/Astatic void check_dest_free_space(u64 src_bytes)
1N/A{
1N/A u64 dest_bytes;
1N/A struct statvfs stvfs;
1N/A struct stat st;
1N/A
1N/A if (opt.metadata || opt.blkdev_out || opt.std_out)
1N/A return;
1N/A /*
1N/A * TODO: save_image needs a bit more space than src_bytes
1N/A * due to the free space encoding overhead.
1N/A */
1N/A if (fstatvfs(fd_out, &stvfs) == -1) {
1N/A Printf("WARNING: Unknown free space on the destination: %s\n",
1N/A strerror(errno));
1N/A return;
1N/A }
1N/A
1N/A /* If file is a FIFO then there is no point in checking the size. */
1N/A if (!fstat(fd_out, &st)) {
1N/A if (S_ISFIFO(st.st_mode))
1N/A return;
1N/A } else
1N/A Printf("WARNING: fstat failed: %s\n", strerror(errno));
1N/A
1N/A dest_bytes = (u64)stvfs.f_frsize * stvfs.f_bfree;
1N/A if (!dest_bytes)
1N/A dest_bytes = (u64)stvfs.f_bsize * stvfs.f_bfree;
1N/A
1N/A if (dest_bytes < src_bytes)
1N/A err_exit("Destination doesn't have enough free space: "
1N/A "%llu MB < %llu MB\n",
1N/A rounded_up_division(dest_bytes, NTFS_MBYTE),
1N/A rounded_up_division(src_bytes, NTFS_MBYTE));
1N/A}
1N/A
1N/Aint main(int argc, char **argv)
1N/A{
1N/A ntfs_walk_clusters_ctx image;
1N/A s64 device_size; /* input device size in bytes */
1N/A s64 ntfs_size;
1N/A unsigned int wiped_total = 0;
1N/A
1N/A /* print to stderr, stdout can be an NTFS image ... */
1N/A fprintf(stderr, "%s v%s (libntfs %s)\n", EXEC_NAME, VERSION,
1N/A ntfs_libntfs_version());
1N/A msg_out = stderr;
1N/A
1N/A parse_options(argc, argv);
1N/A
1N/A utils_set_locale();
1N/A
1N/A if (opt.restore_image) {
1N/A device_size = open_image();
1N/A ntfs_size = sle64_to_cpu(image_hdr.nr_clusters) *
1N/A le32_to_cpu(image_hdr.cluster_size);
1N/A } else {
1N/A device_size = open_volume();
1N/A ntfs_size = vol->nr_clusters * vol->cluster_size;
1N/A }
1N/A // FIXME: This needs to be the cluster size...
1N/A ntfs_size += 512; /* add backup boot sector */
1N/A
1N/A if (opt.std_out) {
1N/A if ((fd_out = fileno(stdout)) == -1)
1N/A perr_exit("fileno for stdout failed");
1N/A } else {
1N/A /* device_size_get() might need to read() */
1N/A int flags = O_RDWR;
1N/A
1N/A if (!opt.blkdev_out) {
1N/A flags |= O_CREAT | O_TRUNC;
1N/A if (!opt.overwrite)
1N/A flags |= O_EXCL;
1N/A }
1N/A
1N/A if ((fd_out = open(opt.output, flags, S_IRUSR | S_IWUSR)) == -1)
1N/A perr_exit("Opening file '%s' failed", opt.output);
1N/A
1N/A if (!opt.save_image)
1N/A check_output_device(ntfs_size);
1N/A }
1N/A
1N/A if (opt.restore_image) {
1N/A print_image_info();
1N/A restore_image();
1N/A fsync_clone(fd_out);
1N/A exit(0);
1N/A }
1N/A
1N/A setup_lcn_bitmap();
1N/A memset(&image, 0, sizeof(image));
1N/A backup_clusters.image = &image;
1N/A
1N/A walk_clusters(vol, &backup_clusters);
1N/A compare_bitmaps(&lcn_bitmap);
1N/A print_disk_usage("", vol->cluster_size, vol->nr_clusters, image.inuse);
1N/A
1N/A check_dest_free_space(vol->cluster_size * image.inuse);
1N/A
1N/A ignore_bad_clusters(&image);
1N/A
1N/A if (opt.save_image)
1N/A initialise_image_hdr(device_size, image.inuse);
1N/A
1N/A /* FIXME: save backup boot sector */
1N/A
1N/A if (opt.std_out || !opt.metadata) {
1N/A s64 nr_clusters_to_save = image.inuse;
1N/A if (opt.std_out && !opt.save_image)
1N/A nr_clusters_to_save = vol->nr_clusters;
1N/A
1N/A clone_ntfs(nr_clusters_to_save);
1N/A fsync_clone(fd_out);
1N/A exit(0);
1N/A }
1N/A
1N/A wipe = 1;
1N/A opt.volume = opt.output;
1N/A /* 'force' again mount for dirty volumes (e.g. after resize).
1N/A FIXME: use mount flags to avoid potential side-effects in future */
1N/A opt.force++;
1N/A mount_volume(0);
1N/A
1N/A free(lcn_bitmap.bm);
1N/A setup_lcn_bitmap();
1N/A memset(&image, 0, sizeof(image));
1N/A backup_clusters.image = &image;
1N/A
1N/A walk_clusters(vol, &backup_clusters);
1N/A
1N/A Printf("Num of MFT records = %10lld\n",
1N/A (long long)vol->mft_na->initialized_size >>
1N/A vol->mft_record_size_bits);
1N/A Printf("Num of used MFT records = %10u\n", nr_used_mft_records);
1N/A
1N/A Printf("Wiped unused MFT data = %10u\n", wiped_unused_mft_data);
1N/A Printf("Wiped deleted MFT data = %10u\n", wiped_unused_mft);
1N/A Printf("Wiped resident user data = %10u\n", wiped_resident_data);
1N/A Printf("Wiped timestamp data = %10u\n", wiped_timestamp_data);
1N/A
1N/A wiped_total += wiped_unused_mft_data;
1N/A wiped_total += wiped_unused_mft;
1N/A wiped_total += wiped_resident_data;
1N/A wiped_total += wiped_timestamp_data;
1N/A Printf("Wiped totally = %10u\n", wiped_total);
1N/A
1N/A fsync_clone(fd_out);
1N/A exit(0);
1N/A}