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 "compat.h" 37 #include "utils.h" 38 #include "mst.h" 39 #include "version.h" 40 #include "support.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->u.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", inumber(ni)); 312 return -1; 313 } 314 return 0; 315 } 316 317 static inline s64 get_nr_mft_records(ntfs_volume *vol) 318 { 319 return vol->mft_na->initialized_size >> vol->mft_record_size_bits; 320 } 321 322 #define NTFSCMP_OK 0 323 #define NTFSCMP_INODE_OPEN_ERROR 1 324 #define NTFSCMP_INODE_OPEN_IO_ERROR 2 325 #define NTFSCMP_INODE_OPEN_ENOENT_ERROR 3 326 #define NTFSCMP_EXTENSION_RECORD 4 327 #define NTFSCMP_INODE_CLOSE_ERROR 5 328 329 static const char *ntfscmp_errs[] = { 330 "OK", 331 "INODE_OPEN_ERROR", 332 "INODE_OPEN_IO_ERROR", 333 "INODE_OPEN_ENOENT_ERROR", 334 "EXTENSION_RECORD", 335 "INODE_CLOSE_ERROR", 336 "" 337 }; 338 339 340 static const char *err2string(int err) 341 { 342 return ntfscmp_errs[err]; 343 } 344 345 static const char *pret2str(void *p) 346 { 347 if (p == NULL) 348 return "FAILED"; 349 return "OK"; 350 } 351 352 static int inode_open(ntfs_volume *vol, MFT_REF mref, ntfs_inode **ni) 353 { 354 *ni = ntfs_inode_open(vol, mref); 355 if (*ni == NULL) { 356 if (errno == EIO) 357 return NTFSCMP_INODE_OPEN_IO_ERROR; 358 if (errno == ENOENT) 359 return NTFSCMP_INODE_OPEN_ENOENT_ERROR; 360 361 perr_println("Reading inode %lld failed", mref); 362 return NTFSCMP_INODE_OPEN_ERROR; 363 } 364 365 if ((*ni)->mrec->base_mft_record) { 366 367 if (inode_close(*ni) != 0) 368 return NTFSCMP_INODE_CLOSE_ERROR; 369 370 return NTFSCMP_EXTENSION_RECORD; 371 } 372 373 return NTFSCMP_OK; 374 } 375 376 static ntfs_inode *base_inode(ntfs_attr_search_ctx *ctx) 377 { 378 if (ctx->base_ntfs_ino) 379 return ctx->base_ntfs_ino; 380 381 return ctx->ntfs_ino; 382 } 383 384 static void print_inode(u64 inum) 385 { 386 printf("Inode %llu ", inum); 387 } 388 389 static void print_inode_ni(ntfs_inode *ni) 390 { 391 print_inode(inumber(ni)); 392 } 393 394 static void print_attribute_type(ATTR_TYPES atype) 395 { 396 printf("attribute 0x%x", atype); 397 } 398 399 static void print_attribute_name(char *name) 400 { 401 if (name) 402 printf(":%s", name); 403 } 404 405 #define GET_ATTR_NAME(a) \ 406 ((ntfschar *)(((u8 *)(a)) + le16_to_cpu((a)->name_offset))), \ 407 ((a)->name_length) 408 409 static void free_name(char **name) 410 { 411 if (*name) { 412 free(*name); 413 *name = NULL; 414 } 415 } 416 417 static char *get_attr_name(u64 mft_no, 418 ATTR_TYPES atype, 419 const ntfschar *uname, 420 const int uname_len) 421 { 422 char *name = NULL; 423 int name_len; 424 425 if (atype == AT_END) 426 return NULL; 427 428 name_len = ntfs_ucstombs(uname, uname_len, &name, 0); 429 if (name_len < 0) { 430 perr_print("ntfs_ucstombs"); 431 print_inode(mft_no); 432 print_attribute_type(atype); 433 puts(""); 434 exit(1); 435 436 } else if (name_len > 0) 437 return name; 438 439 free_name(&name); 440 return NULL; 441 } 442 443 static char *get_attr_name_na(ntfs_attr *na) 444 { 445 return get_attr_name(inumber(na->ni), na->type, na->name, na->name_len); 446 } 447 448 static char *get_attr_name_ctx(ntfs_attr_search_ctx *ctx) 449 { 450 u64 mft_no = inumber(ctx->ntfs_ino); 451 ATTR_TYPES atype = ctx->attr->type; 452 453 return get_attr_name(mft_no, atype, GET_ATTR_NAME(ctx->attr)); 454 } 455 456 static void print_attribute(ATTR_TYPES atype, char *name) 457 { 458 print_attribute_type(atype); 459 print_attribute_name(name); 460 printf(" "); 461 } 462 463 static void print_na(ntfs_attr *na) 464 { 465 char *name = get_attr_name_na(na); 466 print_inode_ni(na->ni); 467 print_attribute(na->type, name); 468 free_name(&name); 469 } 470 471 static void print_attribute_ctx(ntfs_attr_search_ctx *ctx) 472 { 473 char *name = get_attr_name_ctx(ctx); 474 print_attribute(ctx->attr->type, name); 475 free_name(&name); 476 } 477 478 static void print_ctx(ntfs_attr_search_ctx *ctx) 479 { 480 char *name = get_attr_name_ctx(ctx); 481 print_inode_ni(base_inode(ctx)); 482 print_attribute(ctx->attr->type, name); 483 free_name(&name); 484 } 485 486 static void print_differ(ntfs_attr *na) 487 { 488 print_na(na); 489 printf("content: DIFFER\n"); 490 } 491 492 static int cmp_buffer(u8 *buf1, u8 *buf2, long long int size, ntfs_attr *na) 493 { 494 if (memcmp(buf1, buf2, size)) { 495 print_differ(na); 496 return -1; 497 } 498 return 0; 499 } 500 501 struct cmp_ia { 502 INDEX_ALLOCATION *ia; 503 INDEX_ALLOCATION *tmp_ia; 504 u8 *bitmap; 505 u8 *byte; 506 s64 bm_size; 507 }; 508 509 static int setup_cmp_ia(ntfs_attr *na, struct cmp_ia *cia) 510 { 511 cia->bitmap = ntfs_attr_readall(na->ni, AT_BITMAP, na->name, 512 na->name_len, &cia->bm_size); 513 if (!cia->bitmap) { 514 perr_println("Failed to readall BITMAP"); 515 return -1; 516 } 517 cia->byte = cia->bitmap; 518 519 cia->tmp_ia = cia->ia = ntfs_malloc(na->data_size); 520 if (!cia->tmp_ia) 521 goto free_bm; 522 523 if (ntfs_attr_pread(na, 0, na->data_size, cia->ia) != na->data_size) { 524 perr_println("Failed to pread INDEX_ALLOCATION"); 525 goto free_ia; 526 } 527 528 return 0; 529 free_ia: 530 free(cia->ia); 531 free_bm: 532 free(cia->bitmap); 533 return -1; 534 } 535 536 static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2) 537 { 538 struct cmp_ia cia1, cia2; 539 int bit, ret1, ret2; 540 u32 ib_size; 541 542 if (setup_cmp_ia(na1, &cia1)) 543 return; 544 if (setup_cmp_ia(na2, &cia2)) 545 return; 546 /* 547 * FIXME: ia can be the same even if the bitmap sizes are different. 548 */ 549 if (cia1.bm_size != cia1.bm_size) 550 goto out; 551 552 if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1)) 553 goto out; 554 555 if (cmp_buffer((u8 *)cia1.ia, (u8 *)cia2.ia, 0x18, na1)) 556 goto out; 557 558 ib_size = le32_to_cpu(cia1.ia->index.allocated_size) + 0x18; 559 560 bit = 0; 561 while ((u8 *)cia1.tmp_ia < (u8 *)cia1.ia + na1->data_size) { 562 if (*cia1.byte & (1 << bit)) { 563 ret1 = ntfs_mst_post_read_fixup((NTFS_RECORD *) 564 cia1.tmp_ia, ib_size); 565 ret2 = ntfs_mst_post_read_fixup((NTFS_RECORD *) 566 cia2.tmp_ia, ib_size); 567 if (ret1 != ret2) { 568 print_differ(na1); 569 goto out; 570 } 571 572 if (ret1 == -1) 573 continue; 574 575 if (cmp_buffer(((u8 *)cia1.tmp_ia) + 0x18, 576 ((u8 *)cia2.tmp_ia) + 0x18, 577 le32_to_cpu(cia1.ia-> 578 index.index_length), na1)) 579 goto out; 580 } 581 582 cia1.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia1.tmp_ia + ib_size); 583 cia2.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia2.tmp_ia + ib_size); 584 585 bit++; 586 if (bit > 7) { 587 bit = 0; 588 cia1.byte++; 589 } 590 } 591 out: 592 free(cia1.ia); 593 free(cia2.ia); 594 free(cia1.bitmap); 595 free(cia2.bitmap); 596 return; 597 } 598 599 static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2) 600 { 601 s64 pos; 602 s64 count1 = 0, count2; 603 u8 buf1[NTFS_BUF_SIZE]; 604 u8 buf2[NTFS_BUF_SIZE]; 605 606 for (pos = 0; pos <= na1->data_size; pos += count1) { 607 608 count1 = ntfs_attr_pread(na1, pos, NTFS_BUF_SIZE, buf1); 609 count2 = ntfs_attr_pread(na2, pos, NTFS_BUF_SIZE, buf2); 610 611 if (count1 != count2) { 612 print_na(na1); 613 printf("abrupt length: %lld != %lld ", 614 na1->data_size, na2->data_size); 615 printf("(count: %lld != %lld)", count1, count2); 616 puts(""); 617 return; 618 } 619 620 if (count1 == -1) { 621 err_printf("%s read error: ", "cmp_attribute_data"); 622 print_na(na1); 623 printf("len = %lld, pos = %lld\n", na1->data_size, pos); 624 exit(1); 625 } 626 627 if (count1 == 0) { 628 629 if (pos + count1 == na1->data_size) 630 return; /* we are ready */ 631 632 err_printf("%s read error before EOF: ", "cmp_attribute_data"); 633 print_na(na1); 634 printf("%lld != %lld\n", pos + count1, na1->data_size); 635 exit(1); 636 } 637 638 if (cmp_buffer(buf1, buf2, count1, na1)) 639 return; 640 } 641 642 err_printf("%s read overrun: ", "cmp_attribute_data"); 643 print_na(na1); 644 err_printf("(len = %lld, pos = %lld, count = %lld)\n", 645 na1->data_size, pos, count1); 646 exit(1); 647 } 648 649 static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2) 650 { 651 u32 header_size = offsetof(ATTR_RECORD, u.res.resident_end); 652 653 if (a1->non_resident != a2->non_resident) 654 return 1; 655 656 if (a1->non_resident) { 657 /* 658 * FIXME: includes paddings which are not handled by ntfsinfo! 659 */ 660 header_size = le32_to_cpu(a1->length); 661 } 662 663 return memcmp(a1, a2, header_size); 664 } 665 666 static void cmp_attribute(ntfs_attr_search_ctx *ctx1, 667 ntfs_attr_search_ctx *ctx2) 668 { 669 ATTR_RECORD *a1 = ctx1->attr; 670 ATTR_RECORD *a2 = ctx2->attr; 671 ntfs_attr *na1, *na2; 672 673 if (cmp_attribute_header(a1, a2)) { 674 print_ctx(ctx1); 675 printf("header: DIFFER\n"); 676 } 677 678 na1 = ntfs_attr_open(base_inode(ctx1), a1->type, GET_ATTR_NAME(a1)); 679 na2 = ntfs_attr_open(base_inode(ctx2), a2->type, GET_ATTR_NAME(a2)); 680 681 if ((!na1 && na2) || (na1 && !na2)) { 682 print_ctx(ctx1); 683 printf("open: %s != %s\n", pret2str(na1), pret2str(na2)); 684 goto close_attribs; 685 } 686 687 if (na1 == NULL) 688 goto close_attribs; 689 690 if (na1->data_size != na2->data_size) { 691 print_na(na1); 692 printf("length: %lld != %lld\n", na1->data_size, na2->data_size); 693 goto close_attribs; 694 } 695 696 if (ntfs_inode_badclus_bad(inumber(ctx1->ntfs_ino), ctx1->attr) == 1) { 697 /* 698 * If difference exists then it's already reported at the 699 * attribute header since the mapping pairs must differ. 700 */ 701 return; 702 } 703 704 if (na1->type == AT_INDEX_ALLOCATION) 705 cmp_index_allocation(na1, na2); 706 else 707 cmp_attribute_data(na1, na2); 708 709 close_attribs: 710 ntfs_attr_close(na1); 711 ntfs_attr_close(na2); 712 } 713 714 static void vprint_attribute(ATTR_TYPES atype, char *name) 715 { 716 if (!opt.verbose) 717 return; 718 719 printf("0x%x", atype); 720 if (name) 721 printf(":%s", name); 722 printf(" "); 723 } 724 725 static void print_attributes(ntfs_inode *ni, 726 ATTR_TYPES atype1, 727 ATTR_TYPES atype2, 728 char *name1, 729 char *name2) 730 { 731 if (!opt.verbose) 732 return; 733 734 printf("Walking inode %llu attributes: ", inumber(ni)); 735 vprint_attribute(atype1, name1); 736 vprint_attribute(atype2, name2); 737 printf("\n"); 738 } 739 740 static int new_name(ntfs_attr_search_ctx *ctx, char *prev_name) 741 { 742 int ret = 0; 743 char *name = get_attr_name_ctx(ctx); 744 745 if (prev_name && name) { 746 if (strcmp(prev_name, name) != 0) 747 ret = 1; 748 } else if (prev_name || name) 749 ret = 1; 750 751 free_name(&name); 752 return ret; 753 754 } 755 756 static int new_attribute(ntfs_attr_search_ctx *ctx, 757 ATTR_TYPES prev_atype, 758 char *prev_name) 759 { 760 if (!prev_atype && !prev_name) 761 return 1; 762 763 if (!ctx->attr->non_resident) 764 return 1; 765 766 if (prev_atype != ctx->attr->type) 767 return 1; 768 769 if (new_name(ctx, prev_name)) 770 return 1; 771 772 if (opt.verbose) { 773 print_inode(base_inode(ctx)->mft_no); 774 print_attribute_ctx(ctx); 775 printf("record %llu lowest_vcn %lld: SKIPPED\n", 776 ctx->ntfs_ino->mft_no, ctx->attr->u.nonres.lowest_vcn); 777 } 778 779 return 0; 780 } 781 782 static void set_prev(char **prev_name, ATTR_TYPES *prev_atype, 783 char *name, ATTR_TYPES atype) 784 { 785 free_name(prev_name); 786 if (name) { 787 *prev_name = strdup(name); 788 if (!*prev_name) 789 perr_exit("strdup error"); 790 } 791 792 *prev_atype = atype; 793 } 794 795 static void set_cmp_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name) 796 { 797 *atype = ctx->attr->type; 798 799 free_name(name); 800 *name = get_attr_name_ctx(ctx); 801 } 802 803 static int next_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name, 804 int *err) 805 { 806 int ret; 807 808 ret = ntfs_attrs_walk(ctx); 809 *err = errno; 810 if (ret) { 811 *atype = AT_END; 812 free_name(name); 813 } else 814 set_cmp_attr(ctx, atype, name); 815 816 return ret; 817 } 818 819 static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2) 820 { 821 int ret = -1; 822 int old_ret1, ret1 = 0, ret2 = 0; 823 int errno1 = 0, errno2 = 0; 824 char *prev_name = NULL, *name1 = NULL, *name2 = NULL; 825 ATTR_TYPES old_atype1, prev_atype = 0, atype1, atype2; 826 ntfs_attr_search_ctx *ctx1, *ctx2; 827 828 if (!(ctx1 = attr_get_search_ctx(ni1))) 829 return -1; 830 if (!(ctx2 = attr_get_search_ctx(ni2))) 831 goto out; 832 833 set_cmp_attr(ctx1, &atype1, &name1); 834 set_cmp_attr(ctx2, &atype2, &name2); 835 836 while (1) { 837 838 old_atype1 = atype1; 839 old_ret1 = ret1; 840 if (!ret1 && (le32_to_cpu(atype1) <= le32_to_cpu(atype2) || 841 ret2)) 842 ret1 = next_attr(ctx1, &atype1, &name1, &errno1); 843 if (!ret2 && (le32_to_cpu(old_atype1) >= le32_to_cpu(atype2) || 844 old_ret1)) 845 ret2 = next_attr(ctx2, &atype2, &name2, &errno2); 846 847 print_attributes(ni1, atype1, atype2, name1, name2); 848 849 if (ret1 && ret2) { 850 if (errno1 != errno2) { 851 print_inode_ni(ni1); 852 printf("attribute walk (errno): %d != %d\n", 853 errno1, errno2); 854 } 855 break; 856 } 857 858 if (ret2 || le32_to_cpu(atype1) < le32_to_cpu(atype2)) { 859 if (new_attribute(ctx1, prev_atype, prev_name)) { 860 print_ctx(ctx1); 861 printf("presence: EXISTS != MISSING\n"); 862 set_prev(&prev_name, &prev_atype, name1, 863 atype1); 864 } 865 866 } else if (ret1 || le32_to_cpu(atype1) > le32_to_cpu(atype2)) { 867 if (new_attribute(ctx2, prev_atype, prev_name)) { 868 print_ctx(ctx2); 869 printf("presence: MISSING != EXISTS \n"); 870 set_prev(&prev_name, &prev_atype, name2, atype2); 871 } 872 873 } else /* atype1 == atype2 */ { 874 if (new_attribute(ctx1, prev_atype, prev_name)) { 875 cmp_attribute(ctx1, ctx2); 876 set_prev(&prev_name, &prev_atype, name1, atype1); 877 } 878 } 879 } 880 881 free_name(&prev_name); 882 ret = 0; 883 ntfs_attr_put_search_ctx(ctx2); 884 out: 885 ntfs_attr_put_search_ctx(ctx1); 886 return ret; 887 } 888 889 static int cmp_inodes(ntfs_volume *vol1, ntfs_volume *vol2) 890 { 891 u64 inode; 892 int ret1, ret2; 893 ntfs_inode *ni1, *ni2; 894 struct progress_bar progress; 895 int pb_flags = 0; /* progress bar flags */ 896 u64 nr_mft_records, nr_mft_records2; 897 898 if (opt.show_progress) 899 pb_flags |= NTFS_PROGBAR; 900 901 nr_mft_records = get_nr_mft_records(vol1); 902 nr_mft_records2 = get_nr_mft_records(vol2); 903 904 if (nr_mft_records != nr_mft_records2) { 905 906 printf("Number of mft records: %lld != %lld\n", 907 nr_mft_records, nr_mft_records2); 908 909 if (nr_mft_records > nr_mft_records2) 910 nr_mft_records = nr_mft_records2; 911 } 912 913 progress_init(&progress, 0, nr_mft_records - 1, pb_flags); 914 progress_update(&progress, 0); 915 916 for (inode = 0; inode < nr_mft_records; inode++) { 917 918 ret1 = inode_open(vol1, (MFT_REF)inode, &ni1); 919 ret2 = inode_open(vol2, (MFT_REF)inode, &ni2); 920 921 if (ret1 != ret2) { 922 print_inode(inode); 923 printf("open: %s != %s\n", 924 err2string(ret1), err2string(ret2)); 925 goto close_inodes; 926 } 927 928 if (ret1 != NTFSCMP_OK) 929 goto close_inodes; 930 931 if (cmp_attributes(ni1, ni2) != 0) { 932 inode_close(ni1); 933 inode_close(ni2); 934 return -1; 935 } 936 close_inodes: 937 if (inode_close(ni1) != 0) 938 return -1; 939 if (inode_close(ni2) != 0) 940 return -1; 941 942 progress_update(&progress, inode); 943 } 944 return 0; 945 } 946 947 static ntfs_volume *mount_volume(const char *volume) 948 { 949 unsigned long mntflag; 950 ntfs_volume *vol = NULL; 951 952 if (ntfs_check_if_mounted(volume, &mntflag)) { 953 perr_println("Failed to check '%s' mount state", volume); 954 printf("Probably /etc/mtab is missing. It's too risky to " 955 "continue. You might try\nan another Linux distro.\n"); 956 exit(1); 957 } 958 if (mntflag & NTFS_MF_MOUNTED) { 959 if (!(mntflag & NTFS_MF_READONLY)) 960 err_exit("Device '%s' is mounted read-write. " 961 "You must 'umount' it first.\n", volume); 962 } 963 964 vol = ntfs_mount(volume, NTFS_MNT_RDONLY); 965 if (vol == NULL) { 966 967 int err = errno; 968 969 perr_println("Opening '%s' as NTFS failed", volume); 970 if (err == EINVAL) 971 printf(invalid_ntfs_msg, volume); 972 else if (err == EIO) 973 puts(corrupt_volume_msg); 974 else if (err == EPERM) 975 puts(hibernated_volume_msg); 976 exit(1); 977 } 978 979 return vol; 980 } 981 982 int main(int argc, char **argv) 983 { 984 ntfs_volume *vol1; 985 ntfs_volume *vol2; 986 987 printf("%s v%s (libntfs %s)\n", EXEC_NAME, VERSION, 988 ntfs_libntfs_version()); 989 990 parse_options(argc, argv); 991 992 utils_set_locale(); 993 994 vol1 = mount_volume(opt.vol1); 995 vol2 = mount_volume(opt.vol2); 996 997 if (cmp_inodes(vol1, vol2) != 0) 998 exit(1); 999 1000 ntfs_umount(vol1, FALSE); 1001 ntfs_umount(vol2, FALSE); 1002 1003 exit(0); 1004 } 1005