1987da915Sopenharmony_ci/** 2987da915Sopenharmony_ci * ntfsclone - Part of the Linux-NTFS project. 3987da915Sopenharmony_ci * 4987da915Sopenharmony_ci * Copyright (c) 2003-2006 Szabolcs Szakacsits 5987da915Sopenharmony_ci * Copyright (c) 2004-2006 Anton Altaparmakov 6987da915Sopenharmony_ci * Copyright (c) 2010-2018 Jean-Pierre Andre 7987da915Sopenharmony_ci * Special image format support copyright (c) 2004 Per Olofsson 8987da915Sopenharmony_ci * 9987da915Sopenharmony_ci * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. 10987da915Sopenharmony_ci * 11987da915Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 12987da915Sopenharmony_ci * it under the terms of the GNU General Public License as published by 13987da915Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 14987da915Sopenharmony_ci * (at your option) any later version. 15987da915Sopenharmony_ci */ 16987da915Sopenharmony_ci 17987da915Sopenharmony_ci#include "config.h" 18987da915Sopenharmony_ci 19987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H 20987da915Sopenharmony_ci#include <unistd.h> 21987da915Sopenharmony_ci#endif 22987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H 23987da915Sopenharmony_ci#include <stdlib.h> 24987da915Sopenharmony_ci#endif 25987da915Sopenharmony_ci#ifdef HAVE_STDIO_H 26987da915Sopenharmony_ci#include <stdio.h> 27987da915Sopenharmony_ci#endif 28987da915Sopenharmony_ci#ifdef HAVE_SYS_TYPES_H 29987da915Sopenharmony_ci#include <sys/types.h> 30987da915Sopenharmony_ci#endif 31987da915Sopenharmony_ci#ifdef HAVE_SYS_STAT_H 32987da915Sopenharmony_ci#include <sys/stat.h> 33987da915Sopenharmony_ci#endif 34987da915Sopenharmony_ci#ifdef HAVE_TIME_H 35987da915Sopenharmony_ci#include <time.h> 36987da915Sopenharmony_ci#endif 37987da915Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H 38987da915Sopenharmony_ci#include <sys/ioctl.h> 39987da915Sopenharmony_ci#endif 40987da915Sopenharmony_ci#ifdef HAVE_SYS_VFS_H 41987da915Sopenharmony_ci#include <sys/vfs.h> 42987da915Sopenharmony_ci#endif 43987da915Sopenharmony_ci#ifdef HAVE_SYS_STATVFS_H 44987da915Sopenharmony_ci#include <sys/statvfs.h> 45987da915Sopenharmony_ci#endif 46987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H 47987da915Sopenharmony_ci#include <fcntl.h> 48987da915Sopenharmony_ci#endif 49987da915Sopenharmony_ci#ifdef HAVE_STDARG_H 50987da915Sopenharmony_ci#include <stdarg.h> 51987da915Sopenharmony_ci#endif 52987da915Sopenharmony_ci#ifdef HAVE_STRING_H 53987da915Sopenharmony_ci#include <string.h> 54987da915Sopenharmony_ci#endif 55987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H 56987da915Sopenharmony_ci#include <errno.h> 57987da915Sopenharmony_ci#endif 58987da915Sopenharmony_ci#ifdef HAVE_GETOPT_H 59987da915Sopenharmony_ci#include <getopt.h> 60987da915Sopenharmony_ci#endif 61987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H 62987da915Sopenharmony_ci#include <unistd.h> 63987da915Sopenharmony_ci#endif 64987da915Sopenharmony_ci#ifdef HAVE_SYS_MOUNT_H 65987da915Sopenharmony_ci#include <sys/mount.h> 66987da915Sopenharmony_ci#endif 67987da915Sopenharmony_ci 68987da915Sopenharmony_ci/* 69987da915Sopenharmony_ci * FIXME: ntfsclone do bad things about endians handling. Fix it and remove 70987da915Sopenharmony_ci * this note and define. 71987da915Sopenharmony_ci */ 72987da915Sopenharmony_ci#define NTFS_DO_NOT_CHECK_ENDIANS 73987da915Sopenharmony_ci 74987da915Sopenharmony_ci#include "param.h" 75987da915Sopenharmony_ci#include "debug.h" 76987da915Sopenharmony_ci#include "types.h" 77987da915Sopenharmony_ci#include "support.h" 78987da915Sopenharmony_ci#include "endians.h" 79987da915Sopenharmony_ci#include "bootsect.h" 80987da915Sopenharmony_ci#include "device.h" 81987da915Sopenharmony_ci#include "attrib.h" 82987da915Sopenharmony_ci#include "mst.h" 83987da915Sopenharmony_ci#include "volume.h" 84987da915Sopenharmony_ci#include "mft.h" 85987da915Sopenharmony_ci#include "bitmap.h" 86987da915Sopenharmony_ci#include "inode.h" 87987da915Sopenharmony_ci#include "index.h" 88987da915Sopenharmony_ci#include "dir.h" 89987da915Sopenharmony_ci#include "runlist.h" 90987da915Sopenharmony_ci#include "ntfstime.h" 91987da915Sopenharmony_ci#include "utils.h" 92987da915Sopenharmony_ci/* #include "version.h" */ 93987da915Sopenharmony_ci#include "misc.h" 94987da915Sopenharmony_ci 95987da915Sopenharmony_ci#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) 96987da915Sopenharmony_ci#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ 97987da915Sopenharmony_ci#endif 98987da915Sopenharmony_ci#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) 99987da915Sopenharmony_ci#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ 100987da915Sopenharmony_ci#endif 101987da915Sopenharmony_ci 102987da915Sopenharmony_ci#if defined(linux) || defined(__uClinux__) || defined(__sun) \ 103987da915Sopenharmony_ci || defined(__APPLE__) || defined(__DARWIN__) 104987da915Sopenharmony_ci /* Make sure the presence of <windows.h> means compiling for Windows */ 105987da915Sopenharmony_ci#undef HAVE_WINDOWS_H 106987da915Sopenharmony_ci#endif 107987da915Sopenharmony_ci 108987da915Sopenharmony_ci#if defined(__sun) | defined(HAVE_WINDOWS_H) 109987da915Sopenharmony_ci#define NO_STATFS 1 /* statfs(2) and f_type are not universal */ 110987da915Sopenharmony_ci#endif 111987da915Sopenharmony_ci 112987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 113987da915Sopenharmony_ci/* 114987da915Sopenharmony_ci * Replacements for functions which do not exist on Windows 115987da915Sopenharmony_ci */ 116987da915Sopenharmony_ciint setmode(int, int); /* from msvcrt.dll */ 117987da915Sopenharmony_ci 118987da915Sopenharmony_ci#define getpid() (0) 119987da915Sopenharmony_ci#define srandom(seed) srand(seed) 120987da915Sopenharmony_ci#define random() rand() 121987da915Sopenharmony_ci#define fsync(fd) (0) 122987da915Sopenharmony_ci#define ioctl(fd,code,buf) (-1) 123987da915Sopenharmony_ci#define ftruncate(fd, size) ntfs_device_win32_ftruncate(dev_out, size) 124987da915Sopenharmony_ci#define BINWMODE "wb" 125987da915Sopenharmony_ci#else 126987da915Sopenharmony_ci#define BINWMODE "w" 127987da915Sopenharmony_ci#endif 128987da915Sopenharmony_ci 129987da915Sopenharmony_ci#ifndef O_BINARY 130987da915Sopenharmony_ci#define O_BINARY 0 131987da915Sopenharmony_ci#endif 132987da915Sopenharmony_ci 133987da915Sopenharmony_cistatic const char *EXEC_NAME = "ntfsclone"; 134987da915Sopenharmony_ci 135987da915Sopenharmony_cistatic const char *bad_sectors_warning_msg = 136987da915Sopenharmony_ci"*************************************************************************\n" 137987da915Sopenharmony_ci"* WARNING: The disk has one or more bad sectors. This means that damage *\n" 138987da915Sopenharmony_ci"* has occurred on the disk surface, possibly caused by deterioration of *\n" 139987da915Sopenharmony_ci"* the physical media, manufacturing faults or other reasons. The *\n" 140987da915Sopenharmony_ci"* reliability of the disk may stay stable or degrade fast. *\n" 141987da915Sopenharmony_ci"* Use the --rescue option to efficiently save as much data as possible! *\n" 142987da915Sopenharmony_ci"*************************************************************************\n"; 143987da915Sopenharmony_ci 144987da915Sopenharmony_cistatic const char *dirty_volume_msg = 145987da915Sopenharmony_ci"Volume '%s' is scheduled for a check or it was shutdown \n" 146987da915Sopenharmony_ci"uncleanly. Please boot Windows or use the --force option to progress.\n"; 147987da915Sopenharmony_ci 148987da915Sopenharmony_cistatic struct { 149987da915Sopenharmony_ci int verbose; 150987da915Sopenharmony_ci int quiet; 151987da915Sopenharmony_ci int debug; 152987da915Sopenharmony_ci int force; 153987da915Sopenharmony_ci int overwrite; 154987da915Sopenharmony_ci int std_out; 155987da915Sopenharmony_ci int blkdev_out; /* output file is block device */ 156987da915Sopenharmony_ci int metadata; /* metadata only cloning */ 157987da915Sopenharmony_ci int no_action; /* do not really restore */ 158987da915Sopenharmony_ci int ignore_fs_check; 159987da915Sopenharmony_ci int rescue; 160987da915Sopenharmony_ci int save_image; 161987da915Sopenharmony_ci int new_serial; 162987da915Sopenharmony_ci int metadata_image; 163987da915Sopenharmony_ci int preserve_timestamps; 164987da915Sopenharmony_ci int full_logfile; 165987da915Sopenharmony_ci int restore_image; 166987da915Sopenharmony_ci char *output; 167987da915Sopenharmony_ci char *volume; 168987da915Sopenharmony_ci#ifndef NO_STATFS 169987da915Sopenharmony_ci struct statfs stfs; 170987da915Sopenharmony_ci#endif 171987da915Sopenharmony_ci} opt; 172987da915Sopenharmony_ci 173987da915Sopenharmony_cistruct bitmap { 174987da915Sopenharmony_ci s64 size; 175987da915Sopenharmony_ci u8 *bm; 176987da915Sopenharmony_ci}; 177987da915Sopenharmony_ci 178987da915Sopenharmony_cistruct progress_bar { 179987da915Sopenharmony_ci u64 start; 180987da915Sopenharmony_ci u64 stop; 181987da915Sopenharmony_ci int resolution; 182987da915Sopenharmony_ci float unit; 183987da915Sopenharmony_ci}; 184987da915Sopenharmony_ci 185987da915Sopenharmony_citypedef struct { 186987da915Sopenharmony_ci ntfs_inode *ni; /* inode being processed */ 187987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ 188987da915Sopenharmony_ci s64 inuse; /* number of clusters in use */ 189987da915Sopenharmony_ci int more_use; /* possibly allocated clusters */ 190987da915Sopenharmony_ci LCN current_lcn; 191987da915Sopenharmony_ci} ntfs_walk_clusters_ctx; 192987da915Sopenharmony_ci 193987da915Sopenharmony_citypedef int (ntfs_walk_op)(ntfs_inode *ni, void *data); 194987da915Sopenharmony_ci 195987da915Sopenharmony_cistruct ntfs_walk_cluster { 196987da915Sopenharmony_ci ntfs_walk_op *inode_op; /* not implemented yet */ 197987da915Sopenharmony_ci ntfs_walk_clusters_ctx *image; 198987da915Sopenharmony_ci}; 199987da915Sopenharmony_ci 200987da915Sopenharmony_ci 201987da915Sopenharmony_cistatic ntfs_volume *vol = NULL; 202987da915Sopenharmony_cistatic struct bitmap lcn_bitmap; 203987da915Sopenharmony_ci 204987da915Sopenharmony_cistatic int fd_in; 205987da915Sopenharmony_cistatic int fd_out; 206987da915Sopenharmony_cistatic FILE *stream_out = (FILE*)NULL; 207987da915Sopenharmony_cistruct ntfs_device *dev_out = (struct ntfs_device*)NULL; 208987da915Sopenharmony_cistatic FILE *msg_out = NULL; 209987da915Sopenharmony_ci 210987da915Sopenharmony_cistatic int wipe = 0; 211987da915Sopenharmony_cistatic unsigned int nr_used_mft_records = 0; 212987da915Sopenharmony_cistatic unsigned int wiped_unused_mft_data = 0; 213987da915Sopenharmony_cistatic unsigned int wiped_unused_mft = 0; 214987da915Sopenharmony_cistatic unsigned int wiped_resident_data = 0; 215987da915Sopenharmony_cistatic unsigned int wiped_timestamp_data = 0; 216987da915Sopenharmony_ci 217987da915Sopenharmony_cistatic le64 volume_serial_number; /* new random serial number */ 218987da915Sopenharmony_cistatic u64 full_device_size; /* full size, including the backup boot sector */ 219987da915Sopenharmony_ci 220987da915Sopenharmony_cistatic BOOL image_is_host_endian = FALSE; 221987da915Sopenharmony_ci 222987da915Sopenharmony_ci#define IMAGE_MAGIC "\0ntfsclone-image" 223987da915Sopenharmony_ci#define IMAGE_MAGIC_SIZE 16 224987da915Sopenharmony_ci#define IMAGE_OFFSET_OFFSET 46 /* must be the same for all versions ! */ 225987da915Sopenharmony_ci#define IMAGE_HDR_ALIGN 8 /* alignment wanted after header */ 226987da915Sopenharmony_ci 227987da915Sopenharmony_ci/* This is the first endianness safe format version. */ 228987da915Sopenharmony_ci#define NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE 10 229987da915Sopenharmony_ci#define NTFSCLONE_IMG_VER_MINOR_ENDIANNESS_SAFE 0 230987da915Sopenharmony_ci 231987da915Sopenharmony_ci/* 232987da915Sopenharmony_ci * Set the version to 10.0 to avoid colisions with old ntfsclone which 233987da915Sopenharmony_ci * stupidly used the volume version as the image version... )-: I hope NTFS 234987da915Sopenharmony_ci * never reaches version 10.0 and if it does one day I hope no-one is using 235987da915Sopenharmony_ci * such an old ntfsclone by then... 236987da915Sopenharmony_ci * 237987da915Sopenharmony_ci * NOTE: Only bump the minor version if the image format and header are still 238987da915Sopenharmony_ci * backwards compatible. Otherwise always bump the major version. If in 239987da915Sopenharmony_ci * doubt, bump the major version. 240987da915Sopenharmony_ci * 241987da915Sopenharmony_ci * Moved to 10.1 : Alternate boot sector now saved. Still compatible. 242987da915Sopenharmony_ci */ 243987da915Sopenharmony_ci#define NTFSCLONE_IMG_VER_MAJOR 10 244987da915Sopenharmony_ci#define NTFSCLONE_IMG_VER_MINOR 1 245987da915Sopenharmony_ci 246987da915Sopenharmony_cienum { CMD_GAP, CMD_NEXT } ; 247987da915Sopenharmony_ci 248987da915Sopenharmony_ci/* All values are in little endian. */ 249987da915Sopenharmony_cistatic struct image_hdr { 250987da915Sopenharmony_ci char magic[IMAGE_MAGIC_SIZE]; 251987da915Sopenharmony_ci u8 major_ver; 252987da915Sopenharmony_ci u8 minor_ver; 253987da915Sopenharmony_ci /* the following is aligned dangerously (too late...) */ 254987da915Sopenharmony_ci le32 cluster_size; 255987da915Sopenharmony_ci le64 device_size; 256987da915Sopenharmony_ci sle64 nr_clusters; 257987da915Sopenharmony_ci le64 inuse; 258987da915Sopenharmony_ci le32 offset_to_image_data; /* From start of image_hdr. */ 259987da915Sopenharmony_ci} __attribute__((__packed__)) image_hdr; 260987da915Sopenharmony_ci 261987da915Sopenharmony_cistatic int compare_bitmaps(struct bitmap *a, BOOL copy); 262987da915Sopenharmony_ci 263987da915Sopenharmony_ci#define NTFSCLONE_IMG_HEADER_SIZE_OLD \ 264987da915Sopenharmony_ci (offsetof(struct image_hdr, offset_to_image_data)) 265987da915Sopenharmony_ci 266987da915Sopenharmony_ci#define NTFS_MBYTE (1000 * 1000) 267987da915Sopenharmony_ci 268987da915Sopenharmony_ci#define ERR_PREFIX "ERROR" 269987da915Sopenharmony_ci#define PERR_PREFIX ERR_PREFIX "(%d): " 270987da915Sopenharmony_ci#define NERR_PREFIX ERR_PREFIX ": " 271987da915Sopenharmony_ci 272987da915Sopenharmony_ci#define LAST_METADATA_INODE 11 273987da915Sopenharmony_ci 274987da915Sopenharmony_ci#define NTFS_SECTOR_SIZE 512 275987da915Sopenharmony_ci 276987da915Sopenharmony_ci#define rounded_up_division(a, b) (((a) + (b - 1)) / (b)) 277987da915Sopenharmony_ci 278987da915Sopenharmony_ci#define read_all(f, p, n) io_all((f), (p), (n), 0) 279987da915Sopenharmony_ci#define write_all(f, p, n) io_all((f), (p), (n), 1) 280987da915Sopenharmony_ci 281987da915Sopenharmony_ci__attribute__((format(printf, 1, 2))) 282987da915Sopenharmony_cistatic void Printf(const char *fmt, ...) 283987da915Sopenharmony_ci{ 284987da915Sopenharmony_ci va_list ap; 285987da915Sopenharmony_ci 286987da915Sopenharmony_ci va_start(ap, fmt); 287987da915Sopenharmony_ci vfprintf(msg_out, fmt, ap); 288987da915Sopenharmony_ci va_end(ap); 289987da915Sopenharmony_ci fflush(msg_out); 290987da915Sopenharmony_ci} 291987da915Sopenharmony_ci 292987da915Sopenharmony_ci__attribute__((format(printf, 1, 2))) 293987da915Sopenharmony_cistatic void perr_printf(const char *fmt, ...) 294987da915Sopenharmony_ci{ 295987da915Sopenharmony_ci va_list ap; 296987da915Sopenharmony_ci int eo = errno; 297987da915Sopenharmony_ci 298987da915Sopenharmony_ci Printf(PERR_PREFIX, eo); 299987da915Sopenharmony_ci va_start(ap, fmt); 300987da915Sopenharmony_ci vfprintf(msg_out, fmt, ap); 301987da915Sopenharmony_ci va_end(ap); 302987da915Sopenharmony_ci Printf(": %s\n", strerror(eo)); 303987da915Sopenharmony_ci fflush(msg_out); 304987da915Sopenharmony_ci} 305987da915Sopenharmony_ci 306987da915Sopenharmony_ci__attribute__((format(printf, 1, 2))) 307987da915Sopenharmony_cistatic void err_printf(const char *fmt, ...) 308987da915Sopenharmony_ci{ 309987da915Sopenharmony_ci va_list ap; 310987da915Sopenharmony_ci 311987da915Sopenharmony_ci Printf(NERR_PREFIX); 312987da915Sopenharmony_ci va_start(ap, fmt); 313987da915Sopenharmony_ci vfprintf(msg_out, fmt, ap); 314987da915Sopenharmony_ci va_end(ap); 315987da915Sopenharmony_ci fflush(msg_out); 316987da915Sopenharmony_ci} 317987da915Sopenharmony_ci 318987da915Sopenharmony_ci__attribute__((noreturn)) 319987da915Sopenharmony_ci__attribute__((format(printf, 1, 2))) 320987da915Sopenharmony_cistatic void err_exit(const char *fmt, ...) 321987da915Sopenharmony_ci{ 322987da915Sopenharmony_ci va_list ap; 323987da915Sopenharmony_ci 324987da915Sopenharmony_ci Printf(NERR_PREFIX); 325987da915Sopenharmony_ci va_start(ap, fmt); 326987da915Sopenharmony_ci vfprintf(msg_out, fmt, ap); 327987da915Sopenharmony_ci va_end(ap); 328987da915Sopenharmony_ci fflush(msg_out); 329987da915Sopenharmony_ci if (vol) 330987da915Sopenharmony_ci ntfs_umount(vol,FALSE); 331987da915Sopenharmony_ci exit(1); 332987da915Sopenharmony_ci} 333987da915Sopenharmony_ci 334987da915Sopenharmony_ci__attribute__((noreturn)) 335987da915Sopenharmony_ci__attribute__((format(printf, 1, 2))) 336987da915Sopenharmony_cistatic void perr_exit(const char *fmt, ...) 337987da915Sopenharmony_ci{ 338987da915Sopenharmony_ci va_list ap; 339987da915Sopenharmony_ci int eo = errno; 340987da915Sopenharmony_ci 341987da915Sopenharmony_ci Printf(PERR_PREFIX, eo); 342987da915Sopenharmony_ci va_start(ap, fmt); 343987da915Sopenharmony_ci vfprintf(msg_out, fmt, ap); 344987da915Sopenharmony_ci va_end(ap); 345987da915Sopenharmony_ci Printf(": %s\n", strerror(eo)); 346987da915Sopenharmony_ci fflush(msg_out); 347987da915Sopenharmony_ci if (vol) 348987da915Sopenharmony_ci ntfs_umount(vol,FALSE); 349987da915Sopenharmony_ci exit(1); 350987da915Sopenharmony_ci} 351987da915Sopenharmony_ci 352987da915Sopenharmony_ci 353987da915Sopenharmony_ci__attribute__((noreturn)) 354987da915Sopenharmony_cistatic void usage(int ret) 355987da915Sopenharmony_ci{ 356987da915Sopenharmony_ci fprintf(stderr, "\nUsage: %s [OPTIONS] SOURCE\n" 357987da915Sopenharmony_ci " Efficiently clone NTFS to a sparse file, image, device or standard output.\n" 358987da915Sopenharmony_ci "\n" 359987da915Sopenharmony_ci " -o, --output FILE Clone NTFS to the non-existent FILE\n" 360987da915Sopenharmony_ci " -O, --overwrite FILE Clone NTFS to FILE, overwriting if exists\n" 361987da915Sopenharmony_ci " -s, --save-image Save to the special image format\n" 362987da915Sopenharmony_ci " -r, --restore-image Restore from the special image format\n" 363987da915Sopenharmony_ci " --rescue Continue after disk read errors\n" 364987da915Sopenharmony_ci " -m, --metadata Clone *only* metadata (for NTFS experts)\n" 365987da915Sopenharmony_ci " -n, --no-action Test restoring, without outputting anything\n" 366987da915Sopenharmony_ci " --ignore-fs-check Ignore the filesystem check result\n" 367987da915Sopenharmony_ci " --new-serial Set a new serial number\n" 368987da915Sopenharmony_ci " --new-half-serial Set a partial new serial number\n" 369987da915Sopenharmony_ci " -t, --preserve-timestamps Do not clear the timestamps\n" 370987da915Sopenharmony_ci " -q, --quiet Do not display any progress bars\n" 371987da915Sopenharmony_ci " -f, --force Force to progress (DANGEROUS)\n" 372987da915Sopenharmony_ci " --full-logfile Include the full logfile in metadata output\n" 373987da915Sopenharmony_ci " -h, --help Display this help\n" 374987da915Sopenharmony_ci#ifdef DEBUG 375987da915Sopenharmony_ci " -d, --debug Show debug information\n" 376987da915Sopenharmony_ci#endif 377987da915Sopenharmony_ci " -V, --version Display version information\n" 378987da915Sopenharmony_ci "\n" 379987da915Sopenharmony_ci " If FILE is '-' then send the image to the standard output. If SOURCE is '-'\n" 380987da915Sopenharmony_ci " and --restore-image is used then read the image from the standard input.\n" 381987da915Sopenharmony_ci "\n", EXEC_NAME); 382987da915Sopenharmony_ci fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); 383987da915Sopenharmony_ci exit(ret); 384987da915Sopenharmony_ci} 385987da915Sopenharmony_ci 386987da915Sopenharmony_ci/** 387987da915Sopenharmony_ci * version 388987da915Sopenharmony_ci */ 389987da915Sopenharmony_ci__attribute__((noreturn)) 390987da915Sopenharmony_cistatic void version(void) 391987da915Sopenharmony_ci{ 392987da915Sopenharmony_ci fprintf(stderr, 393987da915Sopenharmony_ci "Efficiently clone, image, restore or rescue an NTFS Volume.\n\n" 394987da915Sopenharmony_ci "Copyright (c) 2003-2006 Szabolcs Szakacsits\n" 395987da915Sopenharmony_ci "Copyright (c) 2004-2006 Anton Altaparmakov\n" 396987da915Sopenharmony_ci "Copyright (c) 2010-2018 Jean-Pierre Andre\n\n"); 397987da915Sopenharmony_ci fprintf(stderr, "%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); 398987da915Sopenharmony_ci exit(0); 399987da915Sopenharmony_ci} 400987da915Sopenharmony_ci 401987da915Sopenharmony_cistatic void parse_options(int argc, char **argv) 402987da915Sopenharmony_ci{ 403987da915Sopenharmony_ci static const char *sopt = "-dfhmno:O:qrstV"; 404987da915Sopenharmony_ci static const struct option lopt[] = { 405987da915Sopenharmony_ci#ifdef DEBUG 406987da915Sopenharmony_ci { "debug", no_argument, NULL, 'd' }, 407987da915Sopenharmony_ci#endif 408987da915Sopenharmony_ci { "quiet", no_argument, NULL, 'q' }, 409987da915Sopenharmony_ci { "force", no_argument, NULL, 'f' }, 410987da915Sopenharmony_ci { "help", no_argument, NULL, 'h' }, 411987da915Sopenharmony_ci { "metadata", no_argument, NULL, 'm' }, 412987da915Sopenharmony_ci { "no-action", no_argument, NULL, 'n' }, 413987da915Sopenharmony_ci { "output", required_argument, NULL, 'o' }, 414987da915Sopenharmony_ci { "overwrite", required_argument, NULL, 'O' }, 415987da915Sopenharmony_ci { "restore-image", no_argument, NULL, 'r' }, 416987da915Sopenharmony_ci { "ignore-fs-check", no_argument, NULL, 'C' }, 417987da915Sopenharmony_ci { "rescue", no_argument, NULL, 'R' }, 418987da915Sopenharmony_ci { "new-serial", no_argument, NULL, 'I' }, 419987da915Sopenharmony_ci { "new-half-serial", no_argument, NULL, 'i' }, 420987da915Sopenharmony_ci { "full-logfile", no_argument, NULL, 'l' }, 421987da915Sopenharmony_ci { "save-image", no_argument, NULL, 's' }, 422987da915Sopenharmony_ci { "preserve-timestamps", no_argument, NULL, 't' }, 423987da915Sopenharmony_ci { "version", no_argument, NULL, 'V' }, 424987da915Sopenharmony_ci { NULL, 0, NULL, 0 } 425987da915Sopenharmony_ci }; 426987da915Sopenharmony_ci 427987da915Sopenharmony_ci int c; 428987da915Sopenharmony_ci 429987da915Sopenharmony_ci memset(&opt, 0, sizeof(opt)); 430987da915Sopenharmony_ci 431987da915Sopenharmony_ci while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { 432987da915Sopenharmony_ci switch (c) { 433987da915Sopenharmony_ci case 1: /* A non-option argument */ 434987da915Sopenharmony_ci if (opt.volume) 435987da915Sopenharmony_ci usage(1); 436987da915Sopenharmony_ci opt.volume = argv[optind-1]; 437987da915Sopenharmony_ci break; 438987da915Sopenharmony_ci case 'd': 439987da915Sopenharmony_ci opt.debug++; 440987da915Sopenharmony_ci break; 441987da915Sopenharmony_ci case 'q': 442987da915Sopenharmony_ci opt.quiet++; 443987da915Sopenharmony_ci break; 444987da915Sopenharmony_ci case 'f': 445987da915Sopenharmony_ci opt.force++; 446987da915Sopenharmony_ci break; 447987da915Sopenharmony_ci case 'h': 448987da915Sopenharmony_ci usage(0); 449987da915Sopenharmony_ci case '?': 450987da915Sopenharmony_ci usage(1); 451987da915Sopenharmony_ci case 'i': /* not proposed as a short option */ 452987da915Sopenharmony_ci opt.new_serial |= 1; 453987da915Sopenharmony_ci break; 454987da915Sopenharmony_ci case 'I': /* not proposed as a short option */ 455987da915Sopenharmony_ci opt.new_serial |= 2; 456987da915Sopenharmony_ci break; 457987da915Sopenharmony_ci case 'l': 458987da915Sopenharmony_ci opt.full_logfile++; 459987da915Sopenharmony_ci break; 460987da915Sopenharmony_ci case 'm': 461987da915Sopenharmony_ci opt.metadata++; 462987da915Sopenharmony_ci break; 463987da915Sopenharmony_ci case 'n': 464987da915Sopenharmony_ci opt.no_action++; 465987da915Sopenharmony_ci break; 466987da915Sopenharmony_ci case 'O': 467987da915Sopenharmony_ci opt.overwrite++; 468987da915Sopenharmony_ci /* FALLTHRU */ 469987da915Sopenharmony_ci case 'o': 470987da915Sopenharmony_ci if (opt.output) 471987da915Sopenharmony_ci usage(1); 472987da915Sopenharmony_ci opt.output = optarg; 473987da915Sopenharmony_ci break; 474987da915Sopenharmony_ci case 'r': 475987da915Sopenharmony_ci opt.restore_image++; 476987da915Sopenharmony_ci break; 477987da915Sopenharmony_ci case 'C': 478987da915Sopenharmony_ci opt.ignore_fs_check++; 479987da915Sopenharmony_ci break; 480987da915Sopenharmony_ci case 'R': 481987da915Sopenharmony_ci opt.rescue++; 482987da915Sopenharmony_ci break; 483987da915Sopenharmony_ci case 's': 484987da915Sopenharmony_ci opt.save_image++; 485987da915Sopenharmony_ci break; 486987da915Sopenharmony_ci case 't': 487987da915Sopenharmony_ci opt.preserve_timestamps++; 488987da915Sopenharmony_ci break; 489987da915Sopenharmony_ci case 'V': 490987da915Sopenharmony_ci version(); 491987da915Sopenharmony_ci break; 492987da915Sopenharmony_ci default: 493987da915Sopenharmony_ci err_printf("Unknown option '%s'.\n", argv[optind-1]); 494987da915Sopenharmony_ci usage(1); 495987da915Sopenharmony_ci } 496987da915Sopenharmony_ci } 497987da915Sopenharmony_ci 498987da915Sopenharmony_ci if (!opt.no_action && (opt.output == NULL)) { 499987da915Sopenharmony_ci err_printf("You must specify an output file.\n"); 500987da915Sopenharmony_ci usage(1); 501987da915Sopenharmony_ci } 502987da915Sopenharmony_ci 503987da915Sopenharmony_ci if (!opt.no_action && (strcmp(opt.output, "-") == 0)) 504987da915Sopenharmony_ci opt.std_out++; 505987da915Sopenharmony_ci 506987da915Sopenharmony_ci if (opt.volume == NULL) { 507987da915Sopenharmony_ci err_printf("You must specify a device file.\n"); 508987da915Sopenharmony_ci usage(1); 509987da915Sopenharmony_ci } 510987da915Sopenharmony_ci 511987da915Sopenharmony_ci if (!opt.restore_image && !strcmp(opt.volume, "-")) { 512987da915Sopenharmony_ci err_printf("Only special images can be read from standard input\n"); 513987da915Sopenharmony_ci usage(1); 514987da915Sopenharmony_ci } 515987da915Sopenharmony_ci 516987da915Sopenharmony_ci if (opt.metadata && opt.save_image) { 517987da915Sopenharmony_ci opt.metadata_image++; 518987da915Sopenharmony_ci opt.save_image = 0; 519987da915Sopenharmony_ci } 520987da915Sopenharmony_ci 521987da915Sopenharmony_ci if (opt.metadata && opt.restore_image) 522987da915Sopenharmony_ci err_exit("Restoring only metadata from an image is not " 523987da915Sopenharmony_ci "supported!\n"); 524987da915Sopenharmony_ci 525987da915Sopenharmony_ci if (opt.metadata && !opt.metadata_image && opt.std_out) 526987da915Sopenharmony_ci err_exit("Cloning only metadata to stdout isn't supported!\n"); 527987da915Sopenharmony_ci 528987da915Sopenharmony_ci if (opt.ignore_fs_check && !opt.metadata && !opt.rescue) 529987da915Sopenharmony_ci err_exit("Filesystem check can be ignored only for metadata " 530987da915Sopenharmony_ci "cloning or rescue situations!\n"); 531987da915Sopenharmony_ci 532987da915Sopenharmony_ci if (opt.save_image && opt.restore_image) 533987da915Sopenharmony_ci err_exit("Saving and restoring an image at the same time " 534987da915Sopenharmony_ci "is not supported!\n"); 535987da915Sopenharmony_ci 536987da915Sopenharmony_ci if (opt.no_action && !opt.restore_image) 537987da915Sopenharmony_ci err_exit("A restoring test requires the restore option!\n"); 538987da915Sopenharmony_ci 539987da915Sopenharmony_ci if (opt.no_action && opt.output) 540987da915Sopenharmony_ci err_exit("A restoring test requires not defining any output!\n"); 541987da915Sopenharmony_ci 542987da915Sopenharmony_ci if (!opt.no_action && !opt.std_out) { 543987da915Sopenharmony_ci struct stat st; 544987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 545987da915Sopenharmony_ci BOOL blkdev = opt.output[0] && (opt.output[1] == ':') 546987da915Sopenharmony_ci && !opt.output[2]; 547987da915Sopenharmony_ci 548987da915Sopenharmony_ci if (!blkdev && (stat(opt.output, &st) == -1)) { 549987da915Sopenharmony_ci#else 550987da915Sopenharmony_ci if (stat(opt.output, &st) == -1) { 551987da915Sopenharmony_ci#endif 552987da915Sopenharmony_ci if (errno != ENOENT) 553987da915Sopenharmony_ci perr_exit("Couldn't access '%s'", opt.output); 554987da915Sopenharmony_ci } else { 555987da915Sopenharmony_ci if (!opt.overwrite) 556987da915Sopenharmony_ci err_exit("Output file '%s' already exists.\n" 557987da915Sopenharmony_ci "Use option --overwrite if you want to" 558987da915Sopenharmony_ci " replace its content.\n", opt.output); 559987da915Sopenharmony_ci 560987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 561987da915Sopenharmony_ci if (blkdev) { 562987da915Sopenharmony_ci#else 563987da915Sopenharmony_ci if (S_ISBLK(st.st_mode)) { 564987da915Sopenharmony_ci#endif 565987da915Sopenharmony_ci opt.blkdev_out = 1; 566987da915Sopenharmony_ci if (opt.metadata && !opt.force) 567987da915Sopenharmony_ci err_exit("Cloning only metadata to a " 568987da915Sopenharmony_ci "block device does not usually " 569987da915Sopenharmony_ci "make sense, aborting...\n" 570987da915Sopenharmony_ci "If you were instructed to do " 571987da915Sopenharmony_ci "this by a developer and/or are " 572987da915Sopenharmony_ci "sure that this is what you want " 573987da915Sopenharmony_ci "to do, run this utility again " 574987da915Sopenharmony_ci "but this time add the force " 575987da915Sopenharmony_ci "option, i.e. add '--force' to " 576987da915Sopenharmony_ci "the command line arguments."); 577987da915Sopenharmony_ci } 578987da915Sopenharmony_ci } 579987da915Sopenharmony_ci } 580987da915Sopenharmony_ci 581987da915Sopenharmony_ci /* 582987da915Sopenharmony_ci * Send messages, debug information and library messages to stdout, 583987da915Sopenharmony_ci * but, if outputing to stdout send them to stderr 584987da915Sopenharmony_ci */ 585987da915Sopenharmony_ci if (opt.std_out) { 586987da915Sopenharmony_ci msg_out = stderr; 587987da915Sopenharmony_ci ntfs_log_set_handler(ntfs_log_handler_stderr); 588987da915Sopenharmony_ci } else { 589987da915Sopenharmony_ci msg_out = stdout; 590987da915Sopenharmony_ci ntfs_log_set_handler(ntfs_log_handler_outerr); 591987da915Sopenharmony_ci } 592987da915Sopenharmony_ci} 593987da915Sopenharmony_ci 594987da915Sopenharmony_ci/* 595987da915Sopenharmony_ci * Initialize the random number generator with the current 596987da915Sopenharmony_ci * time, and generate a 64-bit random number for the serial 597987da915Sopenharmony_ci * number 598987da915Sopenharmony_ci */ 599987da915Sopenharmony_cistatic void generate_serial_number(void) { 600987da915Sopenharmony_ci u64 sn; 601987da915Sopenharmony_ci 602987da915Sopenharmony_ci /* different values for parallel processes */ 603987da915Sopenharmony_ci srandom(time((time_t*)NULL) ^ (getpid() << 16)); 604987da915Sopenharmony_ci sn = ((u64)random() << 32) | ((u64)random() & 0xffffffff); 605987da915Sopenharmony_ci volume_serial_number = cpu_to_le64(sn); 606987da915Sopenharmony_ci} 607987da915Sopenharmony_ci 608987da915Sopenharmony_cistatic void progress_init(struct progress_bar *p, u64 start, u64 stop, int res) 609987da915Sopenharmony_ci{ 610987da915Sopenharmony_ci p->start = start; 611987da915Sopenharmony_ci p->stop = stop; 612987da915Sopenharmony_ci p->unit = 100.0 / (stop - start); 613987da915Sopenharmony_ci p->resolution = res; 614987da915Sopenharmony_ci} 615987da915Sopenharmony_ci 616987da915Sopenharmony_ci 617987da915Sopenharmony_cistatic void progress_update(struct progress_bar *p, u64 current) 618987da915Sopenharmony_ci{ 619987da915Sopenharmony_ci float percent = p->unit * current; 620987da915Sopenharmony_ci 621987da915Sopenharmony_ci if (opt.quiet) 622987da915Sopenharmony_ci return; 623987da915Sopenharmony_ci 624987da915Sopenharmony_ci if (current != p->stop) { 625987da915Sopenharmony_ci if ((current - p->start) % p->resolution) 626987da915Sopenharmony_ci return; 627987da915Sopenharmony_ci Printf("%6.2f percent completed\r", percent); 628987da915Sopenharmony_ci } else 629987da915Sopenharmony_ci Printf("100.00 percent completed\n"); 630987da915Sopenharmony_ci fflush(msg_out); 631987da915Sopenharmony_ci} 632987da915Sopenharmony_ci 633987da915Sopenharmony_cistatic s64 is_critical_metadata(ntfs_walk_clusters_ctx *image, runlist *rl) 634987da915Sopenharmony_ci{ 635987da915Sopenharmony_ci s64 inode = image->ni->mft_no; 636987da915Sopenharmony_ci 637987da915Sopenharmony_ci if (inode <= LAST_METADATA_INODE) { 638987da915Sopenharmony_ci 639987da915Sopenharmony_ci /* Don't save bad sectors (both $Bad and unnamed are ignored */ 640987da915Sopenharmony_ci if (inode == FILE_BadClus && image->ctx->attr->type == AT_DATA) 641987da915Sopenharmony_ci return 0; 642987da915Sopenharmony_ci 643987da915Sopenharmony_ci if ((inode != FILE_LogFile) || opt.full_logfile) 644987da915Sopenharmony_ci return rl->length; 645987da915Sopenharmony_ci 646987da915Sopenharmony_ci if (image->ctx->attr->type == AT_DATA) { 647987da915Sopenharmony_ci 648987da915Sopenharmony_ci /* Save at least the first 16 KiB of FILE_LogFile */ 649987da915Sopenharmony_ci s64 s = (s64)16384 - rl->vcn * vol->cluster_size; 650987da915Sopenharmony_ci if (s > 0) { 651987da915Sopenharmony_ci s = rounded_up_division(s, vol->cluster_size); 652987da915Sopenharmony_ci if (rl->length < s) 653987da915Sopenharmony_ci s = rl->length; 654987da915Sopenharmony_ci return s; 655987da915Sopenharmony_ci } 656987da915Sopenharmony_ci return 0; 657987da915Sopenharmony_ci } 658987da915Sopenharmony_ci } 659987da915Sopenharmony_ci 660987da915Sopenharmony_ci if (image->ctx->attr->type != AT_DATA) 661987da915Sopenharmony_ci return rl->length; 662987da915Sopenharmony_ci 663987da915Sopenharmony_ci return 0; 664987da915Sopenharmony_ci} 665987da915Sopenharmony_ci 666987da915Sopenharmony_cistatic off_t tellin(int in) 667987da915Sopenharmony_ci{ 668987da915Sopenharmony_ci return (lseek(in, 0, SEEK_CUR)); 669987da915Sopenharmony_ci} 670987da915Sopenharmony_ci 671987da915Sopenharmony_cistatic int io_all(void *fd, void *buf, int count, int do_write) 672987da915Sopenharmony_ci{ 673987da915Sopenharmony_ci int i; 674987da915Sopenharmony_ci struct ntfs_device *dev = fd; 675987da915Sopenharmony_ci 676987da915Sopenharmony_ci while (count > 0) { 677987da915Sopenharmony_ci if (do_write) { 678987da915Sopenharmony_ci if (opt.no_action) { 679987da915Sopenharmony_ci i = count; 680987da915Sopenharmony_ci } else { 681987da915Sopenharmony_ci if (opt.save_image || opt.metadata_image) 682987da915Sopenharmony_ci i = fwrite(buf, 1, count, stream_out); 683987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 684987da915Sopenharmony_ci else if (dev_out) 685987da915Sopenharmony_ci i = dev_out->d_ops->write(dev_out, 686987da915Sopenharmony_ci buf, count); 687987da915Sopenharmony_ci#endif 688987da915Sopenharmony_ci else 689987da915Sopenharmony_ci i = write(*(int *)fd, buf, count); 690987da915Sopenharmony_ci } 691987da915Sopenharmony_ci } else if (opt.restore_image) 692987da915Sopenharmony_ci i = read(*(int *)fd, buf, count); 693987da915Sopenharmony_ci else 694987da915Sopenharmony_ci i = dev->d_ops->read(dev, buf, count); 695987da915Sopenharmony_ci if (i < 0) { 696987da915Sopenharmony_ci if (errno != EAGAIN && errno != EINTR) 697987da915Sopenharmony_ci return -1; 698987da915Sopenharmony_ci } else if (i == 0 && !do_write && opt.restore_image) { 699987da915Sopenharmony_ci return -1; 700987da915Sopenharmony_ci } else { 701987da915Sopenharmony_ci count -= i; 702987da915Sopenharmony_ci buf = i + (char *) buf; 703987da915Sopenharmony_ci } 704987da915Sopenharmony_ci } 705987da915Sopenharmony_ci return 0; 706987da915Sopenharmony_ci} 707987da915Sopenharmony_ci 708987da915Sopenharmony_ci 709987da915Sopenharmony_cistatic void rescue_sector(void *fd, u32 bytes_per_sector, off_t pos, void *buff) 710987da915Sopenharmony_ci{ 711987da915Sopenharmony_ci const char badsector_magic[] = "BadSectoR"; 712987da915Sopenharmony_ci struct ntfs_device *dev = fd; 713987da915Sopenharmony_ci 714987da915Sopenharmony_ci if (opt.restore_image) { 715987da915Sopenharmony_ci if (!opt.no_action 716987da915Sopenharmony_ci && (lseek(*(int *)fd, pos, SEEK_SET) == (off_t)-1)) 717987da915Sopenharmony_ci perr_exit("lseek"); 718987da915Sopenharmony_ci } else { 719987da915Sopenharmony_ci if (vol->dev->d_ops->seek(dev, pos, SEEK_SET) == (off_t)-1) 720987da915Sopenharmony_ci perr_exit("seek input"); 721987da915Sopenharmony_ci } 722987da915Sopenharmony_ci 723987da915Sopenharmony_ci if (read_all(fd, buff, bytes_per_sector) == -1) { 724987da915Sopenharmony_ci Printf("WARNING: Can't read sector at %llu, lost data.\n", 725987da915Sopenharmony_ci (unsigned long long)pos); 726987da915Sopenharmony_ci memset(buff, '?', bytes_per_sector); 727987da915Sopenharmony_ci memmove(buff, badsector_magic, sizeof(badsector_magic)); 728987da915Sopenharmony_ci } 729987da915Sopenharmony_ci} 730987da915Sopenharmony_ci 731987da915Sopenharmony_ci/* 732987da915Sopenharmony_ci * Read a cluster, try to rescue if cannot read 733987da915Sopenharmony_ci */ 734987da915Sopenharmony_ci 735987da915Sopenharmony_cistatic void read_rescue(void *fd, char *buff, u32 csize, u32 bytes_per_sector, 736987da915Sopenharmony_ci u64 rescue_lcn) 737987da915Sopenharmony_ci{ 738987da915Sopenharmony_ci off_t rescue_pos; 739987da915Sopenharmony_ci 740987da915Sopenharmony_ci if (read_all(fd, buff, csize) == -1) { 741987da915Sopenharmony_ci 742987da915Sopenharmony_ci if (errno != EIO) 743987da915Sopenharmony_ci perr_exit("read_all"); 744987da915Sopenharmony_ci else if (opt.rescue){ 745987da915Sopenharmony_ci u32 i; 746987da915Sopenharmony_ci 747987da915Sopenharmony_ci rescue_pos = (off_t)(rescue_lcn * csize); 748987da915Sopenharmony_ci for (i = 0; i < csize; i += bytes_per_sector) 749987da915Sopenharmony_ci rescue_sector(fd, bytes_per_sector, 750987da915Sopenharmony_ci rescue_pos + i, buff + i); 751987da915Sopenharmony_ci } else { 752987da915Sopenharmony_ci Printf("%s", bad_sectors_warning_msg); 753987da915Sopenharmony_ci err_exit("Disk is faulty, can't make full backup!"); 754987da915Sopenharmony_ci } 755987da915Sopenharmony_ci } 756987da915Sopenharmony_ci} 757987da915Sopenharmony_ci 758987da915Sopenharmony_cistatic void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) 759987da915Sopenharmony_ci{ 760987da915Sopenharmony_ci char *buff; 761987da915Sopenharmony_ci /* vol is NULL if opt.restore_image is set */ 762987da915Sopenharmony_ci s32 csize = le32_to_cpu(image_hdr.cluster_size); 763987da915Sopenharmony_ci BOOL backup_bootsector; 764987da915Sopenharmony_ci void *fd = (void *)&fd_in; 765987da915Sopenharmony_ci off_t rescue_pos; 766987da915Sopenharmony_ci NTFS_BOOT_SECTOR *bs; 767987da915Sopenharmony_ci le64 mask; 768987da915Sopenharmony_ci static u16 bytes_per_sector = NTFS_SECTOR_SIZE; 769987da915Sopenharmony_ci 770987da915Sopenharmony_ci if (!opt.restore_image) { 771987da915Sopenharmony_ci csize = vol->cluster_size; 772987da915Sopenharmony_ci bytes_per_sector = vol->sector_size; 773987da915Sopenharmony_ci fd = vol->dev; 774987da915Sopenharmony_ci } 775987da915Sopenharmony_ci 776987da915Sopenharmony_ci rescue_pos = (off_t)(rescue_lcn * csize); 777987da915Sopenharmony_ci buff = (char*)ntfs_malloc(csize); 778987da915Sopenharmony_ci if (!buff) 779987da915Sopenharmony_ci err_exit("Not enough memory"); 780987da915Sopenharmony_ci 781987da915Sopenharmony_ci /* possible partial cluster holding the backup boot sector */ 782987da915Sopenharmony_ci backup_bootsector = (lcn + 1)*csize >= full_device_size; 783987da915Sopenharmony_ci if (backup_bootsector) { 784987da915Sopenharmony_ci csize = full_device_size - lcn*csize; 785987da915Sopenharmony_ci if (csize < 0) { 786987da915Sopenharmony_ci err_exit("Corrupted input, copy aborted"); 787987da915Sopenharmony_ci } 788987da915Sopenharmony_ci } 789987da915Sopenharmony_ci 790987da915Sopenharmony_ci// need reading when not about to write ? 791987da915Sopenharmony_ci if (read_all(fd, buff, csize) == -1) { 792987da915Sopenharmony_ci 793987da915Sopenharmony_ci if (errno != EIO) { 794987da915Sopenharmony_ci if (!errno && opt.restore_image) 795987da915Sopenharmony_ci err_exit("Short image file...\n"); 796987da915Sopenharmony_ci else 797987da915Sopenharmony_ci perr_exit("read_all"); 798987da915Sopenharmony_ci } 799987da915Sopenharmony_ci else if (rescue){ 800987da915Sopenharmony_ci s32 i; 801987da915Sopenharmony_ci for (i = 0; i < csize; i += bytes_per_sector) 802987da915Sopenharmony_ci rescue_sector(fd, bytes_per_sector, 803987da915Sopenharmony_ci rescue_pos + i, buff + i); 804987da915Sopenharmony_ci } else { 805987da915Sopenharmony_ci Printf("%s", bad_sectors_warning_msg); 806987da915Sopenharmony_ci err_exit("Disk is faulty, can't make full backup!"); 807987da915Sopenharmony_ci } 808987da915Sopenharmony_ci } 809987da915Sopenharmony_ci 810987da915Sopenharmony_ci /* Set the new serial number if requested */ 811987da915Sopenharmony_ci if (opt.new_serial 812987da915Sopenharmony_ci && !opt.save_image 813987da915Sopenharmony_ci && (!lcn || backup_bootsector)) { 814987da915Sopenharmony_ci /* 815987da915Sopenharmony_ci * For updating the backup boot sector, we need to 816987da915Sopenharmony_ci * know the sector size, but this is not recorded 817987da915Sopenharmony_ci * in the image header, so we collect it on the fly 818987da915Sopenharmony_ci * while reading the first boot sector. 819987da915Sopenharmony_ci */ 820987da915Sopenharmony_ci if (!lcn) { 821987da915Sopenharmony_ci bs = (NTFS_BOOT_SECTOR*)buff; 822987da915Sopenharmony_ci bytes_per_sector = le16_to_cpu(bs->bpb.bytes_per_sector); 823987da915Sopenharmony_ci if ((bytes_per_sector > csize) 824987da915Sopenharmony_ci || (bytes_per_sector < NTFS_SECTOR_SIZE)) 825987da915Sopenharmony_ci bytes_per_sector = NTFS_SECTOR_SIZE; 826987da915Sopenharmony_ci } else 827987da915Sopenharmony_ci bs = (NTFS_BOOT_SECTOR*)(buff 828987da915Sopenharmony_ci + csize - bytes_per_sector); 829987da915Sopenharmony_ci if (opt.new_serial & 2) 830987da915Sopenharmony_ci bs->volume_serial_number = volume_serial_number; 831987da915Sopenharmony_ci else { 832987da915Sopenharmony_ci mask = const_cpu_to_le64(~0x0ffffffffULL); 833987da915Sopenharmony_ci bs->volume_serial_number 834987da915Sopenharmony_ci = (volume_serial_number & mask) 835987da915Sopenharmony_ci | (bs->volume_serial_number & ~mask); 836987da915Sopenharmony_ci } 837987da915Sopenharmony_ci /* Show the new full serial after merging */ 838987da915Sopenharmony_ci if (!lcn) 839987da915Sopenharmony_ci Printf("New serial number : 0x%llx\n", 840987da915Sopenharmony_ci (long long)le64_to_cpu( 841987da915Sopenharmony_ci bs->volume_serial_number)); 842987da915Sopenharmony_ci } 843987da915Sopenharmony_ci 844987da915Sopenharmony_ci if (opt.save_image || (opt.metadata_image && wipe)) { 845987da915Sopenharmony_ci char cmd = CMD_NEXT; 846987da915Sopenharmony_ci if (write_all(&fd_out, &cmd, sizeof(cmd)) == -1) 847987da915Sopenharmony_ci perr_exit("write_all"); 848987da915Sopenharmony_ci } 849987da915Sopenharmony_ci 850987da915Sopenharmony_ci if ((!opt.metadata_image || wipe) 851987da915Sopenharmony_ci && (write_all(&fd_out, buff, csize) == -1)) { 852987da915Sopenharmony_ci#ifndef NO_STATFS 853987da915Sopenharmony_ci int err = errno; 854987da915Sopenharmony_ci perr_printf("Write failed"); 855987da915Sopenharmony_ci if (err == EIO && opt.stfs.f_type == 0x517b) 856987da915Sopenharmony_ci Printf("Apparently you tried to clone to a remote " 857987da915Sopenharmony_ci "Windows computer but they don't\nhave " 858987da915Sopenharmony_ci "efficient sparse file handling by default. " 859987da915Sopenharmony_ci "Please try a different method.\n"); 860987da915Sopenharmony_ci exit(1); 861987da915Sopenharmony_ci#else 862987da915Sopenharmony_ci perr_printf("Write failed"); 863987da915Sopenharmony_ci#endif 864987da915Sopenharmony_ci } 865987da915Sopenharmony_ci free(buff); 866987da915Sopenharmony_ci} 867987da915Sopenharmony_ci 868987da915Sopenharmony_cistatic s64 lseek_out(int fd, s64 pos, int mode) 869987da915Sopenharmony_ci{ 870987da915Sopenharmony_ci s64 ret; 871987da915Sopenharmony_ci 872987da915Sopenharmony_ci if (dev_out) 873987da915Sopenharmony_ci ret = (dev_out->d_ops->seek)(dev_out, pos, mode); 874987da915Sopenharmony_ci else 875987da915Sopenharmony_ci ret = lseek(fd, pos, mode); 876987da915Sopenharmony_ci return (ret); 877987da915Sopenharmony_ci} 878987da915Sopenharmony_ci 879987da915Sopenharmony_cistatic void lseek_to_cluster(s64 lcn) 880987da915Sopenharmony_ci{ 881987da915Sopenharmony_ci off_t pos; 882987da915Sopenharmony_ci 883987da915Sopenharmony_ci pos = (off_t)(lcn * vol->cluster_size); 884987da915Sopenharmony_ci 885987da915Sopenharmony_ci if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) 886987da915Sopenharmony_ci perr_exit("lseek input"); 887987da915Sopenharmony_ci 888987da915Sopenharmony_ci if (opt.std_out || opt.save_image || opt.metadata_image) 889987da915Sopenharmony_ci return; 890987da915Sopenharmony_ci 891987da915Sopenharmony_ci if (lseek_out(fd_out, pos, SEEK_SET) == (off_t)-1) 892987da915Sopenharmony_ci perr_exit("lseek output"); 893987da915Sopenharmony_ci} 894987da915Sopenharmony_ci 895987da915Sopenharmony_cistatic void gap_to_cluster(s64 gap) 896987da915Sopenharmony_ci{ 897987da915Sopenharmony_ci sle64 count; 898987da915Sopenharmony_ci char buf[1 + sizeof(count)]; 899987da915Sopenharmony_ci 900987da915Sopenharmony_ci if (gap) { 901987da915Sopenharmony_ci count = cpu_to_sle64(gap); 902987da915Sopenharmony_ci buf[0] = CMD_GAP; 903987da915Sopenharmony_ci memcpy(&buf[1], &count, sizeof(count)); 904987da915Sopenharmony_ci if (write_all(&fd_out, buf, sizeof(buf)) == -1) 905987da915Sopenharmony_ci perr_exit("write_all"); 906987da915Sopenharmony_ci } 907987da915Sopenharmony_ci} 908987da915Sopenharmony_ci 909987da915Sopenharmony_cistatic void image_skip_clusters(s64 count) 910987da915Sopenharmony_ci{ 911987da915Sopenharmony_ci if (opt.save_image && count > 0) { 912987da915Sopenharmony_ci sle64 count_buf; 913987da915Sopenharmony_ci char buff[1 + sizeof(count)]; 914987da915Sopenharmony_ci 915987da915Sopenharmony_ci buff[0] = CMD_GAP; 916987da915Sopenharmony_ci count_buf = cpu_to_sle64(count); 917987da915Sopenharmony_ci memcpy(buff + 1, &count_buf, sizeof(count_buf)); 918987da915Sopenharmony_ci 919987da915Sopenharmony_ci if (write_all(&fd_out, buff, sizeof(buff)) == -1) 920987da915Sopenharmony_ci perr_exit("write_all"); 921987da915Sopenharmony_ci } 922987da915Sopenharmony_ci} 923987da915Sopenharmony_ci 924987da915Sopenharmony_cistatic void write_image_hdr(void) 925987da915Sopenharmony_ci{ 926987da915Sopenharmony_ci char alignment[IMAGE_HDR_ALIGN]; 927987da915Sopenharmony_ci 928987da915Sopenharmony_ci if (opt.save_image || opt.metadata_image) { 929987da915Sopenharmony_ci int alignsize = le32_to_cpu(image_hdr.offset_to_image_data) 930987da915Sopenharmony_ci - sizeof(image_hdr); 931987da915Sopenharmony_ci memset(alignment,0,IMAGE_HDR_ALIGN); 932987da915Sopenharmony_ci if ((alignsize < 0) 933987da915Sopenharmony_ci || write_all(&fd_out, &image_hdr, sizeof(image_hdr)) 934987da915Sopenharmony_ci || write_all(&fd_out, alignment, alignsize)) 935987da915Sopenharmony_ci perr_exit("write_all"); 936987da915Sopenharmony_ci } 937987da915Sopenharmony_ci} 938987da915Sopenharmony_ci 939987da915Sopenharmony_cistatic void clone_ntfs(u64 nr_clusters, int more_use) 940987da915Sopenharmony_ci{ 941987da915Sopenharmony_ci u64 cl, last_cl; /* current and last used cluster */ 942987da915Sopenharmony_ci void *buf; 943987da915Sopenharmony_ci u32 csize = vol->cluster_size; 944987da915Sopenharmony_ci u64 p_counter = 0; 945987da915Sopenharmony_ci char alignment[IMAGE_HDR_ALIGN]; 946987da915Sopenharmony_ci struct progress_bar progress; 947987da915Sopenharmony_ci 948987da915Sopenharmony_ci if (opt.save_image) 949987da915Sopenharmony_ci Printf("Saving NTFS to image ...\n"); 950987da915Sopenharmony_ci else 951987da915Sopenharmony_ci Printf("Cloning NTFS ...\n"); 952987da915Sopenharmony_ci 953987da915Sopenharmony_ci if (opt.new_serial) 954987da915Sopenharmony_ci generate_serial_number(); 955987da915Sopenharmony_ci 956987da915Sopenharmony_ci buf = ntfs_calloc(csize); 957987da915Sopenharmony_ci if (!buf) 958987da915Sopenharmony_ci perr_exit("clone_ntfs"); 959987da915Sopenharmony_ci 960987da915Sopenharmony_ci progress_init(&progress, p_counter, nr_clusters, 100); 961987da915Sopenharmony_ci 962987da915Sopenharmony_ci if (opt.save_image) { 963987da915Sopenharmony_ci int alignsize = le32_to_cpu(image_hdr.offset_to_image_data) 964987da915Sopenharmony_ci - sizeof(image_hdr); 965987da915Sopenharmony_ci memset(alignment,0,IMAGE_HDR_ALIGN); 966987da915Sopenharmony_ci if ((alignsize < 0) 967987da915Sopenharmony_ci || write_all(&fd_out, &image_hdr, sizeof(image_hdr)) 968987da915Sopenharmony_ci || write_all(&fd_out, alignment, alignsize)) 969987da915Sopenharmony_ci perr_exit("write_all"); 970987da915Sopenharmony_ci } 971987da915Sopenharmony_ci 972987da915Sopenharmony_ci /* save suspicious clusters if required */ 973987da915Sopenharmony_ci if (more_use && opt.ignore_fs_check) { 974987da915Sopenharmony_ci compare_bitmaps(&lcn_bitmap, TRUE); 975987da915Sopenharmony_ci } 976987da915Sopenharmony_ci /* Examine up to the alternate boot sector */ 977987da915Sopenharmony_ci for (last_cl = cl = 0; cl <= (u64)vol->nr_clusters; cl++) { 978987da915Sopenharmony_ci 979987da915Sopenharmony_ci if (ntfs_bit_get(lcn_bitmap.bm, cl)) { 980987da915Sopenharmony_ci progress_update(&progress, ++p_counter); 981987da915Sopenharmony_ci lseek_to_cluster(cl); 982987da915Sopenharmony_ci image_skip_clusters(cl - last_cl - 1); 983987da915Sopenharmony_ci 984987da915Sopenharmony_ci copy_cluster(opt.rescue, cl, cl); 985987da915Sopenharmony_ci last_cl = cl; 986987da915Sopenharmony_ci continue; 987987da915Sopenharmony_ci } 988987da915Sopenharmony_ci 989987da915Sopenharmony_ci if (opt.std_out && !opt.save_image) { 990987da915Sopenharmony_ci progress_update(&progress, ++p_counter); 991987da915Sopenharmony_ci if (write_all(&fd_out, buf, csize) == -1) 992987da915Sopenharmony_ci perr_exit("write_all"); 993987da915Sopenharmony_ci } 994987da915Sopenharmony_ci } 995987da915Sopenharmony_ci image_skip_clusters(cl - last_cl - 1); 996987da915Sopenharmony_ci free(buf); 997987da915Sopenharmony_ci} 998987da915Sopenharmony_ci 999987da915Sopenharmony_cistatic void write_empty_clusters(s32 csize, s64 count, 1000987da915Sopenharmony_ci struct progress_bar *progress, u64 *p_counter) 1001987da915Sopenharmony_ci{ 1002987da915Sopenharmony_ci s64 i; 1003987da915Sopenharmony_ci char *buff; 1004987da915Sopenharmony_ci 1005987da915Sopenharmony_ci buff = (char*)ntfs_malloc(csize); 1006987da915Sopenharmony_ci if (!buff) 1007987da915Sopenharmony_ci err_exit("Not enough memory"); 1008987da915Sopenharmony_ci 1009987da915Sopenharmony_ci memset(buff, 0, csize); 1010987da915Sopenharmony_ci 1011987da915Sopenharmony_ci for (i = 0; i < count; i++) { 1012987da915Sopenharmony_ci if (write_all(&fd_out, buff, csize) == -1) 1013987da915Sopenharmony_ci perr_exit("write_all"); 1014987da915Sopenharmony_ci progress_update(progress, ++(*p_counter)); 1015987da915Sopenharmony_ci } 1016987da915Sopenharmony_ci free(buff); 1017987da915Sopenharmony_ci} 1018987da915Sopenharmony_ci 1019987da915Sopenharmony_cistatic void restore_image(void) 1020987da915Sopenharmony_ci{ 1021987da915Sopenharmony_ci s64 pos = 0, count; 1022987da915Sopenharmony_ci s32 csize = le32_to_cpu(image_hdr.cluster_size); 1023987da915Sopenharmony_ci char cmd; 1024987da915Sopenharmony_ci u64 p_counter = 0; 1025987da915Sopenharmony_ci struct progress_bar progress; 1026987da915Sopenharmony_ci 1027987da915Sopenharmony_ci Printf("Restoring NTFS from image ...\n"); 1028987da915Sopenharmony_ci 1029987da915Sopenharmony_ci progress_init(&progress, p_counter, opt.std_out ? 1030987da915Sopenharmony_ci (u64)sle64_to_cpu(image_hdr.nr_clusters) + 1 : 1031987da915Sopenharmony_ci le64_to_cpu(image_hdr.inuse) + 1, 1032987da915Sopenharmony_ci 100); 1033987da915Sopenharmony_ci 1034987da915Sopenharmony_ci if (opt.new_serial) 1035987da915Sopenharmony_ci generate_serial_number(); 1036987da915Sopenharmony_ci 1037987da915Sopenharmony_ci /* Restore up to the alternate boot sector */ 1038987da915Sopenharmony_ci while (pos <= sle64_to_cpu(image_hdr.nr_clusters)) { 1039987da915Sopenharmony_ci if (read_all(&fd_in, &cmd, sizeof(cmd)) == -1) { 1040987da915Sopenharmony_ci if (pos == sle64_to_cpu(image_hdr.nr_clusters)) { 1041987da915Sopenharmony_ci /* alternate boot sector no present in old images */ 1042987da915Sopenharmony_ci Printf("Warning : no alternate boot" 1043987da915Sopenharmony_ci " sector in image\n"); 1044987da915Sopenharmony_ci break; 1045987da915Sopenharmony_ci } else 1046987da915Sopenharmony_ci perr_exit("read_all"); 1047987da915Sopenharmony_ci } 1048987da915Sopenharmony_ci 1049987da915Sopenharmony_ci if (cmd == CMD_GAP) { 1050987da915Sopenharmony_ci if (!image_is_host_endian) { 1051987da915Sopenharmony_ci sle64 lecount; 1052987da915Sopenharmony_ci 1053987da915Sopenharmony_ci /* little endian image, on any computer */ 1054987da915Sopenharmony_ci if (read_all(&fd_in, &lecount, 1055987da915Sopenharmony_ci sizeof(lecount)) == -1) 1056987da915Sopenharmony_ci perr_exit("read_all"); 1057987da915Sopenharmony_ci count = sle64_to_cpu(lecount); 1058987da915Sopenharmony_ci } else { 1059987da915Sopenharmony_ci /* big endian image on big endian computer */ 1060987da915Sopenharmony_ci if (read_all(&fd_in, &count, 1061987da915Sopenharmony_ci sizeof(count)) == -1) 1062987da915Sopenharmony_ci perr_exit("read_all"); 1063987da915Sopenharmony_ci } 1064987da915Sopenharmony_ci if (!count) 1065987da915Sopenharmony_ci err_exit("Bad offset at input location 0x%llx\n", 1066987da915Sopenharmony_ci (long long)tellin(fd_in) - 9); 1067987da915Sopenharmony_ci if (opt.std_out) { 1068987da915Sopenharmony_ci if ((!p_counter && count) || (count < 0)) 1069987da915Sopenharmony_ci err_exit("Cannot restore a metadata" 1070987da915Sopenharmony_ci " image to stdout\n"); 1071987da915Sopenharmony_ci else 1072987da915Sopenharmony_ci write_empty_clusters(csize, count, 1073987da915Sopenharmony_ci &progress, &p_counter); 1074987da915Sopenharmony_ci } else { 1075987da915Sopenharmony_ci if (((pos + count) < 0) 1076987da915Sopenharmony_ci || ((pos + count) 1077987da915Sopenharmony_ci > sle64_to_cpu(image_hdr.nr_clusters))) 1078987da915Sopenharmony_ci err_exit("restore_image: corrupt image " 1079987da915Sopenharmony_ci "at input offset %lld\n", 1080987da915Sopenharmony_ci (long long)tellin(fd_in) - 9); 1081987da915Sopenharmony_ci else { 1082987da915Sopenharmony_ci if (!opt.no_action 1083987da915Sopenharmony_ci && (lseek_out(fd_out, count * csize, 1084987da915Sopenharmony_ci SEEK_CUR) == (off_t)-1)) 1085987da915Sopenharmony_ci perr_exit("restore_image: lseek"); 1086987da915Sopenharmony_ci } 1087987da915Sopenharmony_ci } 1088987da915Sopenharmony_ci pos += count; 1089987da915Sopenharmony_ci } else if (cmd == CMD_NEXT) { 1090987da915Sopenharmony_ci copy_cluster(0, 0, pos); 1091987da915Sopenharmony_ci pos++; 1092987da915Sopenharmony_ci progress_update(&progress, ++p_counter); 1093987da915Sopenharmony_ci } else 1094987da915Sopenharmony_ci err_exit("Invalid command code %d at input offset 0x%llx\n", 1095987da915Sopenharmony_ci cmd, (long long)tellin(fd_in) - 1); 1096987da915Sopenharmony_ci } 1097987da915Sopenharmony_ci} 1098987da915Sopenharmony_ci 1099987da915Sopenharmony_cistatic void wipe_index_entry_timestams(INDEX_ENTRY *e) 1100987da915Sopenharmony_ci{ 1101987da915Sopenharmony_ci static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; 1102987da915Sopenharmony_ci sle64 timestamp = timespec2ntfs(zero_time); 1103987da915Sopenharmony_ci 1104987da915Sopenharmony_ci /* FIXME: can fall into infinite loop if corrupted */ 1105987da915Sopenharmony_ci while (!(e->ie_flags & INDEX_ENTRY_END)) { 1106987da915Sopenharmony_ci 1107987da915Sopenharmony_ci e->key.file_name.creation_time = timestamp; 1108987da915Sopenharmony_ci e->key.file_name.last_data_change_time = timestamp; 1109987da915Sopenharmony_ci e->key.file_name.last_mft_change_time = timestamp; 1110987da915Sopenharmony_ci e->key.file_name.last_access_time = timestamp; 1111987da915Sopenharmony_ci 1112987da915Sopenharmony_ci wiped_timestamp_data += 32; 1113987da915Sopenharmony_ci 1114987da915Sopenharmony_ci e = (INDEX_ENTRY *)((u8 *)e + le16_to_cpu(e->length)); 1115987da915Sopenharmony_ci } 1116987da915Sopenharmony_ci} 1117987da915Sopenharmony_ci 1118987da915Sopenharmony_cistatic void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr) 1119987da915Sopenharmony_ci{ 1120987da915Sopenharmony_ci INDEX_ALLOCATION *indexa, *tmp_indexa; 1121987da915Sopenharmony_ci INDEX_ENTRY *entry; 1122987da915Sopenharmony_ci INDEX_ROOT *indexr; 1123987da915Sopenharmony_ci u8 *bitmap, *byte; 1124987da915Sopenharmony_ci int bit; 1125987da915Sopenharmony_ci ntfs_attr *na; 1126987da915Sopenharmony_ci ntfschar *name; 1127987da915Sopenharmony_ci u32 name_len; 1128987da915Sopenharmony_ci 1129987da915Sopenharmony_ci indexr = ntfs_index_root_get(ni, attr); 1130987da915Sopenharmony_ci if (!indexr) { 1131987da915Sopenharmony_ci perr_printf("Failed to read $INDEX_ROOT attribute of inode " 1132987da915Sopenharmony_ci "%lld", (long long)ni->mft_no); 1133987da915Sopenharmony_ci return; 1134987da915Sopenharmony_ci } 1135987da915Sopenharmony_ci 1136987da915Sopenharmony_ci if (indexr->type != AT_FILE_NAME) 1137987da915Sopenharmony_ci goto out_indexr; 1138987da915Sopenharmony_ci 1139987da915Sopenharmony_ci name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); 1140987da915Sopenharmony_ci name_len = attr->name_length; 1141987da915Sopenharmony_ci 1142987da915Sopenharmony_ci byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, 1143987da915Sopenharmony_ci NULL); 1144987da915Sopenharmony_ci if (!byte) { 1145987da915Sopenharmony_ci perr_printf("Failed to read $BITMAP attribute"); 1146987da915Sopenharmony_ci goto out_indexr; 1147987da915Sopenharmony_ci } 1148987da915Sopenharmony_ci 1149987da915Sopenharmony_ci na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, name, name_len); 1150987da915Sopenharmony_ci if (!na) { 1151987da915Sopenharmony_ci perr_printf("Failed to open $INDEX_ALLOCATION attribute"); 1152987da915Sopenharmony_ci goto out_bitmap; 1153987da915Sopenharmony_ci } 1154987da915Sopenharmony_ci 1155987da915Sopenharmony_ci if (!na->data_size) 1156987da915Sopenharmony_ci goto out_na; 1157987da915Sopenharmony_ci 1158987da915Sopenharmony_ci tmp_indexa = indexa = ntfs_malloc(na->data_size); 1159987da915Sopenharmony_ci if (!tmp_indexa) 1160987da915Sopenharmony_ci goto out_na; 1161987da915Sopenharmony_ci 1162987da915Sopenharmony_ci if (ntfs_attr_pread(na, 0, na->data_size, indexa) != na->data_size) { 1163987da915Sopenharmony_ci perr_printf("Failed to read $INDEX_ALLOCATION attribute"); 1164987da915Sopenharmony_ci goto out_indexa; 1165987da915Sopenharmony_ci } 1166987da915Sopenharmony_ci 1167987da915Sopenharmony_ci bit = 0; 1168987da915Sopenharmony_ci while ((u8 *)tmp_indexa < (u8 *)indexa + na->data_size) { 1169987da915Sopenharmony_ci if (*byte & (1 << bit)) { 1170987da915Sopenharmony_ci if (ntfs_mst_post_read_fixup((NTFS_RECORD *)tmp_indexa, 1171987da915Sopenharmony_ci le32_to_cpu( 1172987da915Sopenharmony_ci indexr->index_block_size))) { 1173987da915Sopenharmony_ci perr_printf("Damaged INDX record"); 1174987da915Sopenharmony_ci goto out_indexa; 1175987da915Sopenharmony_ci } 1176987da915Sopenharmony_ci entry = (INDEX_ENTRY *)((u8 *)tmp_indexa + le32_to_cpu( 1177987da915Sopenharmony_ci tmp_indexa->index.entries_offset) + 0x18); 1178987da915Sopenharmony_ci 1179987da915Sopenharmony_ci wipe_index_entry_timestams(entry); 1180987da915Sopenharmony_ci 1181987da915Sopenharmony_ci if (ntfs_mft_usn_dec((MFT_RECORD *)tmp_indexa)) 1182987da915Sopenharmony_ci perr_exit("ntfs_mft_usn_dec"); 1183987da915Sopenharmony_ci 1184987da915Sopenharmony_ci if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)tmp_indexa, 1185987da915Sopenharmony_ci le32_to_cpu( 1186987da915Sopenharmony_ci indexr->index_block_size))) { 1187987da915Sopenharmony_ci perr_printf("INDX write fixup failed"); 1188987da915Sopenharmony_ci goto out_indexa; 1189987da915Sopenharmony_ci } 1190987da915Sopenharmony_ci } 1191987da915Sopenharmony_ci tmp_indexa = (INDEX_ALLOCATION *)((u8 *)tmp_indexa + 1192987da915Sopenharmony_ci le32_to_cpu(indexr->index_block_size)); 1193987da915Sopenharmony_ci bit++; 1194987da915Sopenharmony_ci if (bit > 7) { 1195987da915Sopenharmony_ci bit = 0; 1196987da915Sopenharmony_ci byte++; 1197987da915Sopenharmony_ci } 1198987da915Sopenharmony_ci } 1199987da915Sopenharmony_ci if (ntfs_rl_pwrite(vol, na->rl, 0, 0, na->data_size, indexa) != na->data_size) 1200987da915Sopenharmony_ci perr_printf("ntfs_rl_pwrite failed for inode %lld", 1201987da915Sopenharmony_ci (long long)ni->mft_no); 1202987da915Sopenharmony_ciout_indexa: 1203987da915Sopenharmony_ci free(indexa); 1204987da915Sopenharmony_ciout_na: 1205987da915Sopenharmony_ci ntfs_attr_close(na); 1206987da915Sopenharmony_ciout_bitmap: 1207987da915Sopenharmony_ci free(bitmap); 1208987da915Sopenharmony_ciout_indexr: 1209987da915Sopenharmony_ci free(indexr); 1210987da915Sopenharmony_ci} 1211987da915Sopenharmony_ci 1212987da915Sopenharmony_cistatic void wipe_index_root_timestamps(ATTR_RECORD *attr, sle64 timestamp) 1213987da915Sopenharmony_ci{ 1214987da915Sopenharmony_ci INDEX_ENTRY *entry; 1215987da915Sopenharmony_ci INDEX_ROOT *iroot; 1216987da915Sopenharmony_ci 1217987da915Sopenharmony_ci iroot = (INDEX_ROOT *)((u8 *)attr + le16_to_cpu(attr->value_offset)); 1218987da915Sopenharmony_ci entry = (INDEX_ENTRY *)((u8 *)iroot + 1219987da915Sopenharmony_ci le32_to_cpu(iroot->index.entries_offset) + 0x10); 1220987da915Sopenharmony_ci 1221987da915Sopenharmony_ci while (!(entry->ie_flags & INDEX_ENTRY_END)) { 1222987da915Sopenharmony_ci 1223987da915Sopenharmony_ci if (iroot->type == AT_FILE_NAME) { 1224987da915Sopenharmony_ci 1225987da915Sopenharmony_ci entry->key.file_name.creation_time = timestamp; 1226987da915Sopenharmony_ci entry->key.file_name.last_access_time = timestamp; 1227987da915Sopenharmony_ci entry->key.file_name.last_data_change_time = timestamp; 1228987da915Sopenharmony_ci entry->key.file_name.last_mft_change_time = timestamp; 1229987da915Sopenharmony_ci 1230987da915Sopenharmony_ci wiped_timestamp_data += 32; 1231987da915Sopenharmony_ci 1232987da915Sopenharmony_ci } else if (ntfs_names_are_equal(NTFS_INDEX_Q, 1233987da915Sopenharmony_ci sizeof(NTFS_INDEX_Q) / 2 - 1, 1234987da915Sopenharmony_ci (ntfschar *)((char *)attr + 1235987da915Sopenharmony_ci le16_to_cpu(attr->name_offset)), 1236987da915Sopenharmony_ci attr->name_length, CASE_SENSITIVE, NULL, 0)) { 1237987da915Sopenharmony_ci 1238987da915Sopenharmony_ci QUOTA_CONTROL_ENTRY *quota_q; 1239987da915Sopenharmony_ci 1240987da915Sopenharmony_ci quota_q = (QUOTA_CONTROL_ENTRY *)((u8 *)entry + 1241987da915Sopenharmony_ci le16_to_cpu(entry->data_offset)); 1242987da915Sopenharmony_ci /* 1243987da915Sopenharmony_ci * FIXME: no guarantee it's indeed /$Extend/$Quota:$Q. 1244987da915Sopenharmony_ci * For now, as a minimal safeguard, we check only for 1245987da915Sopenharmony_ci * quota version 2 ... 1246987da915Sopenharmony_ci */ 1247987da915Sopenharmony_ci if (le32_to_cpu(quota_q->version) == 2) { 1248987da915Sopenharmony_ci quota_q->change_time = timestamp; 1249987da915Sopenharmony_ci wiped_timestamp_data += 4; 1250987da915Sopenharmony_ci } 1251987da915Sopenharmony_ci } 1252987da915Sopenharmony_ci 1253987da915Sopenharmony_ci entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)); 1254987da915Sopenharmony_ci } 1255987da915Sopenharmony_ci} 1256987da915Sopenharmony_ci 1257987da915Sopenharmony_ci#define WIPE_TIMESTAMPS(atype, attr, timestamp) \ 1258987da915Sopenharmony_cido { \ 1259987da915Sopenharmony_ci atype *ats; \ 1260987da915Sopenharmony_ci ats = (atype *)((char *)(attr) + le16_to_cpu((attr)->value_offset)); \ 1261987da915Sopenharmony_ci \ 1262987da915Sopenharmony_ci ats->creation_time = (timestamp); \ 1263987da915Sopenharmony_ci ats->last_data_change_time = (timestamp); \ 1264987da915Sopenharmony_ci ats->last_mft_change_time= (timestamp); \ 1265987da915Sopenharmony_ci ats->last_access_time = (timestamp); \ 1266987da915Sopenharmony_ci \ 1267987da915Sopenharmony_ci wiped_timestamp_data += 32; \ 1268987da915Sopenharmony_ci \ 1269987da915Sopenharmony_ci} while (0) 1270987da915Sopenharmony_ci 1271987da915Sopenharmony_cistatic void wipe_timestamps(ntfs_walk_clusters_ctx *image) 1272987da915Sopenharmony_ci{ 1273987da915Sopenharmony_ci static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; 1274987da915Sopenharmony_ci ATTR_RECORD *a = image->ctx->attr; 1275987da915Sopenharmony_ci sle64 timestamp = timespec2ntfs(zero_time); 1276987da915Sopenharmony_ci 1277987da915Sopenharmony_ci if (a->type == AT_FILE_NAME) 1278987da915Sopenharmony_ci WIPE_TIMESTAMPS(FILE_NAME_ATTR, a, timestamp); 1279987da915Sopenharmony_ci 1280987da915Sopenharmony_ci else if (a->type == AT_STANDARD_INFORMATION) 1281987da915Sopenharmony_ci WIPE_TIMESTAMPS(STANDARD_INFORMATION, a, timestamp); 1282987da915Sopenharmony_ci 1283987da915Sopenharmony_ci else if (a->type == AT_INDEX_ROOT) 1284987da915Sopenharmony_ci wipe_index_root_timestamps(a, timestamp); 1285987da915Sopenharmony_ci} 1286987da915Sopenharmony_ci 1287987da915Sopenharmony_cistatic void wipe_resident_data(ntfs_walk_clusters_ctx *image) 1288987da915Sopenharmony_ci{ 1289987da915Sopenharmony_ci ATTR_RECORD *a; 1290987da915Sopenharmony_ci u32 i; 1291987da915Sopenharmony_ci int n = 0; 1292987da915Sopenharmony_ci u8 *p; 1293987da915Sopenharmony_ci 1294987da915Sopenharmony_ci a = image->ctx->attr; 1295987da915Sopenharmony_ci p = (u8*)a + le16_to_cpu(a->value_offset); 1296987da915Sopenharmony_ci 1297987da915Sopenharmony_ci if (image->ni->mft_no <= LAST_METADATA_INODE) 1298987da915Sopenharmony_ci return; 1299987da915Sopenharmony_ci 1300987da915Sopenharmony_ci if (a->type != AT_DATA) 1301987da915Sopenharmony_ci return; 1302987da915Sopenharmony_ci 1303987da915Sopenharmony_ci for (i = 0; i < le32_to_cpu(a->value_length); i++) { 1304987da915Sopenharmony_ci if (p[i]) { 1305987da915Sopenharmony_ci p[i] = 0; 1306987da915Sopenharmony_ci n++; 1307987da915Sopenharmony_ci } 1308987da915Sopenharmony_ci } 1309987da915Sopenharmony_ci 1310987da915Sopenharmony_ci wiped_resident_data += n; 1311987da915Sopenharmony_ci} 1312987da915Sopenharmony_ci 1313987da915Sopenharmony_cistatic int wipe_data(char *p, int pos, int len) 1314987da915Sopenharmony_ci{ 1315987da915Sopenharmony_ci int wiped = 0; 1316987da915Sopenharmony_ci 1317987da915Sopenharmony_ci for (p += pos; --len >= 0;) { 1318987da915Sopenharmony_ci if (p[len]) { 1319987da915Sopenharmony_ci p[len] = 0; 1320987da915Sopenharmony_ci wiped++; 1321987da915Sopenharmony_ci } 1322987da915Sopenharmony_ci } 1323987da915Sopenharmony_ci 1324987da915Sopenharmony_ci return wiped; 1325987da915Sopenharmony_ci} 1326987da915Sopenharmony_ci 1327987da915Sopenharmony_cistatic void wipe_unused_mft_data(ntfs_inode *ni) 1328987da915Sopenharmony_ci{ 1329987da915Sopenharmony_ci int unused; 1330987da915Sopenharmony_ci MFT_RECORD *m = ni->mrec; 1331987da915Sopenharmony_ci 1332987da915Sopenharmony_ci /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */ 1333987da915Sopenharmony_ci if (ni->mft_no <= LAST_METADATA_INODE) 1334987da915Sopenharmony_ci return; 1335987da915Sopenharmony_ci 1336987da915Sopenharmony_ci unused = le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use); 1337987da915Sopenharmony_ci wiped_unused_mft_data += wipe_data((char *)m, 1338987da915Sopenharmony_ci le32_to_cpu(m->bytes_in_use), unused); 1339987da915Sopenharmony_ci} 1340987da915Sopenharmony_ci 1341987da915Sopenharmony_cistatic void wipe_unused_mft(ntfs_inode *ni) 1342987da915Sopenharmony_ci{ 1343987da915Sopenharmony_ci int unused; 1344987da915Sopenharmony_ci MFT_RECORD *m = ni->mrec; 1345987da915Sopenharmony_ci 1346987da915Sopenharmony_ci /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */ 1347987da915Sopenharmony_ci if (ni->mft_no <= LAST_METADATA_INODE) 1348987da915Sopenharmony_ci return; 1349987da915Sopenharmony_ci 1350987da915Sopenharmony_ci unused = le32_to_cpu(m->bytes_in_use) - sizeof(MFT_RECORD); 1351987da915Sopenharmony_ci wiped_unused_mft += wipe_data((char *)m, sizeof(MFT_RECORD), unused); 1352987da915Sopenharmony_ci} 1353987da915Sopenharmony_ci 1354987da915Sopenharmony_cistatic void clone_logfile_parts(ntfs_walk_clusters_ctx *image, runlist *rl) 1355987da915Sopenharmony_ci{ 1356987da915Sopenharmony_ci s64 offset = 0, lcn, vcn; 1357987da915Sopenharmony_ci 1358987da915Sopenharmony_ci while (1) { 1359987da915Sopenharmony_ci 1360987da915Sopenharmony_ci vcn = offset / image->ni->vol->cluster_size; 1361987da915Sopenharmony_ci lcn = ntfs_rl_vcn_to_lcn(rl, vcn); 1362987da915Sopenharmony_ci if (lcn < 0) 1363987da915Sopenharmony_ci break; 1364987da915Sopenharmony_ci 1365987da915Sopenharmony_ci lseek_to_cluster(lcn); 1366987da915Sopenharmony_ci 1367987da915Sopenharmony_ci if ((lcn + 1) != image->current_lcn) { 1368987da915Sopenharmony_ci /* do not duplicate a cluster */ 1369987da915Sopenharmony_ci if (opt.metadata_image && wipe) 1370987da915Sopenharmony_ci gap_to_cluster(lcn - image->current_lcn); 1371987da915Sopenharmony_ci 1372987da915Sopenharmony_ci copy_cluster(opt.rescue, lcn, lcn); 1373987da915Sopenharmony_ci } 1374987da915Sopenharmony_ci image->current_lcn = lcn + 1; 1375987da915Sopenharmony_ci if (opt.metadata_image && !wipe) 1376987da915Sopenharmony_ci image->inuse++; 1377987da915Sopenharmony_ci 1378987da915Sopenharmony_ci if (offset == 0) 1379987da915Sopenharmony_ci offset = NTFS_BLOCK_SIZE >> 1; 1380987da915Sopenharmony_ci else 1381987da915Sopenharmony_ci offset <<= 1; 1382987da915Sopenharmony_ci } 1383987da915Sopenharmony_ci} 1384987da915Sopenharmony_ci 1385987da915Sopenharmony_ci/* 1386987da915Sopenharmony_ci * In-memory wiping of MFT record or MFTMirr record 1387987da915Sopenharmony_ci * (only for metadata images) 1388987da915Sopenharmony_ci * 1389987da915Sopenharmony_ci * The resident data and (optionally) the timestamps are wiped. 1390987da915Sopenharmony_ci */ 1391987da915Sopenharmony_ci 1392987da915Sopenharmony_cistatic void wipe_mft(char *mrec, u32 mrecsz, u64 mft_no) 1393987da915Sopenharmony_ci{ 1394987da915Sopenharmony_ci ntfs_walk_clusters_ctx image; 1395987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 1396987da915Sopenharmony_ci ntfs_inode ni; 1397987da915Sopenharmony_ci 1398987da915Sopenharmony_ci ni.mft_no = mft_no; 1399987da915Sopenharmony_ci ni.mrec = (MFT_RECORD*)mrec; 1400987da915Sopenharmony_ci ni.vol = vol; /* Hmm */ 1401987da915Sopenharmony_ci image.ni = ∋ 1402987da915Sopenharmony_ci ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)mrec,mrecsz,FALSE); 1403987da915Sopenharmony_ci wipe_unused_mft_data(&ni); 1404987da915Sopenharmony_ci if (!(((MFT_RECORD*)mrec)->flags & MFT_RECORD_IN_USE)) { 1405987da915Sopenharmony_ci wipe_unused_mft(&ni); 1406987da915Sopenharmony_ci } else { 1407987da915Sopenharmony_ci /* ctx with no ntfs_inode prevents from searching external attrs */ 1408987da915Sopenharmony_ci if (!(ctx = ntfs_attr_get_search_ctx((ntfs_inode*)NULL, (MFT_RECORD*)mrec))) 1409987da915Sopenharmony_ci perr_exit("ntfs_get_attr_search_ctx"); 1410987da915Sopenharmony_ci 1411987da915Sopenharmony_ci while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, 1412987da915Sopenharmony_ci NULL, 0, ctx)) { 1413987da915Sopenharmony_ci if (ctx->attr->type == AT_END) 1414987da915Sopenharmony_ci break; 1415987da915Sopenharmony_ci 1416987da915Sopenharmony_ci image.ctx = ctx; 1417987da915Sopenharmony_ci if (!ctx->attr->non_resident 1418987da915Sopenharmony_ci && (mft_no > LAST_METADATA_INODE)) 1419987da915Sopenharmony_ci wipe_resident_data(&image); 1420987da915Sopenharmony_ci if (!opt.preserve_timestamps) 1421987da915Sopenharmony_ci wipe_timestamps(&image); 1422987da915Sopenharmony_ci } 1423987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 1424987da915Sopenharmony_ci } 1425987da915Sopenharmony_ci ntfs_mft_usn_dec((MFT_RECORD*)mrec); 1426987da915Sopenharmony_ci ntfs_mst_pre_write_fixup((NTFS_RECORD*)mrec,mrecsz); 1427987da915Sopenharmony_ci} 1428987da915Sopenharmony_ci 1429987da915Sopenharmony_ci/* 1430987da915Sopenharmony_ci * In-memory wiping of a directory record (I30) 1431987da915Sopenharmony_ci * (only for metadata images) 1432987da915Sopenharmony_ci * 1433987da915Sopenharmony_ci * The timestamps are (optionally) wiped 1434987da915Sopenharmony_ci */ 1435987da915Sopenharmony_ci 1436987da915Sopenharmony_cistatic void wipe_indx(char *mrec, u32 mrecsz) 1437987da915Sopenharmony_ci{ 1438987da915Sopenharmony_ci INDEX_ENTRY *entry; 1439987da915Sopenharmony_ci INDEX_ALLOCATION *indexa; 1440987da915Sopenharmony_ci 1441987da915Sopenharmony_ci if (ntfs_mst_post_read_fixup((NTFS_RECORD *)mrec, mrecsz)) { 1442987da915Sopenharmony_ci perr_printf("Damaged INDX record"); 1443987da915Sopenharmony_ci goto out_indexa; 1444987da915Sopenharmony_ci } 1445987da915Sopenharmony_ci indexa = (INDEX_ALLOCATION*)mrec; 1446987da915Sopenharmony_ci /* 1447987da915Sopenharmony_ci * The index bitmap is not checked, obsoleted records are 1448987da915Sopenharmony_ci * wiped if they pass the safety checks 1449987da915Sopenharmony_ci */ 1450987da915Sopenharmony_ci if ((indexa->magic == magic_INDX) 1451987da915Sopenharmony_ci && (le32_to_cpu(indexa->index.entries_offset) >= sizeof(INDEX_HEADER)) 1452987da915Sopenharmony_ci && (le32_to_cpu(indexa->index.allocated_size) <= mrecsz)) { 1453987da915Sopenharmony_ci entry = (INDEX_ENTRY *)((u8 *)mrec + le32_to_cpu( 1454987da915Sopenharmony_ci indexa->index.entries_offset) + 0x18); 1455987da915Sopenharmony_ci wipe_index_entry_timestams(entry); 1456987da915Sopenharmony_ci } 1457987da915Sopenharmony_ci 1458987da915Sopenharmony_ci if (ntfs_mft_usn_dec((MFT_RECORD *)mrec)) 1459987da915Sopenharmony_ci perr_exit("ntfs_mft_usn_dec"); 1460987da915Sopenharmony_ci 1461987da915Sopenharmony_ci if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)mrec, mrecsz)) { 1462987da915Sopenharmony_ci perr_printf("INDX write fixup failed"); 1463987da915Sopenharmony_ci goto out_indexa; 1464987da915Sopenharmony_ci } 1465987da915Sopenharmony_ciout_indexa : ; 1466987da915Sopenharmony_ci} 1467987da915Sopenharmony_ci 1468987da915Sopenharmony_ci/* 1469987da915Sopenharmony_ci * Output a set of related clusters (MFT record or index block) 1470987da915Sopenharmony_ci */ 1471987da915Sopenharmony_ci 1472987da915Sopenharmony_cistatic void write_set(char *buff, u32 csize, s64 *current_lcn, 1473987da915Sopenharmony_ci runlist_element *rl, u32 wi, u32 wj, u32 cnt) 1474987da915Sopenharmony_ci{ 1475987da915Sopenharmony_ci u32 k; 1476987da915Sopenharmony_ci s64 target_lcn; 1477987da915Sopenharmony_ci char cmd = CMD_NEXT; 1478987da915Sopenharmony_ci 1479987da915Sopenharmony_ci for (k=0; k<cnt; k++) { 1480987da915Sopenharmony_ci target_lcn = rl[wi].lcn + wj; 1481987da915Sopenharmony_ci if (target_lcn != *current_lcn) 1482987da915Sopenharmony_ci gap_to_cluster(target_lcn - *current_lcn); 1483987da915Sopenharmony_ci if ((write_all(&fd_out, &cmd, sizeof(cmd)) == -1) 1484987da915Sopenharmony_ci || (write_all(&fd_out, &buff[k*csize], csize) == -1)) 1485987da915Sopenharmony_ci perr_exit("Failed to write_all"); 1486987da915Sopenharmony_ci *current_lcn = target_lcn + 1; 1487987da915Sopenharmony_ci 1488987da915Sopenharmony_ci if (++wj >= rl[wi].length) { 1489987da915Sopenharmony_ci wj = 0; 1490987da915Sopenharmony_ci wi++; 1491987da915Sopenharmony_ci } 1492987da915Sopenharmony_ci } 1493987da915Sopenharmony_ci} 1494987da915Sopenharmony_ci 1495987da915Sopenharmony_ci/* 1496987da915Sopenharmony_ci * Copy and wipe the full MFT or MFTMirr data. 1497987da915Sopenharmony_ci * (only for metadata images) 1498987da915Sopenharmony_ci * 1499987da915Sopenharmony_ci * Data are read and written by full clusters, but the wiping is done 1500987da915Sopenharmony_ci * per MFT record. 1501987da915Sopenharmony_ci */ 1502987da915Sopenharmony_ci 1503987da915Sopenharmony_cistatic void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) 1504987da915Sopenharmony_ci{ 1505987da915Sopenharmony_ci char *buff; 1506987da915Sopenharmony_ci void *fd; 1507987da915Sopenharmony_ci s64 mft_no; 1508987da915Sopenharmony_ci u32 mft_record_size; 1509987da915Sopenharmony_ci u32 csize; 1510987da915Sopenharmony_ci u32 buff_size; 1511987da915Sopenharmony_ci u32 bytes_per_sector; 1512987da915Sopenharmony_ci u32 records_per_set; 1513987da915Sopenharmony_ci u32 clusters_per_set; 1514987da915Sopenharmony_ci u32 wi,wj; /* indexes for reading */ 1515987da915Sopenharmony_ci u32 ri,rj; /* indexes for writing */ 1516987da915Sopenharmony_ci u32 k; /* lcn within run */ 1517987da915Sopenharmony_ci u32 r; /* mft_record within set */ 1518987da915Sopenharmony_ci s64 current_lcn; 1519987da915Sopenharmony_ci 1520987da915Sopenharmony_ci current_lcn = image->current_lcn; 1521987da915Sopenharmony_ci mft_record_size = image->ni->vol->mft_record_size; 1522987da915Sopenharmony_ci csize = image->ni->vol->cluster_size; 1523987da915Sopenharmony_ci bytes_per_sector = image->ni->vol->sector_size; 1524987da915Sopenharmony_ci fd = image->ni->vol->dev; 1525987da915Sopenharmony_ci /* 1526987da915Sopenharmony_ci * Depending on the sizes, there may be several records 1527987da915Sopenharmony_ci * per cluster, or several clusters per record. 1528987da915Sopenharmony_ci * Anyway, records are read and rescued by full clusters. 1529987da915Sopenharmony_ci */ 1530987da915Sopenharmony_ci if (csize >= mft_record_size) { 1531987da915Sopenharmony_ci records_per_set = csize/mft_record_size; 1532987da915Sopenharmony_ci clusters_per_set = 1; 1533987da915Sopenharmony_ci buff_size = csize; 1534987da915Sopenharmony_ci } else { 1535987da915Sopenharmony_ci clusters_per_set = mft_record_size/csize; 1536987da915Sopenharmony_ci records_per_set = 1; 1537987da915Sopenharmony_ci buff_size = mft_record_size; 1538987da915Sopenharmony_ci } 1539987da915Sopenharmony_ci buff = (char*)ntfs_malloc(buff_size); 1540987da915Sopenharmony_ci if (!buff) 1541987da915Sopenharmony_ci err_exit("Not enough memory"); 1542987da915Sopenharmony_ci 1543987da915Sopenharmony_ci mft_no = 0; 1544987da915Sopenharmony_ci ri = rj = 0; 1545987da915Sopenharmony_ci wi = wj = 0; 1546987da915Sopenharmony_ci if (rl[ri].length) 1547987da915Sopenharmony_ci lseek_to_cluster(rl[ri].lcn); 1548987da915Sopenharmony_ci while (rl[ri].length) { 1549987da915Sopenharmony_ci for (k=0; (k<clusters_per_set) && rl[ri].length; k++) { 1550987da915Sopenharmony_ci read_rescue(fd, &buff[k*csize], csize, bytes_per_sector, 1551987da915Sopenharmony_ci rl[ri].lcn + rj); 1552987da915Sopenharmony_ci if (++rj >= rl[ri].length) { 1553987da915Sopenharmony_ci rj = 0; 1554987da915Sopenharmony_ci if (rl[++ri].length) 1555987da915Sopenharmony_ci lseek_to_cluster(rl[ri].lcn); 1556987da915Sopenharmony_ci } 1557987da915Sopenharmony_ci } 1558987da915Sopenharmony_ci if (k == clusters_per_set) { 1559987da915Sopenharmony_ci for (r=0; r<records_per_set; r++) { 1560987da915Sopenharmony_ci if (!strncmp(&buff[r*mft_record_size],"FILE",4)) 1561987da915Sopenharmony_ci wipe_mft(&buff[r*mft_record_size], 1562987da915Sopenharmony_ci mft_record_size, mft_no); 1563987da915Sopenharmony_ci mft_no++; 1564987da915Sopenharmony_ci } 1565987da915Sopenharmony_ci write_set(buff, csize, ¤t_lcn, 1566987da915Sopenharmony_ci rl, wi, wj, clusters_per_set); 1567987da915Sopenharmony_ci wj += clusters_per_set; 1568987da915Sopenharmony_ci while (rl[wi].length && (wj >= rl[wi].length)) 1569987da915Sopenharmony_ci wj -= rl[wi++].length; 1570987da915Sopenharmony_ci } else { 1571987da915Sopenharmony_ci err_exit("Short last MFT record\n"); 1572987da915Sopenharmony_ci } 1573987da915Sopenharmony_ci } 1574987da915Sopenharmony_ci image->current_lcn = current_lcn; 1575987da915Sopenharmony_ci free(buff); 1576987da915Sopenharmony_ci} 1577987da915Sopenharmony_ci 1578987da915Sopenharmony_ci/* 1579987da915Sopenharmony_ci * Copy and wipe the non-resident part of a directory index 1580987da915Sopenharmony_ci * (only for metadata images) 1581987da915Sopenharmony_ci * 1582987da915Sopenharmony_ci * Data are read and written by full clusters, but the wiping is done 1583987da915Sopenharmony_ci * per index record. 1584987da915Sopenharmony_ci */ 1585987da915Sopenharmony_ci 1586987da915Sopenharmony_cistatic void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) 1587987da915Sopenharmony_ci{ 1588987da915Sopenharmony_ci char *buff; 1589987da915Sopenharmony_ci void *fd; 1590987da915Sopenharmony_ci u32 indx_record_size; 1591987da915Sopenharmony_ci u32 csize; 1592987da915Sopenharmony_ci u32 buff_size; 1593987da915Sopenharmony_ci u32 bytes_per_sector; 1594987da915Sopenharmony_ci u32 records_per_set; 1595987da915Sopenharmony_ci u32 clusters_per_set; 1596987da915Sopenharmony_ci u32 wi,wj; /* indexes for reading */ 1597987da915Sopenharmony_ci u32 ri,rj; /* indexes for writing */ 1598987da915Sopenharmony_ci u32 k; /* lcn within run */ 1599987da915Sopenharmony_ci u32 r; /* mft_record within set */ 1600987da915Sopenharmony_ci s64 current_lcn; 1601987da915Sopenharmony_ci 1602987da915Sopenharmony_ci current_lcn = image->current_lcn; 1603987da915Sopenharmony_ci csize = image->ni->vol->cluster_size; 1604987da915Sopenharmony_ci bytes_per_sector = image->ni->vol->sector_size; 1605987da915Sopenharmony_ci fd = image->ni->vol->dev; 1606987da915Sopenharmony_ci /* 1607987da915Sopenharmony_ci * Depending on the sizes, there may be several records 1608987da915Sopenharmony_ci * per cluster, or several clusters per record. 1609987da915Sopenharmony_ci * Anyway, records are read and rescued by full clusters. 1610987da915Sopenharmony_ci */ 1611987da915Sopenharmony_ci indx_record_size = image->ni->vol->indx_record_size; 1612987da915Sopenharmony_ci if (csize >= indx_record_size) { 1613987da915Sopenharmony_ci records_per_set = csize/indx_record_size; 1614987da915Sopenharmony_ci clusters_per_set = 1; 1615987da915Sopenharmony_ci buff_size = csize; 1616987da915Sopenharmony_ci } else { 1617987da915Sopenharmony_ci clusters_per_set = indx_record_size/csize; 1618987da915Sopenharmony_ci records_per_set = 1; 1619987da915Sopenharmony_ci buff_size = indx_record_size; 1620987da915Sopenharmony_ci } 1621987da915Sopenharmony_ci buff = (char*)ntfs_malloc(buff_size); 1622987da915Sopenharmony_ci if (!buff) 1623987da915Sopenharmony_ci err_exit("Not enough memory"); 1624987da915Sopenharmony_ci 1625987da915Sopenharmony_ci ri = rj = 0; 1626987da915Sopenharmony_ci wi = wj = 0; 1627987da915Sopenharmony_ci if (rl[ri].length) 1628987da915Sopenharmony_ci lseek_to_cluster(rl[ri].lcn); 1629987da915Sopenharmony_ci while (rl[ri].length) { 1630987da915Sopenharmony_ci for (k=0; (k<clusters_per_set) && rl[ri].length; k++) { 1631987da915Sopenharmony_ci read_rescue(fd, &buff[k*csize], csize, bytes_per_sector, 1632987da915Sopenharmony_ci rl[ri].lcn + rj); 1633987da915Sopenharmony_ci if (++rj >= rl[ri].length) { 1634987da915Sopenharmony_ci rj = 0; 1635987da915Sopenharmony_ci if (rl[++ri].length) 1636987da915Sopenharmony_ci lseek_to_cluster(rl[ri].lcn); 1637987da915Sopenharmony_ci } 1638987da915Sopenharmony_ci } 1639987da915Sopenharmony_ci if (k == clusters_per_set) { 1640987da915Sopenharmony_ci /* wipe records_per_set records */ 1641987da915Sopenharmony_ci if (!opt.preserve_timestamps) 1642987da915Sopenharmony_ci for (r=0; r<records_per_set; r++) { 1643987da915Sopenharmony_ci if (!strncmp(&buff[r*indx_record_size],"INDX",4)) 1644987da915Sopenharmony_ci wipe_indx(&buff[r*indx_record_size], 1645987da915Sopenharmony_ci indx_record_size); 1646987da915Sopenharmony_ci } 1647987da915Sopenharmony_ci write_set(buff, csize, ¤t_lcn, 1648987da915Sopenharmony_ci rl, wi, wj, clusters_per_set); 1649987da915Sopenharmony_ci wj += clusters_per_set; 1650987da915Sopenharmony_ci while (rl[wi].length && (wj >= rl[wi].length)) 1651987da915Sopenharmony_ci wj -= rl[wi++].length; 1652987da915Sopenharmony_ci } else { 1653987da915Sopenharmony_ci err_exit("Short last directory index record\n"); 1654987da915Sopenharmony_ci } 1655987da915Sopenharmony_ci } 1656987da915Sopenharmony_ci image->current_lcn = current_lcn; 1657987da915Sopenharmony_ci free(buff); 1658987da915Sopenharmony_ci} 1659987da915Sopenharmony_ci 1660987da915Sopenharmony_cistatic void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl) 1661987da915Sopenharmony_ci{ 1662987da915Sopenharmony_ci s64 i, len; /* number of clusters to copy */ 1663987da915Sopenharmony_ci 1664987da915Sopenharmony_ci if (opt.restore_image) 1665987da915Sopenharmony_ci err_exit("Bug : invalid dump_clusters()\n"); 1666987da915Sopenharmony_ci 1667987da915Sopenharmony_ci if ((opt.std_out && !opt.metadata_image) || !opt.metadata) 1668987da915Sopenharmony_ci return; 1669987da915Sopenharmony_ci if (!(len = is_critical_metadata(image, rl))) 1670987da915Sopenharmony_ci return; 1671987da915Sopenharmony_ci 1672987da915Sopenharmony_ci lseek_to_cluster(rl->lcn); 1673987da915Sopenharmony_ci if (opt.metadata_image ? wipe : !wipe) { 1674987da915Sopenharmony_ci if (opt.metadata_image) 1675987da915Sopenharmony_ci gap_to_cluster(rl->lcn - image->current_lcn); 1676987da915Sopenharmony_ci /* FIXME: this could give pretty suboptimal performance */ 1677987da915Sopenharmony_ci for (i = 0; i < len; i++) 1678987da915Sopenharmony_ci copy_cluster(opt.rescue, rl->lcn + i, rl->lcn + i); 1679987da915Sopenharmony_ci if (opt.metadata_image) 1680987da915Sopenharmony_ci image->current_lcn = rl->lcn + len; 1681987da915Sopenharmony_ci } 1682987da915Sopenharmony_ci} 1683987da915Sopenharmony_ci 1684987da915Sopenharmony_cistatic void walk_runs(struct ntfs_walk_cluster *walk) 1685987da915Sopenharmony_ci{ 1686987da915Sopenharmony_ci int i, j; 1687987da915Sopenharmony_ci runlist *rl; 1688987da915Sopenharmony_ci ATTR_RECORD *a; 1689987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 1690987da915Sopenharmony_ci BOOL mft_data; 1691987da915Sopenharmony_ci BOOL index_i30; 1692987da915Sopenharmony_ci 1693987da915Sopenharmony_ci ctx = walk->image->ctx; 1694987da915Sopenharmony_ci a = ctx->attr; 1695987da915Sopenharmony_ci 1696987da915Sopenharmony_ci if (!a->non_resident) { 1697987da915Sopenharmony_ci if (wipe) { 1698987da915Sopenharmony_ci wipe_resident_data(walk->image); 1699987da915Sopenharmony_ci if (!opt.preserve_timestamps) 1700987da915Sopenharmony_ci wipe_timestamps(walk->image); 1701987da915Sopenharmony_ci } 1702987da915Sopenharmony_ci return; 1703987da915Sopenharmony_ci } 1704987da915Sopenharmony_ci 1705987da915Sopenharmony_ci if (wipe 1706987da915Sopenharmony_ci && !opt.preserve_timestamps 1707987da915Sopenharmony_ci && walk->image->ctx->attr->type == AT_INDEX_ALLOCATION) 1708987da915Sopenharmony_ci wipe_index_allocation_timestamps(walk->image->ni, a); 1709987da915Sopenharmony_ci 1710987da915Sopenharmony_ci if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) 1711987da915Sopenharmony_ci perr_exit("ntfs_decompress_mapping_pairs"); 1712987da915Sopenharmony_ci 1713987da915Sopenharmony_ci /* special wipings for MFT records and directory indexes */ 1714987da915Sopenharmony_ci mft_data = ((walk->image->ni->mft_no == FILE_MFT) 1715987da915Sopenharmony_ci || (walk->image->ni->mft_no == FILE_MFTMirr)) 1716987da915Sopenharmony_ci && (a->type == AT_DATA); 1717987da915Sopenharmony_ci index_i30 = (walk->image->ctx->attr->type == AT_INDEX_ALLOCATION) 1718987da915Sopenharmony_ci && (a->name_length == 4) 1719987da915Sopenharmony_ci && !memcmp((char*)a + le16_to_cpu(a->name_offset), 1720987da915Sopenharmony_ci NTFS_INDEX_I30,8); 1721987da915Sopenharmony_ci 1722987da915Sopenharmony_ci for (i = 0; rl[i].length; i++) { 1723987da915Sopenharmony_ci s64 lcn = rl[i].lcn; 1724987da915Sopenharmony_ci s64 lcn_length = rl[i].length; 1725987da915Sopenharmony_ci 1726987da915Sopenharmony_ci if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) 1727987da915Sopenharmony_ci continue; 1728987da915Sopenharmony_ci 1729987da915Sopenharmony_ci /* FIXME: ntfs_mapping_pairs_decompress should return error */ 1730987da915Sopenharmony_ci if (lcn < 0 || lcn_length < 0) 1731987da915Sopenharmony_ci err_exit("Corrupt runlist in inode %lld attr %x LCN " 1732987da915Sopenharmony_ci "%llx length %llx\n", 1733987da915Sopenharmony_ci (long long)ctx->ntfs_ino->mft_no, 1734987da915Sopenharmony_ci (unsigned int)le32_to_cpu(a->type), 1735987da915Sopenharmony_ci (long long)lcn, (long long)lcn_length); 1736987da915Sopenharmony_ci 1737987da915Sopenharmony_ci if (opt.metadata_image ? wipe && !mft_data && !index_i30 : !wipe) 1738987da915Sopenharmony_ci dump_clusters(walk->image, rl + i); 1739987da915Sopenharmony_ci 1740987da915Sopenharmony_ci for (j = 0; j < lcn_length; j++) { 1741987da915Sopenharmony_ci u64 k = (u64)lcn + j; 1742987da915Sopenharmony_ci if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1)) { 1743987da915Sopenharmony_ci if (opt.ignore_fs_check) 1744987da915Sopenharmony_ci Printf("Cluster %llu is referenced" 1745987da915Sopenharmony_ci " twice!\n", 1746987da915Sopenharmony_ci (unsigned long long)k); 1747987da915Sopenharmony_ci else 1748987da915Sopenharmony_ci err_exit("Cluster %llu referenced" 1749987da915Sopenharmony_ci " twice!\nYou didn't shutdown" 1750987da915Sopenharmony_ci " your Windows properly?\n", 1751987da915Sopenharmony_ci (unsigned long long)k); 1752987da915Sopenharmony_ci } 1753987da915Sopenharmony_ci } 1754987da915Sopenharmony_ci 1755987da915Sopenharmony_ci if (!opt.metadata_image) 1756987da915Sopenharmony_ci walk->image->inuse += lcn_length; 1757987da915Sopenharmony_ci /* 1758987da915Sopenharmony_ci * For a metadata image, we have to compute the 1759987da915Sopenharmony_ci * number of metadata clusters for the percentages 1760987da915Sopenharmony_ci * to be displayed correctly while restoring. 1761987da915Sopenharmony_ci */ 1762987da915Sopenharmony_ci if (!wipe && opt.metadata_image) { 1763987da915Sopenharmony_ci if ((walk->image->ni->mft_no == FILE_LogFile) 1764987da915Sopenharmony_ci && (walk->image->ctx->attr->type == AT_DATA)) { 1765987da915Sopenharmony_ci /* 16 KiB of FILE_LogFile */ 1766987da915Sopenharmony_ci walk->image->inuse 1767987da915Sopenharmony_ci += is_critical_metadata(walk->image,rl); 1768987da915Sopenharmony_ci } else { 1769987da915Sopenharmony_ci if ((walk->image->ni->mft_no 1770987da915Sopenharmony_ci <= LAST_METADATA_INODE) 1771987da915Sopenharmony_ci || (walk->image->ctx->attr->type != AT_DATA)) 1772987da915Sopenharmony_ci walk->image->inuse += lcn_length; 1773987da915Sopenharmony_ci } 1774987da915Sopenharmony_ci } 1775987da915Sopenharmony_ci } 1776987da915Sopenharmony_ci if (wipe && opt.metadata_image) { 1777987da915Sopenharmony_ci ntfs_attr *na; 1778987da915Sopenharmony_ci /* 1779987da915Sopenharmony_ci * Non-resident metadata has to be wiped globally, 1780987da915Sopenharmony_ci * because its logical blocks may be larger than 1781987da915Sopenharmony_ci * a cluster and split over two extents. 1782987da915Sopenharmony_ci */ 1783987da915Sopenharmony_ci if (mft_data && !a->lowest_vcn) { 1784987da915Sopenharmony_ci na = ntfs_attr_open(walk->image->ni, 1785987da915Sopenharmony_ci AT_DATA, NULL, 0); 1786987da915Sopenharmony_ci if (na) { 1787987da915Sopenharmony_ci na->rl = rl; 1788987da915Sopenharmony_ci rl = (runlist_element*)NULL; 1789987da915Sopenharmony_ci if (!ntfs_attr_map_whole_runlist(na)) { 1790987da915Sopenharmony_ci copy_wipe_mft(walk->image,na->rl); 1791987da915Sopenharmony_ci } else 1792987da915Sopenharmony_ci perr_exit("Failed to map data of inode %lld", 1793987da915Sopenharmony_ci (long long)walk->image->ni->mft_no); 1794987da915Sopenharmony_ci ntfs_attr_close(na); 1795987da915Sopenharmony_ci } else 1796987da915Sopenharmony_ci perr_exit("Failed to open data of inode %lld", 1797987da915Sopenharmony_ci (long long)walk->image->ni->mft_no); 1798987da915Sopenharmony_ci } 1799987da915Sopenharmony_ci if (index_i30 && !a->lowest_vcn) { 1800987da915Sopenharmony_ci na = ntfs_attr_open(walk->image->ni, 1801987da915Sopenharmony_ci AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); 1802987da915Sopenharmony_ci if (na) { 1803987da915Sopenharmony_ci na->rl = rl; 1804987da915Sopenharmony_ci rl = (runlist_element*)NULL; 1805987da915Sopenharmony_ci if (!ntfs_attr_map_whole_runlist(na)) { 1806987da915Sopenharmony_ci copy_wipe_i30(walk->image,na->rl); 1807987da915Sopenharmony_ci } else 1808987da915Sopenharmony_ci perr_exit("Failed to map index of inode %lld", 1809987da915Sopenharmony_ci (long long)walk->image->ni->mft_no); 1810987da915Sopenharmony_ci ntfs_attr_close(na); 1811987da915Sopenharmony_ci } else 1812987da915Sopenharmony_ci perr_exit("Failed to open index of inode %lld", 1813987da915Sopenharmony_ci (long long)walk->image->ni->mft_no); 1814987da915Sopenharmony_ci } 1815987da915Sopenharmony_ci } 1816987da915Sopenharmony_ci if (opt.metadata 1817987da915Sopenharmony_ci && (opt.metadata_image || !wipe) 1818987da915Sopenharmony_ci && (walk->image->ni->mft_no == FILE_LogFile) 1819987da915Sopenharmony_ci && (walk->image->ctx->attr->type == AT_DATA)) 1820987da915Sopenharmony_ci clone_logfile_parts(walk->image, rl); 1821987da915Sopenharmony_ci 1822987da915Sopenharmony_ci free(rl); 1823987da915Sopenharmony_ci} 1824987da915Sopenharmony_ci 1825987da915Sopenharmony_ci 1826987da915Sopenharmony_cistatic void walk_attributes(struct ntfs_walk_cluster *walk) 1827987da915Sopenharmony_ci{ 1828987da915Sopenharmony_ci ntfs_attr_search_ctx *ctx; 1829987da915Sopenharmony_ci 1830987da915Sopenharmony_ci if (!(ctx = ntfs_attr_get_search_ctx(walk->image->ni, NULL))) 1831987da915Sopenharmony_ci perr_exit("ntfs_get_attr_search_ctx"); 1832987da915Sopenharmony_ci 1833987da915Sopenharmony_ci while (!ntfs_attrs_walk(ctx)) { 1834987da915Sopenharmony_ci if (ctx->attr->type == AT_END) 1835987da915Sopenharmony_ci break; 1836987da915Sopenharmony_ci 1837987da915Sopenharmony_ci walk->image->ctx = ctx; 1838987da915Sopenharmony_ci walk_runs(walk); 1839987da915Sopenharmony_ci } 1840987da915Sopenharmony_ci 1841987da915Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 1842987da915Sopenharmony_ci} 1843987da915Sopenharmony_ci 1844987da915Sopenharmony_ci/* 1845987da915Sopenharmony_ci * Compare the actual bitmap to the list of clusters 1846987da915Sopenharmony_ci * allocated to identified files. 1847987da915Sopenharmony_ci * 1848987da915Sopenharmony_ci * Clusters found in use, though not marked in the bitmap are copied 1849987da915Sopenharmony_ci * if the option --ignore-fs-checks is set. 1850987da915Sopenharmony_ci */ 1851987da915Sopenharmony_ci 1852987da915Sopenharmony_cistatic int compare_bitmaps(struct bitmap *a, BOOL copy) 1853987da915Sopenharmony_ci{ 1854987da915Sopenharmony_ci s64 i, pos, count; 1855987da915Sopenharmony_ci int mismatch = 0; 1856987da915Sopenharmony_ci int more_use = 0; 1857987da915Sopenharmony_ci s64 new_cl; 1858987da915Sopenharmony_ci u8 bm[NTFS_BUF_SIZE]; 1859987da915Sopenharmony_ci 1860987da915Sopenharmony_ci Printf("Accounting clusters ...\n"); 1861987da915Sopenharmony_ci 1862987da915Sopenharmony_ci pos = 0; 1863987da915Sopenharmony_ci new_cl = 0; 1864987da915Sopenharmony_ci while (1) { 1865987da915Sopenharmony_ci count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); 1866987da915Sopenharmony_ci if (count == -1) 1867987da915Sopenharmony_ci perr_exit("Couldn't get $Bitmap $DATA"); 1868987da915Sopenharmony_ci 1869987da915Sopenharmony_ci if (count == 0) { 1870987da915Sopenharmony_ci /* the backup bootsector need not be accounted for */ 1871987da915Sopenharmony_ci if (((vol->nr_clusters + 7) >> 3) > pos) 1872987da915Sopenharmony_ci err_exit("$Bitmap size is smaller than expected" 1873987da915Sopenharmony_ci " (%lld < %lld)\n", 1874987da915Sopenharmony_ci (long long)pos, (long long)a->size); 1875987da915Sopenharmony_ci break; 1876987da915Sopenharmony_ci } 1877987da915Sopenharmony_ci 1878987da915Sopenharmony_ci for (i = 0; i < count; i++, pos++) { 1879987da915Sopenharmony_ci s64 cl; /* current cluster */ 1880987da915Sopenharmony_ci 1881987da915Sopenharmony_ci if (a->size <= pos) 1882987da915Sopenharmony_ci goto done; 1883987da915Sopenharmony_ci 1884987da915Sopenharmony_ci if (a->bm[pos] == bm[i]) 1885987da915Sopenharmony_ci continue; 1886987da915Sopenharmony_ci 1887987da915Sopenharmony_ci for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { 1888987da915Sopenharmony_ci char bit; 1889987da915Sopenharmony_ci 1890987da915Sopenharmony_ci bit = ntfs_bit_get(a->bm, cl); 1891987da915Sopenharmony_ci if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) 1892987da915Sopenharmony_ci continue; 1893987da915Sopenharmony_ci 1894987da915Sopenharmony_ci if (!bit) 1895987da915Sopenharmony_ci more_use++; 1896987da915Sopenharmony_ci if (opt.ignore_fs_check && !bit && copy) { 1897987da915Sopenharmony_ci lseek_to_cluster(cl); 1898987da915Sopenharmony_ci if (opt.save_image 1899987da915Sopenharmony_ci || (opt.metadata 1900987da915Sopenharmony_ci && opt.metadata_image)) { 1901987da915Sopenharmony_ci gap_to_cluster(cl - new_cl); 1902987da915Sopenharmony_ci new_cl = cl + 1; 1903987da915Sopenharmony_ci } 1904987da915Sopenharmony_ci copy_cluster(opt.rescue, cl, cl); 1905987da915Sopenharmony_ci } 1906987da915Sopenharmony_ci 1907987da915Sopenharmony_ci if (++mismatch > 10) 1908987da915Sopenharmony_ci continue; 1909987da915Sopenharmony_ci 1910987da915Sopenharmony_ci Printf("Cluster accounting failed at %lld " 1911987da915Sopenharmony_ci "(0x%llx): %s cluster in $Bitmap\n", 1912987da915Sopenharmony_ci (long long)cl, (unsigned long long)cl, 1913987da915Sopenharmony_ci bit ? "missing" : "extra"); 1914987da915Sopenharmony_ci } 1915987da915Sopenharmony_ci } 1916987da915Sopenharmony_ci } 1917987da915Sopenharmony_cidone: 1918987da915Sopenharmony_ci if (mismatch) { 1919987da915Sopenharmony_ci Printf("Totally %d cluster accounting mismatches.\n", mismatch); 1920987da915Sopenharmony_ci if (opt.ignore_fs_check) { 1921987da915Sopenharmony_ci Printf("WARNING: The NTFS inconsistency was overruled " 1922987da915Sopenharmony_ci "by the --ignore-fs-check option.\n"); 1923987da915Sopenharmony_ci if (new_cl) { 1924987da915Sopenharmony_ci gap_to_cluster(-new_cl); 1925987da915Sopenharmony_ci } 1926987da915Sopenharmony_ci return (more_use); 1927987da915Sopenharmony_ci } 1928987da915Sopenharmony_ci err_exit("Filesystem check failed! Windows wasn't shutdown " 1929987da915Sopenharmony_ci "properly or inconsistent\nfilesystem. Please run " 1930987da915Sopenharmony_ci "chkdsk /f on Windows then reboot it TWICE.\n"); 1931987da915Sopenharmony_ci } 1932987da915Sopenharmony_ci return (more_use); 1933987da915Sopenharmony_ci} 1934987da915Sopenharmony_ci 1935987da915Sopenharmony_ci 1936987da915Sopenharmony_cistatic void mft_record_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) 1937987da915Sopenharmony_ci{ 1938987da915Sopenharmony_ci if (ntfs_mft_usn_dec(ni->mrec)) 1939987da915Sopenharmony_ci perr_exit("ntfs_mft_usn_dec"); 1940987da915Sopenharmony_ci 1941987da915Sopenharmony_ci if (ntfs_mft_record_write(volume, ni->mft_no, ni->mrec)) 1942987da915Sopenharmony_ci perr_exit("ntfs_mft_record_write"); 1943987da915Sopenharmony_ci} 1944987da915Sopenharmony_ci 1945987da915Sopenharmony_cistatic void mft_inode_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) 1946987da915Sopenharmony_ci{ 1947987da915Sopenharmony_ci s32 i; 1948987da915Sopenharmony_ci 1949987da915Sopenharmony_ci mft_record_write_with_same_usn(volume, ni); 1950987da915Sopenharmony_ci 1951987da915Sopenharmony_ci if (ni->nr_extents <= 0) 1952987da915Sopenharmony_ci return; 1953987da915Sopenharmony_ci 1954987da915Sopenharmony_ci for (i = 0; i < ni->nr_extents; ++i) { 1955987da915Sopenharmony_ci ntfs_inode *eni = ni->extent_nis[i]; 1956987da915Sopenharmony_ci mft_record_write_with_same_usn(volume, eni); 1957987da915Sopenharmony_ci } 1958987da915Sopenharmony_ci} 1959987da915Sopenharmony_ci 1960987da915Sopenharmony_cistatic int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) 1961987da915Sopenharmony_ci{ 1962987da915Sopenharmony_ci s64 inode = 0; 1963987da915Sopenharmony_ci s64 last_mft_rec; 1964987da915Sopenharmony_ci u64 nr_clusters; 1965987da915Sopenharmony_ci ntfs_inode *ni; 1966987da915Sopenharmony_ci struct progress_bar progress; 1967987da915Sopenharmony_ci 1968987da915Sopenharmony_ci if (opt.restore_image || (!opt.metadata && wipe)) 1969987da915Sopenharmony_ci err_exit("Bug : invalid walk_clusters()\n"); 1970987da915Sopenharmony_ci Printf("Scanning volume ...\n"); 1971987da915Sopenharmony_ci 1972987da915Sopenharmony_ci last_mft_rec = (volume->mft_na->initialized_size >> 1973987da915Sopenharmony_ci volume->mft_record_size_bits) - 1; 1974987da915Sopenharmony_ci walk->image->current_lcn = 0; 1975987da915Sopenharmony_ci progress_init(&progress, inode, last_mft_rec, 100); 1976987da915Sopenharmony_ci 1977987da915Sopenharmony_ci NVolSetNoFixupWarn(volume); 1978987da915Sopenharmony_ci for (; inode <= last_mft_rec; inode++) { 1979987da915Sopenharmony_ci 1980987da915Sopenharmony_ci int err, deleted_inode; 1981987da915Sopenharmony_ci MFT_REF mref = (MFT_REF)inode; 1982987da915Sopenharmony_ci 1983987da915Sopenharmony_ci progress_update(&progress, inode); 1984987da915Sopenharmony_ci 1985987da915Sopenharmony_ci /* FIXME: Terrible kludge for libntfs not being able to return 1986987da915Sopenharmony_ci a deleted MFT record as inode */ 1987987da915Sopenharmony_ci ni = ntfs_calloc(sizeof(ntfs_inode)); 1988987da915Sopenharmony_ci if (!ni) 1989987da915Sopenharmony_ci perr_exit("walk_clusters"); 1990987da915Sopenharmony_ci 1991987da915Sopenharmony_ci ni->vol = volume; 1992987da915Sopenharmony_ci 1993987da915Sopenharmony_ci err = ntfs_file_record_read(volume, mref, &ni->mrec, NULL); 1994987da915Sopenharmony_ci if (err == -1) { 1995987da915Sopenharmony_ci free(ni); 1996987da915Sopenharmony_ci continue; 1997987da915Sopenharmony_ci } 1998987da915Sopenharmony_ci 1999987da915Sopenharmony_ci deleted_inode = !(ni->mrec->flags & MFT_RECORD_IN_USE); 2000987da915Sopenharmony_ci 2001987da915Sopenharmony_ci if (deleted_inode && !opt.metadata_image) { 2002987da915Sopenharmony_ci 2003987da915Sopenharmony_ci ni->mft_no = MREF(mref); 2004987da915Sopenharmony_ci if (wipe) { 2005987da915Sopenharmony_ci wipe_unused_mft(ni); 2006987da915Sopenharmony_ci wipe_unused_mft_data(ni); 2007987da915Sopenharmony_ci mft_record_write_with_same_usn(volume, ni); 2008987da915Sopenharmony_ci } 2009987da915Sopenharmony_ci } 2010987da915Sopenharmony_ci 2011987da915Sopenharmony_ci free(ni->mrec); 2012987da915Sopenharmony_ci free(ni); 2013987da915Sopenharmony_ci 2014987da915Sopenharmony_ci if (deleted_inode) 2015987da915Sopenharmony_ci continue; 2016987da915Sopenharmony_ci 2017987da915Sopenharmony_ci if ((ni = ntfs_inode_open(volume, mref)) == NULL) { 2018987da915Sopenharmony_ci /* FIXME: continue only if it make sense, e.g. 2019987da915Sopenharmony_ci MFT record not in use based on $MFT bitmap */ 2020987da915Sopenharmony_ci if (errno == EIO || errno == ENOENT) 2021987da915Sopenharmony_ci continue; 2022987da915Sopenharmony_ci perr_exit("Reading inode %lld failed", 2023987da915Sopenharmony_ci (long long)inode); 2024987da915Sopenharmony_ci } 2025987da915Sopenharmony_ci 2026987da915Sopenharmony_ci if (wipe) 2027987da915Sopenharmony_ci nr_used_mft_records++; 2028987da915Sopenharmony_ci 2029987da915Sopenharmony_ci if (ni->mrec->base_mft_record) 2030987da915Sopenharmony_ci goto out; 2031987da915Sopenharmony_ci 2032987da915Sopenharmony_ci walk->image->ni = ni; 2033987da915Sopenharmony_ci walk_attributes(walk); 2034987da915Sopenharmony_ciout: 2035987da915Sopenharmony_ci if (wipe && !opt.metadata_image) { 2036987da915Sopenharmony_ci int i; 2037987da915Sopenharmony_ci 2038987da915Sopenharmony_ci wipe_unused_mft_data(ni); 2039987da915Sopenharmony_ci for (i = 0; i < ni->nr_extents; ++i) { 2040987da915Sopenharmony_ci wipe_unused_mft_data(ni->extent_nis[i]); 2041987da915Sopenharmony_ci } 2042987da915Sopenharmony_ci mft_inode_write_with_same_usn(volume, ni); 2043987da915Sopenharmony_ci } 2044987da915Sopenharmony_ci 2045987da915Sopenharmony_ci if (ntfs_inode_close(ni)) 2046987da915Sopenharmony_ci perr_exit("ntfs_inode_close for inode %lld", 2047987da915Sopenharmony_ci (long long)inode); 2048987da915Sopenharmony_ci } 2049987da915Sopenharmony_ci if (opt.metadata) { 2050987da915Sopenharmony_ci if (opt.metadata_image && wipe && opt.ignore_fs_check) { 2051987da915Sopenharmony_ci gap_to_cluster(-walk->image->current_lcn); 2052987da915Sopenharmony_ci compare_bitmaps(&lcn_bitmap, TRUE); 2053987da915Sopenharmony_ci walk->image->current_lcn = 0; 2054987da915Sopenharmony_ci } 2055987da915Sopenharmony_ci if (opt.metadata_image ? wipe : !wipe) { 2056987da915Sopenharmony_ci /* also get the backup bootsector */ 2057987da915Sopenharmony_ci nr_clusters = vol->nr_clusters; 2058987da915Sopenharmony_ci lseek_to_cluster(nr_clusters); 2059987da915Sopenharmony_ci if (opt.metadata_image && wipe) 2060987da915Sopenharmony_ci gap_to_cluster(nr_clusters 2061987da915Sopenharmony_ci - walk->image->current_lcn); 2062987da915Sopenharmony_ci copy_cluster(opt.rescue, nr_clusters, nr_clusters); 2063987da915Sopenharmony_ci walk->image->current_lcn = nr_clusters; 2064987da915Sopenharmony_ci } 2065987da915Sopenharmony_ci /* Not counted, for compatibility with older versions */ 2066987da915Sopenharmony_ci if (!opt.metadata_image) 2067987da915Sopenharmony_ci walk->image->inuse++; 2068987da915Sopenharmony_ci } 2069987da915Sopenharmony_ci return 0; 2070987da915Sopenharmony_ci} 2071987da915Sopenharmony_ci 2072987da915Sopenharmony_ci 2073987da915Sopenharmony_ci/* 2074987da915Sopenharmony_ci * $Bitmap can overlap the end of the volume. Any bits in this region 2075987da915Sopenharmony_ci * must be set. This region also encompasses the backup boot sector. 2076987da915Sopenharmony_ci */ 2077987da915Sopenharmony_cistatic void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) 2078987da915Sopenharmony_ci{ 2079987da915Sopenharmony_ci for (; cluster < bm->size << 3; cluster++) 2080987da915Sopenharmony_ci ntfs_bit_set(bm->bm, (u64)cluster, 1); 2081987da915Sopenharmony_ci} 2082987da915Sopenharmony_ci 2083987da915Sopenharmony_ci 2084987da915Sopenharmony_ci/* 2085987da915Sopenharmony_ci * Allocate a block of memory with one bit for each cluster of the disk. 2086987da915Sopenharmony_ci * All the bits are set to 0, except those representing the region beyond the 2087987da915Sopenharmony_ci * end of the disk. 2088987da915Sopenharmony_ci */ 2089987da915Sopenharmony_cistatic void setup_lcn_bitmap(void) 2090987da915Sopenharmony_ci{ 2091987da915Sopenharmony_ci /* Determine lcn bitmap byte size and allocate it. */ 2092987da915Sopenharmony_ci /* include the alternate boot sector in the bitmap count */ 2093987da915Sopenharmony_ci lcn_bitmap.size = rounded_up_division(vol->nr_clusters + 1, 8); 2094987da915Sopenharmony_ci 2095987da915Sopenharmony_ci lcn_bitmap.bm = ntfs_calloc(lcn_bitmap.size); 2096987da915Sopenharmony_ci if (!lcn_bitmap.bm) 2097987da915Sopenharmony_ci perr_exit("Failed to allocate internal buffer"); 2098987da915Sopenharmony_ci 2099987da915Sopenharmony_ci bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap); 2100987da915Sopenharmony_ci} 2101987da915Sopenharmony_ci 2102987da915Sopenharmony_ci 2103987da915Sopenharmony_cistatic s64 volume_size(ntfs_volume *volume, s64 nr_clusters) 2104987da915Sopenharmony_ci{ 2105987da915Sopenharmony_ci return nr_clusters * volume->cluster_size; 2106987da915Sopenharmony_ci} 2107987da915Sopenharmony_ci 2108987da915Sopenharmony_ci 2109987da915Sopenharmony_cistatic void print_volume_size(const char *str, s64 bytes) 2110987da915Sopenharmony_ci{ 2111987da915Sopenharmony_ci Printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, 2112987da915Sopenharmony_ci (long long)rounded_up_division(bytes, NTFS_MBYTE)); 2113987da915Sopenharmony_ci} 2114987da915Sopenharmony_ci 2115987da915Sopenharmony_ci 2116987da915Sopenharmony_cistatic void print_disk_usage(const char *spacer, u32 cluster_size, 2117987da915Sopenharmony_ci s64 nr_clusters, s64 inuse) 2118987da915Sopenharmony_ci{ 2119987da915Sopenharmony_ci s64 total, used; 2120987da915Sopenharmony_ci 2121987da915Sopenharmony_ci total = nr_clusters * cluster_size; 2122987da915Sopenharmony_ci used = inuse * cluster_size; 2123987da915Sopenharmony_ci 2124987da915Sopenharmony_ci Printf("Space in use %s: %lld MB (%.1f%%) ", spacer, 2125987da915Sopenharmony_ci (long long)rounded_up_division(used, NTFS_MBYTE), 2126987da915Sopenharmony_ci 100.0 * ((float)used / total)); 2127987da915Sopenharmony_ci 2128987da915Sopenharmony_ci Printf("\n"); 2129987da915Sopenharmony_ci} 2130987da915Sopenharmony_ci 2131987da915Sopenharmony_cistatic void print_image_info(void) 2132987da915Sopenharmony_ci{ 2133987da915Sopenharmony_ci Printf("Ntfsclone image version: %d.%d\n", 2134987da915Sopenharmony_ci image_hdr.major_ver, image_hdr.minor_ver); 2135987da915Sopenharmony_ci Printf("Cluster size : %u bytes\n", 2136987da915Sopenharmony_ci (unsigned)le32_to_cpu(image_hdr.cluster_size)); 2137987da915Sopenharmony_ci print_volume_size("Image volume size ", 2138987da915Sopenharmony_ci sle64_to_cpu(image_hdr.nr_clusters) * 2139987da915Sopenharmony_ci le32_to_cpu(image_hdr.cluster_size)); 2140987da915Sopenharmony_ci Printf("Image device size : %lld bytes\n", 2141987da915Sopenharmony_ci (long long)le64_to_cpu(image_hdr.device_size)); 2142987da915Sopenharmony_ci print_disk_usage(" ", le32_to_cpu(image_hdr.cluster_size), 2143987da915Sopenharmony_ci sle64_to_cpu(image_hdr.nr_clusters), 2144987da915Sopenharmony_ci le64_to_cpu(image_hdr.inuse)); 2145987da915Sopenharmony_ci Printf("Offset to image data : %u (0x%x) bytes\n", 2146987da915Sopenharmony_ci (unsigned)le32_to_cpu(image_hdr.offset_to_image_data), 2147987da915Sopenharmony_ci (unsigned)le32_to_cpu(image_hdr.offset_to_image_data)); 2148987da915Sopenharmony_ci} 2149987da915Sopenharmony_ci 2150987da915Sopenharmony_cistatic void check_if_mounted(const char *device, unsigned long new_mntflag) 2151987da915Sopenharmony_ci{ 2152987da915Sopenharmony_ci unsigned long mntflag; 2153987da915Sopenharmony_ci 2154987da915Sopenharmony_ci if (ntfs_check_if_mounted(device, &mntflag)) 2155987da915Sopenharmony_ci perr_exit("Failed to check '%s' mount state", device); 2156987da915Sopenharmony_ci 2157987da915Sopenharmony_ci if (mntflag & NTFS_MF_MOUNTED) { 2158987da915Sopenharmony_ci if (!(mntflag & NTFS_MF_READONLY)) 2159987da915Sopenharmony_ci err_exit("Device '%s' is mounted read-write. " 2160987da915Sopenharmony_ci "You must 'umount' it first.\n", device); 2161987da915Sopenharmony_ci if (!new_mntflag) 2162987da915Sopenharmony_ci err_exit("Device '%s' is mounted. " 2163987da915Sopenharmony_ci "You must 'umount' it first.\n", device); 2164987da915Sopenharmony_ci } 2165987da915Sopenharmony_ci} 2166987da915Sopenharmony_ci 2167987da915Sopenharmony_ci/** 2168987da915Sopenharmony_ci * mount_volume - 2169987da915Sopenharmony_ci * 2170987da915Sopenharmony_ci * First perform some checks to determine if the volume is already mounted, or 2171987da915Sopenharmony_ci * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount 2172987da915Sopenharmony_ci * the volume (load the metadata into memory). 2173987da915Sopenharmony_ci */ 2174987da915Sopenharmony_cistatic void mount_volume(unsigned long new_mntflag) 2175987da915Sopenharmony_ci{ 2176987da915Sopenharmony_ci check_if_mounted(opt.volume, new_mntflag); 2177987da915Sopenharmony_ci 2178987da915Sopenharmony_ci if (!(vol = ntfs_mount(opt.volume, new_mntflag))) { 2179987da915Sopenharmony_ci 2180987da915Sopenharmony_ci int err = errno; 2181987da915Sopenharmony_ci 2182987da915Sopenharmony_ci perr_printf("Opening '%s' as NTFS failed", opt.volume); 2183987da915Sopenharmony_ci if (err == EINVAL) { 2184987da915Sopenharmony_ci Printf("Apparently device '%s' doesn't have a " 2185987da915Sopenharmony_ci "valid NTFS. Maybe you selected\nthe whole " 2186987da915Sopenharmony_ci "disk instead of a partition (e.g. /dev/hda, " 2187987da915Sopenharmony_ci "not /dev/hda1)?\n", opt.volume); 2188987da915Sopenharmony_ci } 2189987da915Sopenharmony_ci /* 2190987da915Sopenharmony_ci * Retry with recovering the log file enabled. 2191987da915Sopenharmony_ci * Normally avoided in order to get the original log file 2192987da915Sopenharmony_ci * data, but needed when remounting the metadata of a 2193987da915Sopenharmony_ci * volume improperly unmounted from Windows. 2194987da915Sopenharmony_ci * If the full log file was requested, it must be kept 2195987da915Sopenharmony_ci * as is, so we just remount read-only. 2196987da915Sopenharmony_ci */ 2197987da915Sopenharmony_ci if (!(new_mntflag & (NTFS_MNT_RDONLY | NTFS_MNT_RECOVER))) { 2198987da915Sopenharmony_ci if (opt.full_logfile) { 2199987da915Sopenharmony_ci Printf("Retrying read-only to ignore" 2200987da915Sopenharmony_ci " the log file...\n"); 2201987da915Sopenharmony_ci vol = ntfs_mount(opt.volume, 2202987da915Sopenharmony_ci new_mntflag | NTFS_MNT_RDONLY); 2203987da915Sopenharmony_ci } else { 2204987da915Sopenharmony_ci Printf("Trying to recover...\n"); 2205987da915Sopenharmony_ci vol = ntfs_mount(opt.volume, 2206987da915Sopenharmony_ci new_mntflag | NTFS_MNT_RECOVER); 2207987da915Sopenharmony_ci } 2208987da915Sopenharmony_ci Printf("... %s\n",(vol ? "Successful" : "Failed")); 2209987da915Sopenharmony_ci } 2210987da915Sopenharmony_ci if (!vol) 2211987da915Sopenharmony_ci exit(1); 2212987da915Sopenharmony_ci } 2213987da915Sopenharmony_ci 2214987da915Sopenharmony_ci if (vol->flags & VOLUME_IS_DIRTY) 2215987da915Sopenharmony_ci if (opt.force-- <= 0) 2216987da915Sopenharmony_ci err_exit(dirty_volume_msg, opt.volume); 2217987da915Sopenharmony_ci 2218987da915Sopenharmony_ci if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) 2219987da915Sopenharmony_ci err_exit("Cluster size %u is too large!\n", 2220987da915Sopenharmony_ci (unsigned int)vol->cluster_size); 2221987da915Sopenharmony_ci 2222987da915Sopenharmony_ci Printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver); 2223987da915Sopenharmony_ci if (ntfs_version_is_supported(vol)) 2224987da915Sopenharmony_ci perr_exit("Unknown NTFS version"); 2225987da915Sopenharmony_ci 2226987da915Sopenharmony_ci Printf("Cluster size : %u bytes\n", 2227987da915Sopenharmony_ci (unsigned int)vol->cluster_size); 2228987da915Sopenharmony_ci print_volume_size("Current volume size", 2229987da915Sopenharmony_ci volume_size(vol, vol->nr_clusters)); 2230987da915Sopenharmony_ci} 2231987da915Sopenharmony_ci 2232987da915Sopenharmony_cistatic struct ntfs_walk_cluster backup_clusters = { NULL, NULL }; 2233987da915Sopenharmony_ci 2234987da915Sopenharmony_cistatic int device_offset_valid(int fd, s64 ofs) 2235987da915Sopenharmony_ci{ 2236987da915Sopenharmony_ci char ch; 2237987da915Sopenharmony_ci 2238987da915Sopenharmony_ci if (lseek(fd, ofs, SEEK_SET) >= 0 && read(fd, &ch, 1) == 1) 2239987da915Sopenharmony_ci return 0; 2240987da915Sopenharmony_ci return -1; 2241987da915Sopenharmony_ci} 2242987da915Sopenharmony_ci 2243987da915Sopenharmony_cistatic s64 device_size_get(int fd) 2244987da915Sopenharmony_ci{ 2245987da915Sopenharmony_ci s64 high, low; 2246987da915Sopenharmony_ci#ifdef BLKGETSIZE64 2247987da915Sopenharmony_ci { u64 size; 2248987da915Sopenharmony_ci 2249987da915Sopenharmony_ci if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { 2250987da915Sopenharmony_ci ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu " 2251987da915Sopenharmony_ci "(0x%llx).\n", (unsigned long long)size, 2252987da915Sopenharmony_ci (unsigned long long)size); 2253987da915Sopenharmony_ci return (s64)size; 2254987da915Sopenharmony_ci } 2255987da915Sopenharmony_ci } 2256987da915Sopenharmony_ci#endif 2257987da915Sopenharmony_ci#ifdef BLKGETSIZE 2258987da915Sopenharmony_ci { unsigned long size; 2259987da915Sopenharmony_ci 2260987da915Sopenharmony_ci if (ioctl(fd, BLKGETSIZE, &size) >= 0) { 2261987da915Sopenharmony_ci ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu " 2262987da915Sopenharmony_ci "(0x%lx).\n", size, size); 2263987da915Sopenharmony_ci return (s64)size * 512; 2264987da915Sopenharmony_ci } 2265987da915Sopenharmony_ci } 2266987da915Sopenharmony_ci#endif 2267987da915Sopenharmony_ci#ifdef FDGETPRM 2268987da915Sopenharmony_ci { struct floppy_struct this_floppy; 2269987da915Sopenharmony_ci 2270987da915Sopenharmony_ci if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { 2271987da915Sopenharmony_ci ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu " 2272987da915Sopenharmony_ci "(0x%lx).\n", this_floppy.size, 2273987da915Sopenharmony_ci this_floppy.size); 2274987da915Sopenharmony_ci return (s64)this_floppy.size * 512; 2275987da915Sopenharmony_ci } 2276987da915Sopenharmony_ci } 2277987da915Sopenharmony_ci#endif 2278987da915Sopenharmony_ci /* 2279987da915Sopenharmony_ci * We couldn't figure it out by using a specialized ioctl, 2280987da915Sopenharmony_ci * so do binary search to find the size of the device. 2281987da915Sopenharmony_ci */ 2282987da915Sopenharmony_ci low = 0LL; 2283987da915Sopenharmony_ci for (high = 1024LL; !device_offset_valid(fd, high); high <<= 1) 2284987da915Sopenharmony_ci low = high; 2285987da915Sopenharmony_ci while (low < high - 1LL) { 2286987da915Sopenharmony_ci const s64 mid = (low + high) / 2; 2287987da915Sopenharmony_ci 2288987da915Sopenharmony_ci if (!device_offset_valid(fd, mid)) 2289987da915Sopenharmony_ci low = mid; 2290987da915Sopenharmony_ci else 2291987da915Sopenharmony_ci high = mid; 2292987da915Sopenharmony_ci } 2293987da915Sopenharmony_ci lseek(fd, 0LL, SEEK_SET); 2294987da915Sopenharmony_ci return (low + 1LL); 2295987da915Sopenharmony_ci} 2296987da915Sopenharmony_ci 2297987da915Sopenharmony_cistatic void fsync_clone(int fd) 2298987da915Sopenharmony_ci{ 2299987da915Sopenharmony_ci Printf("Syncing ...\n"); 2300987da915Sopenharmony_ci if (opt.save_image && stream_out && fflush(stream_out)) 2301987da915Sopenharmony_ci perr_exit("fflush"); 2302987da915Sopenharmony_ci if (fsync(fd) && errno != EINVAL) 2303987da915Sopenharmony_ci perr_exit("fsync"); 2304987da915Sopenharmony_ci} 2305987da915Sopenharmony_ci 2306987da915Sopenharmony_cistatic void set_filesize(s64 filesize) 2307987da915Sopenharmony_ci{ 2308987da915Sopenharmony_ci#ifndef NO_STATFS 2309987da915Sopenharmony_ci long fs_type = 0; /* Unknown filesystem type */ 2310987da915Sopenharmony_ci 2311987da915Sopenharmony_ci if (fstatfs(fd_out, &opt.stfs) == -1) 2312987da915Sopenharmony_ci Printf("WARNING: Couldn't get filesystem type: " 2313987da915Sopenharmony_ci "%s\n", strerror(errno)); 2314987da915Sopenharmony_ci else 2315987da915Sopenharmony_ci fs_type = opt.stfs.f_type; 2316987da915Sopenharmony_ci 2317987da915Sopenharmony_ci if (fs_type == 0x52654973) 2318987da915Sopenharmony_ci Printf("WARNING: You're using ReiserFS, it has very poor " 2319987da915Sopenharmony_ci "performance creating\nlarge sparse files. The next " 2320987da915Sopenharmony_ci "operation might take a very long time!\n" 2321987da915Sopenharmony_ci "Creating sparse output file ...\n"); 2322987da915Sopenharmony_ci else if (fs_type == 0x517b) 2323987da915Sopenharmony_ci Printf("WARNING: You're using SMBFS and if the remote share " 2324987da915Sopenharmony_ci "isn't Samba but a Windows\ncomputer then the clone " 2325987da915Sopenharmony_ci "operation will be very inefficient and may fail!\n"); 2326987da915Sopenharmony_ci#endif 2327987da915Sopenharmony_ci 2328987da915Sopenharmony_ci if (!opt.no_action && (ftruncate(fd_out, filesize) == -1)) { 2329987da915Sopenharmony_ci int err = errno; 2330987da915Sopenharmony_ci perr_printf("ftruncate failed for file '%s'", opt.output); 2331987da915Sopenharmony_ci#ifndef NO_STATFS 2332987da915Sopenharmony_ci if (fs_type) 2333987da915Sopenharmony_ci Printf("Destination filesystem type is 0x%lx.\n", 2334987da915Sopenharmony_ci (unsigned long)fs_type); 2335987da915Sopenharmony_ci#endif 2336987da915Sopenharmony_ci if (err == E2BIG) { 2337987da915Sopenharmony_ci Printf("Your system or the destination filesystem " 2338987da915Sopenharmony_ci "doesn't support large files.\n"); 2339987da915Sopenharmony_ci#ifndef NO_STATFS 2340987da915Sopenharmony_ci if (fs_type == 0x517b) { 2341987da915Sopenharmony_ci Printf("SMBFS needs minimum Linux kernel " 2342987da915Sopenharmony_ci "version 2.4.25 and\n the 'lfs' option" 2343987da915Sopenharmony_ci "\nfor smbmount to have large " 2344987da915Sopenharmony_ci "file support.\n"); 2345987da915Sopenharmony_ci } 2346987da915Sopenharmony_ci#endif 2347987da915Sopenharmony_ci } else if (err == EPERM) { 2348987da915Sopenharmony_ci Printf("Apparently the destination filesystem doesn't " 2349987da915Sopenharmony_ci "support sparse files.\nYou can overcome this " 2350987da915Sopenharmony_ci "by using the more efficient --save-image " 2351987da915Sopenharmony_ci "option\nof ntfsclone. Use the --restore-image " 2352987da915Sopenharmony_ci "option to restore the image.\n"); 2353987da915Sopenharmony_ci } 2354987da915Sopenharmony_ci exit(1); 2355987da915Sopenharmony_ci } 2356987da915Sopenharmony_ci /* 2357987da915Sopenharmony_ci * If truncate just created a sparse file, the ability 2358987da915Sopenharmony_ci * to generically store big files has been checked, but no 2359987da915Sopenharmony_ci * space has been reserved and available space has probably 2360987da915Sopenharmony_ci * not been checked. Better reset the file so that we write 2361987da915Sopenharmony_ci * sequentially to the end. 2362987da915Sopenharmony_ci */ 2363987da915Sopenharmony_ci if (!opt.no_action) { 2364987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 2365987da915Sopenharmony_ci if (ftruncate(fd_out, 0)) 2366987da915Sopenharmony_ci Printf("Failed to reset the output file.\n"); 2367987da915Sopenharmony_ci#else 2368987da915Sopenharmony_ci struct stat st; 2369987da915Sopenharmony_ci int s; 2370987da915Sopenharmony_ci 2371987da915Sopenharmony_ci s = fstat(fd_out, &st); 2372987da915Sopenharmony_ci if (s || (!st.st_blocks && ftruncate(fd_out, 0))) 2373987da915Sopenharmony_ci Printf("Failed to reset the output file.\n"); 2374987da915Sopenharmony_ci#endif 2375987da915Sopenharmony_ci /* Proceed even if ftruncate failed */ 2376987da915Sopenharmony_ci } 2377987da915Sopenharmony_ci} 2378987da915Sopenharmony_ci 2379987da915Sopenharmony_cistatic s64 open_image(void) 2380987da915Sopenharmony_ci{ 2381987da915Sopenharmony_ci if (strcmp(opt.volume, "-") == 0) { 2382987da915Sopenharmony_ci if ((fd_in = fileno(stdin)) == -1) 2383987da915Sopenharmony_ci perr_exit("fileno for stdin failed"); 2384987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 2385987da915Sopenharmony_ci if (setmode(fd_in,O_BINARY) == -1) 2386987da915Sopenharmony_ci perr_exit("setting binary stdin failed"); 2387987da915Sopenharmony_ci#endif 2388987da915Sopenharmony_ci } else { 2389987da915Sopenharmony_ci if ((fd_in = open(opt.volume, O_RDONLY | O_BINARY)) == -1) 2390987da915Sopenharmony_ci perr_exit("failed to open image"); 2391987da915Sopenharmony_ci } 2392987da915Sopenharmony_ci if (read_all(&fd_in, &image_hdr, NTFSCLONE_IMG_HEADER_SIZE_OLD) == -1) 2393987da915Sopenharmony_ci perr_exit("read_all"); 2394987da915Sopenharmony_ci if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0) 2395987da915Sopenharmony_ci err_exit("Input file is not an image! (invalid magic)\n"); 2396987da915Sopenharmony_ci if (image_hdr.major_ver < NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE) { 2397987da915Sopenharmony_ci image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; 2398987da915Sopenharmony_ci image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; 2399987da915Sopenharmony_ci#if (__BYTE_ORDER == __BIG_ENDIAN) 2400987da915Sopenharmony_ci /* 2401987da915Sopenharmony_ci * old image read on a big endian computer, 2402987da915Sopenharmony_ci * assuming it was created big endian and read cpu-wise, 2403987da915Sopenharmony_ci * so we should translate to little endian 2404987da915Sopenharmony_ci */ 2405987da915Sopenharmony_ci Printf("Old image format detected. If the image was created " 2406987da915Sopenharmony_ci "on a little endian architecture it will not " 2407987da915Sopenharmony_ci "work. Use a more recent version of " 2408987da915Sopenharmony_ci "ntfsclone to recreate the image.\n"); 2409987da915Sopenharmony_ci image_hdr.cluster_size = cpu_to_le32(image_hdr.cluster_size); 2410987da915Sopenharmony_ci image_hdr.device_size = cpu_to_sle64(image_hdr.device_size); 2411987da915Sopenharmony_ci image_hdr.nr_clusters = cpu_to_sle64(image_hdr.nr_clusters); 2412987da915Sopenharmony_ci image_hdr.inuse = cpu_to_sle64(image_hdr.inuse); 2413987da915Sopenharmony_ci#endif 2414987da915Sopenharmony_ci image_hdr.offset_to_image_data = 2415987da915Sopenharmony_ci const_cpu_to_le32((sizeof(image_hdr) 2416987da915Sopenharmony_ci + IMAGE_HDR_ALIGN - 1) & -IMAGE_HDR_ALIGN); 2417987da915Sopenharmony_ci image_is_host_endian = TRUE; 2418987da915Sopenharmony_ci } else { 2419987da915Sopenharmony_ci /* safe image : little endian data */ 2420987da915Sopenharmony_ci le32 offset_to_image_data; 2421987da915Sopenharmony_ci int delta; 2422987da915Sopenharmony_ci 2423987da915Sopenharmony_ci if (image_hdr.major_ver > NTFSCLONE_IMG_VER_MAJOR) 2424987da915Sopenharmony_ci err_exit("Do not know how to handle image format " 2425987da915Sopenharmony_ci "version %d.%d. Please obtain a " 2426987da915Sopenharmony_ci "newer version of ntfsclone.\n", 2427987da915Sopenharmony_ci image_hdr.major_ver, 2428987da915Sopenharmony_ci image_hdr.minor_ver); 2429987da915Sopenharmony_ci /* Read the image header data offset. */ 2430987da915Sopenharmony_ci if (read_all(&fd_in, &offset_to_image_data, 2431987da915Sopenharmony_ci sizeof(offset_to_image_data)) == -1) 2432987da915Sopenharmony_ci perr_exit("read_all"); 2433987da915Sopenharmony_ci /* do not translate little endian data */ 2434987da915Sopenharmony_ci image_hdr.offset_to_image_data = offset_to_image_data; 2435987da915Sopenharmony_ci /* 2436987da915Sopenharmony_ci * Read any fields from the header that we have not read yet so 2437987da915Sopenharmony_ci * that the input stream is positioned correctly. This means 2438987da915Sopenharmony_ci * we can support future minor versions that just extend the 2439987da915Sopenharmony_ci * header in a backwards compatible way. 2440987da915Sopenharmony_ci */ 2441987da915Sopenharmony_ci delta = le32_to_cpu(offset_to_image_data) 2442987da915Sopenharmony_ci - (NTFSCLONE_IMG_HEADER_SIZE_OLD + 2443987da915Sopenharmony_ci sizeof(image_hdr.offset_to_image_data)); 2444987da915Sopenharmony_ci if (delta > 0) { 2445987da915Sopenharmony_ci char *dummy_buf; 2446987da915Sopenharmony_ci 2447987da915Sopenharmony_ci dummy_buf = malloc(delta); 2448987da915Sopenharmony_ci if (!dummy_buf) 2449987da915Sopenharmony_ci perr_exit("malloc dummy_buffer"); 2450987da915Sopenharmony_ci if (read_all(&fd_in, dummy_buf, delta) == -1) 2451987da915Sopenharmony_ci perr_exit("read_all"); 2452987da915Sopenharmony_ci free(dummy_buf); 2453987da915Sopenharmony_ci } 2454987da915Sopenharmony_ci } 2455987da915Sopenharmony_ci return le64_to_cpu(image_hdr.device_size); 2456987da915Sopenharmony_ci} 2457987da915Sopenharmony_ci 2458987da915Sopenharmony_cistatic s64 open_volume(void) 2459987da915Sopenharmony_ci{ 2460987da915Sopenharmony_ci s64 device_size; 2461987da915Sopenharmony_ci 2462987da915Sopenharmony_ci mount_volume(NTFS_MNT_RDONLY); 2463987da915Sopenharmony_ci 2464987da915Sopenharmony_ci device_size = ntfs_device_size_get(vol->dev, 1); 2465987da915Sopenharmony_ci if (device_size <= 0) 2466987da915Sopenharmony_ci err_exit("Couldn't get device size (%lld)!\n", 2467987da915Sopenharmony_ci (long long)device_size); 2468987da915Sopenharmony_ci 2469987da915Sopenharmony_ci print_volume_size("Current device size", device_size); 2470987da915Sopenharmony_ci 2471987da915Sopenharmony_ci if (device_size < vol->nr_clusters * vol->cluster_size) 2472987da915Sopenharmony_ci err_exit("Current NTFS volume size is bigger than the device " 2473987da915Sopenharmony_ci "size (%lld)!\nCorrupt partition table or incorrect " 2474987da915Sopenharmony_ci "device partitioning?\n", (long long)device_size); 2475987da915Sopenharmony_ci 2476987da915Sopenharmony_ci return device_size; 2477987da915Sopenharmony_ci} 2478987da915Sopenharmony_ci 2479987da915Sopenharmony_cistatic void initialise_image_hdr(s64 device_size, s64 inuse) 2480987da915Sopenharmony_ci{ 2481987da915Sopenharmony_ci memcpy(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE); 2482987da915Sopenharmony_ci image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; 2483987da915Sopenharmony_ci image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; 2484987da915Sopenharmony_ci image_hdr.cluster_size = cpu_to_le32(vol->cluster_size); 2485987da915Sopenharmony_ci image_hdr.device_size = cpu_to_le64(device_size); 2486987da915Sopenharmony_ci image_hdr.nr_clusters = cpu_to_sle64(vol->nr_clusters); 2487987da915Sopenharmony_ci image_hdr.inuse = cpu_to_le64(inuse); 2488987da915Sopenharmony_ci image_hdr.offset_to_image_data = cpu_to_le32((sizeof(image_hdr) 2489987da915Sopenharmony_ci + IMAGE_HDR_ALIGN - 1) & -IMAGE_HDR_ALIGN); 2490987da915Sopenharmony_ci} 2491987da915Sopenharmony_ci 2492987da915Sopenharmony_cistatic void check_output_device(s64 input_size) 2493987da915Sopenharmony_ci{ 2494987da915Sopenharmony_ci if (opt.blkdev_out) { 2495987da915Sopenharmony_ci s64 dest_size; 2496987da915Sopenharmony_ci 2497987da915Sopenharmony_ci if (dev_out) 2498987da915Sopenharmony_ci dest_size = ntfs_device_size_get(dev_out, 1); 2499987da915Sopenharmony_ci else 2500987da915Sopenharmony_ci dest_size = device_size_get(fd_out); 2501987da915Sopenharmony_ci if (dest_size < input_size) 2502987da915Sopenharmony_ci err_exit("Output device is too small (%lld) to fit the " 2503987da915Sopenharmony_ci "NTFS image (%lld).\n", 2504987da915Sopenharmony_ci (long long)dest_size, (long long)input_size); 2505987da915Sopenharmony_ci 2506987da915Sopenharmony_ci check_if_mounted(opt.output, 0); 2507987da915Sopenharmony_ci } else 2508987da915Sopenharmony_ci set_filesize(input_size); 2509987da915Sopenharmony_ci} 2510987da915Sopenharmony_ci 2511987da915Sopenharmony_cistatic void ignore_bad_clusters(ntfs_walk_clusters_ctx *image) 2512987da915Sopenharmony_ci{ 2513987da915Sopenharmony_ci ntfs_inode *ni; 2514987da915Sopenharmony_ci ntfs_attr *na; 2515987da915Sopenharmony_ci runlist *rl; 2516987da915Sopenharmony_ci s64 nr_bad_clusters = 0; 2517987da915Sopenharmony_ci static le16 Bad[4] = { 2518987da915Sopenharmony_ci const_cpu_to_le16('$'), const_cpu_to_le16('B'), 2519987da915Sopenharmony_ci const_cpu_to_le16('a'), const_cpu_to_le16('d') 2520987da915Sopenharmony_ci } ; 2521987da915Sopenharmony_ci 2522987da915Sopenharmony_ci if (!(ni = ntfs_inode_open(vol, FILE_BadClus))) 2523987da915Sopenharmony_ci perr_exit("ntfs_open_inode"); 2524987da915Sopenharmony_ci 2525987da915Sopenharmony_ci na = ntfs_attr_open(ni, AT_DATA, Bad, 4); 2526987da915Sopenharmony_ci if (!na) 2527987da915Sopenharmony_ci perr_exit("ntfs_attr_open"); 2528987da915Sopenharmony_ci if (ntfs_attr_map_whole_runlist(na)) 2529987da915Sopenharmony_ci perr_exit("ntfs_attr_map_whole_runlist"); 2530987da915Sopenharmony_ci 2531987da915Sopenharmony_ci for (rl = na->rl; rl->length; rl++) { 2532987da915Sopenharmony_ci s64 lcn = rl->lcn; 2533987da915Sopenharmony_ci 2534987da915Sopenharmony_ci if (lcn == LCN_HOLE || lcn < 0) 2535987da915Sopenharmony_ci continue; 2536987da915Sopenharmony_ci 2537987da915Sopenharmony_ci for (; lcn < rl->lcn + rl->length; lcn++, nr_bad_clusters++) { 2538987da915Sopenharmony_ci if (ntfs_bit_get_and_set(lcn_bitmap.bm, lcn, 0)) 2539987da915Sopenharmony_ci image->inuse--; 2540987da915Sopenharmony_ci } 2541987da915Sopenharmony_ci } 2542987da915Sopenharmony_ci if (nr_bad_clusters) 2543987da915Sopenharmony_ci Printf("WARNING: The disk has %lld or more bad sectors" 2544987da915Sopenharmony_ci " (hardware faults).\n", (long long)nr_bad_clusters); 2545987da915Sopenharmony_ci ntfs_attr_close(na); 2546987da915Sopenharmony_ci if (ntfs_inode_close(ni)) 2547987da915Sopenharmony_ci perr_exit("ntfs_inode_close failed for $BadClus"); 2548987da915Sopenharmony_ci} 2549987da915Sopenharmony_ci 2550987da915Sopenharmony_cistatic void check_dest_free_space(u64 src_bytes) 2551987da915Sopenharmony_ci{ 2552987da915Sopenharmony_ci#ifndef HAVE_WINDOWS_H 2553987da915Sopenharmony_ci u64 dest_bytes; 2554987da915Sopenharmony_ci struct statvfs stvfs; 2555987da915Sopenharmony_ci struct stat st; 2556987da915Sopenharmony_ci 2557987da915Sopenharmony_ci if (opt.metadata || opt.blkdev_out || opt.std_out) 2558987da915Sopenharmony_ci return; 2559987da915Sopenharmony_ci /* 2560987da915Sopenharmony_ci * TODO: save_image needs a bit more space than src_bytes 2561987da915Sopenharmony_ci * due to the free space encoding overhead. 2562987da915Sopenharmony_ci */ 2563987da915Sopenharmony_ci if (fstatvfs(fd_out, &stvfs) == -1) { 2564987da915Sopenharmony_ci Printf("WARNING: Unknown free space on the destination: %s\n", 2565987da915Sopenharmony_ci strerror(errno)); 2566987da915Sopenharmony_ci return; 2567987da915Sopenharmony_ci } 2568987da915Sopenharmony_ci 2569987da915Sopenharmony_ci /* If file is a FIFO then there is no point in checking the size. */ 2570987da915Sopenharmony_ci if (!fstat(fd_out, &st)) { 2571987da915Sopenharmony_ci if (S_ISFIFO(st.st_mode)) 2572987da915Sopenharmony_ci return; 2573987da915Sopenharmony_ci } else 2574987da915Sopenharmony_ci Printf("WARNING: fstat failed: %s\n", strerror(errno)); 2575987da915Sopenharmony_ci 2576987da915Sopenharmony_ci dest_bytes = (u64)stvfs.f_frsize * stvfs.f_bfree; 2577987da915Sopenharmony_ci if (!dest_bytes) 2578987da915Sopenharmony_ci dest_bytes = (u64)stvfs.f_bsize * stvfs.f_bfree; 2579987da915Sopenharmony_ci 2580987da915Sopenharmony_ci if (dest_bytes < src_bytes) 2581987da915Sopenharmony_ci err_exit("Destination doesn't have enough free space: " 2582987da915Sopenharmony_ci "%llu MB < %llu MB\n", 2583987da915Sopenharmony_ci (unsigned long long)rounded_up_division(dest_bytes, NTFS_MBYTE), 2584987da915Sopenharmony_ci (unsigned long long)rounded_up_division(src_bytes, NTFS_MBYTE)); 2585987da915Sopenharmony_ci#endif 2586987da915Sopenharmony_ci} 2587987da915Sopenharmony_ci 2588987da915Sopenharmony_ciint main(int argc, char **argv) 2589987da915Sopenharmony_ci{ 2590987da915Sopenharmony_ci ntfs_walk_clusters_ctx image; 2591987da915Sopenharmony_ci s64 device_size; /* input device size in bytes */ 2592987da915Sopenharmony_ci s64 ntfs_size; 2593987da915Sopenharmony_ci unsigned int wiped_total = 0; 2594987da915Sopenharmony_ci 2595987da915Sopenharmony_ci /* make sure the layout of header is not affected by alignments */ 2596987da915Sopenharmony_ci if (offsetof(struct image_hdr, offset_to_image_data) 2597987da915Sopenharmony_ci != IMAGE_OFFSET_OFFSET) { 2598987da915Sopenharmony_ci fprintf(stderr,"ntfsclone is not compiled properly. " 2599987da915Sopenharmony_ci "Please fix\n"); 2600987da915Sopenharmony_ci exit(1); 2601987da915Sopenharmony_ci } 2602987da915Sopenharmony_ci /* print to stderr, stdout can be an NTFS image ... */ 2603987da915Sopenharmony_ci fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); 2604987da915Sopenharmony_ci msg_out = stderr; 2605987da915Sopenharmony_ci 2606987da915Sopenharmony_ci parse_options(argc, argv); 2607987da915Sopenharmony_ci 2608987da915Sopenharmony_ci utils_set_locale(); 2609987da915Sopenharmony_ci 2610987da915Sopenharmony_ci if (opt.restore_image) { 2611987da915Sopenharmony_ci device_size = open_image(); 2612987da915Sopenharmony_ci ntfs_size = sle64_to_cpu(image_hdr.nr_clusters) * 2613987da915Sopenharmony_ci le32_to_cpu(image_hdr.cluster_size); 2614987da915Sopenharmony_ci } else { 2615987da915Sopenharmony_ci device_size = open_volume(); 2616987da915Sopenharmony_ci ntfs_size = vol->nr_clusters * vol->cluster_size; 2617987da915Sopenharmony_ci } 2618987da915Sopenharmony_ci // FIXME: This needs to be the cluster size... 2619987da915Sopenharmony_ci ntfs_size += 512; /* add backup boot sector */ 2620987da915Sopenharmony_ci full_device_size = device_size; 2621987da915Sopenharmony_ci 2622987da915Sopenharmony_ci if (opt.std_out) { 2623987da915Sopenharmony_ci if ((fd_out = fileno(stdout)) == -1) 2624987da915Sopenharmony_ci perr_exit("fileno for stdout failed"); 2625987da915Sopenharmony_ci stream_out = stdout; 2626987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 2627987da915Sopenharmony_ci if (setmode(fileno(stdout),O_BINARY) == -1) 2628987da915Sopenharmony_ci perr_exit("setting binary stdout failed"); 2629987da915Sopenharmony_ci#endif 2630987da915Sopenharmony_ci } else { 2631987da915Sopenharmony_ci /* device_size_get() might need to read() */ 2632987da915Sopenharmony_ci int flags = O_RDWR | O_BINARY; 2633987da915Sopenharmony_ci 2634987da915Sopenharmony_ci fd_out = 0; 2635987da915Sopenharmony_ci if (!opt.blkdev_out) { 2636987da915Sopenharmony_ci flags |= O_CREAT | O_TRUNC; 2637987da915Sopenharmony_ci if (!opt.overwrite) 2638987da915Sopenharmony_ci flags |= O_EXCL; 2639987da915Sopenharmony_ci } 2640987da915Sopenharmony_ci 2641987da915Sopenharmony_ci if (opt.save_image || opt.metadata_image) { 2642987da915Sopenharmony_ci stream_out = fopen(opt.output,BINWMODE); 2643987da915Sopenharmony_ci if (!stream_out) 2644987da915Sopenharmony_ci perr_exit("Opening file '%s' failed", 2645987da915Sopenharmony_ci opt.output); 2646987da915Sopenharmony_ci fd_out = fileno(stream_out); 2647987da915Sopenharmony_ci } else { 2648987da915Sopenharmony_ci#ifdef HAVE_WINDOWS_H 2649987da915Sopenharmony_ci if (!opt.no_action) { 2650987da915Sopenharmony_ci dev_out = ntfs_device_alloc(opt.output, 0, 2651987da915Sopenharmony_ci &ntfs_device_default_io_ops, NULL); 2652987da915Sopenharmony_ci if (!dev_out 2653987da915Sopenharmony_ci || (dev_out->d_ops->open)(dev_out, flags)) 2654987da915Sopenharmony_ci perr_exit("Opening volume '%s' failed", 2655987da915Sopenharmony_ci opt.output); 2656987da915Sopenharmony_ci } 2657987da915Sopenharmony_ci#else 2658987da915Sopenharmony_ci if (!opt.no_action 2659987da915Sopenharmony_ci && ((fd_out = open(opt.output, flags, 2660987da915Sopenharmony_ci S_IRUSR | S_IWUSR)) == -1)) 2661987da915Sopenharmony_ci perr_exit("Opening file '%s' failed", 2662987da915Sopenharmony_ci opt.output); 2663987da915Sopenharmony_ci#endif 2664987da915Sopenharmony_ci } 2665987da915Sopenharmony_ci 2666987da915Sopenharmony_ci if (!opt.save_image && !opt.metadata_image && !opt.no_action) 2667987da915Sopenharmony_ci check_output_device(ntfs_size); 2668987da915Sopenharmony_ci } 2669987da915Sopenharmony_ci 2670987da915Sopenharmony_ci if (opt.restore_image) { 2671987da915Sopenharmony_ci print_image_info(); 2672987da915Sopenharmony_ci restore_image(); 2673987da915Sopenharmony_ci if (!opt.no_action) 2674987da915Sopenharmony_ci fsync_clone(fd_out); 2675987da915Sopenharmony_ci exit(0); 2676987da915Sopenharmony_ci } 2677987da915Sopenharmony_ci 2678987da915Sopenharmony_ci setup_lcn_bitmap(); 2679987da915Sopenharmony_ci memset(&image, 0, sizeof(image)); 2680987da915Sopenharmony_ci backup_clusters.image = ℑ 2681987da915Sopenharmony_ci 2682987da915Sopenharmony_ci walk_clusters(vol, &backup_clusters); 2683987da915Sopenharmony_ci image.more_use = compare_bitmaps(&lcn_bitmap, 2684987da915Sopenharmony_ci opt.metadata && !opt.metadata_image); 2685987da915Sopenharmony_ci print_disk_usage("", vol->cluster_size, vol->nr_clusters, image.inuse); 2686987da915Sopenharmony_ci 2687987da915Sopenharmony_ci check_dest_free_space(vol->cluster_size * image.inuse); 2688987da915Sopenharmony_ci 2689987da915Sopenharmony_ci ignore_bad_clusters(&image); 2690987da915Sopenharmony_ci 2691987da915Sopenharmony_ci if (opt.save_image) 2692987da915Sopenharmony_ci initialise_image_hdr(device_size, image.inuse); 2693987da915Sopenharmony_ci 2694987da915Sopenharmony_ci if ((opt.std_out && !opt.metadata_image) || !opt.metadata) { 2695987da915Sopenharmony_ci s64 nr_clusters_to_save = image.inuse; 2696987da915Sopenharmony_ci if (opt.std_out && !opt.save_image) 2697987da915Sopenharmony_ci nr_clusters_to_save = vol->nr_clusters; 2698987da915Sopenharmony_ci nr_clusters_to_save++; /* account for the backup boot sector */ 2699987da915Sopenharmony_ci 2700987da915Sopenharmony_ci clone_ntfs(nr_clusters_to_save, image.more_use); 2701987da915Sopenharmony_ci fsync_clone(fd_out); 2702987da915Sopenharmony_ci if (opt.save_image) 2703987da915Sopenharmony_ci fclose(stream_out); 2704987da915Sopenharmony_ci ntfs_umount(vol,FALSE); 2705987da915Sopenharmony_ci free(lcn_bitmap.bm); 2706987da915Sopenharmony_ci exit(0); 2707987da915Sopenharmony_ci } 2708987da915Sopenharmony_ci 2709987da915Sopenharmony_ci wipe = 1; 2710987da915Sopenharmony_ci if (opt.metadata_image) { 2711987da915Sopenharmony_ci initialise_image_hdr(device_size, image.inuse); 2712987da915Sopenharmony_ci write_image_hdr(); 2713987da915Sopenharmony_ci } else { 2714987da915Sopenharmony_ci if (dev_out) { 2715987da915Sopenharmony_ci (dev_out->d_ops->close)(dev_out); 2716987da915Sopenharmony_ci dev_out = NULL; 2717987da915Sopenharmony_ci } else 2718987da915Sopenharmony_ci fsync_clone(fd_out); /* sync copy before mounting */ 2719987da915Sopenharmony_ci opt.volume = opt.output; 2720987da915Sopenharmony_ci /* 'force' again mount for dirty volumes (e.g. after resize). 2721987da915Sopenharmony_ci FIXME: use mount flags to avoid potential side-effects in future */ 2722987da915Sopenharmony_ci opt.force++; 2723987da915Sopenharmony_ci ntfs_umount(vol,FALSE); 2724987da915Sopenharmony_ci mount_volume(0 /*NTFS_MNT_NOATIME*/); 2725987da915Sopenharmony_ci } 2726987da915Sopenharmony_ci 2727987da915Sopenharmony_ci free(lcn_bitmap.bm); 2728987da915Sopenharmony_ci setup_lcn_bitmap(); 2729987da915Sopenharmony_ci memset(&image, 0, sizeof(image)); 2730987da915Sopenharmony_ci backup_clusters.image = ℑ 2731987da915Sopenharmony_ci 2732987da915Sopenharmony_ci walk_clusters(vol, &backup_clusters); 2733987da915Sopenharmony_ci 2734987da915Sopenharmony_ci Printf("Num of MFT records = %10lld\n", 2735987da915Sopenharmony_ci (long long)vol->mft_na->initialized_size >> 2736987da915Sopenharmony_ci vol->mft_record_size_bits); 2737987da915Sopenharmony_ci Printf("Num of used MFT records = %10u\n", nr_used_mft_records); 2738987da915Sopenharmony_ci 2739987da915Sopenharmony_ci Printf("Wiped unused MFT data = %10u\n", wiped_unused_mft_data); 2740987da915Sopenharmony_ci Printf("Wiped deleted MFT data = %10u\n", wiped_unused_mft); 2741987da915Sopenharmony_ci Printf("Wiped resident user data = %10u\n", wiped_resident_data); 2742987da915Sopenharmony_ci Printf("Wiped timestamp data = %10u\n", wiped_timestamp_data); 2743987da915Sopenharmony_ci 2744987da915Sopenharmony_ci wiped_total += wiped_unused_mft_data; 2745987da915Sopenharmony_ci wiped_total += wiped_unused_mft; 2746987da915Sopenharmony_ci wiped_total += wiped_resident_data; 2747987da915Sopenharmony_ci wiped_total += wiped_timestamp_data; 2748987da915Sopenharmony_ci Printf("Wiped totally = %10u\n", wiped_total); 2749987da915Sopenharmony_ci 2750987da915Sopenharmony_ci if (opt.metadata_image) 2751987da915Sopenharmony_ci fclose(stream_out); 2752987da915Sopenharmony_ci else 2753987da915Sopenharmony_ci fsync_clone(fd_out); 2754987da915Sopenharmony_ci ntfs_umount(vol,FALSE); 2755987da915Sopenharmony_ci free(lcn_bitmap.bm); 2756987da915Sopenharmony_ci return (0); 2757987da915Sopenharmony_ci} 2758