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 = &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, &current_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, &current_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 = &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 = &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