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