193 files changed, 80411 insertions, 44563 deletions
diff --git a/ntfsprogs/ntfscmp.c b/ntfsprogs/ntfscmp.c new file mode 100755 index 0000000..469d1d9 --- a/dev/null +++ b/ntfsprogs/ntfscmp.c @@ -0,0 +1,1012 @@ +/** + * ntfscmp - Part of the Linux-NTFS project. + * + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy + * + * This utility compare two NTFS volumes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include "mst.h" +#include "support.h" +#include "utils.h" +#include "misc.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfscmp"; + +static const char *invalid_ntfs_msg = +"Apparently device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong partition? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)?\n"; + +static const char *corrupt_volume_msg = +"Apparently you have a corrupted NTFS. Please run the filesystem checker\n" +"on Windows by invoking chkdsk /f. Don't forget the /f (force) parameter,\n" +"it's important! You probably also need to reboot Windows to take effect.\n"; + +static const char *hibernated_volume_msg = +"Apparently the NTFS partition is hibernated. Windows must be resumed and\n" +"turned off properly\n"; + + +static struct { + int debug; + int show_progress; + int verbose; + char *vol1; + char *vol2; +} opt; + + +#define NTFS_PROGBAR 0x0001 +#define NTFS_PROGBAR_SUPPRESS 0x0002 + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + int flags; + float unit; +}; + +/* WARNING: don't modify the text, external tools grep for it */ +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +__attribute__((format(printf, 2, 3))) +static void perr_printf(int newline, const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, ": %s", strerror(eo)); + if (newline) + fprintf(stdout, "\n"); + fflush(stdout); + fflush(stderr); +} + +#define perr_print(...) perr_printf(0, __VA_ARGS__) +#define perr_println(...) perr_printf(1, __VA_ARGS__) + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); +} + +/** + * err_exit + * + * Print and error message and exit the program. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * perr_exit + * + * Print and error message and exit the program + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + printf(": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +__attribute__((noreturn)) +static void usage(void) +{ + + printf("\nUsage: %s [OPTIONS] DEVICE1 DEVICE2\n" + " Compare two NTFS volumes and tell the differences.\n" + "\n" + " -P, --no-progress-bar Don't show progress bar\n" + " -v, --verbose More output\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n", EXEC_NAME); + printf("%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + + +static void parse_options(int argc, char **argv) +{ + static const char *sopt = "-dhPv"; + static const struct option lopt[] = { +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "help", no_argument, NULL, 'h' }, + { "no-progress-bar", no_argument, NULL, 'P' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + memset(&opt, 0, sizeof(opt)); + opt.show_progress = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opt.vol1) { + opt.vol1 = argv[optind - 1]; + } else if (!opt.vol2) { + opt.vol2 = argv[optind - 1]; + } else { + err_printf("Too many arguments!\n"); + usage(); + } + break; +#ifdef DEBUG + case 'd': + opt.debug++; + break; +#endif + case 'h': + case '?': + usage(); + case 'P': + opt.show_progress = 0; + break; + case 'v': + opt.verbose++; + break; + default: + err_printf("Unknown option '%s'.\n", argv[optind - 1]); + usage(); + break; + } + } + + if (opt.vol1 == NULL || opt.vol2 == NULL) { + err_printf("You must specify exactly 2 volumes.\n"); + usage(); + } + + /* Redirect stderr to stdout, note fflush()es are essential! */ + fflush(stdout); + fflush(stderr); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + perror("Failed to redirect stderr to stdout"); + exit(1); + } + fflush(stdout); + fflush(stderr); + +#ifdef DEBUG + if (!opt.debug) + if (!freopen("/dev/null", "w", stderr)) + perr_exit("Failed to redirect stderr to /dev/null"); +#endif +} + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL) + perr_println("ntfs_attr_get_search_ctx"); + + return ret; +} + +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = 100; + p->flags = flags; +} + +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent; + + if (!(p->flags & NTFS_PROGBAR)) + return; + if (p->flags & NTFS_PROGBAR_SUPPRESS) + return; + + /* WARNING: don't modify the texts, external tools grep for them */ + percent = p->unit * current; + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + printf("%6.2f percent completed\r", percent); + } else + printf("100.00 percent completed\n"); + fflush(stdout); +} + +static u64 inumber(ntfs_inode *ni) +{ + if (ni->nr_extents >= 0) + return ni->mft_no; + + return ni->base_ni->mft_no; +} + +static int inode_close(ntfs_inode *ni) +{ + if (ni == NULL) + return 0; + + if (ntfs_inode_close(ni)) { + perr_println("ntfs_inode_close: inode %llu", + (unsigned long long)inumber(ni)); + return -1; + } + return 0; +} + +static inline s64 get_nr_mft_records(ntfs_volume *vol) +{ + return vol->mft_na->initialized_size >> vol->mft_record_size_bits; +} + +#define NTFSCMP_OK 0 +#define NTFSCMP_INODE_OPEN_ERROR 1 +#define NTFSCMP_INODE_OPEN_IO_ERROR 2 +#define NTFSCMP_INODE_OPEN_ENOENT_ERROR 3 +#define NTFSCMP_EXTENSION_RECORD 4 +#define NTFSCMP_INODE_CLOSE_ERROR 5 + +static const char *ntfscmp_errs[] = { + "OK", + "INODE_OPEN_ERROR", + "INODE_OPEN_IO_ERROR", + "INODE_OPEN_ENOENT_ERROR", + "EXTENSION_RECORD", + "INODE_CLOSE_ERROR", + "" +}; + + +static const char *err2string(int err) +{ + return ntfscmp_errs[err]; +} + +static const char *pret2str(void *p) +{ + if (p == NULL) + return "FAILED"; + return "OK"; +} + +static int inode_open(ntfs_volume *vol, MFT_REF mref, ntfs_inode **ni) +{ + *ni = ntfs_inode_open(vol, mref); + if (*ni == NULL) { + if (errno == EIO) + return NTFSCMP_INODE_OPEN_IO_ERROR; + if (errno == ENOENT) + return NTFSCMP_INODE_OPEN_ENOENT_ERROR; + + perr_println("Reading inode %lld failed", (long long)mref); + return NTFSCMP_INODE_OPEN_ERROR; + } + + if ((*ni)->mrec->base_mft_record) { + + if (inode_close(*ni) != 0) + return NTFSCMP_INODE_CLOSE_ERROR; + + return NTFSCMP_EXTENSION_RECORD; + } + + return NTFSCMP_OK; +} + +static ntfs_inode *base_inode(ntfs_attr_search_ctx *ctx) +{ + if (ctx->base_ntfs_ino) + return ctx->base_ntfs_ino; + + return ctx->ntfs_ino; +} + +static void print_inode(u64 inum) +{ + printf("Inode %llu ", (unsigned long long)inum); +} + +static void print_inode_ni(ntfs_inode *ni) +{ + print_inode(inumber(ni)); +} + +static void print_attribute_type(ATTR_TYPES atype) +{ + printf("attribute 0x%x", atype); +} + +static void print_attribute_name(char *name) +{ + if (name) + printf(":%s", name); +} + +#define GET_ATTR_NAME(a) \ + ((ntfschar *)(((u8 *)(a)) + le16_to_cpu((a)->name_offset))), \ + ((a)->name_length) + +static void free_name(char **name) +{ + if (*name) { + free(*name); + *name = NULL; + } +} + +static char *get_attr_name(u64 mft_no, + ATTR_TYPES atype, + const ntfschar *uname, + const int uname_len) +{ + char *name = NULL; + int name_len; + + if (atype == AT_END) + return NULL; + + name_len = ntfs_ucstombs(uname, uname_len, &name, 0); + if (name_len < 0) { + perr_print("ntfs_ucstombs"); + print_inode(mft_no); + print_attribute_type(atype); + puts(""); + exit(1); + + } else if (name_len > 0) + return name; + + free_name(&name); + return NULL; +} + +static char *get_attr_name_na(ntfs_attr *na) +{ + return get_attr_name(inumber(na->ni), na->type, na->name, na->name_len); +} + +static char *get_attr_name_ctx(ntfs_attr_search_ctx *ctx) +{ + u64 mft_no = inumber(ctx->ntfs_ino); + ATTR_TYPES atype = ctx->attr->type; + + return get_attr_name(mft_no, atype, GET_ATTR_NAME(ctx->attr)); +} + +static void print_attribute(ATTR_TYPES atype, char *name) +{ + print_attribute_type(atype); + print_attribute_name(name); + printf(" "); +} + +static void print_na(ntfs_attr *na) +{ + char *name = get_attr_name_na(na); + print_inode_ni(na->ni); + print_attribute(na->type, name); + free_name(&name); +} + +static void print_attribute_ctx(ntfs_attr_search_ctx *ctx) +{ + char *name = get_attr_name_ctx(ctx); + print_attribute(ctx->attr->type, name); + free_name(&name); +} + +static void print_ctx(ntfs_attr_search_ctx *ctx) +{ + char *name = get_attr_name_ctx(ctx); + print_inode_ni(base_inode(ctx)); + print_attribute(ctx->attr->type, name); + free_name(&name); +} + +static void print_differ(ntfs_attr *na) +{ + print_na(na); + printf("content: DIFFER\n"); +} + +static int cmp_buffer(u8 *buf1, u8 *buf2, long long int size, ntfs_attr *na) +{ + if (memcmp(buf1, buf2, size)) { + print_differ(na); + return -1; + } + return 0; +} + +struct cmp_ia { + INDEX_ALLOCATION *ia; + INDEX_ALLOCATION *tmp_ia; + u8 *bitmap; + u8 *byte; + s64 bm_size; +}; + +static int setup_cmp_ia(ntfs_attr *na, struct cmp_ia *cia) +{ + cia->bitmap = ntfs_attr_readall(na->ni, AT_BITMAP, na->name, + na->name_len, &cia->bm_size); + if (!cia->bitmap) { + perr_println("Failed to readall BITMAP"); + return -1; + } + cia->byte = cia->bitmap; + + cia->tmp_ia = cia->ia = ntfs_malloc(na->data_size); + if (!cia->tmp_ia) + goto free_bm; + + if (ntfs_attr_pread(na, 0, na->data_size, cia->ia) != na->data_size) { + perr_println("Failed to pread INDEX_ALLOCATION"); + goto free_ia; + } + + return 0; +free_ia: + free(cia->ia); +free_bm: + free(cia->bitmap); + return -1; +} + +static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2) +{ + struct cmp_ia cia1, cia2; + int bit, ret1, ret2; + u32 ib_size; + + if (setup_cmp_ia(na1, &cia1)) + return; + if (setup_cmp_ia(na2, &cia2)) + return; + /* + * FIXME: ia can be the same even if the bitmap sizes are different. + */ + if (cia1.bm_size != cia1.bm_size) + goto out; + + if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1)) + goto out; + + if (cmp_buffer((u8 *)cia1.ia, (u8 *)cia2.ia, 0x18, na1)) + goto out; + + ib_size = le32_to_cpu(cia1.ia->index.allocated_size) + 0x18; + + bit = 0; + while ((u8 *)cia1.tmp_ia < (u8 *)cia1.ia + na1->data_size) { + if (*cia1.byte & (1 << bit)) { + ret1 = ntfs_mst_post_read_fixup((NTFS_RECORD *) + cia1.tmp_ia, ib_size); + ret2 = ntfs_mst_post_read_fixup((NTFS_RECORD *) + cia2.tmp_ia, ib_size); + if (ret1 != ret2) { + print_differ(na1); + goto out; + } + + if (ret1 == -1) + continue; + + if (cmp_buffer(((u8 *)cia1.tmp_ia) + 0x18, + ((u8 *)cia2.tmp_ia) + 0x18, + le32_to_cpu(cia1.ia-> + index.index_length), na1)) + goto out; + } + + cia1.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia1.tmp_ia + ib_size); + cia2.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia2.tmp_ia + ib_size); + + bit++; + if (bit > 7) { + bit = 0; + cia1.byte++; + } + } +out: + free(cia1.ia); + free(cia2.ia); + free(cia1.bitmap); + free(cia2.bitmap); + return; +} + +static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2) +{ + s64 pos; + s64 count1 = 0, count2; + u8 buf1[NTFS_BUF_SIZE]; + u8 buf2[NTFS_BUF_SIZE]; + + for (pos = 0; pos <= na1->data_size; pos += count1) { + + count1 = ntfs_attr_pread(na1, pos, NTFS_BUF_SIZE, buf1); + count2 = ntfs_attr_pread(na2, pos, NTFS_BUF_SIZE, buf2); + + if (count1 != count2) { + print_na(na1); + printf("abrupt length: %lld != %lld ", + (long long)na1->data_size, + (long long)na2->data_size); + printf("(count: %lld != %lld)", + (long long)count1, (long long)count2); + puts(""); + return; + } + + if (count1 == -1) { + err_printf("%s read error: ", __FUNCTION__); + print_na(na1); + printf("len = %lld, pos = %lld\n", + (long long)na1->data_size, (long long)pos); + exit(1); + } + + if (count1 == 0) { + + if (pos + count1 == na1->data_size) + return; /* we are ready */ + + err_printf("%s read error before EOF: ", __FUNCTION__); + print_na(na1); + printf("%lld != %lld\n", (long long)pos + count1, + (long long)na1->data_size); + exit(1); + } + + if (cmp_buffer(buf1, buf2, count1, na1)) + return; + } + + err_printf("%s read overrun: ", __FUNCTION__); + print_na(na1); + err_printf("(len = %lld, pos = %lld, count = %lld)\n", + (long long)na1->data_size, (long long)pos, (long long)count1); + exit(1); +} + +static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2) +{ + u32 header_size = offsetof(ATTR_RECORD, resident_end); + + if (a1->non_resident != a2->non_resident) + return 1; + + if (a1->non_resident) { + /* + * FIXME: includes paddings which are not handled by ntfsinfo! + */ + header_size = le32_to_cpu(a1->length); + } + + return memcmp(a1, a2, header_size); +} + +static void cmp_attribute(ntfs_attr_search_ctx *ctx1, + ntfs_attr_search_ctx *ctx2) +{ + ATTR_RECORD *a1 = ctx1->attr; + ATTR_RECORD *a2 = ctx2->attr; + ntfs_attr *na1, *na2; + + if (cmp_attribute_header(a1, a2)) { + print_ctx(ctx1); + printf("header: DIFFER\n"); + } + + na1 = ntfs_attr_open(base_inode(ctx1), a1->type, GET_ATTR_NAME(a1)); + na2 = ntfs_attr_open(base_inode(ctx2), a2->type, GET_ATTR_NAME(a2)); + + if ((!na1 && na2) || (na1 && !na2)) { + print_ctx(ctx1); + printf("open: %s != %s\n", pret2str(na1), pret2str(na2)); + goto close_attribs; + } + + if (na1 == NULL) + goto close_attribs; + + if (na1->data_size != na2->data_size) { + print_na(na1); + printf("length: %lld != %lld\n", + (long long)na1->data_size, (long long)na2->data_size); + goto close_attribs; + } + + if (ntfs_inode_badclus_bad(inumber(ctx1->ntfs_ino), ctx1->attr) == 1) { + /* + * If difference exists then it's already reported at the + * attribute header since the mapping pairs must differ. + */ + goto close_attribs; + } + + if (na1->type == AT_INDEX_ALLOCATION) + cmp_index_allocation(na1, na2); + else + cmp_attribute_data(na1, na2); + +close_attribs: + ntfs_attr_close(na1); + ntfs_attr_close(na2); +} + +static void vprint_attribute(ATTR_TYPES atype, char *name) +{ + if (!opt.verbose) + return; + + printf("0x%x", atype); + if (name) + printf(":%s", name); + printf(" "); +} + +static void print_attributes(ntfs_inode *ni, + ATTR_TYPES atype1, + ATTR_TYPES atype2, + char *name1, + char *name2) +{ + if (!opt.verbose) + return; + + printf("Walking inode %llu attributes: ", + (unsigned long long)inumber(ni)); + vprint_attribute(atype1, name1); + vprint_attribute(atype2, name2); + printf("\n"); +} + +static int new_name(ntfs_attr_search_ctx *ctx, char *prev_name) +{ + int ret = 0; + char *name = get_attr_name_ctx(ctx); + + if (prev_name && name) { + if (strcmp(prev_name, name) != 0) + ret = 1; + } else if (prev_name || name) + ret = 1; + + free_name(&name); + return ret; + +} + +static int new_attribute(ntfs_attr_search_ctx *ctx, + ATTR_TYPES prev_atype, + char *prev_name) +{ + if (!prev_atype && !prev_name) + return 1; + + if (!ctx->attr->non_resident) + return 1; + + if (prev_atype != ctx->attr->type) + return 1; + + if (new_name(ctx, prev_name)) + return 1; + + if (opt.verbose) { + print_inode(base_inode(ctx)->mft_no); + print_attribute_ctx(ctx); + printf("record %llu lowest_vcn %lld: SKIPPED\n", + (unsigned long long)ctx->ntfs_ino->mft_no, + (long long)ctx->attr->lowest_vcn); + } + + return 0; +} + +static void set_prev(char **prev_name, ATTR_TYPES *prev_atype, + char *name, ATTR_TYPES atype) +{ + free_name(prev_name); + if (name) { + *prev_name = strdup(name); + if (!*prev_name) + perr_exit("strdup error"); + } + + *prev_atype = atype; +} + +static void set_cmp_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name) +{ + *atype = ctx->attr->type; + + free_name(name); + *name = get_attr_name_ctx(ctx); +} + +static int next_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name, + int *err) +{ + int ret; + + ret = ntfs_attrs_walk(ctx); + *err = errno; + if (ret) { + *atype = AT_END; + free_name(name); + } else + set_cmp_attr(ctx, atype, name); + + return ret; +} + +static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2) +{ + int ret = -1; + int old_ret1, ret1 = 0, ret2 = 0; + int errno1 = 0, errno2 = 0; + char *prev_name = NULL, *name1 = NULL, *name2 = NULL; + ATTR_TYPES old_atype1, prev_atype = 0, atype1, atype2; + ntfs_attr_search_ctx *ctx1, *ctx2; + + if (!(ctx1 = attr_get_search_ctx(ni1))) + return -1; + if (!(ctx2 = attr_get_search_ctx(ni2))) + goto out; + + set_cmp_attr(ctx1, &atype1, &name1); + set_cmp_attr(ctx2, &atype2, &name2); + + while (1) { + + old_atype1 = atype1; + old_ret1 = ret1; + if (!ret1 && (le32_to_cpu(atype1) <= le32_to_cpu(atype2) || + ret2)) + ret1 = next_attr(ctx1, &atype1, &name1, &errno1); + if (!ret2 && (le32_to_cpu(old_atype1) >= le32_to_cpu(atype2) || + old_ret1)) + ret2 = next_attr(ctx2, &atype2, &name2, &errno2); + + print_attributes(ni1, atype1, atype2, name1, name2); + + if (ret1 && ret2) { + if (errno1 != errno2) { + print_inode_ni(ni1); + printf("attribute walk (errno): %d != %d\n", + errno1, errno2); + } + break; + } + + if (ret2 || le32_to_cpu(atype1) < le32_to_cpu(atype2)) { + if (new_attribute(ctx1, prev_atype, prev_name)) { + print_ctx(ctx1); + printf("presence: EXISTS != MISSING\n"); + set_prev(&prev_name, &prev_atype, name1, + atype1); + } + + } else if (ret1 || le32_to_cpu(atype1) > le32_to_cpu(atype2)) { + if (new_attribute(ctx2, prev_atype, prev_name)) { + print_ctx(ctx2); + printf("presence: MISSING != EXISTS \n"); + set_prev(&prev_name, &prev_atype, name2, atype2); + } + + } else /* atype1 == atype2 */ { + if (new_attribute(ctx1, prev_atype, prev_name)) { + cmp_attribute(ctx1, ctx2); + set_prev(&prev_name, &prev_atype, name1, atype1); + } + } + } + + free_name(&prev_name); + ret = 0; + ntfs_attr_put_search_ctx(ctx2); +out: + ntfs_attr_put_search_ctx(ctx1); + return ret; +} + +static int cmp_inodes(ntfs_volume *vol1, ntfs_volume *vol2) +{ + u64 inode; + int ret1, ret2; + ntfs_inode *ni1, *ni2; + struct progress_bar progress; + int pb_flags = 0; /* progress bar flags */ + u64 nr_mft_records, nr_mft_records2; + + if (opt.show_progress) + pb_flags |= NTFS_PROGBAR; + + nr_mft_records = get_nr_mft_records(vol1); + nr_mft_records2 = get_nr_mft_records(vol2); + + if (nr_mft_records != nr_mft_records2) { + + printf("Number of mft records: %lld != %lld\n", + (long long)nr_mft_records, (long long)nr_mft_records2); + + if (nr_mft_records > nr_mft_records2) + nr_mft_records = nr_mft_records2; + } + + progress_init(&progress, 0, nr_mft_records - 1, pb_flags); + progress_update(&progress, 0); + + for (inode = 0; inode < nr_mft_records; inode++) { + + ret1 = inode_open(vol1, (MFT_REF)inode, &ni1); + ret2 = inode_open(vol2, (MFT_REF)inode, &ni2); + + if (ret1 != ret2) { + print_inode(inode); + printf("open: %s != %s\n", + err2string(ret1), err2string(ret2)); + goto close_inodes; + } + + if (ret1 != NTFSCMP_OK) + goto close_inodes; + + if (cmp_attributes(ni1, ni2) != 0) { + inode_close(ni1); + inode_close(ni2); + return -1; + } +close_inodes: + if (inode_close(ni1) != 0) + return -1; + if (inode_close(ni2) != 0) + return -1; + + progress_update(&progress, inode); + } + return 0; +} + +static ntfs_volume *mount_volume(const char *volume) +{ + unsigned long mntflag; + ntfs_volume *vol = NULL; + + if (ntfs_check_if_mounted(volume, &mntflag)) { + perr_println("Failed to check '%s' mount state", volume); + printf("Probably /etc/mtab is missing. It's too risky to " + "continue. You might try\nan another Linux distro.\n"); + exit(1); + } + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", volume); + } + + vol = ntfs_mount(volume, NTFS_MNT_RDONLY); + if (vol == NULL) { + + int err = errno; + + perr_println("Opening '%s' as NTFS failed", volume); + if (err == EINVAL) + printf(invalid_ntfs_msg, volume); + else if (err == EIO) + puts(corrupt_volume_msg); + else if (err == EPERM) + puts(hibernated_volume_msg); + exit(1); + } + + return vol; +} + +int main(int argc, char **argv) +{ + ntfs_volume *vol1; + ntfs_volume *vol2; + + printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + + parse_options(argc, argv); + + utils_set_locale(); + + vol1 = mount_volume(opt.vol1); + vol2 = mount_volume(opt.vol2); + + if (cmp_inodes(vol1, vol2) != 0) + exit(1); + + ntfs_umount(vol1, FALSE); + ntfs_umount(vol2, FALSE); + + return (0); +} + |