aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--difftool/Makemodule.am2
-rw-r--r--difftool/difftool.h2
-rw-r--r--difftool/sqfsdiff.c35
-rw-r--r--difftool/super.c124
4 files changed, 155 insertions, 8 deletions
diff --git a/difftool/Makemodule.am b/difftool/Makemodule.am
index 62c9197..1b0011b 100644
--- a/difftool/Makemodule.am
+++ b/difftool/Makemodule.am
@@ -5,7 +5,7 @@ fscompare_LDADD = libfstree.a libutil.a
sqfsdiff_SOURCES = difftool/sqfsdiff.c difftool/difftool.h difftool/util.c
sqfsdiff_SOURCES += difftool/compare_dir.c difftool/node_compare.c
-sqfsdiff_SOURCES += difftool/compare_files_sqfs.c
+sqfsdiff_SOURCES += difftool/compare_files_sqfs.c difftool/super.c
sqfsdiff_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a
sqfsdiff_LDADD += $(XZ_LIBS) $(ZLIB_LIBS) $(LZO_LIBS) $(LZ4_LIBS) $(ZSTD_LIBS)
diff --git a/difftool/difftool.h b/difftool/difftool.h
index 4b6173a..23a161c 100644
--- a/difftool/difftool.h
+++ b/difftool/difftool.h
@@ -45,4 +45,6 @@ int compare_files(file_info_t *a, file_info_t *b, const char *path);
int node_compare(tree_node_t *a, tree_node_t *b);
+int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b);
+
#endif /* DIFFTOOL_H */
diff --git a/difftool/sqfsdiff.c b/difftool/sqfsdiff.c
index 96a8f31..8818a37 100644
--- a/difftool/sqfsdiff.c
+++ b/difftool/sqfsdiff.c
@@ -12,11 +12,12 @@ static struct option long_opts[] = {
{ "no-contents", no_argument, NULL, 'C' },
{ "timestamps", no_argument, NULL, 'T' },
{ "inode-num", no_argument, NULL, 'I' },
+ { "super", no_argument, NULL, 'S' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
};
-static const char *short_opts = "OPCTIhV";
+static const char *short_opts = "OPCTIShV";
static const char *usagestr =
"Usage: sqfsdiff [OPTIONS...] <first> <second>\n"
@@ -42,6 +43,7 @@ static const char *usagestr =
"\n"
" --timestamps, -T Compare file timestamps.\n"
" --inode-num, -I Compare inode numbers of all files.\n"
+" --super, -S Also compare metadata in super blocks.\n"
"\n"
" --help, -h Print help text and exit.\n"
" --version, -V Print version information and exit.\n"
@@ -52,6 +54,7 @@ const char *first_path;
const char *second_path;
sqfs_reader_t sqfs_a;
sqfs_reader_t sqfs_b;
+static bool compare_super = false;
static void process_options(int argc, char **argv)
{
@@ -78,6 +81,9 @@ static void process_options(int argc, char **argv)
case 'I':
compare_flags |= COMPARE_INODE_NUM;
break;
+ case 'S':
+ compare_super = true;
+ break;
case 'h':
fputs(usagestr, stdout);
exit(EXIT_SUCCESS);
@@ -115,22 +121,37 @@ fail_arg:
int main(int argc, char **argv)
{
- int ret = EXIT_FAILURE;
+ int status, ret = 0;
process_options(argc, argv);
if (sqfs_reader_open(&sqfs_a, first_path, 0))
- return EXIT_FAILURE;
+ return 2;
- if (sqfs_reader_open(&sqfs_b, second_path, 0))
+ if (sqfs_reader_open(&sqfs_b, second_path, 0)) {
+ status = 2;
goto out_sqfs_a;
+ }
ret = node_compare(sqfs_a.fs.root, sqfs_b.fs.root);
- if (ret < 0)
- ret = 2;
+ if (ret != 0)
+ goto out;
+ if (compare_super) {
+ ret = compare_super_blocks(&sqfs_a.super, &sqfs_b.super);
+ if (ret != 0)
+ goto out;
+ }
+out:
+ if (ret < 0) {
+ status = 2;
+ } else if (ret > 0) {
+ status = 1;
+ } else {
+ status = 0;
+ }
sqfs_reader_close(&sqfs_b);
out_sqfs_a:
sqfs_reader_close(&sqfs_a);
- return ret;
+ return status;
}
diff --git a/difftool/super.c b/difftool/super.c
new file mode 100644
index 0000000..7bda728
--- /dev/null
+++ b/difftool/super.c
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * super.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "difftool.h"
+
+static const struct {
+ uint16_t 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_DUPLICATES, "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, uint64_t a, uint64_t b)
+{
+ uint64_t 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, diff);
+ }
+}
+
+static void print_offset_diff(const char *name, uint64_t a, uint64_t b)
+{
+ if (a != b)
+ fprintf(stdout, "Location of %s differs\n", name);
+}
+
+static void print_flag_diff(uint16_t a, uint16_t b)
+{
+ uint16_t 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",
+ compressor_name_from_id(a->compression_id),
+ 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;
+}