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