xref: /onnv-gate/usr/src/cmd/ntfsprogs/ntfsundelete.c (revision 9663:ace9a2ac3683)
1 /**
2  * ntfsundelete - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2002-2005 Richard Russon
5  * Copyright (c) 2004-2005 Holger Ohmacht
6  * Copyright (c) 2005      Anton Altaparmakov
7  * Copyright (c) 2007      Yura Pakhuchiy
8  *
9  * This utility will recover deleted files from an NTFS volume.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program (in the main directory of the Linux-NTFS
23  * distribution in the file COPYING); if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  */
26 
27 #include "config.h"
28 
29 #ifdef HAVE_FEATURES_H
30 #include <features.h>
31 #endif
32 #ifdef HAVE_STDIO_H
33 #include <stdio.h>
34 #endif
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38 #ifdef HAVE_STRING_H
39 #include <string.h>
40 #endif
41 #ifdef HAVE_ERRNO_H
42 #include <errno.h>
43 #endif
44 #ifdef HAVE_SYS_TYPES_H
45 #include <sys/types.h>
46 #endif
47 #ifdef HAVE_SYS_STAT_H
48 #include <sys/stat.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 #ifdef HAVE_FCNTL_H
54 #include <fcntl.h>
55 #endif
56 #ifdef HAVE_GETOPT_H
57 #include <getopt.h>
58 #endif
59 #ifdef HAVE_TIME_H
60 #include <time.h>
61 #endif
62 #ifdef HAVE_LIMITS_H
63 #include <limits.h>
64 #endif
65 #ifdef HAVE_STDARG_H
66 #include <stdarg.h>
67 #endif
68 #ifdef HAVE_UTIME_H
69 #include <utime.h>
70 #endif
71 #include <regex.h>
72 
73 #if !defined(REG_NOERROR) || (REG_NOERROR != 0)
74 #define REG_NOERROR 0
75 #endif
76 
77 #include "ntfsundelete.h"
78 #include "bootsect.h"
79 #include "mft.h"
80 #include "attrib.h"
81 #include "layout.h"
82 #include "inode.h"
83 #include "device.h"
84 #include "utils.h"
85 #include "debug.h"
86 #include "ntfstime.h"
87 #include "version.h"
88 #include "logging.h"
89 
90 static const char *EXEC_NAME = "ntfsundelete";
91 static const char *MFTFILE   = "mft";
92 static const char *UNNAMED   = "<unnamed>";
93 static const char *NONE      = "<none>";
94 static const char *UNKNOWN   = "unknown";
95 static struct options opts;
96 
97 typedef struct
98 {
99 	u32 begin;
100 	u32 end;
101 } range;
102 
103 static short	with_regex;			/* Flag  Regular expression available */
104 static short	avoid_duplicate_printing;	/* Flag  No duplicate printing of file infos */
105 static range	*ranges;			/* Array containing all Inode-Ranges for undelete */
106 static long	nr_entries;			/* Number of range entries */
107 
108 /**
109  * parse_inode_arg - parses the inode expression
110  *
111  * Parses the optarg after parameter -u for valid ranges
112  *
113  * Return: Number of correct inode specifications or -1 for error
114  */
115 static int parse_inode_arg(void)
116 {
117 	int p;
118 	u32 imax;
119 	u32 range_begin;
120 	u32 range_end;
121 	u32 range_temp;
122 	u32 inode;
123 	char *opt_arg_ptr;
124 	char *opt_arg_temp;
125 	char *opt_arg_end1;
126 	char *opt_arg_end2;
127 
128 	/* Check whether optarg is available or not */
129 	nr_entries = 0;
130 	if (optarg == NULL)
131 		return (0);	/* bailout if no optarg */
132 
133 	/* init variables */
134 	p = strlen(optarg);
135 	imax = p;
136 	opt_arg_ptr = optarg;
137 	opt_arg_end1 = optarg;
138 	opt_arg_end2 = &(optarg[p]);
139 
140 	/* alloc mem for range table */
141 	ranges = (range *) malloc((p + 1) * sizeof(range));
142 	if (ranges == NULL) {
143 		ntfs_log_error("ERROR: Couldn't alloc mem for parsing inodes!\n");
144 		return (-1);
145 	}
146 
147 	/* loop */
148 	while ((opt_arg_end1 != opt_arg_end2) && (p > 0)) {
149 		/* Try to get inode */
150 		inode = strtoul(opt_arg_ptr, &opt_arg_end1, 0);
151 		p--;
152 
153 		/* invalid char at begin */
154 		if ((opt_arg_ptr == opt_arg_end1) || (opt_arg_ptr == opt_arg_end2)) {
155 			ntfs_log_error("ERROR: Invalid Number: %s\n", opt_arg_ptr);
156 			return (-1);
157 		}
158 
159 		/* RANGE - Check for range */
160 		if (opt_arg_end1[0] == '-') {
161 			/* get range end */
162 			opt_arg_temp = opt_arg_end1;
163 			opt_arg_end1 = & (opt_arg_temp[1]);
164 			if (opt_arg_temp >= opt_arg_end2) {
165 				ntfs_log_error("ERROR: Missing range end!\n");
166 				return (-1);
167 			}
168 			range_begin = inode;
169 
170 			/* get count */
171 			range_end = strtoul(opt_arg_end1, &opt_arg_temp, 0);
172 			if (opt_arg_temp == opt_arg_end1) {
173 				ntfs_log_error("ERROR: Invalid Number: %s\n", opt_arg_temp);
174 				return (-1);
175 			}
176 
177 			/* check for correct values */
178 			if (range_begin > range_end) {
179 				range_temp = range_end;
180 				range_end = range_begin;
181 				range_begin = range_temp;
182 			}
183 
184 			/* put into struct */
185 			ranges[nr_entries].begin = range_begin;
186 			ranges[nr_entries].end = range_end;
187 			nr_entries++;
188 
189 			/* Last check */
190 			opt_arg_ptr = & (opt_arg_temp[1]);
191 			if (opt_arg_ptr >= opt_arg_end2)
192 				break;
193 		} else if (opt_arg_end1[0] == ',') {
194 			/* SINGLE VALUE, BUT CONTINUING */
195 			/* put inode into range list */
196 			ranges[nr_entries].begin = inode;
197 			ranges[nr_entries].end = inode;
198 			nr_entries++;
199 
200 			/* Next inode */
201 			opt_arg_ptr = & (opt_arg_end1[1]);
202 			if (opt_arg_ptr >= opt_arg_end2) {
203 				ntfs_log_error("ERROR: Missing new value at end of input!\n");
204 				return (-1);
205 			}
206 			continue;
207 		} else { /* SINGLE VALUE, END */
208 			ranges[nr_entries].begin = inode;
209 			ranges[nr_entries].end = inode;
210 			nr_entries++;
211 		}
212 	}
213 	return (nr_entries);
214 }
215 
216 /**
217  * version - Print version information about the program
218  *
219  * Print a copyright statement and a brief description of the program.
220  *
221  * Return:  none
222  */
223 static void version(void)
224 {
225 	ntfs_log_info("\n%s v%s (libntfs %s) - Recover deleted files from an "
226 			"NTFS Volume.\n\n", EXEC_NAME, VERSION,
227 			ntfs_libntfs_version());
228 	ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n"
229 			"Copyright (c) 2004-2005 Holger Ohmacht\n"
230 			"Copyright (c) 2005      Anton Altaparmakov\n"
231 			"Copyright (c) 2007      Yura Pakhuchiy\n");
232 	ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
233 }
234 
235 /**
236  * usage - Print a list of the parameters to the program
237  *
238  * Print a list of the parameters and options for the program.
239  *
240  * Return:  none
241  */
242 static void usage(void)
243 {
244 	ntfs_log_info("\nUsage: %s [options] device\n"
245 		"    -s, --scan             Scan for files (default)\n"
246 		"    -p, --percentage NUM   Minimum percentage recoverable\n"
247 		"    -m, --match PATTERN    Only work on files with matching names\n"
248 		"    -C, --case             Case sensitive matching\n"
249 		"    -S, --size RANGE       Match files of this size\n"
250 		"    -t, --time SINCE       Last referenced since this time\n"
251 		"\n"
252 		"    -u, --undelete         Undelete mode\n"
253 		"    -i, --inodes RANGE     Recover these inodes\n"
254 		//"    -I, --interactive      Interactive mode\n"
255 		"    -o, --output FILE      Save with this filename\n"
256 		"    -O, --optimistic       Undelete in-use clusters as well\n"
257 		"    -d, --destination DIR  Destination directory\n"
258 		"    -b, --byte NUM         Fill missing parts with this byte\n"
259 		"    -T, --truncate         Truncate 100%% recoverable file to exact size.\n"
260 		"    -P, --parent           Show parent directory\n"
261 		"\n"
262 		"    -c, --copy RANGE       Write a range of MFT records to a file\n"
263 		"\n"
264 		"    -f, --force            Use less caution\n"
265 		"    -q, --quiet            Less output\n"
266 		"    -v, --verbose          More output\n"
267 		"    -V, --version          Display version information\n"
268 		"    -h, --help             Display this help\n\n",
269 		EXEC_NAME);
270 	ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
271 }
272 
273 /**
274  * transform - Convert a shell style pattern to a regex
275  * @pattern:  String to be converted
276  * @regex:    Resulting regular expression is put here
277  *
278  * This will transform patterns, such as "*.doc" to true regular expressions.
279  * The function will also place '^' and '$' around the expression to make it
280  * behave as the user would expect
281  *
282  * Before  After
283  *   .       \.
284  *   *       .*
285  *   ?       .
286  *
287  * Notes:
288  *     The returned string must be freed by the caller.
289  *     If transform fails, @regex will not be changed.
290  *
291  * Return:  1, Success, the string was transformed
292  *	    0, An error occurred
293  */
294 static int transform(const char *pattern, char **regex)
295 {
296 	char *result;
297 	int length, i, j;
298 
299 	if (!pattern || !regex)
300 		return 0;
301 
302 	length = strlen(pattern);
303 	if (length < 1) {
304 		ntfs_log_error("Pattern to transform is empty\n");
305 		return 0;
306 	}
307 
308 	for (i = 0; pattern[i]; i++) {
309 		if ((pattern[i] == '*') || (pattern[i] == '.'))
310 			length++;
311 	}
312 
313 	result = malloc(length + 3);
314 	if (!result) {
315 		ntfs_log_error("Couldn't allocate memory in transform()\n");
316 		return 0;
317 	}
318 
319 	result[0] = '^';
320 
321 	for (i = 0, j = 1; pattern[i]; i++, j++) {
322 		if (pattern[i] == '*') {
323 			result[j] = '.';
324 			j++;
325 			result[j] = '*';
326 		} else if (pattern[i] == '.') {
327 			result[j] = '\\';
328 			j++;
329 			result[j] = '.';
330 		} else if (pattern[i] == '?') {
331 			result[j] = '.';
332 		} else {
333 			result[j] = pattern[i];
334 		}
335 	}
336 
337 	result[j]   = '$';
338 	result[j+1] = 0;
339 	ntfs_log_debug("Pattern '%s' replaced with regex '%s'.\n", pattern,
340 			result);
341 
342 	*regex = result;
343 	return 1;
344 }
345 
346 /**
347  * parse_time - Convert a time abbreviation to seconds
348  * @string:  The string to be converted
349  * @since:   The absolute time referred to
350  *
351  * Strings representing times will be converted into a time_t.  The numbers will
352  * be regarded as seconds unless suffixed.
353  *
354  * Suffix  Description
355  *  [yY]      Year
356  *  [mM]      Month
357  *  [wW]      Week
358  *  [dD]      Day
359  *  [sS]      Second
360  *
361  * Therefore, passing "1W" will return the time_t representing 1 week ago.
362  *
363  * Notes:
364  *     Only the first character of the suffix is read.
365  *     If parse_time fails, @since will not be changed
366  *
367  * Return:  1  Success
368  *	    0  Error, the string was malformed
369  */
370 static int parse_time(const char *value, time_t *since)
371 {
372 	long long result;
373 	time_t now;
374 	char *suffix = NULL;
375 
376 	if (!value || !since)
377 		return -1;
378 
379 	ntfs_log_trace("Parsing time '%s' ago.\n", value);
380 
381 	result = strtoll(value, &suffix, 10);
382 	if (result < 0 || errno == ERANGE) {
383 		ntfs_log_error("Invalid time '%s'.\n", value);
384 		return 0;
385 	}
386 
387 	if (!suffix) {
388 		ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
389 		return 0;
390 	}
391 
392 	if (strlen(suffix) > 1) {
393 		ntfs_log_error("Invalid time suffix '%s'.  Use Y, M, W, D or H.\n", suffix);
394 		return 0;
395 	}
396 
397 	switch (suffix[0]) {
398 		case 'y': case 'Y': result *=   12;
399 		case 'm': case 'M': result *=    4;
400 		case 'w': case 'W': result *=    7;
401 		case 'd': case 'D': result *=   24;
402 		case 'h': case 'H': result *= 3600;
403 		case 0:
404 		    break;
405 
406 		default:
407 			ntfs_log_error("Invalid time suffix '%s'.  Use Y, M, W, D or H.\n", suffix);
408 			return 0;
409 	}
410 
411 	now = time(NULL);
412 
413 	ntfs_log_debug("Time now = %lld, Time then = %lld.\n", (long long) now,
414 			(long long) result);
415 	*since = now - result;
416 	return 1;
417 }
418 
419 /**
420  * parse_options - Read and validate the programs command line
421  *
422  * Read the command line, verify the syntax and parse the options.
423  * This function is very long, but quite simple.
424  *
425  * Return:  1 Success
426  *	    0 Error, one or more problems
427  */
428 static int parse_options(int argc, char *argv[])
429 {
430 	static const char *sopt = "-b:Cc:d:fh?i:m:o:OPp:sS:t:TuqvV";
431 	static const struct option lopt[] = {
432 		{ "byte",	 required_argument,	NULL, 'b' },
433 		{ "case",	 no_argument,		NULL, 'C' },
434 		{ "copy",	 required_argument,	NULL, 'c' },
435 		{ "destination", required_argument,	NULL, 'd' },
436 		{ "force",	 no_argument,		NULL, 'f' },
437 		{ "help",	 no_argument,		NULL, 'h' },
438 		{ "inodes",	 required_argument,	NULL, 'i' },
439 		//{ "interactive", no_argument,		NULL, 'I' },
440 		{ "match",	 required_argument,	NULL, 'm' },
441 		{ "optimistic",  no_argument,		NULL, 'O' },
442 		{ "output",	 required_argument,	NULL, 'o' },
443 		{ "parent",	 no_argument,		NULL, 'P' },
444 		{ "percentage",  required_argument,	NULL, 'p' },
445 		{ "quiet",	 no_argument,		NULL, 'q' },
446 		{ "scan",	 no_argument,		NULL, 's' },
447 		{ "size",	 required_argument,	NULL, 'S' },
448 		{ "time",	 required_argument,	NULL, 't' },
449 		{ "truncate",	 no_argument,		NULL, 'T' },
450 		{ "undelete",	 no_argument,		NULL, 'u' },
451 		{ "verbose",	 no_argument,		NULL, 'v' },
452 		{ "version",	 no_argument,		NULL, 'V' },
453 		{ NULL, 0, NULL, 0 }
454 	};
455 
456 	int c = -1;
457 	char *end = NULL;
458 	int err  = 0;
459 	int ver  = 0;
460 	int help = 0;
461 	int levels = 0;
462 
463 	opterr = 0; /* We'll handle the errors, thank you. */
464 
465 	opts.mode     = MODE_NONE;
466 	opts.uinode   = -1;
467 	opts.percent  = -1;
468 	opts.fillbyte = -1;
469 	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
470 		switch (c) {
471 		case 1:	/* A non-option argument */
472 			if (!opts.device) {
473 				opts.device = argv[optind-1];
474 			} else {
475 				opts.device = NULL;
476 				err++;
477 			}
478 			break;
479 		case 'b':
480 			if (opts.fillbyte == (char)-1) {
481 				end = NULL;
482 				opts.fillbyte = strtol(optarg, &end, 0);
483 				if (end && *end)
484 					err++;
485 			} else {
486 				err++;
487 			}
488 			break;
489 		case 'C':
490 			opts.match_case++;
491 			break;
492 		case 'c':
493 			if (opts.mode == MODE_NONE) {
494 				if (!utils_parse_range(optarg,
495 				    &opts.mft_begin, &opts.mft_end, TRUE))
496 					err++;
497 				opts.mode = MODE_COPY;
498 			} else {
499 				opts.mode = MODE_ERROR;
500 			}
501 			break;
502 		case 'd':
503 			if (!opts.dest)
504 				opts.dest = optarg;
505 			else
506 				err++;
507 			break;
508 		case 'f':
509 			opts.force++;
510 			break;
511 		case 'h':
512 		case '?':
513 			if (ntfs_log_parse_option (argv[optind-1]))
514 				break;
515 			help++;
516 			break;
517 		case 'i':
518 			end = NULL;
519 			/* parse inodes */
520 			if (parse_inode_arg() == -1)
521 				err++;
522 			if (end && *end)
523 				err++;
524 			break;
525 		case 'm':
526 			if (!opts.match) {
527 				if (!transform(optarg, &opts.match)) {
528 					err++;
529 				} else {
530 					/* set regex-flag on true ;) */
531 					with_regex= 1;
532 				}
533 			} else {
534 				err++;
535 			}
536 			break;
537 		case 'o':
538 			if (!opts.output) {
539 				opts.output = optarg;
540 			} else {
541 				err++;
542 			}
543 			break;
544 		case 'O':
545 			if (!opts.optimistic) {
546 				opts.optimistic++;
547 			} else {
548 				err++;
549 			}
550 			break;
551 		case 'P':
552 			if (!opts.parent) {
553 				opts.parent++;
554 			} else {
555 				err++;
556 			}
557 			break;
558 		case 'p':
559 			if (opts.percent == -1) {
560 				end = NULL;
561 				opts.percent = strtol(optarg, &end, 0);
562 				if (end && ((*end != '%') && (*end != 0)))
563 					err++;
564 			} else {
565 				err++;
566 			}
567 			break;
568 		case 'q':
569 			opts.quiet++;
570 			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
571 			break;
572 		case 's':
573 			if (opts.mode == MODE_NONE)
574 				opts.mode = MODE_SCAN;
575 			else
576 				opts.mode = MODE_ERROR;
577 			break;
578 		case 'S':
579 			if ((opts.size_begin > 0) || (opts.size_end > 0) ||
580 			    !utils_parse_range(optarg, &opts.size_begin,
581 			     &opts.size_end, TRUE)) {
582 			    err++;
583 			}
584 			break;
585 		case 't':
586 			if (opts.since == 0) {
587 				if (!parse_time(optarg, &opts.since))
588 					err++;
589 			} else {
590 			    err++;
591 			}
592 			break;
593 		case 'T':
594 			opts.truncate++;
595 			break;
596 		case 'u':
597 			if (opts.mode == MODE_NONE) {
598 				opts.mode = MODE_UNDELETE;
599 			} else {
600 				opts.mode = MODE_ERROR;
601 			}
602 			break;
603 		case 'v':
604 			opts.verbose++;
605 			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
606 			break;
607 		case 'V':
608 			ver++;
609 			break;
610 		default:
611 			if (((optopt == 'b') || (optopt == 'c') ||
612 			     (optopt == 'd') || (optopt == 'm') ||
613 			     (optopt == 'o') || (optopt == 'p') ||
614 			     (optopt == 'S') || (optopt == 't') ||
615 			     (optopt == 'u')) && (!optarg)) {
616 				ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]);
617 			} else {
618 				ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
619 			}
620 			err++;
621 			break;
622 		}
623 	}
624 
625 	/* Make sure we're in sync with the log levels */
626 	levels = ntfs_log_get_levels();
627 	if (levels & NTFS_LOG_LEVEL_VERBOSE)
628 		opts.verbose++;
629 	if (!(levels & NTFS_LOG_LEVEL_QUIET))
630 		opts.quiet++;
631 
632 	if (help || ver) {
633 		opts.quiet = 0;
634 	} else {
635 		if (opts.device == NULL) {
636 			if (argc > 1)
637 				ntfs_log_error("You must specify exactly one device.\n");
638 			err++;
639 		}
640 
641 		if (opts.mode == MODE_NONE) {
642 			opts.mode = MODE_SCAN;
643 		}
644 
645 		switch (opts.mode) {
646 		case MODE_SCAN:
647 			if (opts.output || opts.dest || opts.truncate ||
648 					(opts.fillbyte != (char)-1)) {
649 				ntfs_log_error("Scan can only be used with --percent, "
650 					"--match, --ignore-case, --size and --time.\n");
651 				err++;
652 			}
653 			if (opts.match_case && !opts.match) {
654 				ntfs_log_error("The --case option doesn't make sense without the --match option\n");
655 				err++;
656 			}
657 			break;
658 
659 		case MODE_UNDELETE:
660 			/*if ((opts.percent != -1) || (opts.size_begin > 0) || (opts.size_end > 0)) {
661 				ntfs_log_error("Undelete can only be used with "
662 					"--output, --destination, --byte and --truncate.\n");
663 				err++;
664 			}*/
665 			break;
666 		case MODE_COPY:
667 			if ((opts.fillbyte != (char)-1) || opts.truncate ||
668 			    (opts.percent != -1) ||
669 			    opts.match || opts.match_case ||
670 			    (opts.size_begin > 0) ||
671 			    (opts.size_end > 0)) {
672 				ntfs_log_error("Copy can only be used with --output and --destination.\n");
673 				err++;
674 			}
675 			break;
676 		default:
677 			ntfs_log_error("You can only select one of Scan, Undelete or Copy.\n");
678 			err++;
679 		}
680 
681 		if ((opts.percent < -1) || (opts.percent > 100)) {
682 			ntfs_log_error("Percentage value must be in the range 0 - 100.\n");
683 			err++;
684 		}
685 
686 		if (opts.quiet) {
687 			if (opts.verbose) {
688 				ntfs_log_error("You may not use --quiet and --verbose at the same time.\n");
689 				err++;
690 			} else if (opts.mode == MODE_SCAN) {
691 				ntfs_log_error("You may not use --quiet when scanning a volume.\n");
692 				err++;
693 			}
694 		}
695 
696 		if (opts.parent && !opts.verbose) {
697 			ntfs_log_error("To use --parent, you must also use --verbose.\n");
698 			err++;
699 		}
700 	}
701 
702 	if (opts.fillbyte == (char)-1)
703 		opts.fillbyte = 0;
704 
705 	if (ver)
706 		version();
707 	if (help || err)
708 		usage();
709 
710 	return (!err && !help && !ver);
711 }
712 
713 /**
714  * free_file - Release the resources used by a file object
715  * @file:  The unwanted file object
716  *
717  * This will free up the memory used by a file object and iterate through the
718  * object's children, freeing their resources too.
719  *
720  * Return:  none
721  */
722 static void free_file(struct ufile *file)
723 {
724 	struct list_head *item, *tmp;
725 
726 	if (!file)
727 		return;
728 
729 	list_for_each_safe(item, tmp, &file->name) { /* List of filenames */
730 		struct filename *f = list_entry(item, struct filename, list);
731 		ntfs_log_debug("freeing filename '%s'", f->name ? f->name :
732 				NONE);
733 		if (f->name)
734 			free(f->name);
735 		if (f->parent_name) {
736 			ntfs_log_debug(" and parent filename '%s'",
737 					f->parent_name);
738 			free(f->parent_name);
739 		}
740 		ntfs_log_debug(".\n");
741 		free(f);
742 	}
743 
744 	list_for_each_safe(item, tmp, &file->data) { /* List of data streams */
745 		struct data *d = list_entry(item, struct data, list);
746 		ntfs_log_debug("Freeing data stream '%s'.\n", d->name ?
747 				d->name : UNNAMED);
748 		if (d->name)
749 			free(d->name);
750 		if (d->runlist)
751 			free(d->runlist);
752 		free(d);
753 	}
754 
755 	free(file->mft);
756 	free(file);
757 }
758 
759 /**
760  * verify_parent - confirm a record is parent of a file
761  * @name:	a filename of the file
762  * @rec:	the mft record of the possible parent
763  *
764  * Check that @rec is the parent of the file represented by @name.
765  * If @rec is a directory, but it is created after @name, then we
766  * can't determine whether @rec is really @name's parent.
767  *
768  * Return:	@rec's filename, either same name space as @name or lowest space.
769  *		NULL if can't determine parenthood or on error.
770  */
771 static FILE_NAME_ATTR* verify_parent(struct filename* name, MFT_RECORD* rec)
772 {
773 	ATTR_RECORD *attr30;
774 	FILE_NAME_ATTR *filename_attr = NULL, *lowest_space_name = NULL;
775 	ntfs_attr_search_ctx *ctx;
776 	int found_same_space = 1;
777 
778 	if (!name || !rec)
779 		return NULL;
780 
781 	if (!(rec->flags & MFT_RECORD_IS_DIRECTORY)) {
782 		return NULL;
783 	}
784 
785 	ctx = ntfs_attr_get_search_ctx(NULL, rec);
786 	if (!ctx) {
787 		ntfs_log_error("ERROR: Couldn't create a search context.\n");
788 		return NULL;
789 	}
790 
791 	attr30 = find_attribute(AT_FILE_NAME, ctx);
792 	if (!attr30) {
793 		return NULL;
794 	}
795 
796 	filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->u.res.value_offset));
797 	/* if name is older than this dir -> can't determine */
798 	if (ntfs2utc(filename_attr->creation_time) > name->date_c) {
799 		return NULL;
800 	}
801 
802 	if (filename_attr->file_name_type != name->name_space) {
803 		found_same_space = 0;
804 		lowest_space_name = filename_attr;
805 
806 		while (!found_same_space && (attr30 = find_attribute(AT_FILE_NAME, ctx))) {
807 			filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->u.res.value_offset));
808 
809 			if (filename_attr->file_name_type == name->name_space) {
810 				found_same_space = 1;
811 			} else {
812 				if (filename_attr->file_name_type < lowest_space_name->file_name_type) {
813 					lowest_space_name = filename_attr;
814 				}
815 			}
816 		}
817 	}
818 
819 	ntfs_attr_put_search_ctx(ctx);
820 
821 	return (found_same_space ? filename_attr : lowest_space_name);
822 }
823 
824 /**
825  * get_parent_name - Find the name of a file's parent.
826  * @name:	the filename whose parent's name to find
827  */
828 static void get_parent_name(struct filename* name, ntfs_volume* vol)
829 {
830 	ntfs_attr* mft_data;
831 	MFT_RECORD* rec;
832 	FILE_NAME_ATTR* filename_attr;
833 	long long inode_num;
834 
835 	if (!name || !vol)
836 		return;
837 
838 	rec = calloc(1, vol->mft_record_size);
839 	if (!rec) {
840 		ntfs_log_error("ERROR: Couldn't allocate memory in "
841 				"get_parent_name()\n");
842 		return;
843 	}
844 
845 	mft_data = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0);
846 	if (!mft_data) {
847 		ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA");
848 	} else {
849 		inode_num = MREF_LE(name->parent_mref);
850 
851 		if (ntfs_attr_pread(mft_data, vol->mft_record_size * inode_num,
852 					vol->mft_record_size, rec) < 1) {
853 			ntfs_log_error("ERROR: Couldn't read MFT Record %lld"
854 					".\n", inode_num);
855 		} else if ((filename_attr = verify_parent(name, rec))) {
856 			if (ntfs_ucstombs(filename_attr->file_name,
857 					filename_attr->file_name_length,
858 					&name->parent_name, 0) < 0) {
859 				ntfs_log_debug("ERROR: Couldn't translate "
860 						"filename to current "
861 						"locale.\n");
862 				name->parent_name = NULL;
863 			}
864 		}
865 	}
866 
867 	if (mft_data) {
868 		ntfs_attr_close(mft_data);
869 	}
870 
871 	if (rec) {
872 		free(rec);
873 	}
874 
875 	return;
876 }
877 
878 /**
879  * get_filenames - Read an MFT Record's $FILENAME attributes
880  * @file:  The file object to work with
881  *
882  * A single file may have more than one filename.  This is quite common.
883  * Windows creates a short DOS name for each long name, e.g. LONGFI~1.XYZ,
884  * LongFiLeName.xyZ.
885  *
886  * The filenames that are found are put in filename objects and added to a
887  * linked list of filenames in the file object.  For convenience, the unicode
888  * filename is converted into the current locale and stored in the filename
889  * object.
890  *
891  * One of the filenames is picked (the one with the lowest numbered namespace)
892  * and its locale friendly name is put in pref_name.
893  *
894  * Return:  n  The number of $FILENAME attributes found
895  *	   -1  Error
896  */
897 static int get_filenames(struct ufile *file, ntfs_volume* vol)
898 {
899 	ATTR_RECORD *rec;
900 	FILE_NAME_ATTR *attr;
901 	ntfs_attr_search_ctx *ctx;
902 	struct filename *name;
903 	int count = 0;
904 	int space = 4;
905 
906 	if (!file)
907 		return -1;
908 
909 	ctx = ntfs_attr_get_search_ctx(NULL, file->mft);
910 	if (!ctx)
911 		return -1;
912 
913 	while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
914 		/* We know this will always be resident. */
915 		attr = (FILE_NAME_ATTR *)((char *)rec +
916 				le16_to_cpu(rec->u.res.value_offset));
917 
918 		name = calloc(1, sizeof(*name));
919 		if (!name) {
920 			ntfs_log_error("ERROR: Couldn't allocate memory in "
921 					"get_filenames().\n");
922 			count = -1;
923 			break;
924 		}
925 
926 		name->uname      = attr->file_name;
927 		name->uname_len  = attr->file_name_length;
928 		name->name_space = attr->file_name_type;
929 		name->size_alloc = sle64_to_cpu(attr->allocated_size);
930 		name->size_data  = sle64_to_cpu(attr->data_size);
931 		name->flags      = attr->file_attributes;
932 
933 		name->date_c     = ntfs2utc(attr->creation_time);
934 		name->date_a     = ntfs2utc(attr->last_data_change_time);
935 		name->date_m     = ntfs2utc(attr->last_mft_change_time);
936 		name->date_r     = ntfs2utc(attr->last_access_time);
937 
938 		if (ntfs_ucstombs(name->uname, name->uname_len, &name->name,
939 				0) < 0) {
940 			ntfs_log_debug("ERROR: Couldn't translate filename to "
941 					"current locale.\n");
942 		}
943 
944 		name->parent_name = NULL;
945 
946 		if (opts.parent) {
947 			name->parent_mref = attr->parent_directory;
948 			get_parent_name(name, vol);
949 		}
950 
951 		if (name->name_space < space) {
952 			file->pref_name = name->name;
953 			file->pref_pname = name->parent_name;
954 			space = name->name_space;
955 		}
956 
957 		file->max_size = max(file->max_size, name->size_alloc);
958 		file->max_size = max(file->max_size, name->size_data);
959 
960 		list_add_tail(&name->list, &file->name);
961 		count++;
962 	}
963 
964 	ntfs_attr_put_search_ctx(ctx);
965 	ntfs_log_debug("File has %d names.\n", count);
966 	return count;
967 }
968 
969 /**
970  * get_data - Read an MFT Record's $DATA attributes
971  * @file:  The file object to work with
972  * @vol:  An ntfs volume obtained from ntfs_mount
973  *
974  * A file may have more than one data stream.  All files will have an unnamed
975  * data stream which contains the file's data.  Some Windows applications store
976  * extra information in a separate stream.
977  *
978  * The streams that are found are put in data objects and added to a linked
979  * list of data streams in the file object.
980  *
981  * Return:  n  The number of $FILENAME attributes found
982  *	   -1  Error
983  */
984 static int get_data(struct ufile *file, ntfs_volume *vol)
985 {
986 	ATTR_RECORD *rec;
987 	ntfs_attr_search_ctx *ctx;
988 	int count = 0;
989 	struct data *data;
990 
991 	if (!file)
992 		return -1;
993 
994 	ctx = ntfs_attr_get_search_ctx(NULL, file->mft);
995 	if (!ctx)
996 		return -1;
997 
998 	while ((rec = find_attribute(AT_DATA, ctx))) {
999 		data = calloc(1, sizeof(*data));
1000 		if (!data) {
1001 			ntfs_log_error("ERROR: Couldn't allocate memory in "
1002 					"get_data().\n");
1003 			count = -1;
1004 			break;
1005 		}
1006 
1007 		data->resident   = !rec->non_resident;
1008 		data->compressed = (rec->flags & ATTR_IS_COMPRESSED) ? 1 : 0;
1009 		data->encrypted  = (rec->flags & ATTR_IS_ENCRYPTED) ? 1 : 0;
1010 
1011 		if (rec->name_length) {
1012 			data->uname = (ntfschar *)((char *)rec +
1013 					le16_to_cpu(rec->name_offset));
1014 			data->uname_len = rec->name_length;
1015 
1016 			if (ntfs_ucstombs(data->uname, data->uname_len,
1017 						&data->name, 0) < 0) {
1018 				ntfs_log_error("ERROR: Cannot translate name "
1019 						"into current locale.\n");
1020 			}
1021 		}
1022 
1023 		if (data->resident) {
1024 			data->size_data = le32_to_cpu(rec->u.res.value_length);
1025 			data->data = (char*)rec +
1026 				le16_to_cpu(rec->u.res.value_offset);
1027 		} else {
1028 			data->size_alloc = sle64_to_cpu(rec->u.nonres.allocated_size);
1029 			data->size_data  = sle64_to_cpu(rec->u.nonres.data_size);
1030 			data->size_init  = sle64_to_cpu(rec->u.nonres.initialized_size);
1031 			data->size_vcn   = sle64_to_cpu(rec->u.nonres.highest_vcn) + 1;
1032 		}
1033 
1034 		data->runlist = ntfs_mapping_pairs_decompress(vol, rec, NULL);
1035 		if (!data->runlist) {
1036 			ntfs_log_debug("Couldn't decompress the data runs.\n");
1037 		}
1038 
1039 		file->max_size = max(file->max_size, data->size_data);
1040 		file->max_size = max(file->max_size, data->size_init);
1041 
1042 		list_add_tail(&data->list, &file->data);
1043 		count++;
1044 	}
1045 
1046 	ntfs_attr_put_search_ctx(ctx);
1047 	ntfs_log_debug("File has %d data streams.\n", count);
1048 	return count;
1049 }
1050 
1051 /**
1052  * read_record - Read an MFT record into memory
1053  * @vol:     An ntfs volume obtained from ntfs_mount
1054  * @record:  The record number to read
1055  *
1056  * Read the specified MFT record and gather as much information about it as
1057  * possible.
1058  *
1059  * Return:  Pointer  A ufile object containing the results
1060  *	    NULL     Error
1061  */
1062 static struct ufile * read_record(ntfs_volume *vol, long long record)
1063 {
1064 	ATTR_RECORD *attr10, *attr20, *attr90;
1065 	struct ufile *file;
1066 	ntfs_attr *mft;
1067 
1068 	if (!vol)
1069 		return NULL;
1070 
1071 	file = calloc(1, sizeof(*file));
1072 	if (!file) {
1073 		ntfs_log_error("ERROR: Couldn't allocate memory in read_record()\n");
1074 		return NULL;
1075 	}
1076 
1077 	INIT_LIST_HEAD(&file->name);
1078 	INIT_LIST_HEAD(&file->data);
1079 	file->inode = record;
1080 
1081 	file->mft = malloc(vol->mft_record_size);
1082 	if (!file->mft) {
1083 		ntfs_log_error("ERROR: Couldn't allocate memory in read_record()\n");
1084 		free_file(file);
1085 		return NULL;
1086 	}
1087 
1088 	mft = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0);
1089 	if (!mft) {
1090 		ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA");
1091 		free_file(file);
1092 		return NULL;
1093 	}
1094 
1095 	if (ntfs_attr_mst_pread(mft, vol->mft_record_size * record, 1, vol->mft_record_size, file->mft) < 1) {
1096 		ntfs_log_error("ERROR: Couldn't read MFT Record %lld.\n", record);
1097 		ntfs_attr_close(mft);
1098 		free_file(file);
1099 		return NULL;
1100 	}
1101 
1102 	ntfs_attr_close(mft);
1103 	mft = NULL;
1104 
1105 	attr10 = find_first_attribute(AT_STANDARD_INFORMATION,	file->mft);
1106 	attr20 = find_first_attribute(AT_ATTRIBUTE_LIST,	file->mft);
1107 	attr90 = find_first_attribute(AT_INDEX_ROOT,		file->mft);
1108 
1109 	ntfs_log_debug("Attributes present: %s %s %s.\n", attr10?"0x10":"",
1110 			attr20?"0x20":"", attr90?"0x90":"");
1111 
1112 	if (attr10) {
1113 		STANDARD_INFORMATION *si;
1114 		si = (STANDARD_INFORMATION *) ((char *) attr10 + le16_to_cpu(attr10->u.res.value_offset));
1115 		file->date = ntfs2utc(si->last_data_change_time);
1116 	}
1117 
1118 	if (attr20 || !attr10)
1119 		file->attr_list = 1;
1120 	if (attr90)
1121 		file->directory = 1;
1122 
1123 	if (get_filenames(file, vol) < 0) {
1124 		ntfs_log_error("ERROR: Couldn't get filenames.\n");
1125 	}
1126 	if (get_data(file, vol) < 0) {
1127 		ntfs_log_error("ERROR: Couldn't get data streams.\n");
1128 	}
1129 
1130 	return file;
1131 }
1132 
1133 /**
1134  * calc_percentage - Calculate how much of the file is recoverable
1135  * @file:  The file object to work with
1136  * @vol:   An ntfs volume obtained from ntfs_mount
1137  *
1138  * Read through all the $DATA streams and determine if each cluster in each
1139  * stream is still free disk space.  This is just measuring the potential for
1140  * recovery.  The data may have still been overwritten by a another file which
1141  * was then deleted.
1142  *
1143  * Files with a resident $DATA stream will have a 100% potential.
1144  *
1145  * N.B.  If $DATA attribute spans more than one MFT record (i.e. badly
1146  *       fragmented) then only the data in this segment will be used for the
1147  *       calculation.
1148  *
1149  * N.B.  Currently, compressed and encrypted files cannot be recovered, so they
1150  *       will return 0%.
1151  *
1152  * Return:  n  The percentage of the file that _could_ be recovered
1153  *	   -1  Error
1154  */
1155 static int calc_percentage(struct ufile *file, ntfs_volume *vol)
1156 {
1157 	runlist_element *rl = NULL;
1158 	struct list_head *pos;
1159 	struct data *data;
1160 	long long i, j;
1161 	long long start, end;
1162 	int clusters_inuse, clusters_free;
1163 	int percent = 0;
1164 
1165 	if (!file || !vol)
1166 		return -1;
1167 
1168 	if (file->directory) {
1169 		ntfs_log_debug("Found a directory: not recoverable.\n");
1170 		return 0;
1171 	}
1172 
1173 	if (list_empty(&file->data)) {
1174 		ntfs_log_verbose("File has no data streams.\n");
1175 		return 0;
1176 	}
1177 
1178 	list_for_each(pos, &file->data) {
1179 		data  = list_entry(pos, struct data, list);
1180 		clusters_inuse = 0;
1181 		clusters_free  = 0;
1182 
1183 		if (data->encrypted) {
1184 			ntfs_log_verbose("File is encrypted, recovery is "
1185 					"impossible.\n");
1186 			continue;
1187 		}
1188 
1189 		if (data->compressed) {
1190 			ntfs_log_verbose("File is compressed, recovery not yet "
1191 					"implemented.\n");
1192 			continue;
1193 		}
1194 
1195 		if (data->resident) {
1196 			ntfs_log_verbose("File is resident, therefore "
1197 					"recoverable.\n");
1198 			percent = 100;
1199 			data->percent = 100;
1200 			continue;
1201 		}
1202 
1203 		rl = data->runlist;
1204 		if (!rl) {
1205 			ntfs_log_verbose("File has no runlist, hence no data."
1206 					"\n");
1207 			continue;
1208 		}
1209 
1210 		if (rl[0].length <= 0) {
1211 			ntfs_log_verbose("File has an empty runlist, hence no "
1212 					"data.\n");
1213 			continue;
1214 		}
1215 
1216 		if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */
1217 			ntfs_log_verbose("Missing segment at beginning, %lld "
1218 					"clusters\n", (long long)rl[0].length);
1219 			clusters_inuse += rl[0].length;
1220 			rl++;
1221 		}
1222 
1223 		for (i = 0; rl[i].length > 0; i++) {
1224 			if (rl[i].lcn == LCN_RL_NOT_MAPPED) {
1225 				ntfs_log_verbose("Missing segment at end, %lld "
1226 						"clusters\n",
1227 						(long long)rl[i].length);
1228 				clusters_inuse += rl[i].length;
1229 				continue;
1230 			}
1231 
1232 			if (rl[i].lcn == LCN_HOLE) {
1233 				clusters_free += rl[i].length;
1234 				continue;
1235 			}
1236 
1237 			start = rl[i].lcn;
1238 			end   = rl[i].lcn + rl[i].length;
1239 
1240 			for (j = start; j < end; j++) {
1241 				if (utils_cluster_in_use(vol, j))
1242 					clusters_inuse++;
1243 				else
1244 					clusters_free++;
1245 			}
1246 		}
1247 
1248 		if ((clusters_inuse + clusters_free) == 0) {
1249 			ntfs_log_error("ERROR: Unexpected error whilst "
1250 				"calculating percentage for inode %lld\n",
1251 				file->inode);
1252 			continue;
1253 		}
1254 
1255 		data->percent = (clusters_free * 100) /
1256 				(clusters_inuse + clusters_free);
1257 
1258 		percent = max(percent, data->percent);
1259 	}
1260 
1261 	ntfs_log_verbose("File is %d%% recoverable\n", percent);
1262 	return percent;
1263 }
1264 
1265 /**
1266  * dump_record - Print everything we know about an MFT record
1267  * @file:  The file to work with
1268  *
1269  * Output the contents of the file object.  This will print everything that has
1270  * been read from the MFT record, or implied by various means.
1271  *
1272  * Because of the redundant nature of NTFS, there will be some duplication of
1273  * information, though it will have been read from different sources.
1274  *
1275  * N.B.  If the filename is missing, or couldn't be converted to the current
1276  *       locale, "<none>" will be displayed.
1277  *
1278  * Return:  none
1279  */
1280 static void dump_record(struct ufile *file)
1281 {
1282 	char buffer[20];
1283 	const char *name;
1284 	struct list_head *item;
1285 	int i;
1286 
1287 	if (!file)
1288 		return;
1289 
1290 	ntfs_log_quiet("MFT Record %lld\n", file->inode);
1291 	ntfs_log_quiet("Type: %s\n", (file->directory) ? "Directory" : "File");
1292 	strftime(buffer, sizeof(buffer), "%F %R", localtime(&file->date));
1293 	ntfs_log_quiet("Date: %s\n", buffer);
1294 
1295 	if (file->attr_list)
1296 		ntfs_log_quiet("Metadata may span more than one MFT record\n");
1297 
1298 	list_for_each(item, &file->name) {
1299 		struct filename *f = list_entry(item, struct filename, list);
1300 
1301 		if (f->name)
1302 			name = f->name;
1303 		else
1304 			name = NONE;
1305 
1306 		ntfs_log_quiet("Filename: (%d) %s\n", f->name_space, f->name);
1307 		ntfs_log_quiet("File Flags: ");
1308 		if (f->flags & FILE_ATTR_SYSTEM)
1309 			ntfs_log_quiet("System ");
1310 		if (f->flags & FILE_ATTR_DIRECTORY)
1311 			ntfs_log_quiet("Directory ");
1312 		if (f->flags & FILE_ATTR_SPARSE_FILE)
1313 			ntfs_log_quiet("Sparse ");
1314 		if (f->flags & FILE_ATTR_REPARSE_POINT)
1315 			ntfs_log_quiet("Reparse ");
1316 		if (f->flags & FILE_ATTR_COMPRESSED)
1317 			ntfs_log_quiet("Compressed ");
1318 		if (f->flags & FILE_ATTR_ENCRYPTED)
1319 			ntfs_log_quiet("Encrypted ");
1320 		if (!(f->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_DIRECTORY |
1321 		    FILE_ATTR_SPARSE_FILE | FILE_ATTR_REPARSE_POINT |
1322 		    FILE_ATTR_COMPRESSED | FILE_ATTR_ENCRYPTED))) {
1323 			ntfs_log_quiet("%s", NONE);
1324 		}
1325 
1326 		ntfs_log_quiet("\n");
1327 
1328 		if (opts.parent) {
1329 			ntfs_log_quiet("Parent: %s\n", f->parent_name ?
1330 				f->parent_name : "<non-determined>");
1331 		}
1332 
1333 		ntfs_log_quiet("Size alloc: %lld\n", f->size_alloc);
1334 		ntfs_log_quiet("Size data: %lld\n", f->size_data);
1335 
1336 		strftime(buffer, sizeof(buffer), "%F %R",
1337 				localtime(&f->date_c));
1338 		ntfs_log_quiet("Date C: %s\n", buffer);
1339 		strftime(buffer, sizeof(buffer), "%F %R",
1340 				localtime(&f->date_a));
1341 		ntfs_log_quiet("Date A: %s\n", buffer);
1342 		strftime(buffer, sizeof(buffer), "%F %R",
1343 				localtime(&f->date_m));
1344 		ntfs_log_quiet("Date M: %s\n", buffer);
1345 		strftime(buffer, sizeof(buffer), "%F %R",
1346 				localtime(&f->date_r));
1347 		ntfs_log_quiet("Date R: %s\n", buffer);
1348 	}
1349 
1350 	ntfs_log_quiet("Data Streams:\n");
1351 	list_for_each(item, &file->data) {
1352 		struct data *d = list_entry(item, struct data, list);
1353 		ntfs_log_quiet("Name: %s\n", (d->name) ? d->name : UNNAMED);
1354 		ntfs_log_quiet("Flags: ");
1355 		if (d->resident)   ntfs_log_quiet("Resident\n");
1356 		if (d->compressed) ntfs_log_quiet("Compressed\n");
1357 		if (d->encrypted)  ntfs_log_quiet("Encrypted\n");
1358 		if (!d->resident && !d->compressed && !d->encrypted)
1359 			ntfs_log_quiet("None\n");
1360 		else
1361 			ntfs_log_quiet("\n");
1362 
1363 		ntfs_log_quiet("Size alloc: %lld\n", d->size_alloc);
1364 		ntfs_log_quiet("Size data: %lld\n", d->size_data);
1365 		ntfs_log_quiet("Size init: %lld\n", d->size_init);
1366 		ntfs_log_quiet("Size vcn: %lld\n", d->size_vcn);
1367 
1368 		ntfs_log_quiet("Data runs:\n");
1369 		if ((!d->runlist) || (d->runlist[0].length <= 0)) {
1370 			ntfs_log_quiet("    None\n");
1371 		} else {
1372 			for (i = 0; d->runlist[i].length > 0; i++) {
1373 				ntfs_log_quiet("    %lld @ %lld\n",
1374 						(long long)d->runlist[i].length,
1375 						(long long)d->runlist[i].lcn);
1376 			}
1377 		}
1378 
1379 		ntfs_log_quiet("Amount potentially recoverable %d%%\n",
1380 				d->percent);
1381 	}
1382 
1383 	ntfs_log_quiet("________________________________________\n\n");
1384 }
1385 
1386 /**
1387  * list_record - Print a one line summary of the file
1388  * @file:  The file to work with
1389  *
1390  * Print a one line description of a file.
1391  *
1392  *   Inode    Flags  %age  Date            Size  Filename
1393  *
1394  * The output will contain the file's inode number (MFT Record), some flags,
1395  * the percentage of the file that is recoverable, the last modification date,
1396  * the size and the filename.
1397  *
1398  * The flags are F/D = File/Directory, N/R = Data is (Non-)Resident,
1399  * C = Compressed, E = Encrypted, ! = Metadata may span multiple records.
1400  *
1401  * N.B.  The file size is stored in many forms in several attributes.   This
1402  *       display the largest it finds.
1403  *
1404  * N.B.  If the filename is missing, or couldn't be converted to the current
1405  *       locale, "<none>" will be displayed.
1406  *
1407  * Return:  none
1408  */
1409 static void list_record(struct ufile *file)
1410 {
1411 	char buffer[20];
1412 	struct list_head *item;
1413 	const char *name = NULL;
1414 	long long size = 0;
1415 	int percent = 0;
1416 
1417 	char flagd = '.', flagr = '.', flagc = '.', flagx = '.';
1418 
1419 	strftime(buffer, sizeof(buffer), "%F", localtime(&file->date));
1420 
1421 	if (file->attr_list)
1422 		flagx = '!';
1423 
1424 	if (file->directory)
1425 		flagd = 'D';
1426 	else
1427 		flagd = 'F';
1428 
1429 	list_for_each(item, &file->data) {
1430 		struct data *d = list_entry(item, struct data, list);
1431 
1432 		if (!d->name) {
1433 			if (d->resident)
1434 				flagr = 'R';
1435 			else
1436 				flagr = 'N';
1437 			if (d->compressed)
1438 				flagc = 'C';
1439 			if (d->encrypted)
1440 				flagc = 'E';
1441 
1442 			percent = max(percent, d->percent);
1443 		}
1444 
1445 		size = max(size, d->size_data);
1446 		size = max(size, d->size_init);
1447 	}
1448 
1449 	if (file->pref_name)
1450 		name = file->pref_name;
1451 	else
1452 		name = NONE;
1453 
1454 	ntfs_log_quiet("%-8lld %c%c%c%c   %3d%%  %s %9lld  %s\n",
1455 		file->inode, flagd, flagr, flagc, flagx,
1456 		percent, buffer, size, name);
1457 
1458 }
1459 
1460 /**
1461  * name_match - Does a file have a name matching a regex
1462  * @re:    The regular expression object
1463  * @file:  The file to be tested
1464  *
1465  * Iterate through the file's $FILENAME attributes and compare them against the
1466  * regular expression, created with regcomp.
1467  *
1468  * Return:  1  There is a matching filename.
1469  *	    0  There is no match.
1470  */
1471 static int name_match(regex_t *re, struct ufile *file)
1472 {
1473 	struct list_head *item;
1474 	int result;
1475 
1476 	if (!re || !file)
1477 		return 0;
1478 
1479 	list_for_each(item, &file->name) {
1480 		struct filename *f = list_entry(item, struct filename, list);
1481 
1482 		if (!f->name)
1483 			continue;
1484 		result = regexec(re, f->name, 0, NULL, 0);
1485 		if (result < 0) {
1486 			ntfs_log_perror("Couldn't compare filename with regex");
1487 			return 0;
1488 		} else if (result == REG_NOERROR) {
1489 			ntfs_log_debug("Found a matching filename.\n");
1490 			return 1;
1491 		}
1492 	}
1493 
1494 	ntfs_log_debug("Filename '%s' doesn't match regex.\n", file->pref_name);
1495 	return 0;
1496 }
1497 
1498 /**
1499  * write_data - Write out a block of data
1500  * @fd:       File descriptor to write to
1501  * @buffer:   Data to write
1502  * @bufsize:  Amount of data to write
1503  *
1504  * Write a block of data to a file descriptor.
1505  *
1506  * Return:  -1  Error, something went wrong
1507  *	     0  Success, all the data was written
1508  */
1509 static unsigned int write_data(int fd, const char *buffer,
1510 	unsigned int bufsize)
1511 {
1512 	ssize_t result1, result2;
1513 
1514 	if (!buffer) {
1515 		errno = EINVAL;
1516 		return -1;
1517 	}
1518 
1519 	result1 = write(fd, buffer, bufsize);
1520 	if ((result1 == (ssize_t) bufsize) || (result1 < 0))
1521 		return result1;
1522 
1523 	/* Try again with the rest of the buffer */
1524 	buffer  += result1;
1525 	bufsize -= result1;
1526 
1527 	result2 = write(fd, buffer, bufsize);
1528 	if (result2 < 0)
1529 		return result1;
1530 
1531 	return result1 + result2;
1532 }
1533 
1534 /**
1535  * create_pathname - Create a path/file from some components
1536  * @dir:      Directory in which to create the file (optional)
1537  * @name:     Filename to give the file (optional)
1538  * @stream:   Name of the stream (optional)
1539  * @buffer:   Store the result here
1540  * @bufsize:  Size of buffer
1541  *
1542  * Create a filename from various pieces.  The output will be of the form:
1543  *	dir/file
1544  *	dir/file:stream
1545  *	file
1546  *	file:stream
1547  *
1548  * All the components are optional.  If the name is missing, "unknown" will be
1549  * used.  If the directory is missing the file will be created in the current
1550  * directory.  If the stream name is present it will be appended to the
1551  * filename, delimited by a colon.
1552  *
1553  * N.B. If the buffer isn't large enough the name will be truncated.
1554  *
1555  * Return:  n  Length of the allocated name
1556  */
1557 static int create_pathname(const char *dir, const char *name,
1558 	const char *stream, char *buffer, int bufsize)
1559 {
1560 	if (!name)
1561 		name = UNKNOWN;
1562 
1563 	if (dir)
1564 		if (stream)
1565 			snprintf(buffer, bufsize, "%s/%s:%s", dir, name, stream);
1566 		else
1567 			snprintf(buffer, bufsize, "%s/%s", dir, name);
1568 	else
1569 		if (stream)
1570 			snprintf(buffer, bufsize, "%s:%s", name, stream);
1571 		else
1572 			snprintf(buffer, bufsize, "%s", name);
1573 
1574 	return strlen(buffer);
1575 }
1576 
1577 /**
1578  * open_file - Open a file to write to
1579  * @pathname:  Path, name and stream of the file to open
1580  *
1581  * Create a file and return the file descriptor.
1582  *
1583  * N.B.  If option force is given and existing file will be overwritten.
1584  *
1585  * Return:  -1  Error, failed to create the file
1586  *	     n  Success, this is the file descriptor
1587  */
1588 static int open_file(const char *pathname)
1589 {
1590 	int flags;
1591 
1592 	ntfs_log_verbose("Creating file: %s\n", pathname);
1593 
1594 	if (opts.force)
1595 		flags = O_RDWR | O_CREAT | O_TRUNC;
1596 	else
1597 		flags = O_RDWR | O_CREAT | O_EXCL;
1598 
1599 	return open(pathname, flags, S_IRUSR | S_IWUSR);
1600 }
1601 
1602 /**
1603  * set_date - Set the file's date and time
1604  * @pathname:  Path and name of the file to alter
1605  * @date:      Date and time to set
1606  *
1607  * Give a file a particular date and time.
1608  *
1609  * Return:  1  Success, set the file's date and time
1610  *	    0  Error, failed to change the file's date and time
1611  */
1612 static int set_date(const char *pathname, time_t date)
1613 {
1614 	struct utimbuf ut;
1615 
1616 	if (!pathname)
1617 		return 0;
1618 
1619 	ut.actime  = date;
1620 	ut.modtime = date;
1621 	if (utime(pathname, &ut)) {
1622 		ntfs_log_error("ERROR: Couldn't set the file's date and time\n");
1623 		return 0;
1624 	}
1625 	return 1;
1626 }
1627 
1628 /**
1629  * undelete_file - Recover a deleted file from an NTFS volume
1630  * @vol:    An ntfs volume obtained from ntfs_mount
1631  * @inode:  MFT Record number to be recovered
1632  *
1633  * Read an MFT Record and try an recover any data associated with it.  Some of
1634  * the clusters may be in use; these will be filled with zeros or the fill byte
1635  * supplied in the options.
1636  *
1637  * Each data stream will be recovered and saved to a file.  The file's name will
1638  * be the original filename and it will be written to the current directory.
1639  * Any named data stream will be saved as filename:streamname.
1640  *
1641  * The output file's name and location can be altered by using the command line
1642  * options.
1643  *
1644  * N.B.  We cannot tell if someone has overwritten some of the data since the
1645  *       file was deleted.
1646  *
1647  * Return:  0  Error, something went wrong
1648  *	    1  Success, the data was recovered
1649  */
1650 static int undelete_file(ntfs_volume *vol, long long inode)
1651 {
1652 	char pathname[256];
1653 	char *buffer = NULL;
1654 	unsigned int bufsize;
1655 	struct ufile *file;
1656 	int i, j;
1657 	long long start, end;
1658 	runlist_element *rl;
1659 	struct list_head *item;
1660 	int fd = -1;
1661 	long long k;
1662 	int result = 0;
1663 	char *name;
1664 	long long cluster_count;	/* I'll need this variable (see below). +mabs */
1665 
1666 	if (!vol)
1667 		return 0;
1668 
1669 	/* try to get record */
1670 	file = read_record(vol, inode);
1671 	if (!file || !file->mft) {
1672 		ntfs_log_error("Can't read info from mft record %lld.\n", inode);
1673 		return 0;
1674 	}
1675 
1676 	/* if flag was not set, print file informations */
1677 	if (avoid_duplicate_printing == 0) {
1678 		if (opts.verbose) {
1679 			dump_record(file);
1680 		} else {
1681 			list_record(file);
1682 			//ntfs_log_quiet("\n");
1683 		}
1684 	}
1685 
1686 	bufsize = vol->cluster_size;
1687 	buffer = malloc(bufsize);
1688 	if (!buffer)
1689 		goto free;
1690 
1691 	/* calc_percentage() must be called before dump_record() or
1692 	 * list_record(). Otherwise, when undeleting, a file will always be
1693 	 * listed as 0% recoverable even if successfully undeleted. +mabs
1694 	 */
1695 	if (file->mft->flags & MFT_RECORD_IN_USE) {
1696 		ntfs_log_error("Record is in use by the mft\n");
1697 		if (!opts.force) {
1698 			free(buffer);
1699 			free_file(file);
1700 			return 0;
1701 		}
1702 		ntfs_log_verbose("Forced to continue.\n");
1703 	}
1704 
1705 	if (calc_percentage(file, vol) == 0) {
1706 		ntfs_log_quiet("File has no recoverable data.\n");
1707 		goto free;
1708 	}
1709 
1710 	if (list_empty(&file->data)) {
1711 		ntfs_log_quiet("File has no data.  There is nothing to recover.\n");
1712 		goto free;
1713 	}
1714 
1715 	list_for_each(item, &file->data) {
1716 		struct data *d = list_entry(item, struct data, list);
1717 
1718 		if (opts.output)
1719 				name = opts.output;
1720 		else
1721 				name = file->pref_name;
1722 
1723 		create_pathname(opts.dest, name, d->name, pathname, sizeof(pathname));
1724 		if (d->resident) {
1725 			fd = open_file(pathname);
1726 			if (fd < 0) {
1727 				ntfs_log_perror("Couldn't create file");
1728 				goto free;
1729 			}
1730 
1731 			ntfs_log_verbose("File has resident data.\n");
1732 			if (write_data(fd, d->data, d->size_data) < d->size_data) {
1733 				ntfs_log_perror("Write failed");
1734 				close(fd);
1735 				goto free;
1736 			}
1737 
1738 			if (close(fd) < 0) {
1739 				ntfs_log_perror("Close failed");
1740 			}
1741 			fd = -1;
1742 		} else {
1743 			rl = d->runlist;
1744 			if (!rl) {
1745 				ntfs_log_verbose("File has no runlist, hence no data.\n");
1746 				continue;
1747 			}
1748 
1749 			if (rl[0].length <= 0) {
1750 				ntfs_log_verbose("File has an empty runlist, hence no data.\n");
1751 				continue;
1752 			}
1753 
1754 			fd = open_file(pathname);
1755 			if (fd < 0) {
1756 				ntfs_log_perror("Couldn't create output file");
1757 				goto free;
1758 			}
1759 
1760 			if (rl[0].lcn == LCN_RL_NOT_MAPPED) {	/* extended mft record */
1761 				ntfs_log_verbose("Missing segment at beginning, %lld "
1762 						"clusters.\n",
1763 						(long long)rl[0].length);
1764 				memset(buffer, opts.fillbyte, bufsize);
1765 				for (k = 0; k < rl[0].length * vol->cluster_size; k += bufsize) {
1766 					if (write_data(fd, buffer, bufsize) < bufsize) {
1767 						ntfs_log_perror("Write failed");
1768 						close(fd);
1769 						goto free;
1770 					}
1771 				}
1772 			}
1773 
1774 			cluster_count = 0LL;
1775 			for (i = 0; rl[i].length > 0; i++) {
1776 
1777 				if (rl[i].lcn == LCN_RL_NOT_MAPPED) {
1778 					ntfs_log_verbose("Missing segment at end, "
1779 							"%lld clusters.\n",
1780 							(long long)rl[i].length);
1781 					memset(buffer, opts.fillbyte, bufsize);
1782 					for (k = 0; k < rl[k].length * vol->cluster_size; k += bufsize) {
1783 						if (write_data(fd, buffer, bufsize) < bufsize) {
1784 							ntfs_log_perror("Write failed");
1785 							close(fd);
1786 							goto free;
1787 						}
1788 						cluster_count++;
1789 					}
1790 					continue;
1791 				}
1792 
1793 				if (rl[i].lcn == LCN_HOLE) {
1794 					ntfs_log_verbose("File has a sparse section.\n");
1795 					memset(buffer, 0, bufsize);
1796 					for (k = 0; k < rl[k].length * vol->cluster_size; k += bufsize) {
1797 						if (write_data(fd, buffer, bufsize) < bufsize) {
1798 							ntfs_log_perror("Write failed");
1799 							close(fd);
1800 							goto free;
1801 						}
1802 					}
1803 					continue;
1804 				}
1805 
1806 				start = rl[i].lcn;
1807 				end   = rl[i].lcn + rl[i].length;
1808 
1809 				for (j = start; j < end; j++) {
1810 					if (utils_cluster_in_use(vol, j) && !opts.optimistic) {
1811 						memset(buffer, opts.fillbyte, bufsize);
1812 						if (write_data(fd, buffer, bufsize) < bufsize) {
1813 							ntfs_log_perror("Write failed");
1814 							close(fd);
1815 							goto free;
1816 						}
1817 					} else {
1818 						if (ntfs_cluster_read(vol, j, 1, buffer) < 1) {
1819 							ntfs_log_perror("Read failed");
1820 							close(fd);
1821 							goto free;
1822 						}
1823 						if (write_data(fd, buffer, bufsize) < bufsize) {
1824 							ntfs_log_perror("Write failed");
1825 							close(fd);
1826 							goto free;
1827 						}
1828 						cluster_count++;
1829 					}
1830 				}
1831 			}
1832 			ntfs_log_quiet("\n");
1833 
1834 			/*
1835 			 * The following block of code implements the --truncate option.
1836 			 * Its semantics are as follows:
1837 			 * IF opts.truncate is set AND data stream currently being recovered is
1838 			 * non-resident AND data stream has no holes (100% recoverability) AND
1839 			 * 0 <= (data->size_alloc - data->size_data) <= vol->cluster_size AND
1840 			 * cluster_count * vol->cluster_size == data->size_alloc THEN file
1841 			 * currently being written is truncated to data->size_data bytes before
1842 			 * it's closed.
1843 			 * This multiple checks try to ensure that only files with consistent
1844 			 * values of size/occupied clusters are eligible for truncation. Note
1845 			 * that resident streams need not be truncated, since the original code
1846 			 * already recovers their exact length.                           +mabs
1847 			 */
1848 			if (opts.truncate) {
1849 				if (d->percent == 100 && d->size_alloc >= d->size_data &&
1850 					(d->size_alloc - d->size_data) <= (long long)vol->cluster_size &&
1851 					cluster_count * (long long)vol->cluster_size == d->size_alloc) {
1852 					if (ftruncate(fd, (off_t)d->size_data))
1853 						ntfs_log_perror("Truncation failed");
1854 				} else ntfs_log_quiet("Truncation not performed because file has an "
1855 					"inconsistent $MFT record.\n");
1856 			}
1857 
1858 			if (close(fd) < 0) {
1859 				ntfs_log_perror("Close failed");
1860 			}
1861 			fd = -1;
1862 
1863 		}
1864 		set_date(pathname, file->date);
1865 		if (d->name)
1866 			ntfs_log_quiet("Undeleted '%s:%s' successfully.\n", file->pref_name, d->name);
1867 		else
1868 			ntfs_log_quiet("Undeleted '%s' successfully.\n", file->pref_name);
1869 	}
1870 	result = 1;
1871 free:
1872 	if (buffer)
1873 		free(buffer);
1874 	free_file(file);
1875 	return result;
1876 }
1877 
1878 /**
1879  * scan_disk - Search an NTFS volume for files that could be undeleted
1880  * @vol:  An ntfs volume obtained from ntfs_mount
1881  *
1882  * Read through all the MFT entries looking for deleted files.  For each one
1883  * determine how much of the data lies in unused disk space.
1884  *
1885  * The list can be filtered by name, size and date, using command line options.
1886  *
1887  * Return:  -1  Error, something went wrong
1888  *	     n  Success, the number of recoverable files
1889  */
1890 static int scan_disk(ntfs_volume *vol)
1891 {
1892 	s64 nr_mft_records;
1893 	const int BUFSIZE = 8192;
1894 	char *buffer = NULL;
1895 	int results = 0;
1896 	ntfs_attr *attr;
1897 	long long size;
1898 	long long bmpsize;
1899 	int i, j, k, b;
1900 	int percent;
1901 	struct ufile *file;
1902 	regex_t re;
1903 
1904 	if (!vol)
1905 		return -1;
1906 
1907 	attr = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0);
1908 	if (!attr) {
1909 		ntfs_log_perror("ERROR: Couldn't open $MFT/$BITMAP");
1910 		return -1;
1911 	}
1912 	bmpsize = attr->initialized_size;
1913 
1914 	buffer = malloc(BUFSIZE);
1915 	if (!buffer) {
1916 		ntfs_log_error("ERROR: Couldn't allocate memory in scan_disk()\n");
1917 		results = -1;
1918 		goto out;
1919 	}
1920 
1921 	if (opts.match) {
1922 		int flags = REG_NOSUB;
1923 
1924 		if (!opts.match_case)
1925 			flags |= REG_ICASE;
1926 		if (regcomp(&re, opts.match, flags)) {
1927 			ntfs_log_error("ERROR: Couldn't create a regex.\n");
1928 			goto out;
1929 		}
1930 	}
1931 
1932 	nr_mft_records = vol->mft_na->initialized_size >>
1933 			vol->mft_record_size_bits;
1934 
1935 	ntfs_log_quiet("Inode    Flags  %%age  Date           Size  Filename\n");
1936 	ntfs_log_quiet("---------------------------------------------------------------\n");
1937 	for (i = 0; i < bmpsize; i += BUFSIZE) {
1938 		long long read_count = min((bmpsize - i), BUFSIZE);
1939 		size = ntfs_attr_pread(attr, i, read_count, buffer);
1940 		if (size < 0)
1941 			break;
1942 
1943 		for (j = 0; j < size; j++) {
1944 			b = buffer[j];
1945 			for (k = 0; k < 8; k++, b>>=1) {
1946 				if (((i+j)*8+k) >= nr_mft_records)
1947 					goto done;
1948 				if (b & 1)
1949 					continue;
1950 				file = read_record(vol, (i+j)*8+k);
1951 				if (!file) {
1952 					ntfs_log_error("Couldn't read MFT Record %d.\n", (i+j)*8+k);
1953 					continue;
1954 				}
1955 
1956 				if ((opts.since > 0) && (file->date <= opts.since))
1957 					goto skip;
1958 				if (opts.match && !name_match(&re, file))
1959 					goto skip;
1960 				if (opts.size_begin && (opts.size_begin > file->max_size))
1961 					goto skip;
1962 				if (opts.size_end && (opts.size_end < file->max_size))
1963 					goto skip;
1964 
1965 				percent = calc_percentage(file, vol);
1966 				if ((opts.percent == -1) || (percent >= opts.percent)) {
1967 					if (opts.verbose)
1968 						dump_record(file);
1969 					else
1970 						list_record(file);
1971 
1972 					/* Was -u specified with no inode
1973 					   so undelete file by regex */
1974 					if (opts.mode == MODE_UNDELETE) {
1975 						if  (!undelete_file(vol, file->inode))
1976 							ntfs_log_verbose("ERROR: Failed to undelete "
1977 								  "inode %lli\n!",
1978 								  file->inode);
1979 						ntfs_log_info("\n");
1980 					}
1981 				}
1982 				if (((opts.percent == -1) && (percent > 0)) ||
1983 				    ((opts.percent > 0)  && (percent >= opts.percent))) {
1984 					results++;
1985 				}
1986 skip:
1987 				free_file(file);
1988 			}
1989 		}
1990 	}
1991 done:
1992 	ntfs_log_quiet("\nFiles with potentially recoverable content: %d\n",
1993 		results);
1994 out:
1995 	if (opts.match)
1996 		regfree(&re);
1997 	free(buffer);
1998 	if (attr)
1999 		ntfs_attr_close(attr);
2000 	return results;
2001 }
2002 
2003 /**
2004  * copy_mft - Write a range of MFT Records to a file
2005  * @vol:	An ntfs volume obtained from ntfs_mount
2006  * @mft_begin:	First MFT Record to save
2007  * @mft_end:	Last MFT Record to save
2008  *
2009  * Read a number of MFT Records and write them to a file.
2010  *
2011  * Return:  0  Success, all the records were written
2012  *	    1  Error, something went wrong
2013  */
2014 static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end)
2015 {
2016 	s64 nr_mft_records;
2017 	char pathname[256];
2018 	ntfs_attr *mft;
2019 	char *buffer;
2020 	const char *name;
2021 	long long i;
2022 	int result = 1;
2023 	int fd;
2024 
2025 	if (!vol)
2026 		return 1;
2027 
2028 	if (mft_end < mft_begin) {
2029 		ntfs_log_error("Range to copy is backwards.\n");
2030 		return 1;
2031 	}
2032 
2033 	buffer = malloc(vol->mft_record_size);
2034 	if (!buffer) {
2035 		ntfs_log_error("Couldn't allocate memory in copy_mft()\n");
2036 		return 1;
2037 	}
2038 
2039 	mft = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0);
2040 	if (!mft) {
2041 		ntfs_log_perror("Couldn't open $MFT/$DATA");
2042 		goto free;
2043 	}
2044 
2045 	name = opts.output;
2046 	if (!name) {
2047 		name = MFTFILE;
2048 		ntfs_log_debug("No output filename, defaulting to '%s'.\n",
2049 				name);
2050 	}
2051 
2052 	create_pathname(opts.dest, name, NULL, pathname, sizeof(pathname));
2053 	fd = open_file(pathname);
2054 	if (fd < 0) {
2055 		ntfs_log_perror("Couldn't open output file '%s'", name);
2056 		goto attr;
2057 	}
2058 
2059 	nr_mft_records = vol->mft_na->initialized_size >>
2060 			vol->mft_record_size_bits;
2061 
2062 	mft_end = min(mft_end, nr_mft_records - 1);
2063 
2064 	ntfs_log_debug("MFT records:\n");
2065 	ntfs_log_debug("\tTotal: %8lld\n", nr_mft_records);
2066 	ntfs_log_debug("\tBegin: %8lld\n", mft_begin);
2067 	ntfs_log_debug("\tEnd:   %8lld\n", mft_end);
2068 
2069 	for (i = mft_begin; i <= mft_end; i++) {
2070 		if (ntfs_attr_pread(mft, vol->mft_record_size * i,
2071 		    vol->mft_record_size, buffer) < vol->mft_record_size) {
2072 			ntfs_log_perror("Couldn't read MFT Record %lld", i);
2073 			goto close;
2074 		}
2075 
2076 		if (write_data(fd, buffer, vol->mft_record_size) < vol->mft_record_size) {
2077 			ntfs_log_perror("Write failed");
2078 			goto close;
2079 		}
2080 	}
2081 
2082 	ntfs_log_verbose("Read %lld MFT Records\n", mft_end - mft_begin + 1);
2083 	result = 0;
2084 close:
2085 	close(fd);
2086 attr:
2087 	ntfs_attr_close(mft);
2088 free:
2089 	free(buffer);
2090 	return result;
2091 }
2092 
2093 /**
2094  * handle_undelete
2095  *
2096  * Handles the undelete
2097  */
2098 static int handle_undelete(ntfs_volume *vol)
2099 {
2100 	int result = 1;
2101 	int i;
2102 	unsigned long long	inode;
2103 
2104 	/* Check whether (an) inode(s) was specified or at least a regex! */
2105 	if (nr_entries == 0) {
2106 		if (with_regex == 0) {
2107 			ntfs_log_error("ERROR: NO inode(s) AND NO match-regex "
2108 				"specified!\n");
2109 		} else {
2110 			avoid_duplicate_printing= 1;
2111 			result = !scan_disk(vol);
2112 			if (result)
2113 				ntfs_log_verbose("ERROR: Failed to scan device "
2114 					"'%s'.\n", opts.device);
2115 		}
2116 	} else {
2117 		/* Normal undelete by specifying inode(s) */
2118 		ntfs_log_quiet("Inode    Flags  %%age  Date            Size  Filename\n");
2119 		ntfs_log_quiet("---------------------------------------------------------------\n");
2120 
2121 		/* loop all given inodes */
2122 		for (i = 0; i < nr_entries; i++) {
2123 			for (inode = ranges[i].begin; inode <= ranges[i].end; inode ++) {
2124 				/* Now undelete file */
2125 				result = !undelete_file(vol, inode);
2126 				if (result)
2127 					ntfs_log_verbose("ERROR: Failed to "
2128 						"undelete inode %lli\n!", inode);
2129 			}
2130 		}
2131 	}
2132 	return (result);
2133 }
2134 
2135 /**
2136  * main - Begin here
2137  *
2138  * Start from here.
2139  *
2140  * Return:  0  Success, the program worked
2141  *	    1  Error, something went wrong
2142  */
2143 int main(int argc, char *argv[])
2144 {
2145 	ntfs_volume *vol;
2146 	int result = 1;
2147 
2148 	ntfs_log_set_handler(ntfs_log_handler_outerr);
2149 
2150 	with_regex = 0;
2151 	avoid_duplicate_printing = 0;
2152 
2153 	if (!parse_options(argc, argv))
2154 		goto free;
2155 
2156 	utils_set_locale();
2157 
2158 	vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY |
2159 			(opts.force ? NTFS_MNT_FORCE : 0));
2160 	if (!vol)
2161 		return 1;
2162 
2163 	/* handling of the different modes */
2164 	switch (opts.mode) {
2165 	/* Scanning */
2166 	case MODE_SCAN:
2167 		result = !scan_disk(vol);
2168 		if (result)
2169 			ntfs_log_verbose("ERROR: Failed to scan device '%s'.\n",
2170 				opts.device);
2171 		break;
2172 
2173 	/* Undelete-handling */
2174 	case MODE_UNDELETE:
2175 		result= handle_undelete(vol);
2176 		break;
2177 
2178 	/* Handling of copy mft */
2179 	case MODE_COPY:
2180 		result = !copy_mft(vol, opts.mft_begin, opts.mft_end);
2181 		if (result)
2182 			ntfs_log_verbose("ERROR: Failed to read MFT blocks "
2183 				"%lld-%lld.\n", opts.mft_begin,
2184 				min((vol->mft_na->initialized_size >>
2185 				vol->mft_record_size_bits) , opts.mft_end));
2186 		break;
2187 	default:
2188 		; /* Cannot happen */
2189 	}
2190 
2191 	ntfs_umount(vol, FALSE);
2192 free:
2193 	if (opts.match)
2194 		free(opts.match);
2195 
2196 	return result;
2197 }
2198 
2199