From: Stefano Babic Date: Fri, 10 Sep 2021 20:48:10 +0000 (+0200) Subject: Add test utility to compare a zck with uncompressed file X-Git-Tag: archive/raspbian/1.2.1+ds1-1+rpi1^2~7^2~1^2~17^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=17f04ab4cfb45e4c299e420198af1cd684dabeba;p=zchunk.git Add test utility to compare a zck with uncompressed file 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 --- diff --git a/test/meson.build b/test/meson.build index 4186126..4f121d8 100644 --- a/test/meson.build +++ b/test/meson.build @@ -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 index 0000000..f7952c1 --- /dev/null +++ b/test/zck_cmp_uncomp.c @@ -0,0 +1,240 @@ +/* + * Copyright 2021 Stefano Babic + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 16384 +static char doc[] = "zck_gen_header - Compare a file with a zck and reports which chunks changed"; + +static char args_doc[] = " "; + +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); +}