From cdccc69c62579b0c13b35fad0728079652b8f3c9 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:21:30 +0100 Subject: Move library source into src sub-directory Signed-off-by: David Oberhollenzer --- bin/sqfsdiff/Makemodule.am | 10 +- bin/sqfsdiff/compare_dir.c | 94 ------------------ bin/sqfsdiff/compare_files.c | 72 -------------- bin/sqfsdiff/extract.c | 58 ----------- bin/sqfsdiff/node_compare.c | 206 --------------------------------------- bin/sqfsdiff/options.c | 131 ------------------------- bin/sqfsdiff/sqfsdiff.c | 167 ------------------------------- bin/sqfsdiff/sqfsdiff.h | 73 -------------- bin/sqfsdiff/src/compare_dir.c | 94 ++++++++++++++++++ bin/sqfsdiff/src/compare_files.c | 72 ++++++++++++++ bin/sqfsdiff/src/extract.c | 58 +++++++++++ bin/sqfsdiff/src/node_compare.c | 206 +++++++++++++++++++++++++++++++++++++++ bin/sqfsdiff/src/options.c | 131 +++++++++++++++++++++++++ bin/sqfsdiff/src/sqfsdiff.c | 167 +++++++++++++++++++++++++++++++ bin/sqfsdiff/src/sqfsdiff.h | 73 ++++++++++++++ bin/sqfsdiff/src/super.c | 125 ++++++++++++++++++++++++ bin/sqfsdiff/src/util.c | 27 +++++ bin/sqfsdiff/super.c | 125 ------------------------ bin/sqfsdiff/util.c | 27 ----- 19 files changed, 958 insertions(+), 958 deletions(-) delete mode 100644 bin/sqfsdiff/compare_dir.c delete mode 100644 bin/sqfsdiff/compare_files.c delete mode 100644 bin/sqfsdiff/extract.c delete mode 100644 bin/sqfsdiff/node_compare.c delete mode 100644 bin/sqfsdiff/options.c delete mode 100644 bin/sqfsdiff/sqfsdiff.c delete mode 100644 bin/sqfsdiff/sqfsdiff.h create mode 100644 bin/sqfsdiff/src/compare_dir.c create mode 100644 bin/sqfsdiff/src/compare_files.c create mode 100644 bin/sqfsdiff/src/extract.c create mode 100644 bin/sqfsdiff/src/node_compare.c create mode 100644 bin/sqfsdiff/src/options.c create mode 100644 bin/sqfsdiff/src/sqfsdiff.c create mode 100644 bin/sqfsdiff/src/sqfsdiff.h create mode 100644 bin/sqfsdiff/src/super.c create mode 100644 bin/sqfsdiff/src/util.c delete mode 100644 bin/sqfsdiff/super.c delete mode 100644 bin/sqfsdiff/util.c (limited to 'bin/sqfsdiff') diff --git a/bin/sqfsdiff/Makemodule.am b/bin/sqfsdiff/Makemodule.am index ff08c7a..4f21901 100644 --- a/bin/sqfsdiff/Makemodule.am +++ b/bin/sqfsdiff/Makemodule.am @@ -1,8 +1,8 @@ -sqfsdiff_SOURCES = bin/sqfsdiff/sqfsdiff.c bin/sqfsdiff/sqfsdiff.h -sqfsdiff_SOURCES += bin/sqfsdiff/util.c bin/sqfsdiff/options.c -sqfsdiff_SOURCES += bin/sqfsdiff/compare_dir.c bin/sqfsdiff/node_compare.c -sqfsdiff_SOURCES += bin/sqfsdiff/compare_files.c bin/sqfsdiff/super.c -sqfsdiff_SOURCES += bin/sqfsdiff/extract.c +sqfsdiff_SOURCES = bin/sqfsdiff/src/sqfsdiff.c bin/sqfsdiff/src/sqfsdiff.h \ + bin/sqfsdiff/src/util.c bin/sqfsdiff/src/options.c \ + bin/sqfsdiff/src/compare_dir.c bin/sqfsdiff/src/node_compare.c \ + bin/sqfsdiff/src/compare_files.c bin/sqfsdiff/src/super.c \ + bin/sqfsdiff/src/extract.c sqfsdiff_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfsdiff_LDADD = libcommon.a libsquashfs.la libio.a libcompat.a libutil.a sqfsdiff_LDADD += $(LZO_LIBS) libfstree.a $(PTHREAD_LIBS) diff --git a/bin/sqfsdiff/compare_dir.c b/bin/sqfsdiff/compare_dir.c deleted file mode 100644 index 1a4c800..0000000 --- a/bin/sqfsdiff/compare_dir.c +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * compare_dir.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static int print_omitted(sqfsdiff_t *sd, bool is_old, sqfs_tree_node_t *n) -{ - char *path = node_path(n); - - if (path == NULL) - return -1; - - fprintf(stdout, "%c %s\n", is_old ? '<' : '>', path); - - if ((sd->compare_flags & COMPARE_EXTRACT_FILES) && - S_ISREG(n->inode->base.mode)) { - if (extract_files(sd, is_old ? n->inode : NULL, - is_old ? NULL : n->inode, path)) { - free(path); - return -1; - } - } - - free(path); - - for (n = n->children; n != NULL; n = n->next) { - if (print_omitted(sd, is_old, n)) - return -1; - } - - return 0; -} - -int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, - sqfs_tree_node_t *new) -{ - sqfs_tree_node_t *old_it = old->children, *old_prev = NULL; - sqfs_tree_node_t *new_it = new->children, *new_prev = NULL; - int ret, result = 0; - - while (old_it != NULL || new_it != NULL) { - if (old_it != NULL && new_it != NULL) { - ret = strcmp((const char *)old_it->name, - (const char *)new_it->name); - } else if (old_it == NULL) { - ret = 1; - } else { - ret = -1; - } - - if (ret < 0) { - result = 1; - - if (print_omitted(sd, true, old_it)) - return -1; - - if (old_prev == NULL) { - old->children = old_it->next; - sqfs_dir_tree_destroy(old_it); - old_it = old->children; - } else { - old_prev->next = old_it->next; - sqfs_dir_tree_destroy(old_it); - old_it = old_prev->next; - } - } else if (ret > 0) { - result = 1; - - if (print_omitted(sd, false, new_it)) - return -1; - - if (new_prev == NULL) { - new->children = new_it->next; - sqfs_dir_tree_destroy(new_it); - new_it = new->children; - } else { - new_prev->next = new_it->next; - sqfs_dir_tree_destroy(new_it); - new_it = new_prev->next; - } - } else { - old_prev = old_it; - old_it = old_it->next; - - new_prev = new_it; - new_it = new_it->next; - } - } - - return result; -} diff --git a/bin/sqfsdiff/compare_files.c b/bin/sqfsdiff/compare_files.c deleted file mode 100644 index 51b66bb..0000000 --- a/bin/sqfsdiff/compare_files.c +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * compare_files.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static unsigned char old_buf[MAX_WINDOW_SIZE]; -static unsigned char new_buf[MAX_WINDOW_SIZE]; - -static int read_blob(const char *prefix, const char *path, - sqfs_data_reader_t *rd, const sqfs_inode_generic_t *inode, - void *buffer, sqfs_u64 offset, size_t size) -{ - ssize_t ret; - - ret = sqfs_data_reader_read(rd, inode, offset, buffer, size); - ret = (ret < 0 || (size_t)ret < size) ? -1 : 0; - - if (ret) { - fprintf(stderr, "Failed to read %s from %s\n", - path, prefix); - return -1; - } - - return 0; -} - -int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, const char *path) -{ - sqfs_u64 offset, diff, oldsz, newsz; - int status = 0, ret; - - sqfs_inode_get_file_size(old, &oldsz); - sqfs_inode_get_file_size(new, &newsz); - - if (oldsz != newsz) - goto out_different; - - if (sd->compare_flags & COMPARE_NO_CONTENTS) - return 0; - - for (offset = 0; offset < oldsz; offset += diff) { - diff = oldsz - offset; - - if (diff > MAX_WINDOW_SIZE) - diff = MAX_WINDOW_SIZE; - - ret = read_blob(sd->old_path, path, - sd->sqfs_old.data, old, old_buf, offset, diff); - if (ret) - return -1; - - ret = read_blob(sd->new_path, path, - sd->sqfs_new.data, new, new_buf, offset, diff); - if (ret) - return -1; - - if (memcmp(old_buf, new_buf, diff) != 0) - goto out_different; - } - - return status; -out_different: - if (sd->compare_flags & COMPARE_EXTRACT_FILES) { - if (extract_files(sd, old, new, path)) - return -1; - } - return 1; -} diff --git a/bin/sqfsdiff/extract.c b/bin/sqfsdiff/extract.c deleted file mode 100644 index f2072d4..0000000 --- a/bin/sqfsdiff/extract.c +++ /dev/null @@ -1,58 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * extract.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static int extract(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, - const char *prefix, const char *path, size_t block_size) -{ - char *ptr, *temp; - ostream_t *fp; - - temp = alloca(strlen(prefix) + strlen(path) + 2); - sprintf(temp, "%s/%s", prefix, path); - - ptr = strrchr(temp, '/'); - *ptr = '\0'; - if (mkdir_p(temp)) - return -1; - *ptr = '/'; - - fp = ostream_open_file(temp, OSTREAM_OPEN_OVERWRITE | - OSTREAM_OPEN_SPARSE); - if (fp == NULL) { - perror(temp); - return -1; - } - - if (sqfs_data_reader_dump(path, data, inode, fp, block_size)) { - sqfs_drop(fp); - return -1; - } - - ostream_flush(fp); - sqfs_drop(fp); - return 0; -} - -int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, - const char *path) -{ - if (old != NULL) { - if (extract(sd->sqfs_old.data, old, "old", - path, sd->sqfs_old.super.block_size)) - return -1; - } - - if (new != NULL) { - if (extract(sd->sqfs_new.data, new, "new", - path, sd->sqfs_new.super.block_size)) - return -1; - } - - return 0; -} diff --git a/bin/sqfsdiff/node_compare.c b/bin/sqfsdiff/node_compare.c deleted file mode 100644 index a0c99c7..0000000 --- a/bin/sqfsdiff/node_compare.c +++ /dev/null @@ -1,206 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * node_compare.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b) -{ - sqfs_tree_node_t *ait, *bit; - bool promoted, demoted; - int ret, status = 0; - char *path; - - ret = sqfs_tree_node_get_path(a, &path); - if (ret != 0) { - sqfs_perror(NULL, "constructing absolute file path", ret); - return -1; - } - - if (a->inode->base.type != b->inode->base.type) { - promoted = demoted = false; - - switch (a->inode->base.type) { - case SQFS_INODE_DIR: - if (b->inode->base.type == SQFS_INODE_EXT_DIR) - promoted = true; - break; - case SQFS_INODE_FILE: - if (b->inode->base.type == SQFS_INODE_EXT_FILE) - promoted = true; - break; - case SQFS_INODE_SLINK: - if (b->inode->base.type == SQFS_INODE_EXT_SLINK) - promoted = true; - break; - case SQFS_INODE_BDEV: - if (b->inode->base.type == SQFS_INODE_EXT_BDEV) - promoted = true; - break; - case SQFS_INODE_CDEV: - if (b->inode->base.type == SQFS_INODE_EXT_CDEV) - promoted = true; - break; - case SQFS_INODE_FIFO: - if (b->inode->base.type == SQFS_INODE_EXT_FIFO) - promoted = true; - break; - case SQFS_INODE_SOCKET: - if (b->inode->base.type == SQFS_INODE_EXT_SOCKET) - promoted = true; - break; - case SQFS_INODE_EXT_DIR: - if (b->inode->base.type == SQFS_INODE_DIR) - demoted = true; - break; - case SQFS_INODE_EXT_FILE: - if (b->inode->base.type == SQFS_INODE_FILE) - demoted = true; - break; - case SQFS_INODE_EXT_SLINK: - if (b->inode->base.type == SQFS_INODE_SLINK) - demoted = true; - break; - case SQFS_INODE_EXT_BDEV: - if (b->inode->base.type == SQFS_INODE_BDEV) - demoted = true; - break; - case SQFS_INODE_EXT_CDEV: - if (b->inode->base.type == SQFS_INODE_CDEV) - demoted = true; - break; - case SQFS_INODE_EXT_FIFO: - if (b->inode->base.type == SQFS_INODE_FIFO) - demoted = true; - break; - case SQFS_INODE_EXT_SOCKET: - if (b->inode->base.type == SQFS_INODE_SOCKET) - demoted = true; - break; - default: - break; - } - - if (promoted) { - fprintf(stdout, "%s has an extended type\n", path); - status = 1; - } else if (demoted) { - fprintf(stdout, "%s has a basic type\n", path); - status = 1; - } else { - fprintf(stdout, "%s has a different type\n", path); - sqfs_free(path); - return 1; - } - } - - if (!(sd->compare_flags & COMPARE_NO_PERM)) { - if ((a->inode->base.mode & ~S_IFMT) != - (b->inode->base.mode & ~S_IFMT)) { - fprintf(stdout, "%s has different permissions\n", - path); - status = 1; - } - } - - if (!(sd->compare_flags & COMPARE_NO_OWNER)) { - if (a->uid != b->uid || a->gid != b->gid) { - fprintf(stdout, "%s has different ownership\n", path); - status = 1; - } - } - - if (sd->compare_flags & COMPARE_TIMESTAMP) { - if (a->inode->base.mod_time != b->inode->base.mod_time) { - fprintf(stdout, "%s has a different timestamp\n", path); - status = 1; - } - } - - if (sd->compare_flags & COMPARE_INODE_NUM) { - if (a->inode->base.inode_number != - b->inode->base.inode_number) { - fprintf(stdout, "%s has a different inode number\n", - path); - status = 1; - } - } - - switch (a->inode->base.type) { - case SQFS_INODE_SOCKET: - case SQFS_INODE_EXT_SOCKET: - case SQFS_INODE_FIFO: - case SQFS_INODE_EXT_FIFO: - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - if (a->inode->data.dev.devno != b->inode->data.dev.devno) { - fprintf(stdout, "%s has different device number\n", - path); - status = 1; - } - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - if (a->inode->data.dev_ext.devno != - b->inode->data.dev_ext.devno) { - fprintf(stdout, "%s has different device number\n", - path); - status = 1; - } - break; - case SQFS_INODE_SLINK: - case SQFS_INODE_EXT_SLINK: - if (strcmp((const char *)a->inode->extra, - (const char *)b->inode->extra)) { - fprintf(stdout, "%s has a different link target\n", - path); - } - break; - case SQFS_INODE_DIR: - case SQFS_INODE_EXT_DIR: - ret = compare_dir_entries(sd, a, b); - if (ret < 0) { - status = -1; - break; - } - if (ret > 0) - status = 1; - - sqfs_free(path); - path = NULL; - - ait = a->children; - bit = b->children; - - while (ait != NULL && bit != NULL) { - ret = node_compare(sd, ait, bit); - if (ret < 0) - return -1; - if (ret > 0) - status = 1; - - ait = ait->next; - bit = bit->next; - } - break; - case SQFS_INODE_FILE: - case SQFS_INODE_EXT_FILE: - ret = compare_files(sd, a->inode, b->inode, path); - if (ret < 0) { - status = -1; - } else if (ret > 0) { - fprintf(stdout, "regular file %s differs\n", path); - status = 1; - } - break; - default: - fprintf(stdout, "%s has unknown type, ignoring\n", path); - break; - } - - sqfs_free(path); - return status; -} diff --git a/bin/sqfsdiff/options.c b/bin/sqfsdiff/options.c deleted file mode 100644 index b8ce7f0..0000000 --- a/bin/sqfsdiff/options.c +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfsdiff.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static struct option long_opts[] = { - { "old", required_argument, NULL, 'a' }, - { "new", required_argument, NULL, 'b' }, - { "no-owner", no_argument, NULL, 'O' }, - { "no-permissions", no_argument, NULL, 'P' }, - { "no-contents", no_argument, NULL, 'C' }, - { "timestamps", no_argument, NULL, 'T' }, - { "inode-num", no_argument, NULL, 'I' }, - { "super", no_argument, NULL, 'S' }, - { "extract", required_argument, NULL, 'e' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = "a:b:OPCTISe:hV"; - -static const char *usagestr = -"Usage: sqfsdiff [OPTIONS...] --old,-a --new,-b \n" -"\n" -"Compare two squashfs images. In contrast to doing a direct diff of the\n" -"images, this actually parses the filesystems and generates a more\n" -"meaningful difference report.\n" -"\n" -"If only contents are compared, any differences in packed file layout,\n" -"ordering, compression, inode meta data and so on is ignored and the two\n" -"images are considered equal if each directory contains the same entries,\n" -"symlink with the same paths have the same targets, device nodes the same\n" -"device number and files the same size and contents.\n" -"\n" -"A report of any difference is printed to stdout. The exit status is similar\n" -"that of diff(1): 0 means equal, 1 means different, 2 means problem.\n" -"\n" -"Possible options:\n" -"\n" -" --old, -a The first of the two filesystems to compare.\n" -" --new, -b The second of the two filesystems to compare.\n" -"\n" -" --no-contents, -C Do not compare file contents.\n" -" --no-owner, -O Do not compare file owners.\n" -" --no-permissions, -P Do not compare permission bits.\n" -"\n" -" --timestamps, -T Compare file timestamps.\n" -" --inode-num, -I Compare inode numbers of all files.\n" -" --super, -S Also compare meta data in super blocks.\n" -"\n" -" --extract, -e Extract files that differ to the specified\n" -" directory. Contents of the first filesystem\n" -" end up in a subdirectory 'old' and of the\n" -" second filesystem in a subdirectory 'new'.\n" -"\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n"; - -void process_options(sqfsdiff_t *sd, int argc, char **argv) -{ - int i; - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case 'a': - sd->old_path = optarg; - break; - case 'b': - sd->new_path = optarg; - break; - case 'O': - sd->compare_flags |= COMPARE_NO_OWNER; - break; - case 'P': - sd->compare_flags |= COMPARE_NO_PERM; - break; - case 'C': - sd->compare_flags |= COMPARE_NO_CONTENTS; - break; - case 'T': - sd->compare_flags |= COMPARE_TIMESTAMP; - break; - case 'I': - sd->compare_flags |= COMPARE_INODE_NUM; - break; - case 'S': - sd->compare_super = true; - break; - case 'e': - sd->compare_flags |= COMPARE_EXTRACT_FILES; - sd->extract_dir = optarg; - break; - case 'h': - fputs(usagestr, stdout); - exit(0); - case 'V': - print_version("sqfsdiff"); - exit(0); - default: - goto fail_arg; - } - } - - if (sd->old_path == NULL) { - fputs("Missing arguments: first filesystem\n", stderr); - goto fail_arg; - } - - if (sd->new_path == NULL) { - fputs("Missing arguments: second filesystem\n", stderr); - goto fail_arg; - } - - if (optind < argc) { - fputs("Unknown extra arguments\n", stderr); - goto fail_arg; - } - return; -fail_arg: - fprintf(stderr, "Try `sqfsdiff --help' for more information.\n"); - exit(2); -} diff --git a/bin/sqfsdiff/sqfsdiff.c b/bin/sqfsdiff/sqfsdiff.c deleted file mode 100644 index d789fe1..0000000 --- a/bin/sqfsdiff/sqfsdiff.c +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfsdiff.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static void close_sfqs(sqfs_state_t *state) -{ - sqfs_drop(state->data); - sqfs_dir_tree_destroy(state->root); - sqfs_drop(state->dr); - sqfs_drop(state->idtbl); - sqfs_drop(state->cmp); - sqfs_drop(state->file); -} - -static int open_sfqs(sqfs_state_t *state, const char *path) -{ - int ret; - - memset(state, 0, sizeof(*state)); - - state->file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); - if (state->file == NULL) { - perror(path); - return -1; - } - - ret = sqfs_super_read(&state->super, state->file); - if (ret) { - sqfs_perror(path, "reading super block", ret); - goto fail; - } - - sqfs_compressor_config_init(&state->cfg, state->super.compression_id, - state->super.block_size, - SQFS_COMP_FLAG_UNCOMPRESS); - - ret = sqfs_compressor_create(&state->cfg, &state->cmp); - -#ifdef WITH_LZO - if (state->super.compression_id == SQFS_COMP_LZO && ret != 0) - ret = lzo_compressor_create(&state->cfg, &state->cmp); -#endif - - if (ret != 0) { - sqfs_perror(path, "creating compressor", ret); - goto fail; - } - - if (state->super.flags & SQFS_FLAG_COMPRESSOR_OPTIONS) { - ret = state->cmp->read_options(state->cmp, state->file); - - if (ret == 0) { - state->cmp->get_configuration(state->cmp, - &state->options); - state->have_options = true; - } else { - sqfs_perror(path, "reading compressor options", ret); - state->have_options = false; - } - } else { - state->have_options = false; - } - - state->idtbl = sqfs_id_table_create(0); - if (state->idtbl == NULL) { - sqfs_perror(path, "creating ID table", SQFS_ERROR_ALLOC); - goto fail; - } - - ret = sqfs_id_table_read(state->idtbl, state->file, - &state->super, state->cmp); - if (ret) { - sqfs_perror(path, "loading ID table", ret); - goto fail; - } - - state->dr = sqfs_dir_reader_create(&state->super, state->cmp, - state->file, 0); - if (state->dr == NULL) { - sqfs_perror(path, "creating directory reader", - SQFS_ERROR_ALLOC); - goto fail; - } - - ret = sqfs_dir_reader_get_full_hierarchy(state->dr, state->idtbl, - NULL, 0, &state->root); - if (ret) { - sqfs_perror(path, "loading filesystem tree", ret); - goto fail; - } - - state->data = sqfs_data_reader_create(state->file, - state->super.block_size, - state->cmp, 0); - if (state->data == NULL) { - sqfs_perror(path, "creating data reader", SQFS_ERROR_ALLOC); - goto fail; - } - - ret = sqfs_data_reader_load_fragment_table(state->data, &state->super); - if (ret) { - sqfs_perror(path, "loading fragment table", ret); - goto fail; - } - - return 0; -fail: - close_sfqs(state); - return -1; -} - -int main(int argc, char **argv) -{ - int status, ret = 0; - sqfsdiff_t sd; - - memset(&sd, 0, sizeof(sd)); - process_options(&sd, argc, argv); - - if (sd.extract_dir != NULL) { - if (mkdir_p(sd.extract_dir)) - return 2; - } - - if (open_sfqs(&sd.sqfs_old, sd.old_path)) - return 2; - - if (open_sfqs(&sd.sqfs_new, sd.new_path)) { - status = 2; - goto out_sqfs_old; - } - - if (sd.extract_dir != NULL) { - if (chdir(sd.extract_dir)) { - perror(sd.extract_dir); - ret = -1; - goto out; - } - } - - ret = node_compare(&sd, sd.sqfs_old.root, sd.sqfs_new.root); - if (ret != 0) - goto out; - - if (sd.compare_super) { - ret = compare_super_blocks(&sd.sqfs_old.super, - &sd.sqfs_new.super); - if (ret != 0) - goto out; - } -out: - if (ret < 0) { - status = 2; - } else if (ret > 0) { - status = 1; - } else { - status = 0; - } - close_sfqs(&sd.sqfs_new); -out_sqfs_old: - close_sfqs(&sd.sqfs_old); - return status; -} diff --git a/bin/sqfsdiff/sqfsdiff.h b/bin/sqfsdiff/sqfsdiff.h deleted file mode 100644 index 65e8120..0000000 --- a/bin/sqfsdiff/sqfsdiff.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfsdiff.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef DIFFTOOL_H -#define DIFFTOOL_H - -#include "config.h" -#include "common.h" -#include "fstree.h" -#include "util/util.h" - -#include -#include -#include -#include - -#define MAX_WINDOW_SIZE (1024 * 1024 * 4) - -typedef struct { - sqfs_compressor_config_t cfg; - sqfs_compressor_t *cmp; - sqfs_super_t super; - sqfs_file_t *file; - sqfs_id_table_t *idtbl; - sqfs_dir_reader_t *dr; - sqfs_tree_node_t *root; - sqfs_data_reader_t *data; - - sqfs_compressor_config_t options; - bool have_options; -} sqfs_state_t; - -typedef struct { - const char *old_path; - const char *new_path; - int compare_flags; - sqfs_state_t sqfs_old; - sqfs_state_t sqfs_new; - bool compare_super; - const char *extract_dir; -} sqfsdiff_t; - -enum { - COMPARE_NO_PERM = 0x01, - COMPARE_NO_OWNER = 0x02, - COMPARE_NO_CONTENTS = 0x04, - COMPARE_TIMESTAMP = 0x08, - COMPARE_INODE_NUM = 0x10, - COMPARE_EXTRACT_FILES = 0x20, -}; - -int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, - sqfs_tree_node_t *new); - -char *node_path(const sqfs_tree_node_t *n); - -int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, const char *path); - -int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b); - -int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b); - -int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, - const char *path); - -void process_options(sqfsdiff_t *sd, int argc, char **argv); - -#endif /* DIFFTOOL_H */ diff --git a/bin/sqfsdiff/src/compare_dir.c b/bin/sqfsdiff/src/compare_dir.c new file mode 100644 index 0000000..1a4c800 --- /dev/null +++ b/bin/sqfsdiff/src/compare_dir.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * compare_dir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static int print_omitted(sqfsdiff_t *sd, bool is_old, sqfs_tree_node_t *n) +{ + char *path = node_path(n); + + if (path == NULL) + return -1; + + fprintf(stdout, "%c %s\n", is_old ? '<' : '>', path); + + if ((sd->compare_flags & COMPARE_EXTRACT_FILES) && + S_ISREG(n->inode->base.mode)) { + if (extract_files(sd, is_old ? n->inode : NULL, + is_old ? NULL : n->inode, path)) { + free(path); + return -1; + } + } + + free(path); + + for (n = n->children; n != NULL; n = n->next) { + if (print_omitted(sd, is_old, n)) + return -1; + } + + return 0; +} + +int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, + sqfs_tree_node_t *new) +{ + sqfs_tree_node_t *old_it = old->children, *old_prev = NULL; + sqfs_tree_node_t *new_it = new->children, *new_prev = NULL; + int ret, result = 0; + + while (old_it != NULL || new_it != NULL) { + if (old_it != NULL && new_it != NULL) { + ret = strcmp((const char *)old_it->name, + (const char *)new_it->name); + } else if (old_it == NULL) { + ret = 1; + } else { + ret = -1; + } + + if (ret < 0) { + result = 1; + + if (print_omitted(sd, true, old_it)) + return -1; + + if (old_prev == NULL) { + old->children = old_it->next; + sqfs_dir_tree_destroy(old_it); + old_it = old->children; + } else { + old_prev->next = old_it->next; + sqfs_dir_tree_destroy(old_it); + old_it = old_prev->next; + } + } else if (ret > 0) { + result = 1; + + if (print_omitted(sd, false, new_it)) + return -1; + + if (new_prev == NULL) { + new->children = new_it->next; + sqfs_dir_tree_destroy(new_it); + new_it = new->children; + } else { + new_prev->next = new_it->next; + sqfs_dir_tree_destroy(new_it); + new_it = new_prev->next; + } + } else { + old_prev = old_it; + old_it = old_it->next; + + new_prev = new_it; + new_it = new_it->next; + } + } + + return result; +} diff --git a/bin/sqfsdiff/src/compare_files.c b/bin/sqfsdiff/src/compare_files.c new file mode 100644 index 0000000..51b66bb --- /dev/null +++ b/bin/sqfsdiff/src/compare_files.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * compare_files.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static unsigned char old_buf[MAX_WINDOW_SIZE]; +static unsigned char new_buf[MAX_WINDOW_SIZE]; + +static int read_blob(const char *prefix, const char *path, + sqfs_data_reader_t *rd, const sqfs_inode_generic_t *inode, + void *buffer, sqfs_u64 offset, size_t size) +{ + ssize_t ret; + + ret = sqfs_data_reader_read(rd, inode, offset, buffer, size); + ret = (ret < 0 || (size_t)ret < size) ? -1 : 0; + + if (ret) { + fprintf(stderr, "Failed to read %s from %s\n", + path, prefix); + return -1; + } + + return 0; +} + +int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, const char *path) +{ + sqfs_u64 offset, diff, oldsz, newsz; + int status = 0, ret; + + sqfs_inode_get_file_size(old, &oldsz); + sqfs_inode_get_file_size(new, &newsz); + + if (oldsz != newsz) + goto out_different; + + if (sd->compare_flags & COMPARE_NO_CONTENTS) + return 0; + + for (offset = 0; offset < oldsz; offset += diff) { + diff = oldsz - offset; + + if (diff > MAX_WINDOW_SIZE) + diff = MAX_WINDOW_SIZE; + + ret = read_blob(sd->old_path, path, + sd->sqfs_old.data, old, old_buf, offset, diff); + if (ret) + return -1; + + ret = read_blob(sd->new_path, path, + sd->sqfs_new.data, new, new_buf, offset, diff); + if (ret) + return -1; + + if (memcmp(old_buf, new_buf, diff) != 0) + goto out_different; + } + + return status; +out_different: + if (sd->compare_flags & COMPARE_EXTRACT_FILES) { + if (extract_files(sd, old, new, path)) + return -1; + } + return 1; +} diff --git a/bin/sqfsdiff/src/extract.c b/bin/sqfsdiff/src/extract.c new file mode 100644 index 0000000..f2072d4 --- /dev/null +++ b/bin/sqfsdiff/src/extract.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * extract.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static int extract(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, + const char *prefix, const char *path, size_t block_size) +{ + char *ptr, *temp; + ostream_t *fp; + + temp = alloca(strlen(prefix) + strlen(path) + 2); + sprintf(temp, "%s/%s", prefix, path); + + ptr = strrchr(temp, '/'); + *ptr = '\0'; + if (mkdir_p(temp)) + return -1; + *ptr = '/'; + + fp = ostream_open_file(temp, OSTREAM_OPEN_OVERWRITE | + OSTREAM_OPEN_SPARSE); + if (fp == NULL) { + perror(temp); + return -1; + } + + if (sqfs_data_reader_dump(path, data, inode, fp, block_size)) { + sqfs_drop(fp); + return -1; + } + + ostream_flush(fp); + sqfs_drop(fp); + return 0; +} + +int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, + const char *path) +{ + if (old != NULL) { + if (extract(sd->sqfs_old.data, old, "old", + path, sd->sqfs_old.super.block_size)) + return -1; + } + + if (new != NULL) { + if (extract(sd->sqfs_new.data, new, "new", + path, sd->sqfs_new.super.block_size)) + return -1; + } + + return 0; +} diff --git a/bin/sqfsdiff/src/node_compare.c b/bin/sqfsdiff/src/node_compare.c new file mode 100644 index 0000000..a0c99c7 --- /dev/null +++ b/bin/sqfsdiff/src/node_compare.c @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * node_compare.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b) +{ + sqfs_tree_node_t *ait, *bit; + bool promoted, demoted; + int ret, status = 0; + char *path; + + ret = sqfs_tree_node_get_path(a, &path); + if (ret != 0) { + sqfs_perror(NULL, "constructing absolute file path", ret); + return -1; + } + + if (a->inode->base.type != b->inode->base.type) { + promoted = demoted = false; + + switch (a->inode->base.type) { + case SQFS_INODE_DIR: + if (b->inode->base.type == SQFS_INODE_EXT_DIR) + promoted = true; + break; + case SQFS_INODE_FILE: + if (b->inode->base.type == SQFS_INODE_EXT_FILE) + promoted = true; + break; + case SQFS_INODE_SLINK: + if (b->inode->base.type == SQFS_INODE_EXT_SLINK) + promoted = true; + break; + case SQFS_INODE_BDEV: + if (b->inode->base.type == SQFS_INODE_EXT_BDEV) + promoted = true; + break; + case SQFS_INODE_CDEV: + if (b->inode->base.type == SQFS_INODE_EXT_CDEV) + promoted = true; + break; + case SQFS_INODE_FIFO: + if (b->inode->base.type == SQFS_INODE_EXT_FIFO) + promoted = true; + break; + case SQFS_INODE_SOCKET: + if (b->inode->base.type == SQFS_INODE_EXT_SOCKET) + promoted = true; + break; + case SQFS_INODE_EXT_DIR: + if (b->inode->base.type == SQFS_INODE_DIR) + demoted = true; + break; + case SQFS_INODE_EXT_FILE: + if (b->inode->base.type == SQFS_INODE_FILE) + demoted = true; + break; + case SQFS_INODE_EXT_SLINK: + if (b->inode->base.type == SQFS_INODE_SLINK) + demoted = true; + break; + case SQFS_INODE_EXT_BDEV: + if (b->inode->base.type == SQFS_INODE_BDEV) + demoted = true; + break; + case SQFS_INODE_EXT_CDEV: + if (b->inode->base.type == SQFS_INODE_CDEV) + demoted = true; + break; + case SQFS_INODE_EXT_FIFO: + if (b->inode->base.type == SQFS_INODE_FIFO) + demoted = true; + break; + case SQFS_INODE_EXT_SOCKET: + if (b->inode->base.type == SQFS_INODE_SOCKET) + demoted = true; + break; + default: + break; + } + + if (promoted) { + fprintf(stdout, "%s has an extended type\n", path); + status = 1; + } else if (demoted) { + fprintf(stdout, "%s has a basic type\n", path); + status = 1; + } else { + fprintf(stdout, "%s has a different type\n", path); + sqfs_free(path); + return 1; + } + } + + if (!(sd->compare_flags & COMPARE_NO_PERM)) { + if ((a->inode->base.mode & ~S_IFMT) != + (b->inode->base.mode & ~S_IFMT)) { + fprintf(stdout, "%s has different permissions\n", + path); + status = 1; + } + } + + if (!(sd->compare_flags & COMPARE_NO_OWNER)) { + if (a->uid != b->uid || a->gid != b->gid) { + fprintf(stdout, "%s has different ownership\n", path); + status = 1; + } + } + + if (sd->compare_flags & COMPARE_TIMESTAMP) { + if (a->inode->base.mod_time != b->inode->base.mod_time) { + fprintf(stdout, "%s has a different timestamp\n", path); + status = 1; + } + } + + if (sd->compare_flags & COMPARE_INODE_NUM) { + if (a->inode->base.inode_number != + b->inode->base.inode_number) { + fprintf(stdout, "%s has a different inode number\n", + path); + status = 1; + } + } + + switch (a->inode->base.type) { + case SQFS_INODE_SOCKET: + case SQFS_INODE_EXT_SOCKET: + case SQFS_INODE_FIFO: + case SQFS_INODE_EXT_FIFO: + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + if (a->inode->data.dev.devno != b->inode->data.dev.devno) { + fprintf(stdout, "%s has different device number\n", + path); + status = 1; + } + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + if (a->inode->data.dev_ext.devno != + b->inode->data.dev_ext.devno) { + fprintf(stdout, "%s has different device number\n", + path); + status = 1; + } + break; + case SQFS_INODE_SLINK: + case SQFS_INODE_EXT_SLINK: + if (strcmp((const char *)a->inode->extra, + (const char *)b->inode->extra)) { + fprintf(stdout, "%s has a different link target\n", + path); + } + break; + case SQFS_INODE_DIR: + case SQFS_INODE_EXT_DIR: + ret = compare_dir_entries(sd, a, b); + if (ret < 0) { + status = -1; + break; + } + if (ret > 0) + status = 1; + + sqfs_free(path); + path = NULL; + + ait = a->children; + bit = b->children; + + while (ait != NULL && bit != NULL) { + ret = node_compare(sd, ait, bit); + if (ret < 0) + return -1; + if (ret > 0) + status = 1; + + ait = ait->next; + bit = bit->next; + } + break; + case SQFS_INODE_FILE: + case SQFS_INODE_EXT_FILE: + ret = compare_files(sd, a->inode, b->inode, path); + if (ret < 0) { + status = -1; + } else if (ret > 0) { + fprintf(stdout, "regular file %s differs\n", path); + status = 1; + } + break; + default: + fprintf(stdout, "%s has unknown type, ignoring\n", path); + break; + } + + sqfs_free(path); + return status; +} diff --git a/bin/sqfsdiff/src/options.c b/bin/sqfsdiff/src/options.c new file mode 100644 index 0000000..b8ce7f0 --- /dev/null +++ b/bin/sqfsdiff/src/options.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfsdiff.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static struct option long_opts[] = { + { "old", required_argument, NULL, 'a' }, + { "new", required_argument, NULL, 'b' }, + { "no-owner", no_argument, NULL, 'O' }, + { "no-permissions", no_argument, NULL, 'P' }, + { "no-contents", no_argument, NULL, 'C' }, + { "timestamps", no_argument, NULL, 'T' }, + { "inode-num", no_argument, NULL, 'I' }, + { "super", no_argument, NULL, 'S' }, + { "extract", required_argument, NULL, 'e' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "a:b:OPCTISe:hV"; + +static const char *usagestr = +"Usage: sqfsdiff [OPTIONS...] --old,-a --new,-b \n" +"\n" +"Compare two squashfs images. In contrast to doing a direct diff of the\n" +"images, this actually parses the filesystems and generates a more\n" +"meaningful difference report.\n" +"\n" +"If only contents are compared, any differences in packed file layout,\n" +"ordering, compression, inode meta data and so on is ignored and the two\n" +"images are considered equal if each directory contains the same entries,\n" +"symlink with the same paths have the same targets, device nodes the same\n" +"device number and files the same size and contents.\n" +"\n" +"A report of any difference is printed to stdout. The exit status is similar\n" +"that of diff(1): 0 means equal, 1 means different, 2 means problem.\n" +"\n" +"Possible options:\n" +"\n" +" --old, -a The first of the two filesystems to compare.\n" +" --new, -b The second of the two filesystems to compare.\n" +"\n" +" --no-contents, -C Do not compare file contents.\n" +" --no-owner, -O Do not compare file owners.\n" +" --no-permissions, -P Do not compare permission bits.\n" +"\n" +" --timestamps, -T Compare file timestamps.\n" +" --inode-num, -I Compare inode numbers of all files.\n" +" --super, -S Also compare meta data in super blocks.\n" +"\n" +" --extract, -e Extract files that differ to the specified\n" +" directory. Contents of the first filesystem\n" +" end up in a subdirectory 'old' and of the\n" +" second filesystem in a subdirectory 'new'.\n" +"\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +void process_options(sqfsdiff_t *sd, int argc, char **argv) +{ + int i; + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'a': + sd->old_path = optarg; + break; + case 'b': + sd->new_path = optarg; + break; + case 'O': + sd->compare_flags |= COMPARE_NO_OWNER; + break; + case 'P': + sd->compare_flags |= COMPARE_NO_PERM; + break; + case 'C': + sd->compare_flags |= COMPARE_NO_CONTENTS; + break; + case 'T': + sd->compare_flags |= COMPARE_TIMESTAMP; + break; + case 'I': + sd->compare_flags |= COMPARE_INODE_NUM; + break; + case 'S': + sd->compare_super = true; + break; + case 'e': + sd->compare_flags |= COMPARE_EXTRACT_FILES; + sd->extract_dir = optarg; + break; + case 'h': + fputs(usagestr, stdout); + exit(0); + case 'V': + print_version("sqfsdiff"); + exit(0); + default: + goto fail_arg; + } + } + + if (sd->old_path == NULL) { + fputs("Missing arguments: first filesystem\n", stderr); + goto fail_arg; + } + + if (sd->new_path == NULL) { + fputs("Missing arguments: second filesystem\n", stderr); + goto fail_arg; + } + + if (optind < argc) { + fputs("Unknown extra arguments\n", stderr); + goto fail_arg; + } + return; +fail_arg: + fprintf(stderr, "Try `sqfsdiff --help' for more information.\n"); + exit(2); +} diff --git a/bin/sqfsdiff/src/sqfsdiff.c b/bin/sqfsdiff/src/sqfsdiff.c new file mode 100644 index 0000000..d789fe1 --- /dev/null +++ b/bin/sqfsdiff/src/sqfsdiff.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfsdiff.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static void close_sfqs(sqfs_state_t *state) +{ + sqfs_drop(state->data); + sqfs_dir_tree_destroy(state->root); + sqfs_drop(state->dr); + sqfs_drop(state->idtbl); + sqfs_drop(state->cmp); + sqfs_drop(state->file); +} + +static int open_sfqs(sqfs_state_t *state, const char *path) +{ + int ret; + + memset(state, 0, sizeof(*state)); + + state->file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); + if (state->file == NULL) { + perror(path); + return -1; + } + + ret = sqfs_super_read(&state->super, state->file); + if (ret) { + sqfs_perror(path, "reading super block", ret); + goto fail; + } + + sqfs_compressor_config_init(&state->cfg, state->super.compression_id, + state->super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&state->cfg, &state->cmp); + +#ifdef WITH_LZO + if (state->super.compression_id == SQFS_COMP_LZO && ret != 0) + ret = lzo_compressor_create(&state->cfg, &state->cmp); +#endif + + if (ret != 0) { + sqfs_perror(path, "creating compressor", ret); + goto fail; + } + + if (state->super.flags & SQFS_FLAG_COMPRESSOR_OPTIONS) { + ret = state->cmp->read_options(state->cmp, state->file); + + if (ret == 0) { + state->cmp->get_configuration(state->cmp, + &state->options); + state->have_options = true; + } else { + sqfs_perror(path, "reading compressor options", ret); + state->have_options = false; + } + } else { + state->have_options = false; + } + + state->idtbl = sqfs_id_table_create(0); + if (state->idtbl == NULL) { + sqfs_perror(path, "creating ID table", SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_id_table_read(state->idtbl, state->file, + &state->super, state->cmp); + if (ret) { + sqfs_perror(path, "loading ID table", ret); + goto fail; + } + + state->dr = sqfs_dir_reader_create(&state->super, state->cmp, + state->file, 0); + if (state->dr == NULL) { + sqfs_perror(path, "creating directory reader", + SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_dir_reader_get_full_hierarchy(state->dr, state->idtbl, + NULL, 0, &state->root); + if (ret) { + sqfs_perror(path, "loading filesystem tree", ret); + goto fail; + } + + state->data = sqfs_data_reader_create(state->file, + state->super.block_size, + state->cmp, 0); + if (state->data == NULL) { + sqfs_perror(path, "creating data reader", SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_data_reader_load_fragment_table(state->data, &state->super); + if (ret) { + sqfs_perror(path, "loading fragment table", ret); + goto fail; + } + + return 0; +fail: + close_sfqs(state); + return -1; +} + +int main(int argc, char **argv) +{ + int status, ret = 0; + sqfsdiff_t sd; + + memset(&sd, 0, sizeof(sd)); + process_options(&sd, argc, argv); + + if (sd.extract_dir != NULL) { + if (mkdir_p(sd.extract_dir)) + return 2; + } + + if (open_sfqs(&sd.sqfs_old, sd.old_path)) + return 2; + + if (open_sfqs(&sd.sqfs_new, sd.new_path)) { + status = 2; + goto out_sqfs_old; + } + + if (sd.extract_dir != NULL) { + if (chdir(sd.extract_dir)) { + perror(sd.extract_dir); + ret = -1; + goto out; + } + } + + ret = node_compare(&sd, sd.sqfs_old.root, sd.sqfs_new.root); + if (ret != 0) + goto out; + + if (sd.compare_super) { + ret = compare_super_blocks(&sd.sqfs_old.super, + &sd.sqfs_new.super); + if (ret != 0) + goto out; + } +out: + if (ret < 0) { + status = 2; + } else if (ret > 0) { + status = 1; + } else { + status = 0; + } + close_sfqs(&sd.sqfs_new); +out_sqfs_old: + close_sfqs(&sd.sqfs_old); + return status; +} diff --git a/bin/sqfsdiff/src/sqfsdiff.h b/bin/sqfsdiff/src/sqfsdiff.h new file mode 100644 index 0000000..65e8120 --- /dev/null +++ b/bin/sqfsdiff/src/sqfsdiff.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfsdiff.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef DIFFTOOL_H +#define DIFFTOOL_H + +#include "config.h" +#include "common.h" +#include "fstree.h" +#include "util/util.h" + +#include +#include +#include +#include + +#define MAX_WINDOW_SIZE (1024 * 1024 * 4) + +typedef struct { + sqfs_compressor_config_t cfg; + sqfs_compressor_t *cmp; + sqfs_super_t super; + sqfs_file_t *file; + sqfs_id_table_t *idtbl; + sqfs_dir_reader_t *dr; + sqfs_tree_node_t *root; + sqfs_data_reader_t *data; + + sqfs_compressor_config_t options; + bool have_options; +} sqfs_state_t; + +typedef struct { + const char *old_path; + const char *new_path; + int compare_flags; + sqfs_state_t sqfs_old; + sqfs_state_t sqfs_new; + bool compare_super; + const char *extract_dir; +} sqfsdiff_t; + +enum { + COMPARE_NO_PERM = 0x01, + COMPARE_NO_OWNER = 0x02, + COMPARE_NO_CONTENTS = 0x04, + COMPARE_TIMESTAMP = 0x08, + COMPARE_INODE_NUM = 0x10, + COMPARE_EXTRACT_FILES = 0x20, +}; + +int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, + sqfs_tree_node_t *new); + +char *node_path(const sqfs_tree_node_t *n); + +int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, const char *path); + +int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b); + +int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b); + +int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, + const char *path); + +void process_options(sqfsdiff_t *sd, int argc, char **argv); + +#endif /* DIFFTOOL_H */ diff --git a/bin/sqfsdiff/src/super.c b/bin/sqfsdiff/src/super.c new file mode 100644 index 0000000..0cf18e0 --- /dev/null +++ b/bin/sqfsdiff/src/super.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static const struct { + sqfs_u16 mask; + const char *name; +} sqfs_flags[] = { + { SQFS_FLAG_UNCOMPRESSED_INODES, "uncompressed inodes" }, + { SQFS_FLAG_UNCOMPRESSED_DATA, "uncompressed data" }, + { SQFS_FLAG_UNCOMPRESSED_FRAGMENTS, "uncompressed fragments" }, + { SQFS_FLAG_NO_FRAGMENTS, "no fragments" }, + { SQFS_FLAG_ALWAYS_FRAGMENTS, "always fragments" }, + { SQFS_FLAG_NO_DUPLICATES, "no duplicates" }, + { SQFS_FLAG_EXPORTABLE, "exportable" }, + { SQFS_FLAG_UNCOMPRESSED_XATTRS, "uncompressed xattrs" }, + { SQFS_FLAG_NO_XATTRS, "no xattrs" }, + { SQFS_FLAG_COMPRESSOR_OPTIONS, "compressor options" }, + { SQFS_FLAG_UNCOMPRESSED_IDS, "uncompressed ids" }, +}; + +static void print_value_difference(const char *name, sqfs_u64 a, sqfs_u64 b) +{ + sqfs_u64 diff; + char c; + + if (a != b) { + if (a < b) { + c = '+'; + diff = b - a; + } else { + c = '-'; + diff = a - b; + } + fprintf(stdout, "%s: %c%lu\n", name, c, + (unsigned long)diff); + } +} + +static void print_offset_diff(const char *name, sqfs_u64 a, sqfs_u64 b) +{ + if (a != b) + fprintf(stdout, "Location of %s differs\n", name); +} + +static void print_flag_diff(sqfs_u16 a, sqfs_u16 b) +{ + sqfs_u16 diff = a ^ b, mask; + size_t i; + char c; + + if (diff == 0) + return; + + fputs("flags:\n", stdout); + + for (i = 0; i < sizeof(sqfs_flags) / sizeof(sqfs_flags[0]); ++i) { + if (diff & sqfs_flags[i].mask) { + c = a & sqfs_flags[i].mask ? '<' : '>'; + + fprintf(stdout, "\t%c%s\n", c, sqfs_flags[i].name); + } + + a &= ~sqfs_flags[i].mask; + b &= ~sqfs_flags[i].mask; + diff &= ~sqfs_flags[i].mask; + } + + for (i = 0, mask = 0x01; i < 16; ++i, mask <<= 1) { + if (diff & mask) { + fprintf(stdout, "\t%c additional unknown\n", + a & mask ? '<' : '>'); + } + } +} + +int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b) +{ + if (memcmp(a, b, sizeof(*a)) == 0) + return 0; + + fputs("======== super blocks are different ========\n", stdout); + + /* TODO: if a new magic number or squashfs version is introduced, + compare them. */ + + print_value_difference("inode count", a->inode_count, b->inode_count); + print_value_difference("modification time", a->modification_time, + b->modification_time); + print_value_difference("block size", a->block_size, b->block_size); + print_value_difference("block log", a->block_log, b->block_log); + print_value_difference("fragment table entries", + a->fragment_entry_count, + b->fragment_entry_count); + print_value_difference("ID table entries", a->id_count, b->id_count); + + if (a->compression_id != b->compression_id) { + fprintf(stdout, "compressor: %s vs %s\n", + sqfs_compressor_name_from_id(a->compression_id), + sqfs_compressor_name_from_id(b->compression_id)); + } + + print_flag_diff(a->flags, b->flags); + + print_value_difference("total bytes used", a->bytes_used, + b->bytes_used); + + print_offset_diff("root inode", a->root_inode_ref, b->root_inode_ref); + print_offset_diff("ID table", a->id_table_start, b->id_table_start); + print_offset_diff("xattr ID table", a->xattr_id_table_start, + b->xattr_id_table_start); + print_offset_diff("inode table", a->inode_table_start, + b->inode_table_start); + print_offset_diff("directory table", a->directory_table_start, + b->directory_table_start); + print_offset_diff("fragment table", a->fragment_table_start, + b->fragment_table_start); + print_offset_diff("export table", a->export_table_start, + b->export_table_start); + return 1; +} diff --git a/bin/sqfsdiff/src/util.c b/bin/sqfsdiff/src/util.c new file mode 100644 index 0000000..a11770f --- /dev/null +++ b/bin/sqfsdiff/src/util.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * util.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +char *node_path(const sqfs_tree_node_t *n) +{ + char *path; + int ret; + + ret = sqfs_tree_node_get_path(n, &path); + if (ret != 0) { + sqfs_perror(NULL, "get path", ret); + return NULL; + } + + if (canonicalize_name(path)) { + fprintf(stderr, "failed to canonicalization '%s'\n", path); + sqfs_free(path); + return NULL; + } + + return path; +} diff --git a/bin/sqfsdiff/super.c b/bin/sqfsdiff/super.c deleted file mode 100644 index 0cf18e0..0000000 --- a/bin/sqfsdiff/super.c +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static const struct { - sqfs_u16 mask; - const char *name; -} sqfs_flags[] = { - { SQFS_FLAG_UNCOMPRESSED_INODES, "uncompressed inodes" }, - { SQFS_FLAG_UNCOMPRESSED_DATA, "uncompressed data" }, - { SQFS_FLAG_UNCOMPRESSED_FRAGMENTS, "uncompressed fragments" }, - { SQFS_FLAG_NO_FRAGMENTS, "no fragments" }, - { SQFS_FLAG_ALWAYS_FRAGMENTS, "always fragments" }, - { SQFS_FLAG_NO_DUPLICATES, "no duplicates" }, - { SQFS_FLAG_EXPORTABLE, "exportable" }, - { SQFS_FLAG_UNCOMPRESSED_XATTRS, "uncompressed xattrs" }, - { SQFS_FLAG_NO_XATTRS, "no xattrs" }, - { SQFS_FLAG_COMPRESSOR_OPTIONS, "compressor options" }, - { SQFS_FLAG_UNCOMPRESSED_IDS, "uncompressed ids" }, -}; - -static void print_value_difference(const char *name, sqfs_u64 a, sqfs_u64 b) -{ - sqfs_u64 diff; - char c; - - if (a != b) { - if (a < b) { - c = '+'; - diff = b - a; - } else { - c = '-'; - diff = a - b; - } - fprintf(stdout, "%s: %c%lu\n", name, c, - (unsigned long)diff); - } -} - -static void print_offset_diff(const char *name, sqfs_u64 a, sqfs_u64 b) -{ - if (a != b) - fprintf(stdout, "Location of %s differs\n", name); -} - -static void print_flag_diff(sqfs_u16 a, sqfs_u16 b) -{ - sqfs_u16 diff = a ^ b, mask; - size_t i; - char c; - - if (diff == 0) - return; - - fputs("flags:\n", stdout); - - for (i = 0; i < sizeof(sqfs_flags) / sizeof(sqfs_flags[0]); ++i) { - if (diff & sqfs_flags[i].mask) { - c = a & sqfs_flags[i].mask ? '<' : '>'; - - fprintf(stdout, "\t%c%s\n", c, sqfs_flags[i].name); - } - - a &= ~sqfs_flags[i].mask; - b &= ~sqfs_flags[i].mask; - diff &= ~sqfs_flags[i].mask; - } - - for (i = 0, mask = 0x01; i < 16; ++i, mask <<= 1) { - if (diff & mask) { - fprintf(stdout, "\t%c additional unknown\n", - a & mask ? '<' : '>'); - } - } -} - -int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b) -{ - if (memcmp(a, b, sizeof(*a)) == 0) - return 0; - - fputs("======== super blocks are different ========\n", stdout); - - /* TODO: if a new magic number or squashfs version is introduced, - compare them. */ - - print_value_difference("inode count", a->inode_count, b->inode_count); - print_value_difference("modification time", a->modification_time, - b->modification_time); - print_value_difference("block size", a->block_size, b->block_size); - print_value_difference("block log", a->block_log, b->block_log); - print_value_difference("fragment table entries", - a->fragment_entry_count, - b->fragment_entry_count); - print_value_difference("ID table entries", a->id_count, b->id_count); - - if (a->compression_id != b->compression_id) { - fprintf(stdout, "compressor: %s vs %s\n", - sqfs_compressor_name_from_id(a->compression_id), - sqfs_compressor_name_from_id(b->compression_id)); - } - - print_flag_diff(a->flags, b->flags); - - print_value_difference("total bytes used", a->bytes_used, - b->bytes_used); - - print_offset_diff("root inode", a->root_inode_ref, b->root_inode_ref); - print_offset_diff("ID table", a->id_table_start, b->id_table_start); - print_offset_diff("xattr ID table", a->xattr_id_table_start, - b->xattr_id_table_start); - print_offset_diff("inode table", a->inode_table_start, - b->inode_table_start); - print_offset_diff("directory table", a->directory_table_start, - b->directory_table_start); - print_offset_diff("fragment table", a->fragment_table_start, - b->fragment_table_start); - print_offset_diff("export table", a->export_table_start, - b->export_table_start); - return 1; -} diff --git a/bin/sqfsdiff/util.c b/bin/sqfsdiff/util.c deleted file mode 100644 index a11770f..0000000 --- a/bin/sqfsdiff/util.c +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * util.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -char *node_path(const sqfs_tree_node_t *n) -{ - char *path; - int ret; - - ret = sqfs_tree_node_get_path(n, &path); - if (ret != 0) { - sqfs_perror(NULL, "get path", ret); - return NULL; - } - - if (canonicalize_name(path)) { - fprintf(stderr, "failed to canonicalization '%s'\n", path); - sqfs_free(path); - return NULL; - } - - return path; -} -- cgit v1.2.3