From b3c3401aee91bc2ff46ba0eab0ceb88c78cb5bbb Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 9 Aug 2019 11:43:15 +0200 Subject: Add option to sqfsdiff to extract regular files that are different Signed-off-by: David Oberhollenzer --- difftool/Makemodule.am | 1 + difftool/compare_files_sqfs.c | 10 +++++++-- difftool/difftool.h | 3 +++ difftool/extract.c | 52 +++++++++++++++++++++++++++++++++++++++++++ difftool/sqfsdiff.c | 26 +++++++++++++++++++++- doc/sqfsdiff.1 | 6 +++++ 6 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 difftool/extract.c diff --git a/difftool/Makemodule.am b/difftool/Makemodule.am index 1b0011b..83e5f83 100644 --- a/difftool/Makemodule.am +++ b/difftool/Makemodule.am @@ -6,6 +6,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 difftool/super.c +sqfsdiff_SOURCES += difftool/extract.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/compare_files_sqfs.c b/difftool/compare_files_sqfs.c index 76fae7a..32b97b1 100644 --- a/difftool/compare_files_sqfs.c +++ b/difftool/compare_files_sqfs.c @@ -15,7 +15,7 @@ int compare_files(file_info_t *a, file_info_t *b, const char *path) ssize_t ret; if (a->size != b->size) - return 1; + goto out_different; if (compare_flags & COMPARE_NO_CONTENTS) return 0; @@ -41,8 +41,14 @@ int compare_files(file_info_t *a, file_info_t *b, const char *path) } if (memcmp(a_buf, b_buf, diff) != 0) - return 1; + goto out_different; } return 0; +out_different: + if (compare_flags & COMPARE_EXTRACT_FILES) { + if (extract_files(a, b, path)) + return -1; + } + return 1; } diff --git a/difftool/difftool.h b/difftool/difftool.h index 23a161c..bd04fd2 100644 --- a/difftool/difftool.h +++ b/difftool/difftool.h @@ -35,6 +35,7 @@ enum { COMPARE_NO_CONTENTS = 0x04, COMPARE_TIMESTAMP = 0x08, COMPARE_INODE_NUM = 0x10, + COMPARE_EXTRACT_FILES = 0x20, }; int compare_dir_entries(tree_node_t *a, tree_node_t *b); @@ -47,4 +48,6 @@ 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); +int extract_files(file_info_t *a, file_info_t *b, const char *path); + #endif /* DIFFTOOL_H */ diff --git a/difftool/extract.c b/difftool/extract.c new file mode 100644 index 0000000..9d84265 --- /dev/null +++ b/difftool/extract.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * extract.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "difftool.h" + +static int extract(data_reader_t *data, file_info_t *fi, + const char *path, char prefix) +{ + char *ptr, *temp = alloca(strlen(path) + 3); + int fd; + + temp[0] = prefix; + temp[1] = '/'; + strcpy(temp + 2, path); + + ptr = strrchr(temp, '/'); + *ptr = '\0'; + if (mkdir_p(temp)) + return -1; + *ptr = '/'; + + fd = open(temp, O_CREAT | O_EXCL | O_WRONLY, 0600); + if (fd < 0) { + perror(temp); + return -1; + } + + if (data_reader_dump_file(data, fi, fd, true)) { + close(fd); + return -1; + } + + close(fd); + return 0; +} + +int extract_files(file_info_t *a, file_info_t *b, const char *path) +{ + if (a != NULL) { + if (extract(sqfs_a.data, a, path, 'a')) + return -1; + } + + if (b != NULL) { + if (extract(sqfs_b.data, b, path, 'b')) + return -1; + } + return 0; +} diff --git a/difftool/sqfsdiff.c b/difftool/sqfsdiff.c index 8818a37..4754557 100644 --- a/difftool/sqfsdiff.c +++ b/difftool/sqfsdiff.c @@ -13,11 +13,12 @@ static struct option long_opts[] = { { "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' }, }; -static const char *short_opts = "OPCTIShV"; +static const char *short_opts = "OPCTISe:hV"; static const char *usagestr = "Usage: sqfsdiff [OPTIONS...] \n" @@ -45,6 +46,11 @@ static const char *usagestr = " --inode-num, -I Compare inode numbers of all files.\n" " --super, -S Also compare metadata in super blocks.\n" "\n" +" --extract, -e Extract files that differ to the specified\n" +" directory. Contents of the first image end up\n" +" in a subdirectory 'a' and of the second image\n" +" in a subdirectory 'b'.\n" +"\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n"; @@ -55,6 +61,7 @@ const char *second_path; sqfs_reader_t sqfs_a; sqfs_reader_t sqfs_b; static bool compare_super = false; +static const char *extract_dir; static void process_options(int argc, char **argv) { @@ -84,6 +91,10 @@ static void process_options(int argc, char **argv) case 'S': compare_super = true; break; + case 'e': + compare_flags |= COMPARE_EXTRACT_FILES; + extract_dir = optarg; + break; case 'h': fputs(usagestr, stdout); exit(EXIT_SUCCESS); @@ -125,6 +136,11 @@ int main(int argc, char **argv) process_options(argc, argv); + if (extract_dir != NULL) { + if (mkdir_p(extract_dir)) + return EXIT_FAILURE; + } + if (sqfs_reader_open(&sqfs_a, first_path, 0)) return 2; @@ -133,6 +149,14 @@ int main(int argc, char **argv) goto out_sqfs_a; } + if (extract_dir != NULL) { + if (chdir(extract_dir)) { + perror(extract_dir); + ret = -1; + goto out; + } + } + ret = node_compare(sqfs_a.fs.root, sqfs_b.fs.root); if (ret != 0) goto out; diff --git a/doc/sqfsdiff.1 b/doc/sqfsdiff.1 index ca3818f..5222c19 100644 --- a/doc/sqfsdiff.1 +++ b/doc/sqfsdiff.1 @@ -38,6 +38,12 @@ Compare inode numbers of all files. \fB\-\-super\fR, \fB\-S\fR Also compare metadata in super blocks. .TP +\fB\-\-extract\fR, \fB\-e\fR +Extract files that exist in both images but have different contents to the +specified directory. Contents of the first image end up in a sub directory +named \fBa\fR and the contents of the second image in a sub directory +named \fBb\fR. +.TP \fB\-\-help\fR, \fB\-h\fR Print help text and exit. .TP -- cgit v1.2.3