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