summaryrefslogtreecommitdiff
path: root/ntfsprogs/ntfscmp.c (plain)
blob: 469d1d9a4a19628976bcd6c066033c5de4b7541c
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
42static const char *EXEC_NAME = "ntfscmp";
43
44static 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
49static 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
54static 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
59static 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
71struct 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)))
85static 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)))
105static 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)))
124static 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)))
144static 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))
167static 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
185static 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
258static 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
268static 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
277static 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
297static 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
305static 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
318static 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
330static 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
341static const char *err2string(int err)
342{
343 return ntfscmp_errs[err];
344}
345
346static const char *pret2str(void *p)
347{
348 if (p == NULL)
349 return "FAILED";
350 return "OK";
351}
352
353static 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
377static 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
385static void print_inode(u64 inum)
386{
387 printf("Inode %llu ", (unsigned long long)inum);
388}
389
390static void print_inode_ni(ntfs_inode *ni)
391{
392 print_inode(inumber(ni));
393}
394
395static void print_attribute_type(ATTR_TYPES atype)
396{
397 printf("attribute 0x%x", atype);
398}
399
400static 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
410static void free_name(char **name)
411{
412 if (*name) {
413 free(*name);
414 *name = NULL;
415 }
416}
417
418static 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
444static 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
449static 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
457static void print_attribute(ATTR_TYPES atype, char *name)
458{
459 print_attribute_type(atype);
460 print_attribute_name(name);
461 printf(" ");
462}
463
464static 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
472static 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
479static 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
487static void print_differ(ntfs_attr *na)
488{
489 print_na(na);
490 printf("content: DIFFER\n");
491}
492
493static 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
502struct cmp_ia {
503 INDEX_ALLOCATION *ia;
504 INDEX_ALLOCATION *tmp_ia;
505 u8 *bitmap;
506 u8 *byte;
507 s64 bm_size;
508};
509
510static 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;
530free_ia:
531 free(cia->ia);
532free_bm:
533 free(cia->bitmap);
534 return -1;
535}
536
537static 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 != cia1.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 }
592out:
593 free(cia1.ia);
594 free(cia2.ia);
595 free(cia1.bitmap);
596 free(cia2.bitmap);
597 return;
598}
599
600static 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
654static 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
671static 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
715close_attribs:
716 ntfs_attr_close(na1);
717 ntfs_attr_close(na2);
718}
719
720static void vprint_attribute(ATTR_TYPES atype, char *name)
721{
722 if (!opt.verbose)
723 return;
724
725 printf("0x%x", atype);
726 if (name)
727 printf(":%s", name);
728 printf(" ");
729}
730
731static 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
747static 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
763static 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)ctx->attr->lowest_vcn);
785 }
786
787 return 0;
788}
789
790static 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
803static 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
811static 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
827static 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 = 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);
892out:
893 ntfs_attr_put_search_ctx(ctx1);
894 return ret;
895}
896
897static 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 }
944close_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
955static 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
990int 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
1013