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