Add test utility to compare a zck with uncompressed file
authorStefano Babic <sbabic@denx.de>
Fri, 10 Sep 2021 20:48:10 +0000 (22:48 +0200)
committerStefano Babic <sbabic@denx.de>
Tue, 12 Oct 2021 09:18:58 +0000 (11:18 +0200)
This utility reads a zck and generates an index from a file, then
compares and output the delta with the chunks that are required.

Signed-off-by: Stefano Babic <sbabic@denx.de>
test/meson.build
test/zck_cmp_uncomp.c [new file with mode: 0644]

index 41861269aa930bd3ced121ce61e1822aaf8affaa..4f121d88c9cb373ba8ab8fee14c1a7dc1262a73a 100644 (file)
@@ -24,6 +24,13 @@ read_single_comp_chunk = executable('read_single_comp_chunk',
                                     include_directories: incdir,
                                     dependencies: [zstd_dep, openssl_dep])
 shacheck = executable('shacheck', ['shacheck.c'] + util_sources, include_directories: incdir, dependencies: [zstd_dep, openssl_dep])
+zck_cmp_uncomp = executable(
+    'zck_cmp_uncomp',
+    ['zck_cmp_uncomp.c'],
+    include_directories: incdir,
+    link_with: zcklib,
+    install: false
+)
 file_path = join_paths(meson.source_root(), 'test/files')
 
 test(
diff --git a/test/zck_cmp_uncomp.c b/test/zck_cmp_uncomp.c
new file mode 100644 (file)
index 0000000..f7952c1
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2021 Stefano Babic <stefano.babic@babic.homelinux.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <argp.h>
+#include <zck.h>
+
+#define BUFSIZE 16384
+static char doc[] = "zck_gen_header - Compare a file with a zck and reports which chunks changed";
+
+static char args_doc[] = "<uncompressed file> <zck file>";
+
+static struct argp_option options[] = {
+    {"verbose",     'v', 0,        0,
+     "Increase verbosity (can be specified more than once for debugging)"},
+    {"show-chunks", 'c', 0,        0, "Show chunk information"},
+    {"quiet",       'q', 0,        0, "Only show errors"},
+    {"version",     'V', 0,        0, "Show program version"},
+    {"verify",      'f', 0,        0, "Verify full zchunk file"},
+    { 0 }
+};
+
+struct arguments {
+  char *args[2];
+  bool verify;
+  bool quiet;
+  bool show_chunks;
+  zck_log_type log_level;
+  bool exit;
+};
+
+static void version(void) {
+    exit(EXIT_SUCCESS);
+}
+
+static error_t parse_opt (int key, char *arg, struct argp_state *state) {
+    struct arguments *arguments = state->input;
+
+    if(arguments->exit)
+        return 0;
+
+    switch (key) {
+        case 'v':
+            arguments->log_level--;
+            if(arguments->log_level < ZCK_LOG_DDEBUG)
+                arguments->log_level = ZCK_LOG_DDEBUG;
+            break;
+        case 'V':
+            version();
+            arguments->exit = true;
+            break;
+
+        case ARGP_KEY_ARG:
+            if (state->arg_num >= 2) {
+                argp_usage (state);
+                return EINVAL;
+            }
+            arguments->args[state->arg_num] = arg;
+
+            break;
+
+        case ARGP_KEY_END:
+            if (state->arg_num < 1) {
+                argp_usage (state);
+                return EINVAL;
+            }
+            break;
+
+        default:
+            return ARGP_ERR_UNKNOWN;
+    }
+    return 0;
+}
+
+static struct argp argp = {options, parse_opt, args_doc, doc};
+
+
+int main (int argc, char *argv[]) {
+    struct arguments arguments = {0};
+    arguments.log_level = ZCK_LOG_ERROR;
+
+    int retval = argp_parse (&argp, argc, argv, 0, 0, &arguments);
+    if(retval || arguments.exit)
+        exit(retval);
+
+    if (argc < 1) {
+               dprintf(STDERR_FILENO, "Usage : %s filename", argv[0]);
+               exit(1);
+    }
+
+    zck_set_log_level(arguments.log_level);
+    int dst_fd = open("/dev/null", O_TRUNC | O_WRONLY | O_CREAT, 0666);
+
+    zckCtx *zckSrc = zck_create();
+    zckCtx *zckDst = zck_create();
+    if(!zckSrc || !zckDst) {
+        dprintf(STDERR_FILENO, "%s", zck_get_error(NULL));
+        zck_clear_error(NULL);
+        exit(1);
+    }
+    if(!zck_init_write(zckSrc, dst_fd)) {
+        dprintf(STDERR_FILENO, "Unable to write %s ",
+                zck_get_error(zckSrc));
+        exit(1);
+    }
+   
+    int in_fd = open(arguments.args[0], O_RDONLY);
+    off_t in_size = 0;
+    if(in_fd < 0) {
+        dprintf(STDERR_FILENO, "Unable to open %s for reading",
+                arguments.args[0]);
+        perror("");
+        exit(1);
+    }
+
+    /*
+     * Read header in the zck file
+     */
+    int zck_fd = open(arguments.args[1], O_RDONLY);
+    if(zck_fd < 0) {
+        dprintf(STDERR_FILENO, "Unable to open %s for reading",
+                arguments.args[1]);
+        perror("");
+        exit(1);
+    }
+
+    if(!zck_init_read(zckDst, zck_fd)) {
+        dprintf(STDERR_FILENO, "Error reading zchunk header: %s",
+                zck_get_error(zckDst));
+        zck_free(&zckSrc);
+        zck_free(&zckDst);
+        exit(1);
+    }
+
+    in_size = lseek(in_fd, 0, SEEK_END);
+    if(in_size < 0) {
+        dprintf(STDERR_FILENO, "Unable to seek to end of input file");
+        exit(1);
+    }
+    if(lseek(in_fd, 0, SEEK_SET) < 0) {
+        perror("Unable to seek to beginning of input file");
+        exit(1);
+    }
+
+    if(!zck_set_ioption(zckSrc, ZCK_UNCOMP_HEADER, 1)) {
+        dprintf(STDERR_FILENO, "%s\n", zck_get_error(zckSrc));
+        exit(1);
+    }
+    if(!zck_set_ioption(zckSrc, ZCK_COMP_TYPE, ZCK_COMP_NONE))
+        exit(1);
+    if(!zck_set_ioption(zckSrc, ZCK_HASH_CHUNK_TYPE, ZCK_HASH_SHA256)) {
+        dprintf(STDERR_FILENO, "Unable to set hash type %s\n", zck_get_error(zckSrc));
+        exit(1);
+    }
+
+    char *buf = malloc(BUFSIZE);
+    if (!buf) {
+        dprintf(STDERR_FILENO, "Unable to allocate buffer\n");
+        exit(1);
+    }
+    ssize_t n;
+    while ((n = read(in_fd, buf, BUFSIZE)) > 0) {
+        if (zck_write(zckSrc, buf, n) < 0) {
+            dprintf(STDERR_FILENO, "zck_write failed: %s\n", zck_get_error(zckSrc));
+            exit(1);
+        }
+    }
+    /*
+     * Start comparison
+     */
+    dprintf(STDOUT_FILENO, "Compare original image with chunks in zck\n");
+    dprintf(STDOUT_FILENO, "-----------------------------------------\n");
+    
+    zck_generate_hashdb(zckSrc);
+    zck_find_matching_chunks(zckSrc, zckDst);
+
+    zckChunk *iter = zck_get_first_chunk(zckDst);
+    size_t todwl = 0;
+    size_t reuse = 0;
+    while (iter) {
+        dprintf(STDOUT_FILENO, "%12lu %s %s %12lu %12lu\n",
+                zck_get_chunk_number(iter),
+                zck_get_chunk_valid(iter) ? "SRC" : "DST",
+                zck_get_chunk_digest_uncompressed(iter),
+                zck_get_chunk_start(iter),
+                zck_get_chunk_size(iter));
+
+        if (!zck_get_chunk_valid(iter)) {
+                   todwl += zck_get_chunk_comp_size(iter);
+        } else {
+            reuse += zck_get_chunk_size(iter);
+
+        }
+        iter = zck_get_next_chunk(iter);
+    }
+
+    dprintf (STDOUT_FILENO, "\n\nTotal to be reused : %12lu\n", reuse);
+    dprintf (STDOUT_FILENO, "Total to be downloaded : %12lu\n", todwl);
+
+    close(in_fd);
+    close(zck_fd);
+    zck_free(&zckSrc);
+    zck_free(&zckDst);
+    close(dst_fd);
+}