xref: /onnv-gate/usr/src/cmd/ntfsprogs/ntfscmp.c (revision 9663:ace9a2ac3683)
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 "utils.h"
37 #include "mst.h"
38 #include "version.h"
39 #include "support.h"
40 
41 static const char *EXEC_NAME = "ntfscmp";
42 
43 static const char *invalid_ntfs_msg =
44 "Apparently device '%s' doesn't have a valid NTFS.\n"
45 "Maybe you selected the wrong partition? Or the whole disk instead of a\n"
46 "partition (e.g. /dev/hda, not /dev/hda1)?\n";
47 
48 static const char *corrupt_volume_msg =
49 "Apparently you have a corrupted NTFS. Please run the filesystem checker\n"
50 "on Windows by invoking chkdsk /f. Don't forget the /f (force) parameter,\n"
51 "it's important! You probably also need to reboot Windows to take effect.\n";
52 
53 static const char *hibernated_volume_msg =
54 "Apparently the NTFS partition is hibernated. Windows must be resumed and\n"
55 "turned off properly\n";
56 
57 
58 static struct {
59 	int debug;
60 	int show_progress;
61 	int verbose;
62 	char *vol1;
63 	char *vol2;
64 } opt;
65 
66 
67 #define NTFS_PROGBAR		0x0001
68 #define NTFS_PROGBAR_SUPPRESS	0x0002
69 
70 struct progress_bar {
71 	u64 start;
72 	u64 stop;
73 	int resolution;
74 	int flags;
75 	float unit;
76 };
77 
78 /* WARNING: don't modify the text, external tools grep for it */
79 #define ERR_PREFIX   "ERROR"
80 #define PERR_PREFIX  ERR_PREFIX "(%d): "
81 #define NERR_PREFIX  ERR_PREFIX ": "
82 
83 __attribute__((format(printf, 2, 3)))
84 static void perr_printf(int newline, const char *fmt, ...)
85 {
86 	va_list ap;
87 	int eo = errno;
88 
89 	fprintf(stdout, PERR_PREFIX, eo);
90 	va_start(ap, fmt);
91 	vfprintf(stdout, fmt, ap);
92 	va_end(ap);
93 	fprintf(stdout, ": %s", strerror(eo));
94 	if (newline)
95 		fprintf(stdout, "\n");
96 	fflush(stdout);
97 	fflush(stderr);
98 }
99 
100 #define perr_print(...)     perr_printf(0, __VA_ARGS__)
101 #define perr_println(...)   perr_printf(1, __VA_ARGS__)
102 
103 __attribute__((format(printf, 1, 2)))
104 static void err_printf(const char *fmt, ...)
105 {
106 	va_list ap;
107 
108 	fprintf(stdout, NERR_PREFIX);
109 	va_start(ap, fmt);
110 	vfprintf(stdout, fmt, ap);
111 	va_end(ap);
112 	fflush(stdout);
113 	fflush(stderr);
114 }
115 
116 /**
117  * err_exit
118  *
119  * Print and error message and exit the program.
120  */
121 __attribute__((noreturn))
122 __attribute__((format(printf, 1, 2)))
123 static int err_exit(const char *fmt, ...)
124 {
125 	va_list ap;
126 
127 	fprintf(stdout, NERR_PREFIX);
128 	va_start(ap, fmt);
129 	vfprintf(stdout, fmt, ap);
130 	va_end(ap);
131 	fflush(stdout);
132 	fflush(stderr);
133 	exit(1);
134 }
135 
136 /**
137  * perr_exit
138  *
139  * Print and error message and exit the program
140  */
141 __attribute__((noreturn))
142 __attribute__((format(printf, 1, 2)))
143 static int perr_exit(const char *fmt, ...)
144 {
145 	va_list ap;
146 	int eo = errno;
147 
148 	fprintf(stdout, PERR_PREFIX, eo);
149 	va_start(ap, fmt);
150 	vfprintf(stdout, fmt, ap);
151 	va_end(ap);
152 	printf(": %s\n", strerror(eo));
153 	fflush(stdout);
154 	fflush(stderr);
155 	exit(1);
156 }
157 
158 /**
159  * usage - Print a list of the parameters to the program
160  *
161  * Print a list of the parameters and options for the program.
162  *
163  * Return:  none
164  */
165 __attribute__((noreturn))
166 static void usage(void)
167 {
168 
169 	printf("\nUsage: %s [OPTIONS] DEVICE1 DEVICE2\n"
170 		"    Compare two NTFS volumes and tell the differences.\n"
171 		"\n"
172 		"    -P, --no-progress-bar  Don't show progress bar\n"
173 		"    -v, --verbose          More output\n"
174 		"    -h, --help             Display this help\n"
175 #ifdef DEBUG
176 		"    -d, --debug            Show debug information\n"
177 #endif
178 		"\n", EXEC_NAME);
179 	printf("%s%s", ntfs_bugs, ntfs_home);
180 	exit(1);
181 }
182 
183 
184 static void parse_options(int argc, char **argv)
185 {
186 	static const char *sopt = "-dhPv";
187 	static const struct option lopt[] = {
188 #ifdef DEBUG
189 		{ "debug",		no_argument,	NULL, 'd' },
190 #endif
191 		{ "help",		no_argument,	NULL, 'h' },
192 		{ "no-progress-bar",	no_argument,	NULL, 'P' },
193 		{ "verbose",		no_argument,	NULL, 'v' },
194 		{ NULL, 0, NULL, 0 }
195 	};
196 
197 	int c;
198 
199 	memset(&opt, 0, sizeof(opt));
200 	opt.show_progress = 1;
201 
202 	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
203 		switch (c) {
204 		case 1:	/* A non-option argument */
205 			if (!opt.vol1) {
206 				opt.vol1 = argv[optind - 1];
207 			} else if (!opt.vol2) {
208 				opt.vol2 = argv[optind - 1];
209 			} else {
210 				err_printf("Too many arguments!\n");
211 				usage();
212 			}
213 			break;
214 #ifdef DEBUG
215 		case 'd':
216 			opt.debug++;
217 			break;
218 #endif
219 		case 'h':
220 		case '?':
221 			usage();
222 		case 'P':
223 			opt.show_progress = 0;
224 			break;
225 		case 'v':
226 			opt.verbose++;
227 			break;
228 		default:
229 			err_printf("Unknown option '%s'.\n", argv[optind - 1]);
230 			usage();
231 			break;
232 		}
233 	}
234 
235 	if (opt.vol1 == NULL || opt.vol2 == NULL) {
236 		err_printf("You must specify exactly 2 volumes.\n");
237 		usage();
238 	}
239 
240 	/* Redirect stderr to stdout, note fflush()es are essential! */
241 	fflush(stdout);
242 	fflush(stderr);
243 	if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
244 		perror("Failed to redirect stderr to stdout");
245 		exit(1);
246 	}
247 	fflush(stdout);
248 	fflush(stderr);
249 
250 #ifdef DEBUG
251 	 if (!opt.debug)
252 		if (!freopen("/dev/null", "w", stderr))
253 			perr_exit("Failed to redirect stderr to /dev/null");
254 #endif
255 }
256 
257 static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni)
258 {
259 	ntfs_attr_search_ctx *ret;
260 
261 	if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL)
262 		perr_println("ntfs_attr_get_search_ctx");
263 
264 	return ret;
265 }
266 
267 static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags)
268 {
269 	p->start = start;
270 	p->stop = stop;
271 	p->unit = 100.0 / (stop - start);
272 	p->resolution = 100;
273 	p->flags = flags;
274 }
275 
276 static void progress_update(struct progress_bar *p, u64 current)
277 {
278 	float percent;
279 
280 	if (!(p->flags & NTFS_PROGBAR))
281 		return;
282 	if (p->flags & NTFS_PROGBAR_SUPPRESS)
283 		return;
284 
285 	/* WARNING: don't modify the texts, external tools grep for them */
286 	percent = p->unit * current;
287 	if (current != p->stop) {
288 		if ((current - p->start) % p->resolution)
289 			return;
290 		printf("%6.2f percent completed\r", percent);
291 	} else
292 		printf("100.00 percent completed\n");
293 	fflush(stdout);
294 }
295 
296 static u64 inumber(ntfs_inode *ni)
297 {
298 	if (ni->nr_extents >= 0)
299 		return ni->mft_no;
300 
301 	return ni->u.base_ni->mft_no;
302 }
303 
304 static int inode_close(ntfs_inode *ni)
305 {
306 	if (ni == NULL)
307 		return 0;
308 
309 	if (ntfs_inode_close(ni)) {
310 		perr_println("ntfs_inode_close: inode %llu", inumber(ni));
311 		return -1;
312 	}
313 	return 0;
314 }
315 
316 static inline s64 get_nr_mft_records(ntfs_volume *vol)
317 {
318 	return vol->mft_na->initialized_size >> vol->mft_record_size_bits;
319 }
320 
321 #define  NTFSCMP_OK				0
322 #define  NTFSCMP_INODE_OPEN_ERROR		1
323 #define  NTFSCMP_INODE_OPEN_IO_ERROR		2
324 #define  NTFSCMP_INODE_OPEN_ENOENT_ERROR	3
325 #define  NTFSCMP_EXTENSION_RECORD		4
326 #define  NTFSCMP_INODE_CLOSE_ERROR		5
327 
328 static const char *ntfscmp_errs[] = {
329 	"OK",
330 	"INODE_OPEN_ERROR",
331 	"INODE_OPEN_IO_ERROR",
332 	"INODE_OPEN_ENOENT_ERROR",
333 	"EXTENSION_RECORD",
334 	"INODE_CLOSE_ERROR",
335 	""
336 };
337 
338 
339 static const char *err2string(int err)
340 {
341 	return ntfscmp_errs[err];
342 }
343 
344 static const char *pret2str(void *p)
345 {
346 	if (p == NULL)
347 		return "FAILED";
348 	return "OK";
349 }
350 
351 static int inode_open(ntfs_volume *vol, MFT_REF mref, ntfs_inode **ni)
352 {
353 	*ni = ntfs_inode_open(vol, mref);
354 	if (*ni == NULL) {
355 		if (errno == EIO)
356 			return NTFSCMP_INODE_OPEN_IO_ERROR;
357 		if (errno == ENOENT)
358 			return NTFSCMP_INODE_OPEN_ENOENT_ERROR;
359 
360 		perr_println("Reading inode %lld failed", mref);
361 		return NTFSCMP_INODE_OPEN_ERROR;
362 	}
363 
364 	if ((*ni)->mrec->base_mft_record) {
365 
366 		if (inode_close(*ni) != 0)
367 			return NTFSCMP_INODE_CLOSE_ERROR;
368 
369 		return NTFSCMP_EXTENSION_RECORD;
370 	}
371 
372 	return NTFSCMP_OK;
373 }
374 
375 static ntfs_inode *base_inode(ntfs_attr_search_ctx *ctx)
376 {
377 	if (ctx->base_ntfs_ino)
378 		return ctx->base_ntfs_ino;
379 
380 	return ctx->ntfs_ino;
381 }
382 
383 static void print_inode(u64 inum)
384 {
385 	printf("Inode %llu ", inum);
386 }
387 
388 static void print_inode_ni(ntfs_inode *ni)
389 {
390 	print_inode(inumber(ni));
391 }
392 
393 static void print_attribute_type(ATTR_TYPES atype)
394 {
395 	printf("attribute 0x%x", atype);
396 }
397 
398 static void print_attribute_name(char *name)
399 {
400 	if (name)
401 		printf(":%s", name);
402 }
403 
404 #define	GET_ATTR_NAME(a) \
405 	((ntfschar *)(((u8 *)(a)) + le16_to_cpu((a)->name_offset))), \
406 	((a)->name_length)
407 
408 static void free_name(char **name)
409 {
410 	if (*name) {
411 		free(*name);
412 		*name = NULL;
413 	}
414 }
415 
416 static char *get_attr_name(u64 mft_no,
417 			   ATTR_TYPES atype,
418 			   const ntfschar *uname,
419 			   const int uname_len)
420 {
421 	char *name = NULL;
422 	int name_len;
423 
424 	if (atype == AT_END)
425 		return NULL;
426 
427 	name_len = ntfs_ucstombs(uname, uname_len, &name, 0);
428 	if (name_len < 0) {
429 		perr_print("ntfs_ucstombs");
430 		print_inode(mft_no);
431 		print_attribute_type(atype);
432 		puts("");
433 		exit(1);
434 
435 	} else if (name_len > 0)
436 		return name;
437 
438 	free_name(&name);
439 	return NULL;
440 }
441 
442 static char *get_attr_name_na(ntfs_attr *na)
443 {
444 	return get_attr_name(inumber(na->ni), na->type, na->name, na->name_len);
445 }
446 
447 static char *get_attr_name_ctx(ntfs_attr_search_ctx *ctx)
448 {
449 	u64 mft_no = inumber(ctx->ntfs_ino);
450 	ATTR_TYPES atype = ctx->attr->type;
451 
452 	return get_attr_name(mft_no, atype, GET_ATTR_NAME(ctx->attr));
453 }
454 
455 static void print_attribute(ATTR_TYPES atype, char *name)
456 {
457 	print_attribute_type(atype);
458 	print_attribute_name(name);
459 	printf(" ");
460 }
461 
462 static void print_na(ntfs_attr *na)
463 {
464 	char *name = get_attr_name_na(na);
465 	print_inode_ni(na->ni);
466 	print_attribute(na->type, name);
467 	free_name(&name);
468 }
469 
470 static void print_attribute_ctx(ntfs_attr_search_ctx *ctx)
471 {
472 	char *name = get_attr_name_ctx(ctx);
473 	print_attribute(ctx->attr->type, name);
474 	free_name(&name);
475 }
476 
477 static void print_ctx(ntfs_attr_search_ctx *ctx)
478 {
479 	char *name = get_attr_name_ctx(ctx);
480 	print_inode_ni(base_inode(ctx));
481 	print_attribute(ctx->attr->type, name);
482 	free_name(&name);
483 }
484 
485 static void print_differ(ntfs_attr *na)
486 {
487 	print_na(na);
488 	printf("content:   DIFFER\n");
489 }
490 
491 static int cmp_buffer(u8 *buf1, u8 *buf2, long long int size, ntfs_attr *na)
492 {
493 	if (memcmp(buf1, buf2, size)) {
494 		print_differ(na);
495 		return -1;
496 	}
497 	return 0;
498 }
499 
500 struct cmp_ia {
501 	INDEX_ALLOCATION *ia;
502 	INDEX_ALLOCATION *tmp_ia;
503 	u8 *bitmap;
504 	u8 *byte;
505 	s64 bm_size;
506 };
507 
508 static int setup_cmp_ia(ntfs_attr *na, struct cmp_ia *cia)
509 {
510 	cia->bitmap = ntfs_attr_readall(na->ni, AT_BITMAP, na->name,
511 					na->name_len, &cia->bm_size);
512 	if (!cia->bitmap) {
513 		perr_println("Failed to readall BITMAP");
514 		return -1;
515 	}
516 	cia->byte = cia->bitmap;
517 
518 	cia->tmp_ia = cia->ia = ntfs_malloc(na->data_size);
519 	if (!cia->tmp_ia)
520 		goto free_bm;
521 
522 	if (ntfs_attr_pread(na, 0, na->data_size, cia->ia) != na->data_size) {
523 		perr_println("Failed to pread INDEX_ALLOCATION");
524 		goto free_ia;
525 	}
526 
527 	return 0;
528 free_ia:
529 	free(cia->ia);
530 free_bm:
531 	free(cia->bitmap);
532 	return -1;
533 }
534 
535 static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2)
536 {
537 	struct cmp_ia cia1, cia2;
538 	int bit, ret1, ret2;
539 	u32 ib_size;
540 
541 	if (setup_cmp_ia(na1, &cia1))
542 		return;
543 	if (setup_cmp_ia(na2, &cia2))
544 		return;
545 	/*
546 	 *  FIXME: ia can be the same even if the bitmap sizes are different.
547 	 */
548 	if (cia1.bm_size != cia1.bm_size)
549 		goto out;
550 
551 	if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1))
552 		goto out;
553 
554 	if (cmp_buffer((u8 *)cia1.ia, (u8 *)cia2.ia, 0x18, na1))
555 		goto out;
556 
557 	ib_size = le32_to_cpu(cia1.ia->index.allocated_size) + 0x18;
558 
559 	bit = 0;
560 	while ((u8 *)cia1.tmp_ia < (u8 *)cia1.ia + na1->data_size) {
561 		if (*cia1.byte & (1 << bit)) {
562 			ret1 = ntfs_mst_post_read_fixup((NTFS_RECORD *)
563 					cia1.tmp_ia, ib_size);
564 			ret2 = ntfs_mst_post_read_fixup((NTFS_RECORD *)
565 					cia2.tmp_ia, ib_size);
566 			if (ret1 != ret2) {
567 				print_differ(na1);
568 				goto out;
569 			}
570 
571 			if (ret1 == -1)
572 				continue;
573 
574 			if (cmp_buffer(((u8 *)cia1.tmp_ia) + 0x18,
575 					((u8 *)cia2.tmp_ia) + 0x18,
576 					le32_to_cpu(cia1.ia->
577 					index.index_length), na1))
578 				goto out;
579 		}
580 
581 		cia1.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia1.tmp_ia + ib_size);
582 		cia2.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia2.tmp_ia + ib_size);
583 
584 		bit++;
585 		if (bit > 7) {
586 			bit = 0;
587 			cia1.byte++;
588 		}
589 	}
590 out:
591 	free(cia1.ia);
592 	free(cia2.ia);
593 	free(cia1.bitmap);
594 	free(cia2.bitmap);
595 	return;
596 }
597 
598 static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2)
599 {
600 	s64 pos;
601 	s64 count1 = 0, count2;
602 	u8  buf1[NTFS_BUF_SIZE];
603 	u8  buf2[NTFS_BUF_SIZE];
604 
605 	for (pos = 0; pos <= na1->data_size; pos += count1) {
606 
607 		count1 = ntfs_attr_pread(na1, pos, NTFS_BUF_SIZE, buf1);
608 		count2 = ntfs_attr_pread(na2, pos, NTFS_BUF_SIZE, buf2);
609 
610 		if (count1 != count2) {
611 			print_na(na1);
612 			printf("abrupt length:   %lld  !=  %lld ",
613 			       na1->data_size, na2->data_size);
614 			printf("(count: %lld  !=  %lld)", count1, count2);
615 			puts("");
616 			return;
617 		}
618 
619 		if (count1 == -1) {
620 			err_printf("%s read error: ", "cmp_attribute_data");
621 			print_na(na1);
622 			printf("len = %lld, pos = %lld\n", na1->data_size, pos);
623 			exit(1);
624 		}
625 
626 		if (count1 == 0) {
627 
628 			if (pos + count1 == na1->data_size)
629 				return; /* we are ready */
630 
631 			err_printf("%s read error before EOF: ", "cmp_attribute_data");
632 			print_na(na1);
633 			printf("%lld  !=  %lld\n", pos + count1, na1->data_size);
634 			exit(1);
635 		}
636 
637 		if (cmp_buffer(buf1, buf2, count1, na1))
638 			return;
639 	}
640 
641 	err_printf("%s read overrun: ", "cmp_attribute_data");
642 	print_na(na1);
643 	err_printf("(len = %lld, pos = %lld, count = %lld)\n",
644 		  na1->data_size, pos, count1);
645 	exit(1);
646 }
647 
648 static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2)
649 {
650 	u32 header_size = offsetof(ATTR_RECORD, u.res.resident_end);
651 
652 	if (a1->non_resident != a2->non_resident)
653 		return 1;
654 
655 	if (a1->non_resident) {
656 		/*
657 		 * FIXME: includes paddings which are not handled by ntfsinfo!
658 		 */
659 		header_size = le32_to_cpu(a1->length);
660 	}
661 
662 	return memcmp(a1, a2, header_size);
663 }
664 
665 static void cmp_attribute(ntfs_attr_search_ctx *ctx1,
666 			  ntfs_attr_search_ctx *ctx2)
667 {
668 	ATTR_RECORD *a1 = ctx1->attr;
669 	ATTR_RECORD *a2 = ctx2->attr;
670 	ntfs_attr *na1, *na2;
671 
672 	if (cmp_attribute_header(a1, a2)) {
673 		print_ctx(ctx1);
674 		printf("header:    DIFFER\n");
675 	}
676 
677 	na1 = ntfs_attr_open(base_inode(ctx1), a1->type, GET_ATTR_NAME(a1));
678 	na2 = ntfs_attr_open(base_inode(ctx2), a2->type, GET_ATTR_NAME(a2));
679 
680 	if ((!na1 && na2) || (na1 && !na2)) {
681 		print_ctx(ctx1);
682 		printf("open:   %s  !=  %s\n", pret2str(na1), pret2str(na2));
683 		goto close_attribs;
684 	}
685 
686 	if (na1 == NULL)
687 		goto close_attribs;
688 
689 	if (na1->data_size != na2->data_size) {
690 		print_na(na1);
691 		printf("length:   %lld  !=  %lld\n", na1->data_size, na2->data_size);
692 		goto close_attribs;
693 	}
694 
695 	if (ntfs_inode_badclus_bad(inumber(ctx1->ntfs_ino), ctx1->attr) == 1) {
696 		/*
697 		 * If difference exists then it's already reported at the
698 		 * attribute header since the mapping pairs must differ.
699 		 */
700 		return;
701 	}
702 
703 	if (na1->type == AT_INDEX_ALLOCATION)
704 		cmp_index_allocation(na1, na2);
705 	else
706 		cmp_attribute_data(na1, na2);
707 
708 close_attribs:
709 	ntfs_attr_close(na1);
710 	ntfs_attr_close(na2);
711 }
712 
713 static void vprint_attribute(ATTR_TYPES atype, char  *name)
714 {
715 	if (!opt.verbose)
716 		return;
717 
718 	printf("0x%x", atype);
719 	if (name)
720 		printf(":%s", name);
721 	printf(" ");
722 }
723 
724 static void print_attributes(ntfs_inode *ni,
725 			     ATTR_TYPES atype1,
726 			     ATTR_TYPES atype2,
727 			     char  *name1,
728 			     char  *name2)
729 {
730 	if (!opt.verbose)
731 		return;
732 
733 	printf("Walking inode %llu attributes: ", inumber(ni));
734 	vprint_attribute(atype1, name1);
735 	vprint_attribute(atype2, name2);
736 	printf("\n");
737 }
738 
739 static int new_name(ntfs_attr_search_ctx *ctx, char *prev_name)
740 {
741 	int ret = 0;
742 	char *name = get_attr_name_ctx(ctx);
743 
744 	if (prev_name && name) {
745 		if (strcmp(prev_name, name) != 0)
746 			ret = 1;
747 	} else if (prev_name || name)
748 		ret = 1;
749 
750 	free_name(&name);
751 	return ret;
752 
753 }
754 
755 static int new_attribute(ntfs_attr_search_ctx *ctx,
756 			 ATTR_TYPES prev_atype,
757 			 char *prev_name)
758 {
759 	if (!prev_atype && !prev_name)
760 		return 1;
761 
762 	if (!ctx->attr->non_resident)
763 		return 1;
764 
765 	if (prev_atype != ctx->attr->type)
766 		return 1;
767 
768 	if (new_name(ctx, prev_name))
769 		return 1;
770 
771 	if (opt.verbose) {
772 		print_inode(base_inode(ctx)->mft_no);
773 		print_attribute_ctx(ctx);
774 		printf("record %llu lowest_vcn %lld:    SKIPPED\n",
775 			ctx->ntfs_ino->mft_no, ctx->attr->u.nonres.lowest_vcn);
776 	}
777 
778 	return 0;
779 }
780 
781 static void set_prev(char **prev_name, ATTR_TYPES *prev_atype,
782 		     char *name, ATTR_TYPES atype)
783 {
784 	free_name(prev_name);
785 	if (name) {
786 		*prev_name = strdup(name);
787 		if (!*prev_name)
788 			perr_exit("strdup error");
789 	}
790 
791 	*prev_atype = atype;
792 }
793 
794 static void set_cmp_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name)
795 {
796 	*atype = ctx->attr->type;
797 
798 	free_name(name);
799 	*name = get_attr_name_ctx(ctx);
800 }
801 
802 static int next_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name,
803 		     int *err)
804 {
805 	int ret;
806 
807 	ret = ntfs_attrs_walk(ctx);
808 	*err = errno;
809 	if (ret) {
810 		*atype = AT_END;
811 		free_name(name);
812 	} else
813 		set_cmp_attr(ctx, atype, name);
814 
815 	return ret;
816 }
817 
818 static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2)
819 {
820 	int ret = -1;
821 	int old_ret1, ret1 = 0, ret2 = 0;
822 	int errno1 = 0, errno2 = 0;
823 	char  *prev_name = NULL, *name1 = NULL, *name2 = NULL;
824 	ATTR_TYPES old_atype1, prev_atype = 0, atype1, atype2;
825 	ntfs_attr_search_ctx *ctx1, *ctx2;
826 
827 	if (!(ctx1 = attr_get_search_ctx(ni1)))
828 		return -1;
829 	if (!(ctx2 = attr_get_search_ctx(ni2)))
830 		goto out;
831 
832 	set_cmp_attr(ctx1, &atype1, &name1);
833 	set_cmp_attr(ctx2, &atype2, &name2);
834 
835 	while (1) {
836 
837 		old_atype1 = atype1;
838 		old_ret1 = ret1;
839 		if (!ret1 && (le32_to_cpu(atype1) <= le32_to_cpu(atype2) ||
840 				ret2))
841 			ret1 = next_attr(ctx1, &atype1, &name1, &errno1);
842 		if (!ret2 && (le32_to_cpu(old_atype1) >= le32_to_cpu(atype2) ||
843 					old_ret1))
844 			ret2 = next_attr(ctx2, &atype2, &name2, &errno2);
845 
846 		print_attributes(ni1, atype1, atype2, name1, name2);
847 
848 		if (ret1 && ret2) {
849 			if (errno1 != errno2) {
850 				print_inode_ni(ni1);
851 				printf("attribute walk (errno):   %d  !=  %d\n",
852 				       errno1, errno2);
853 			}
854 			break;
855 		}
856 
857 		if (ret2 || le32_to_cpu(atype1) < le32_to_cpu(atype2)) {
858 			if (new_attribute(ctx1, prev_atype, prev_name)) {
859 				print_ctx(ctx1);
860 				printf("presence:   EXISTS   !=   MISSING\n");
861 				set_prev(&prev_name, &prev_atype, name1,
862 						atype1);
863 			}
864 
865 		} else if (ret1 || le32_to_cpu(atype1) > le32_to_cpu(atype2)) {
866 			if (new_attribute(ctx2, prev_atype, prev_name)) {
867 				print_ctx(ctx2);
868 				printf("presence:   MISSING  !=  EXISTS \n");
869 				set_prev(&prev_name, &prev_atype, name2, atype2);
870 			}
871 
872 		} else /* atype1 == atype2 */ {
873 			if (new_attribute(ctx1, prev_atype, prev_name)) {
874 				cmp_attribute(ctx1, ctx2);
875 				set_prev(&prev_name, &prev_atype, name1, atype1);
876 			}
877 		}
878 	}
879 
880 	free_name(&prev_name);
881 	ret = 0;
882 	ntfs_attr_put_search_ctx(ctx2);
883 out:
884 	ntfs_attr_put_search_ctx(ctx1);
885 	return ret;
886 }
887 
888 static int cmp_inodes(ntfs_volume *vol1, ntfs_volume *vol2)
889 {
890 	u64 inode;
891 	int ret1, ret2;
892 	ntfs_inode *ni1, *ni2;
893 	struct progress_bar progress;
894 	int pb_flags = 0;	/* progress bar flags */
895 	u64 nr_mft_records, nr_mft_records2;
896 
897 	if (opt.show_progress)
898 		pb_flags |= NTFS_PROGBAR;
899 
900 	nr_mft_records  = get_nr_mft_records(vol1);
901 	nr_mft_records2 = get_nr_mft_records(vol2);
902 
903 	if (nr_mft_records != nr_mft_records2) {
904 
905 		printf("Number of mft records:   %lld  !=  %lld\n",
906 		       nr_mft_records, nr_mft_records2);
907 
908 		if (nr_mft_records > nr_mft_records2)
909 			nr_mft_records = nr_mft_records2;
910 	}
911 
912 	progress_init(&progress, 0, nr_mft_records - 1, pb_flags);
913 	progress_update(&progress, 0);
914 
915 	for (inode = 0; inode < nr_mft_records; inode++) {
916 
917 		ret1 = inode_open(vol1, (MFT_REF)inode, &ni1);
918 		ret2 = inode_open(vol2, (MFT_REF)inode, &ni2);
919 
920 		if (ret1 != ret2) {
921 			print_inode(inode);
922 			printf("open:   %s  !=  %s\n",
923 			       err2string(ret1), err2string(ret2));
924 			goto close_inodes;
925 		}
926 
927 		if (ret1 != NTFSCMP_OK)
928 			goto close_inodes;
929 
930 		if (cmp_attributes(ni1, ni2) != 0) {
931 			inode_close(ni1);
932 			inode_close(ni2);
933 			return -1;
934 		}
935 close_inodes:
936 		if (inode_close(ni1) != 0)
937 			return -1;
938 		if (inode_close(ni2) != 0)
939 			return -1;
940 
941 		progress_update(&progress, inode);
942 	}
943 	return 0;
944 }
945 
946 static ntfs_volume *mount_volume(const char *volume)
947 {
948 	unsigned long mntflag;
949 	ntfs_volume *vol = NULL;
950 
951 	if (ntfs_check_if_mounted(volume, &mntflag)) {
952 		perr_println("Failed to check '%s' mount state", volume);
953 		printf("Probably /etc/mtab is missing. It's too risky to "
954 		       "continue. You might try\nan another Linux distro.\n");
955 		exit(1);
956 	}
957 	if (mntflag & NTFS_MF_MOUNTED) {
958 		if (!(mntflag & NTFS_MF_READONLY))
959 			err_exit("Device '%s' is mounted read-write. "
960 				 "You must 'umount' it first.\n", volume);
961 	}
962 
963 	vol = ntfs_mount(volume, NTFS_MNT_RDONLY);
964 	if (vol == NULL) {
965 
966 		int err = errno;
967 
968 		perr_println("Opening '%s' as NTFS failed", volume);
969 		if (err == EINVAL)
970 			printf(invalid_ntfs_msg, volume);
971 		else if (err == EIO)
972 			puts(corrupt_volume_msg);
973 		else if (err == EPERM)
974 			puts(hibernated_volume_msg);
975 		exit(1);
976 	}
977 
978 	return vol;
979 }
980 
981 int main(int argc, char **argv)
982 {
983 	ntfs_volume *vol1;
984 	ntfs_volume *vol2;
985 
986 	printf("%s v%s (libntfs %s)\n", EXEC_NAME, VERSION,
987 			ntfs_libntfs_version());
988 
989 	parse_options(argc, argv);
990 
991 	utils_set_locale();
992 
993 	vol1 = mount_volume(opt.vol1);
994         vol2 = mount_volume(opt.vol2);
995 
996 	if (cmp_inodes(vol1, vol2) != 0)
997 		exit(1);
998 
999 	ntfs_umount(vol1, FALSE);
1000 	ntfs_umount(vol2, FALSE);
1001 
1002 	exit(0);
1003 }
1004 
1005