• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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