/*- * Copyright (c) 2019 Martin Matuska * All rights reserved. * * 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 AUTHOR(S) ``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 AUTHOR(S) 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. */ /* * Archivetest verifies reading archives with libarchive * * It may be used to reproduce failures in testcases discovered by OSS-Fuzz * https://github.com/google/oss-fuzz/blob/master/projects/libarchive */ #include #include #include #include #include #include #if defined __MINGW32__ #include #endif static const char *errnostr(int e) { char *estr; switch(e) { case ARCHIVE_EOF: estr = "ARCHIVE_EOF"; break; case ARCHIVE_OK: estr = "ARCHIVE_OK"; break; case ARCHIVE_WARN: estr = "ARCHIVE_WARN"; break; case ARCHIVE_RETRY: estr = "ARCHIVE_RETRY"; break; case ARCHIVE_FAILED: estr = "ARCHIVE_FAILED"; break; case ARCHIVE_FATAL: estr = "ARCHIVE_FATAL"; break; default: estr = "Unknown"; break; } return (estr); } static void usage(const char *prog) { fprintf(stderr, "Usage: %s [-f filename] [-h] [-q] [-s]\n", prog); } static void printhelp() { fprintf(stdout, "archivetest: verify reading archives with " "libarchive\n\n" "Options:\n" " -f filename Filename to verify\n" " -h Show this help\n" " -q Quiet mode\n" " -r Enforce raw archive format\n" " -s Verify only headers (skip data)\n\n" "If no filename is specified, data is read from standard input.\n" "\n%s\n", archive_version_details()); } static int v_print(int verbose, const char *format, ...) { int r = 0; if (verbose) { va_list args; va_start(args, format); r = vfprintf(stdout, format, args); va_end(args); } return (r); } int main(int argc, char *argv[]) { struct archive *a; struct archive_entry *entry; char *filename; const char *p; char buffer[4096]; int c; int v, skip_data, raw; int r = ARCHIVE_OK; int format_printed; mode_t mode; filename = NULL; skip_data = 0; raw = 0; v = 1; while ((c = getopt (argc, argv, "f:hrqs")) != -1) { switch (c) { case 'f': filename = optarg; break; case 'h': printhelp(); exit(0); case 'q': v = 0; break; case 'r': raw = 1; break; case 's': skip_data = 1; break; case '?': if (optopt == 'f') fprintf(stderr, "Option -%c requires " "an argument.\n", optopt); else if (isprint(optopt)) fprintf(stderr, "Unknown option '-%c'" ".\n", optopt); else fprintf(stderr, "Unknown option " "character '\\x%x'.\n", optopt); usage(argv[0]); exit(1); break; default: exit(1); } } a = archive_read_new(); archive_read_support_filter_all(a); if (raw) archive_read_support_format_raw(a); else archive_read_support_format_all(a); v_print(v, "Data source: "); if (filename == NULL) { v_print(v, "standard input\n"); r = archive_read_open_fd(a, STDIN_FILENO, 4096); } else { v_print(v, "filename: %s\n", filename); r = archive_read_open_filename(a, filename, 4096); } if (r != ARCHIVE_OK) { archive_read_free(a); fprintf(stderr, "Invalid or unsupported data source\n"); exit(1); } format_printed = 0; c = 1; while (1) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_FATAL) { v_print(v, "Entry %d: fatal error reading " "header\n", c); break; } if (!format_printed) { v_print(v, "Filter: %s\nFormat: %s\n", archive_filter_name(a, 0), archive_format_name(a)); format_printed = 1; } if (r == ARCHIVE_RETRY) continue; if (r == ARCHIVE_EOF) break; p = archive_entry_pathname(entry); mode = archive_entry_mode(entry); v_print(v, "Entry %d: %s, pathname", c, errnostr(r)); if (p == NULL || p[0] == '\0') v_print(v, " unreadable"); else v_print(v, ": %s", p); v_print(v, ", mode: %o", mode); v_print(v, ", data: "); if (skip_data) { v_print(v, "skipping"); } else { while ((r = archive_read_data(a, buffer, 4096)) > 0) ; if (r == ARCHIVE_FATAL) { v_print(v, "ERROR\nError string: %s\n", archive_error_string(a)); break; } v_print(v, "OK"); } v_print(v, "\n"); c++; } v_print(v, "Last return code: %s (%d)\n", errnostr(r), r); if (r == ARCHIVE_EOF || r == ARCHIVE_OK) { archive_read_free(a); exit(0); } v_print(v, "Error string: %s\n", archive_error_string(a)); archive_read_free(a); exit(2); }