"Fossies" - the Fresh Open Source Software Archive

Member "ntfs-3g_ntfsprogs-2017.3.23/ntfsprogs/ntfscmp.c" (23 Mar 2017, 22040 Bytes) of package /linux/misc/ntfs-3g_ntfsprogs-2017.3.23.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ntfscmp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3g_ntfsprogs-2016.2.22_vs_3g_ntfsprogs-2017.3.23.

    1 /**
    2  * ntfscmp - Part of the Linux-NTFS project.
    3  *
    4  * Copyright (c) 2005-2006 Szabolcs Szakacsits
    5  * Copyright (c) 2005      Anton Altaparmakov
    6  * Copyright (c) 2007      Yura Pakhuchiy
    7  *
    8  * This utility compare two NTFS volumes.
    9  *
   10  * This program is free software; you can redistribute it and/or modify
   11  * it under the terms of the GNU General Public License as published by
   12  * the Free Software Foundation; either version 2 of the License, or
   13  * (at your option) any later version.
   14  *
   15  * This program is distributed in the hope that it will be useful,
   16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18  * GNU General Public License for more details.
   19  *
   20  * You should have received a copy of the GNU General Public License
   21  * along with this program (in the main directory of the Linux-NTFS
   22  * distribution in the file COPYING); if not, write to the Free Software
   23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   24  */
   25 
   26 #include "config.h"
   27 
   28 #include <unistd.h>
   29 #include <stdlib.h>
   30 #include <stdio.h>
   31 #include <stdarg.h>
   32 #include <string.h>
   33 #include <errno.h>
   34 #include <getopt.h>
   35 
   36 #include "mst.h"
   37 #include "support.h"
   38 #include "utils.h"
   39 #include "misc.h"
   40 /* #include "version.h" */
   41 
   42 static const char *EXEC_NAME = "ntfscmp";
   43 
   44 static const char *invalid_ntfs_msg =
   45 "Apparently device '%s' doesn't have a valid NTFS.\n"
   46 "Maybe you selected the wrong partition? Or the whole disk instead of a\n"
   47 "partition (e.g. /dev/hda, not /dev/hda1)?\n";
   48 
   49 static const char *corrupt_volume_msg =
   50 "Apparently you have a corrupted NTFS. Please run the filesystem checker\n"
   51 "on Windows by invoking chkdsk /f. Don't forget the /f (force) parameter,\n"
   52 "it's important! You probably also need to reboot Windows to take effect.\n";
   53 
   54 static const char *hibernated_volume_msg =
   55 "Apparently the NTFS partition is hibernated. Windows must be resumed and\n"
   56 "turned off properly\n";
   57 
   58 
   59 static struct {
   60     int debug;
   61     int show_progress;
   62     int verbose;
   63     char *vol1;
   64     char *vol2;
   65 } opt;
   66 
   67 
   68 #define NTFS_PROGBAR        0x0001
   69 #define NTFS_PROGBAR_SUPPRESS   0x0002
   70 
   71 struct progress_bar {
   72     u64 start;
   73     u64 stop;
   74     int resolution;
   75     int flags;
   76     float unit;
   77 };
   78 
   79 /* WARNING: don't modify the text, external tools grep for it */
   80 #define ERR_PREFIX   "ERROR"
   81 #define PERR_PREFIX  ERR_PREFIX "(%d): "
   82 #define NERR_PREFIX  ERR_PREFIX ": "
   83 
   84 __attribute__((format(printf, 2, 3)))
   85 static void perr_printf(int newline, const char *fmt, ...)
   86 {
   87     va_list ap;
   88     int eo = errno;
   89 
   90     fprintf(stdout, PERR_PREFIX, eo);
   91     va_start(ap, fmt);
   92     vfprintf(stdout, fmt, ap);
   93     va_end(ap);
   94     fprintf(stdout, ": %s", strerror(eo));
   95     if (newline)
   96         fprintf(stdout, "\n");
   97     fflush(stdout);
   98     fflush(stderr);
   99 }
  100 
  101 #define perr_print(...)     perr_printf(0, __VA_ARGS__)
  102 #define perr_println(...)   perr_printf(1, __VA_ARGS__)
  103 
  104 __attribute__((format(printf, 1, 2)))
  105 static void err_printf(const char *fmt, ...)
  106 {
  107     va_list ap;
  108 
  109     fprintf(stdout, NERR_PREFIX);
  110     va_start(ap, fmt);
  111     vfprintf(stdout, fmt, ap);
  112     va_end(ap);
  113     fflush(stdout);
  114     fflush(stderr);
  115 }
  116 
  117 /**
  118  * err_exit
  119  *
  120  * Print and error message and exit the program.
  121  */
  122 __attribute__((noreturn))
  123 __attribute__((format(printf, 1, 2)))
  124 static int err_exit(const char *fmt, ...)
  125 {
  126     va_list ap;
  127 
  128     fprintf(stdout, NERR_PREFIX);
  129     va_start(ap, fmt);
  130     vfprintf(stdout, fmt, ap);
  131     va_end(ap);
  132     fflush(stdout);
  133     fflush(stderr);
  134     exit(1);
  135 }
  136 
  137 /**
  138  * perr_exit
  139  *
  140  * Print and error message and exit the program
  141  */
  142 __attribute__((noreturn))
  143 __attribute__((format(printf, 1, 2)))
  144 static int perr_exit(const char *fmt, ...)
  145 {
  146     va_list ap;
  147     int eo = errno;
  148 
  149     fprintf(stdout, PERR_PREFIX, eo);
  150     va_start(ap, fmt);
  151     vfprintf(stdout, fmt, ap);
  152     va_end(ap);
  153     printf(": %s\n", strerror(eo));
  154     fflush(stdout);
  155     fflush(stderr);
  156     exit(1);
  157 }
  158 
  159 /**
  160  * usage - Print a list of the parameters to the program
  161  *
  162  * Print a list of the parameters and options for the program.
  163  *
  164  * Return:  none
  165  */
  166 __attribute__((noreturn))
  167 static void usage(void)
  168 {
  169 
  170     printf("\nUsage: %s [OPTIONS] DEVICE1 DEVICE2\n"
  171         "    Compare two NTFS volumes and tell the differences.\n"
  172         "\n"
  173         "    -P, --no-progress-bar  Don't show progress bar\n"
  174         "    -v, --verbose          More output\n"
  175         "    -h, --help             Display this help\n"
  176 #ifdef DEBUG
  177         "    -d, --debug            Show debug information\n"
  178 #endif
  179         "\n", EXEC_NAME);
  180     printf("%s%s", ntfs_bugs, ntfs_home);
  181     exit(1);
  182 }
  183 
  184 
  185 static void parse_options(int argc, char **argv)
  186 {
  187     static const char *sopt = "-dhPv";
  188     static const struct option lopt[] = {
  189 #ifdef DEBUG
  190         { "debug",      no_argument,    NULL, 'd' },
  191 #endif
  192         { "help",       no_argument,    NULL, 'h' },
  193         { "no-progress-bar",    no_argument,    NULL, 'P' },
  194         { "verbose",        no_argument,    NULL, 'v' },
  195         { NULL, 0, NULL, 0 }
  196     };
  197 
  198     int c;
  199 
  200     memset(&opt, 0, sizeof(opt));
  201     opt.show_progress = 1;
  202 
  203     while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
  204         switch (c) {
  205         case 1: /* A non-option argument */
  206             if (!opt.vol1) {
  207                 opt.vol1 = argv[optind - 1];
  208             } else if (!opt.vol2) {
  209                 opt.vol2 = argv[optind - 1];
  210             } else {
  211                 err_printf("Too many arguments!\n");
  212                 usage();
  213             }
  214             break;
  215 #ifdef DEBUG
  216         case 'd':
  217             opt.debug++;
  218             break;
  219 #endif
  220         case 'h':
  221         case '?':
  222             usage();
  223         case 'P':
  224             opt.show_progress = 0;
  225             break;
  226         case 'v':
  227             opt.verbose++;
  228             break;
  229         default:
  230             err_printf("Unknown option '%s'.\n", argv[optind - 1]);
  231             usage();
  232             break;
  233         }
  234     }
  235 
  236     if (opt.vol1 == NULL || opt.vol2 == NULL) {
  237         err_printf("You must specify exactly 2 volumes.\n");
  238         usage();
  239     }
  240 
  241     /* Redirect stderr to stdout, note fflush()es are essential! */
  242     fflush(stdout);
  243     fflush(stderr);
  244     if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
  245         perror("Failed to redirect stderr to stdout");
  246         exit(1);
  247     }
  248     fflush(stdout);
  249     fflush(stderr);
  250 
  251 #ifdef DEBUG
  252      if (!opt.debug)
  253         if (!freopen("/dev/null", "w", stderr))
  254             perr_exit("Failed to redirect stderr to /dev/null");
  255 #endif
  256 }
  257 
  258 static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni)
  259 {
  260     ntfs_attr_search_ctx *ret;
  261 
  262     if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL)
  263         perr_println("ntfs_attr_get_search_ctx");
  264 
  265     return ret;
  266 }
  267 
  268 static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags)
  269 {
  270     p->start = start;
  271     p->stop = stop;
  272     p->unit = 100.0 / (stop - start);
  273     p->resolution = 100;
  274     p->flags = flags;
  275 }
  276 
  277 static void progress_update(struct progress_bar *p, u64 current)
  278 {
  279     float percent;
  280 
  281     if (!(p->flags & NTFS_PROGBAR))
  282         return;
  283     if (p->flags & NTFS_PROGBAR_SUPPRESS)
  284         return;
  285 
  286     /* WARNING: don't modify the texts, external tools grep for them */
  287     percent = p->unit * current;
  288     if (current != p->stop) {
  289         if ((current - p->start) % p->resolution)
  290             return;
  291         printf("%6.2f percent completed\r", percent);
  292     } else
  293         printf("100.00 percent completed\n");
  294     fflush(stdout);
  295 }
  296 
  297 static u64 inumber(ntfs_inode *ni)
  298 {
  299     if (ni->nr_extents >= 0)
  300         return ni->mft_no;
  301 
  302     return ni->base_ni->mft_no;
  303 }
  304 
  305 static int inode_close(ntfs_inode *ni)
  306 {
  307     if (ni == NULL)
  308         return 0;
  309 
  310     if (ntfs_inode_close(ni)) {
  311         perr_println("ntfs_inode_close: inode %llu",
  312                 (unsigned long long)inumber(ni));
  313         return -1;
  314     }
  315     return 0;
  316 }
  317 
  318 static inline s64 get_nr_mft_records(ntfs_volume *vol)
  319 {
  320     return vol->mft_na->initialized_size >> vol->mft_record_size_bits;
  321 }
  322 
  323 #define  NTFSCMP_OK             0
  324 #define  NTFSCMP_INODE_OPEN_ERROR       1
  325 #define  NTFSCMP_INODE_OPEN_IO_ERROR        2
  326 #define  NTFSCMP_INODE_OPEN_ENOENT_ERROR    3
  327 #define  NTFSCMP_EXTENSION_RECORD       4
  328 #define  NTFSCMP_INODE_CLOSE_ERROR      5
  329 
  330 static const char *ntfscmp_errs[] = {
  331     "OK",
  332     "INODE_OPEN_ERROR",
  333     "INODE_OPEN_IO_ERROR",
  334     "INODE_OPEN_ENOENT_ERROR",
  335     "EXTENSION_RECORD",
  336     "INODE_CLOSE_ERROR",
  337     ""
  338 };
  339 
  340 
  341 static const char *err2string(int err)
  342 {
  343     return ntfscmp_errs[err];
  344 }
  345 
  346 static const char *pret2str(void *p)
  347 {
  348     if (p == NULL)
  349         return "FAILED";
  350     return "OK";
  351 }
  352 
  353 static int inode_open(ntfs_volume *vol, MFT_REF mref, ntfs_inode **ni)
  354 {
  355     *ni = ntfs_inode_open(vol, mref);
  356     if (*ni == NULL) {
  357         if (errno == EIO)
  358             return NTFSCMP_INODE_OPEN_IO_ERROR;
  359         if (errno == ENOENT)
  360             return NTFSCMP_INODE_OPEN_ENOENT_ERROR;
  361 
  362         perr_println("Reading inode %lld failed", (long long)mref);
  363         return NTFSCMP_INODE_OPEN_ERROR;
  364     }
  365 
  366     if ((*ni)->mrec->base_mft_record) {
  367 
  368         if (inode_close(*ni) != 0)
  369             return NTFSCMP_INODE_CLOSE_ERROR;
  370 
  371         return NTFSCMP_EXTENSION_RECORD;
  372     }
  373 
  374     return NTFSCMP_OK;
  375 }
  376 
  377 static ntfs_inode *base_inode(ntfs_attr_search_ctx *ctx)
  378 {
  379     if (ctx->base_ntfs_ino)
  380         return ctx->base_ntfs_ino;
  381 
  382     return ctx->ntfs_ino;
  383 }
  384 
  385 static void print_inode(u64 inum)
  386 {
  387     printf("Inode %llu ", (unsigned long long)inum);
  388 }
  389 
  390 static void print_inode_ni(ntfs_inode *ni)
  391 {
  392     print_inode(inumber(ni));
  393 }
  394 
  395 static void print_attribute_type(ATTR_TYPES atype)
  396 {
  397     printf("attribute 0x%x", le32_to_cpu(atype));
  398 }
  399 
  400 static void print_attribute_name(char *name)
  401 {
  402     if (name)
  403         printf(":%s", name);
  404 }
  405 
  406 #define GET_ATTR_NAME(a) \
  407     ((ntfschar *)(((u8 *)(a)) + le16_to_cpu((a)->name_offset))), \
  408     ((a)->name_length)
  409 
  410 static void free_name(char **name)
  411 {
  412     if (*name) {
  413         free(*name);
  414         *name = NULL;
  415     }
  416 }
  417 
  418 static char *get_attr_name(u64 mft_no,
  419                ATTR_TYPES atype,
  420                const ntfschar *uname,
  421                const int uname_len)
  422 {
  423     char *name = NULL;
  424     int name_len;
  425 
  426     if (atype == AT_END)
  427         return NULL;
  428 
  429     name_len = ntfs_ucstombs(uname, uname_len, &name, 0);
  430     if (name_len < 0) {
  431         perr_print("ntfs_ucstombs");
  432         print_inode(mft_no);
  433         print_attribute_type(atype);
  434         puts("");
  435         exit(1);
  436 
  437     } else if (name_len > 0)
  438         return name;
  439 
  440     free_name(&name);
  441     return NULL;
  442 }
  443 
  444 static char *get_attr_name_na(ntfs_attr *na)
  445 {
  446     return get_attr_name(inumber(na->ni), na->type, na->name, na->name_len);
  447 }
  448 
  449 static char *get_attr_name_ctx(ntfs_attr_search_ctx *ctx)
  450 {
  451     u64 mft_no = inumber(ctx->ntfs_ino);
  452     ATTR_TYPES atype = ctx->attr->type;
  453 
  454     return get_attr_name(mft_no, atype, GET_ATTR_NAME(ctx->attr));
  455 }
  456 
  457 static void print_attribute(ATTR_TYPES atype, char *name)
  458 {
  459     print_attribute_type(atype);
  460     print_attribute_name(name);
  461     printf(" ");
  462 }
  463 
  464 static void print_na(ntfs_attr *na)
  465 {
  466     char *name = get_attr_name_na(na);
  467     print_inode_ni(na->ni);
  468     print_attribute(na->type, name);
  469     free_name(&name);
  470 }
  471 
  472 static void print_attribute_ctx(ntfs_attr_search_ctx *ctx)
  473 {
  474     char *name = get_attr_name_ctx(ctx);
  475     print_attribute(ctx->attr->type, name);
  476     free_name(&name);
  477 }
  478 
  479 static void print_ctx(ntfs_attr_search_ctx *ctx)
  480 {
  481     char *name = get_attr_name_ctx(ctx);
  482     print_inode_ni(base_inode(ctx));
  483     print_attribute(ctx->attr->type, name);
  484     free_name(&name);
  485 }
  486 
  487 static void print_differ(ntfs_attr *na)
  488 {
  489     print_na(na);
  490     printf("content:   DIFFER\n");
  491 }
  492 
  493 static int cmp_buffer(u8 *buf1, u8 *buf2, long long int size, ntfs_attr *na)
  494 {
  495     if (memcmp(buf1, buf2, size)) {
  496         print_differ(na);
  497         return -1;
  498     }
  499     return 0;
  500 }
  501 
  502 struct cmp_ia {
  503     INDEX_ALLOCATION *ia;
  504     INDEX_ALLOCATION *tmp_ia;
  505     u8 *bitmap;
  506     u8 *byte;
  507     s64 bm_size;
  508 };
  509 
  510 static int setup_cmp_ia(ntfs_attr *na, struct cmp_ia *cia)
  511 {
  512     cia->bitmap = ntfs_attr_readall(na->ni, AT_BITMAP, na->name,
  513                     na->name_len, &cia->bm_size);
  514     if (!cia->bitmap) {
  515         perr_println("Failed to readall BITMAP");
  516         return -1;
  517     }
  518     cia->byte = cia->bitmap;
  519 
  520     cia->tmp_ia = cia->ia = ntfs_malloc(na->data_size);
  521     if (!cia->tmp_ia)
  522         goto free_bm;
  523 
  524     if (ntfs_attr_pread(na, 0, na->data_size, cia->ia) != na->data_size) {
  525         perr_println("Failed to pread INDEX_ALLOCATION");
  526         goto free_ia;
  527     }
  528 
  529     return 0;
  530 free_ia:
  531     free(cia->ia);
  532 free_bm:
  533     free(cia->bitmap);
  534     return -1;
  535 }
  536 
  537 static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2)
  538 {
  539     struct cmp_ia cia1, cia2;
  540     int bit, ret1, ret2;
  541     u32 ib_size;
  542 
  543     if (setup_cmp_ia(na1, &cia1))
  544         return;
  545     if (setup_cmp_ia(na2, &cia2))
  546         return;
  547     /*
  548      *  FIXME: ia can be the same even if the bitmap sizes are different.
  549      */
  550     if (cia1.bm_size != cia2.bm_size)
  551         goto out;
  552 
  553     if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1))
  554         goto out;
  555 
  556     if (cmp_buffer((u8 *)cia1.ia, (u8 *)cia2.ia, 0x18, na1))
  557         goto out;
  558 
  559     ib_size = le32_to_cpu(cia1.ia->index.allocated_size) + 0x18;
  560 
  561     bit = 0;
  562     while ((u8 *)cia1.tmp_ia < (u8 *)cia1.ia + na1->data_size) {
  563         if (*cia1.byte & (1 << bit)) {
  564             ret1 = ntfs_mst_post_read_fixup((NTFS_RECORD *)
  565                     cia1.tmp_ia, ib_size);
  566             ret2 = ntfs_mst_post_read_fixup((NTFS_RECORD *)
  567                     cia2.tmp_ia, ib_size);
  568             if (ret1 != ret2) {
  569                 print_differ(na1);
  570                 goto out;
  571             }
  572 
  573             if (ret1 == -1)
  574                 continue;
  575 
  576             if (cmp_buffer(((u8 *)cia1.tmp_ia) + 0x18,
  577                     ((u8 *)cia2.tmp_ia) + 0x18,
  578                     le32_to_cpu(cia1.ia->
  579                     index.index_length), na1))
  580                 goto out;
  581         }
  582 
  583         cia1.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia1.tmp_ia + ib_size);
  584         cia2.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia2.tmp_ia + ib_size);
  585 
  586         bit++;
  587         if (bit > 7) {
  588             bit = 0;
  589             cia1.byte++;
  590         }
  591     }
  592 out:
  593     free(cia1.ia);
  594     free(cia2.ia);
  595     free(cia1.bitmap);
  596     free(cia2.bitmap);
  597     return;
  598 }
  599 
  600 static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2)
  601 {
  602     s64 pos;
  603     s64 count1 = 0, count2;
  604     u8  buf1[NTFS_BUF_SIZE];
  605     u8  buf2[NTFS_BUF_SIZE];
  606 
  607     for (pos = 0; pos <= na1->data_size; pos += count1) {
  608 
  609         count1 = ntfs_attr_pread(na1, pos, NTFS_BUF_SIZE, buf1);
  610         count2 = ntfs_attr_pread(na2, pos, NTFS_BUF_SIZE, buf2);
  611 
  612         if (count1 != count2) {
  613             print_na(na1);
  614             printf("abrupt length:   %lld  !=  %lld ",
  615                 (long long)na1->data_size,
  616                 (long long)na2->data_size);
  617             printf("(count: %lld  !=  %lld)",
  618                 (long long)count1, (long long)count2);
  619             puts("");
  620             return;
  621         }
  622 
  623         if (count1 == -1) {
  624             err_printf("%s read error: ", __FUNCTION__);
  625             print_na(na1);
  626             printf("len = %lld, pos = %lld\n",
  627                 (long long)na1->data_size, (long long)pos);
  628             exit(1);
  629         }
  630 
  631         if (count1 == 0) {
  632 
  633             if (pos + count1 == na1->data_size)
  634                 return; /* we are ready */
  635 
  636             err_printf("%s read error before EOF: ", __FUNCTION__);
  637             print_na(na1);
  638             printf("%lld  !=  %lld\n", (long long)pos + count1,
  639                     (long long)na1->data_size);
  640             exit(1);
  641         }
  642 
  643         if (cmp_buffer(buf1, buf2, count1, na1))
  644             return;
  645     }
  646 
  647     err_printf("%s read overrun: ", __FUNCTION__);
  648     print_na(na1);
  649     err_printf("(len = %lld, pos = %lld, count = %lld)\n",
  650           (long long)na1->data_size, (long long)pos, (long long)count1);
  651     exit(1);
  652 }
  653 
  654 static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2)
  655 {
  656     u32 header_size = offsetof(ATTR_RECORD, resident_end);
  657 
  658     if (a1->non_resident != a2->non_resident)
  659         return 1;
  660 
  661     if (a1->non_resident) {
  662         /*
  663          * FIXME: includes paddings which are not handled by ntfsinfo!
  664          */
  665         header_size = le32_to_cpu(a1->length);
  666     }
  667 
  668     return memcmp(a1, a2, header_size);
  669 }
  670 
  671 static void cmp_attribute(ntfs_attr_search_ctx *ctx1,
  672               ntfs_attr_search_ctx *ctx2)
  673 {
  674     ATTR_RECORD *a1 = ctx1->attr;
  675     ATTR_RECORD *a2 = ctx2->attr;
  676     ntfs_attr *na1, *na2;
  677 
  678     if (cmp_attribute_header(a1, a2)) {
  679         print_ctx(ctx1);
  680         printf("header:    DIFFER\n");
  681     }
  682 
  683     na1 = ntfs_attr_open(base_inode(ctx1), a1->type, GET_ATTR_NAME(a1));
  684     na2 = ntfs_attr_open(base_inode(ctx2), a2->type, GET_ATTR_NAME(a2));
  685 
  686     if ((!na1 && na2) || (na1 && !na2)) {
  687         print_ctx(ctx1);
  688         printf("open:   %s  !=  %s\n", pret2str(na1), pret2str(na2));
  689         goto close_attribs;
  690     }
  691 
  692     if (na1 == NULL)
  693         goto close_attribs;
  694 
  695     if (na1->data_size != na2->data_size) {
  696         print_na(na1);
  697         printf("length:   %lld  !=  %lld\n",
  698             (long long)na1->data_size, (long long)na2->data_size);
  699         goto close_attribs;
  700     }
  701 
  702     if (ntfs_inode_badclus_bad(inumber(ctx1->ntfs_ino), ctx1->attr) == 1) {
  703         /*
  704          * If difference exists then it's already reported at the
  705          * attribute header since the mapping pairs must differ.
  706          */
  707         goto close_attribs;
  708     }
  709 
  710     if (na1->type == AT_INDEX_ALLOCATION)
  711         cmp_index_allocation(na1, na2);
  712     else
  713         cmp_attribute_data(na1, na2);
  714 
  715 close_attribs:
  716     ntfs_attr_close(na1);
  717     ntfs_attr_close(na2);
  718 }
  719 
  720 static void vprint_attribute(ATTR_TYPES atype, char  *name)
  721 {
  722     if (!opt.verbose)
  723         return;
  724 
  725     printf("0x%x", le32_to_cpu(atype));
  726     if (name)
  727         printf(":%s", name);
  728     printf(" ");
  729 }
  730 
  731 static void print_attributes(ntfs_inode *ni,
  732                  ATTR_TYPES atype1,
  733                  ATTR_TYPES atype2,
  734                  char  *name1,
  735                  char  *name2)
  736 {
  737     if (!opt.verbose)
  738         return;
  739 
  740     printf("Walking inode %llu attributes: ",
  741             (unsigned long long)inumber(ni));
  742     vprint_attribute(atype1, name1);
  743     vprint_attribute(atype2, name2);
  744     printf("\n");
  745 }
  746 
  747 static int new_name(ntfs_attr_search_ctx *ctx, char *prev_name)
  748 {
  749     int ret = 0;
  750     char *name = get_attr_name_ctx(ctx);
  751 
  752     if (prev_name && name) {
  753         if (strcmp(prev_name, name) != 0)
  754             ret = 1;
  755     } else if (prev_name || name)
  756         ret = 1;
  757 
  758     free_name(&name);
  759     return ret;
  760 
  761 }
  762 
  763 static int new_attribute(ntfs_attr_search_ctx *ctx,
  764              ATTR_TYPES prev_atype,
  765              char *prev_name)
  766 {
  767     if (!prev_atype && !prev_name)
  768         return 1;
  769 
  770     if (!ctx->attr->non_resident)
  771         return 1;
  772 
  773     if (prev_atype != ctx->attr->type)
  774         return 1;
  775 
  776     if (new_name(ctx, prev_name))
  777         return 1;
  778 
  779     if (opt.verbose) {
  780         print_inode(base_inode(ctx)->mft_no);
  781         print_attribute_ctx(ctx);
  782         printf("record %llu lowest_vcn %lld:    SKIPPED\n",
  783             (unsigned long long)ctx->ntfs_ino->mft_no,
  784             (long long)sle64_to_cpu(ctx->attr->lowest_vcn));
  785     }
  786 
  787     return 0;
  788 }
  789 
  790 static void set_prev(char **prev_name, ATTR_TYPES *prev_atype,
  791              char *name, ATTR_TYPES atype)
  792 {
  793     free_name(prev_name);
  794     if (name) {
  795         *prev_name = strdup(name);
  796         if (!*prev_name)
  797             perr_exit("strdup error");
  798     }
  799 
  800     *prev_atype = atype;
  801 }
  802 
  803 static void set_cmp_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name)
  804 {
  805     *atype = ctx->attr->type;
  806 
  807     free_name(name);
  808     *name = get_attr_name_ctx(ctx);
  809 }
  810 
  811 static int next_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name,
  812              int *err)
  813 {
  814     int ret;
  815 
  816     ret = ntfs_attrs_walk(ctx);
  817     *err = errno;
  818     if (ret) {
  819         *atype = AT_END;
  820         free_name(name);
  821     } else
  822         set_cmp_attr(ctx, atype, name);
  823 
  824     return ret;
  825 }
  826 
  827 static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2)
  828 {
  829     int ret = -1;
  830     int old_ret1, ret1 = 0, ret2 = 0;
  831     int errno1 = 0, errno2 = 0;
  832     char  *prev_name = NULL, *name1 = NULL, *name2 = NULL;
  833     ATTR_TYPES old_atype1, prev_atype = const_cpu_to_le32(0), atype1, atype2;
  834     ntfs_attr_search_ctx *ctx1, *ctx2;
  835 
  836     if (!(ctx1 = attr_get_search_ctx(ni1)))
  837         return -1;
  838     if (!(ctx2 = attr_get_search_ctx(ni2)))
  839         goto out;
  840 
  841     set_cmp_attr(ctx1, &atype1, &name1);
  842     set_cmp_attr(ctx2, &atype2, &name2);
  843 
  844     while (1) {
  845 
  846         old_atype1 = atype1;
  847         old_ret1 = ret1;
  848         if (!ret1 && (le32_to_cpu(atype1) <= le32_to_cpu(atype2) ||
  849                 ret2))
  850             ret1 = next_attr(ctx1, &atype1, &name1, &errno1);
  851         if (!ret2 && (le32_to_cpu(old_atype1) >= le32_to_cpu(atype2) ||
  852                     old_ret1))
  853             ret2 = next_attr(ctx2, &atype2, &name2, &errno2);
  854 
  855         print_attributes(ni1, atype1, atype2, name1, name2);
  856 
  857         if (ret1 && ret2) {
  858             if (errno1 != errno2) {
  859                 print_inode_ni(ni1);
  860                 printf("attribute walk (errno):   %d  !=  %d\n",
  861                        errno1, errno2);
  862             }
  863             break;
  864         }
  865 
  866         if (ret2 || le32_to_cpu(atype1) < le32_to_cpu(atype2)) {
  867             if (new_attribute(ctx1, prev_atype, prev_name)) {
  868                 print_ctx(ctx1);
  869                 printf("presence:   EXISTS   !=   MISSING\n");
  870                 set_prev(&prev_name, &prev_atype, name1,
  871                         atype1);
  872             }
  873 
  874         } else if (ret1 || le32_to_cpu(atype1) > le32_to_cpu(atype2)) {
  875             if (new_attribute(ctx2, prev_atype, prev_name)) {
  876                 print_ctx(ctx2);
  877                 printf("presence:   MISSING  !=  EXISTS \n");
  878                 set_prev(&prev_name, &prev_atype, name2, atype2);
  879             }
  880 
  881         } else /* atype1 == atype2 */ {
  882             if (new_attribute(ctx1, prev_atype, prev_name)) {
  883                 cmp_attribute(ctx1, ctx2);
  884                 set_prev(&prev_name, &prev_atype, name1, atype1);
  885             }
  886         }
  887     }
  888 
  889     free_name(&prev_name);
  890     ret = 0;
  891     ntfs_attr_put_search_ctx(ctx2);
  892 out:
  893     ntfs_attr_put_search_ctx(ctx1);
  894     return ret;
  895 }
  896 
  897 static int cmp_inodes(ntfs_volume *vol1, ntfs_volume *vol2)
  898 {
  899     u64 inode;
  900     int ret1, ret2;
  901     ntfs_inode *ni1, *ni2;
  902     struct progress_bar progress;
  903     int pb_flags = 0;   /* progress bar flags */
  904     u64 nr_mft_records, nr_mft_records2;
  905 
  906     if (opt.show_progress)
  907         pb_flags |= NTFS_PROGBAR;
  908 
  909     nr_mft_records  = get_nr_mft_records(vol1);
  910     nr_mft_records2 = get_nr_mft_records(vol2);
  911 
  912     if (nr_mft_records != nr_mft_records2) {
  913 
  914         printf("Number of mft records:   %lld  !=  %lld\n",
  915                (long long)nr_mft_records, (long long)nr_mft_records2);
  916 
  917         if (nr_mft_records > nr_mft_records2)
  918             nr_mft_records = nr_mft_records2;
  919     }
  920 
  921     progress_init(&progress, 0, nr_mft_records - 1, pb_flags);
  922     progress_update(&progress, 0);
  923 
  924     for (inode = 0; inode < nr_mft_records; inode++) {
  925 
  926         ret1 = inode_open(vol1, (MFT_REF)inode, &ni1);
  927         ret2 = inode_open(vol2, (MFT_REF)inode, &ni2);
  928 
  929         if (ret1 != ret2) {
  930             print_inode(inode);
  931             printf("open:   %s  !=  %s\n",
  932                    err2string(ret1), err2string(ret2));
  933             goto close_inodes;
  934         }
  935 
  936         if (ret1 != NTFSCMP_OK)
  937             goto close_inodes;
  938 
  939         if (cmp_attributes(ni1, ni2) != 0) {
  940             inode_close(ni1);
  941             inode_close(ni2);
  942             return -1;
  943         }
  944 close_inodes:
  945         if (inode_close(ni1) != 0)
  946             return -1;
  947         if (inode_close(ni2) != 0)
  948             return -1;
  949 
  950         progress_update(&progress, inode);
  951     }
  952     return 0;
  953 }
  954 
  955 static ntfs_volume *mount_volume(const char *volume)
  956 {
  957     unsigned long mntflag;
  958     ntfs_volume *vol = NULL;
  959 
  960     if (ntfs_check_if_mounted(volume, &mntflag)) {
  961         perr_println("Failed to check '%s' mount state", volume);
  962         printf("Probably /etc/mtab is missing. It's too risky to "
  963                "continue. You might try\nan another Linux distro.\n");
  964         exit(1);
  965     }
  966     if (mntflag & NTFS_MF_MOUNTED) {
  967         if (!(mntflag & NTFS_MF_READONLY))
  968             err_exit("Device '%s' is mounted read-write. "
  969                  "You must 'umount' it first.\n", volume);
  970     }
  971 
  972     vol = ntfs_mount(volume, NTFS_MNT_RDONLY);
  973     if (vol == NULL) {
  974 
  975         int err = errno;
  976 
  977         perr_println("Opening '%s' as NTFS failed", volume);
  978         if (err == EINVAL)
  979             printf(invalid_ntfs_msg, volume);
  980         else if (err == EIO)
  981             puts(corrupt_volume_msg);
  982         else if (err == EPERM)
  983             puts(hibernated_volume_msg);
  984         exit(1);
  985     }
  986 
  987     return vol;
  988 }
  989 
  990 int main(int argc, char **argv)
  991 {
  992     ntfs_volume *vol1;
  993     ntfs_volume *vol2;
  994 
  995     printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION);
  996 
  997     parse_options(argc, argv);
  998 
  999     utils_set_locale();
 1000 
 1001     vol1 = mount_volume(opt.vol1);
 1002         vol2 = mount_volume(opt.vol2);
 1003 
 1004     if (cmp_inodes(vol1, vol2) != 0)
 1005         exit(1);
 1006 
 1007     ntfs_umount(vol1, FALSE);
 1008     ntfs_umount(vol2, FALSE);
 1009 
 1010     return (0);
 1011 }
 1012