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