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