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