blob: 815eda93cdd0708fac617d27d93304ded5e211fe [file] [log] [blame]
Dieter Baron392787a2003-03-14 14:53:29 +00001/*
Dieter Baronb2ed74d2004-04-14 14:01:31 +00002 zipcmp.c -- compare zip files
Thomas Klausner80491c82019-09-16 07:06:39 +02003 Copyright (C) 2003-2019 Dieter Baron and Thomas Klausner
Dieter Baron392787a2003-03-14 14:53:29 +00004
Dieter Barondd9afca2003-10-02 14:13:37 +00005 This file is part of libzip, a library to manipulate ZIP archives.
Dieter Baron40a21972007-11-07 00:11:26 +01006 The authors can be contacted at <libzip@nih.at>
Dieter Baron392787a2003-03-14 14:53:29 +00007
Dieter Barondd9afca2003-10-02 14:13:37 +00008 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in
15 the documentation and/or other materials provided with the
16 distribution.
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
19 written permission.
Thomas Klausner8eab1a22018-01-15 14:10:11 +010020
Dieter Barondd9afca2003-10-02 14:13:37 +000021 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Dieter Baron392787a2003-03-14 14:53:29 +000032*/
33
Dieter Baron392787a2003-03-14 14:53:29 +000034
Dieter Barona6192cf2011-02-04 16:25:01 +010035#include "config.h"
36
Dieter Barone3f91ef2003-10-06 02:50:14 +000037#include <errno.h>
Dieter Baron392787a2003-03-14 14:53:29 +000038#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
Thomas Klausner8eab1a22018-01-15 14:10:11 +010041#include <sys/stat.h>
Dieter Baronc0866112012-08-08 16:54:19 +020042#ifdef HAVE_STRINGS_H
43#include <strings.h>
44#endif
Thomas Klausnerde610c32014-05-08 12:19:28 +020045#ifdef HAVE_FTS_H
46#include <fts.h>
47#endif
Thomas Klausner24a722b2004-11-30 21:51:30 +000048#include <zlib.h>
Dieter Baron392787a2003-03-14 14:53:29 +000049
Thomas Klausner81e94332011-03-08 14:10:27 +010050#ifndef HAVE_GETOPT
51#include "getopt.h"
52#endif
53
Thomas Klausner069aee62017-06-21 10:11:01 +020054#include "zip.h"
Dieter Baron392787a2003-03-14 14:53:29 +000055
Thomas Klausnerb49e8c82020-04-19 18:00:47 +020056#include "compat.h"
57
Thomas Klausnera191f7b2014-01-29 15:14:37 +010058struct archive {
59 const char *name;
Dieter Baron1d9dfeb2014-09-28 23:02:54 +020060 zip_t *za;
Thomas Klausnera191f7b2014-01-29 15:14:37 +010061 zip_uint64_t nentry;
62 struct entry *entry;
63 const char *comment;
Thomas Klausnerea8ba492014-09-23 16:54:47 +020064 size_t comment_length;
Thomas Klausnera191f7b2014-01-29 15:14:37 +010065};
66
Dieter Baron0e5eeab2012-04-24 18:47:12 +020067struct ef {
68 const char *name;
69 zip_uint16_t flags;
70 zip_uint16_t id;
71 zip_uint16_t size;
72 const zip_uint8_t *data;
73};
74
Dieter Baron392787a2003-03-14 14:53:29 +000075struct entry {
76 char *name;
Dieter Baron0e5eeab2012-04-24 18:47:12 +020077 zip_uint64_t size;
78 zip_uint32_t crc;
79 zip_uint32_t comp_method;
Dieter Baron0e5eeab2012-04-24 18:47:12 +020080 struct ef *extra_fields;
Thomas Klausnerea8ba492014-09-23 16:54:47 +020081 zip_uint16_t n_extra_fields;
Dieter Baron3efab992012-05-04 09:29:29 +020082 const char *comment;
Thomas Klausner216701e2013-03-14 11:47:39 +010083 zip_uint32_t comment_length;
Dieter Baron392787a2003-03-14 14:53:29 +000084};
85
Dieter Baron392787a2003-03-14 14:53:29 +000086
Thomas Klausner5061edd2018-09-29 18:13:16 +020087const char *progname;
Dieter Baron392787a2003-03-14 14:53:29 +000088
Thomas Klausner8eab1a22018-01-15 14:10:11 +010089#define PROGRAM "zipcmp"
Dieter Baron80d9ff32004-12-22 17:31:32 +000090
Thomas Klausner199a91f2014-03-10 16:17:10 +010091#define USAGE "usage: %s [-hipqtVv] archive1 archive2\n"
Dieter Baron392787a2003-03-14 14:53:29 +000092
Thomas Klausner8eab1a22018-01-15 14:10:11 +010093char help_head[] = PROGRAM " (" PACKAGE ") by Dieter Baron and Thomas Klausner\n\n";
Dieter Baron392787a2003-03-14 14:53:29 +000094
95char help[] = "\n\
96 -h display this help message\n\
Dieter Baron3014e882003-10-03 23:54:32 +000097 -i compare names ignoring case distinctions\n\
Dieter Baron0e5eeab2012-04-24 18:47:12 +020098 -p compare as many details as possible\n\
Dieter Baron392787a2003-03-14 14:53:29 +000099 -q be quiet\n\
Thomas Klausneraf2c40e2014-08-06 00:02:25 +0200100 -t test zip files (compare file contents to checksum)\n\
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100101 -V display version number\n\
Dieter Baron392787a2003-03-14 14:53:29 +0000102 -v be verbose (print differences, default)\n\
103\n\
Dieter Baron40a21972007-11-07 00:11:26 +0100104Report bugs to <libzip@nih.at>.\n";
Dieter Baron392787a2003-03-14 14:53:29 +0000105
Dieter Baron80d9ff32004-12-22 17:31:32 +0000106char version_string[] = PROGRAM " (" PACKAGE " " VERSION ")\n\
Thomas Klausner93f03392020-01-08 15:04:57 +0100107Copyright (C) 2003-2019 Dieter Baron and Thomas Klausner\n\
Dieter Baron79aca392007-11-08 16:00:37 +0100108" PACKAGE " comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
Dieter Baron392787a2003-03-14 14:53:29 +0000109
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200110#define OPTIONS "hVipqtv"
Dieter Baron392787a2003-03-14 14:53:29 +0000111
Dieter Baron392787a2003-03-14 14:53:29 +0000112
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100113#define BOTH_ARE_ZIPS(a) (a[0].za && a[1].za)
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100114
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200115static int comment_compare(const char *c1, size_t l1, const char *c2, size_t l2);
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100116static int compare_list(char *const name[], const void *l[], const zip_uint64_t n[], int size, int (*cmp)(const void *, const void *), int (*checks)(char *const name[2], const void *, const void *), void (*print)(const void *));
117static int compare_zip(char *const zn[]);
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100118static int ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2);
119static int ef_order(const void *a, const void *b);
120static void ef_print(const void *p);
Dieter Baron1d9dfeb2014-09-28 23:02:54 +0200121static int ef_read(zip_t *za, zip_uint64_t idx, struct entry *e);
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100122static int entry_cmp(const void *p1, const void *p2);
123static int entry_paranoia_checks(char *const name[2], const void *p1, const void *p2);
124static void entry_print(const void *p);
125static int is_directory(const char *name);
Thomas Klausnerde610c32014-05-08 12:19:28 +0200126#ifdef HAVE_FTS_H
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100127static int list_directory(const char *name, struct archive *a);
Thomas Klausnerde610c32014-05-08 12:19:28 +0200128#endif
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100129static int list_zip(const char *name, struct archive *a);
Dieter Baron1d9dfeb2014-09-28 23:02:54 +0200130static int test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc);
Dieter Baron392787a2003-03-14 14:53:29 +0000131
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200132int ignore_case, test_files, paranoid, verbose;
133int header_done;
Dieter Baron3014e882003-10-03 23:54:32 +0000134
Dieter Baron392787a2003-03-14 14:53:29 +0000135
136int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100137main(int argc, char *const argv[]) {
Dieter Baron392787a2003-03-14 14:53:29 +0000138 int c;
139
Thomas Klausner5061edd2018-09-29 18:13:16 +0200140 progname = argv[0];
Dieter Baron392787a2003-03-14 14:53:29 +0000141
Dieter Baron3014e882003-10-03 23:54:32 +0000142 ignore_case = 0;
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000143 test_files = 0;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200144 paranoid = 0;
Dieter Baron392787a2003-03-14 14:53:29 +0000145 verbose = 1;
146
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100147 while ((c = getopt(argc, argv, OPTIONS)) != -1) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200148 switch (c) {
149 case 'i':
150 ignore_case = 1;
151 break;
152 case 'p':
153 paranoid = 1;
154 break;
155 case 'q':
156 verbose = 0;
157 break;
158 case 't':
159 test_files = 1;
160 break;
161 case 'v':
162 verbose = 1;
163 break;
Dieter Baron392787a2003-03-14 14:53:29 +0000164
Thomas Klausnera14fded2020-07-21 16:07:23 +0200165 case 'h':
166 fputs(help_head, stdout);
167 printf(USAGE, progname);
168 fputs(help, stdout);
169 exit(0);
170 case 'V':
171 fputs(version_string, stdout);
172 exit(0);
Dieter Baron392787a2003-03-14 14:53:29 +0000173
Thomas Klausnera14fded2020-07-21 16:07:23 +0200174 default:
175 fprintf(stderr, USAGE, progname);
176 exit(2);
177 }
Dieter Baron392787a2003-03-14 14:53:29 +0000178 }
179
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100180 if (argc != optind + 2) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200181 fprintf(stderr, USAGE, progname);
182 exit(2);
Dieter Baron392787a2003-03-14 14:53:29 +0000183 }
184
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100185 exit((compare_zip(argv + optind) == 0) ? 0 : 1);
Dieter Baron392787a2003-03-14 14:53:29 +0000186}
187
Dieter Baron392787a2003-03-14 14:53:29 +0000188
189static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100190compare_zip(char *const zn[]) {
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100191 struct archive a[2];
Dieter Baron392787a2003-03-14 14:53:29 +0000192 struct entry *e[2];
Dieter Barona1f8e2c2013-05-14 22:21:34 +0200193 zip_uint64_t n[2];
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100194 int i;
Dieter Baron3efab992012-05-04 09:29:29 +0200195 int res;
196
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100197 for (i = 0; i < 2; i++) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200198 a[i].name = zn[i];
199 a[i].entry = NULL;
200 a[i].nentry = 0;
201 a[i].za = NULL;
202 a[i].comment = NULL;
203 a[i].comment_length = 0;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100204
Thomas Klausnera14fded2020-07-21 16:07:23 +0200205 if (is_directory(zn[i])) {
Thomas Klausnerde610c32014-05-08 12:19:28 +0200206#ifndef HAVE_FTS_H
Thomas Klausnera14fded2020-07-21 16:07:23 +0200207 fprintf(stderr, "%s: reading directories not supported\n", progname);
208 exit(2);
Thomas Klausnerde610c32014-05-08 12:19:28 +0200209#else
Thomas Klausnera14fded2020-07-21 16:07:23 +0200210 if (list_directory(zn[i], a + i) < 0)
211 exit(2);
212 paranoid = 0; /* paranoid checks make no sense for directories, since they compare zip metadata */
Thomas Klausnerde610c32014-05-08 12:19:28 +0200213#endif
Thomas Klausnera14fded2020-07-21 16:07:23 +0200214 }
215 else {
216 if (list_zip(zn[i], a + i) < 0)
217 exit(2);
218 }
219 if (a[i].nentry > 0)
220 qsort(a[i].entry, a[i].nentry, sizeof(a[i].entry[0]), entry_cmp);
Dieter Baron392787a2003-03-14 14:53:29 +0000221 }
222
Dieter Baron3efab992012-05-04 09:29:29 +0200223 header_done = 0;
224
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100225 e[0] = a[0].entry;
226 e[1] = a[1].entry;
227 n[0] = a[0].nentry;
228 n[1] = a[1].nentry;
229 res = compare_list(zn, (const void **)e, n, sizeof(e[i][0]), entry_cmp, paranoid ? entry_paranoia_checks : NULL, entry_print);
Dieter Baron3efab992012-05-04 09:29:29 +0200230
231 if (paranoid) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200232 if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
233 if (verbose) {
234 printf("--- archive comment (%zu)\n", a[0].comment_length);
235 printf("+++ archive comment (%zu)\n", a[1].comment_length);
236 }
237 res = 1;
238 }
Dieter Baron3efab992012-05-04 09:29:29 +0200239 }
240
Thomas Klausner80491c82019-09-16 07:06:39 +0200241 for (i = 0; i < 2; i++) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200242 zip_uint64_t j;
Thomas Klausner80491c82019-09-16 07:06:39 +0200243
Thomas Klausnera14fded2020-07-21 16:07:23 +0200244 if (a[i].za) {
245 zip_close(a[i].za);
246 }
247 for (j = 0; j < a[i].nentry; j++) {
248 free(a[i].entry[j].name);
249 }
250 free(a[i].entry);
Thomas Klausner80491c82019-09-16 07:06:39 +0200251 }
Dieter Baron3efab992012-05-04 09:29:29 +0200252
253 switch (res) {
Dieter Baron9afd7bd2003-10-01 18:27:46 +0000254 case 0:
Thomas Klausnera14fded2020-07-21 16:07:23 +0200255 exit(0);
Dieter Baron9afd7bd2003-10-01 18:27:46 +0000256
257 case 1:
Thomas Klausnera14fded2020-07-21 16:07:23 +0200258 exit(1);
Dieter Baron9afd7bd2003-10-01 18:27:46 +0000259
260 default:
Thomas Klausnera14fded2020-07-21 16:07:23 +0200261 exit(2);
Dieter Baron9afd7bd2003-10-01 18:27:46 +0000262 }
Dieter Baron3efab992012-05-04 09:29:29 +0200263}
Thomas Klausner3a2ce0e2005-05-20 21:56:54 +0000264
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200265#ifdef HAVE_FTS_H
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100266static zip_int64_t
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100267compute_crc(const char *fname) {
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100268 FILE *f;
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200269 uLong crc = crc32(0L, Z_NULL, 0);
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100270 size_t n;
271 Bytef buffer[8192];
272
273
Michał Janiszewskia9bf6162020-03-08 21:34:36 +0100274 if ((f = fopen(fname, "rb")) == NULL) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200275 fprintf(stderr, "%s: can't open %s: %s\n", progname, fname, strerror(errno));
276 return -1;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100277 }
278
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100279 while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200280 crc = crc32(crc, buffer, (unsigned int)n);
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100281 }
282
283 if (ferror(f)) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200284 fprintf(stderr, "%s: read error on %s: %s\n", progname, fname, strerror(errno));
285 fclose(f);
286 return -1;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100287 }
288
289 fclose(f);
290
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200291 return (zip_int64_t)crc;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100292}
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200293#endif
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100294
295
296static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100297is_directory(const char *name) {
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100298 struct stat st;
299
300 if (stat(name, &st) < 0)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200301 return 0;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100302
303 return S_ISDIR(st.st_mode);
304}
305
306
Thomas Klausnerde610c32014-05-08 12:19:28 +0200307#ifdef HAVE_FTS_H
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100308static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100309list_directory(const char *name, struct archive *a) {
Thomas Klausnerde610c32014-05-08 12:19:28 +0200310 FTS *fts;
311 FTSENT *ent;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100312 zip_uint64_t nalloc;
Thomas Klausnerdceead52017-02-26 11:01:57 +0100313 size_t prefix_length;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100314
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100315 char *const names[2] = {(char *)name, NULL};
Thomas Klausnerde610c32014-05-08 12:19:28 +0200316
317
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100318 if ((fts = fts_open(names, FTS_NOCHDIR | FTS_LOGICAL, NULL)) == NULL) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200319 fprintf(stderr, "%s: can't open directory '%s': %s\n", progname, name, strerror(errno));
320 return -1;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100321 }
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100322 prefix_length = strlen(name) + 1;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100323
324 nalloc = 0;
Thomas Klausnerde610c32014-05-08 12:19:28 +0200325
326 while ((ent = fts_read(fts))) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200327 zip_int64_t crc;
Thomas Klausner90918402014-03-08 22:13:47 +0100328
Thomas Klausnera14fded2020-07-21 16:07:23 +0200329 switch (ent->fts_info) {
330 case FTS_D:
331 case FTS_DOT:
332 case FTS_DP:
333 case FTS_DEFAULT:
334 case FTS_SL:
335 case FTS_NSOK:
336 break;
Thomas Klausner90918402014-03-08 22:13:47 +0100337
Thomas Klausnera14fded2020-07-21 16:07:23 +0200338 case FTS_DC:
339 case FTS_DNR:
340 case FTS_ERR:
341 case FTS_NS:
342 case FTS_SLNONE:
343 /* TODO: error */
344 fts_close(fts);
345 return -1;
Thomas Klausnerde610c32014-05-08 12:19:28 +0200346
Thomas Klausnera14fded2020-07-21 16:07:23 +0200347 case FTS_F:
348 if (a->nentry >= nalloc) {
349 nalloc += 16;
350 if (nalloc > SIZE_MAX / sizeof(a->entry[0])) {
351 fprintf(stderr, "%s: malloc failure\n", progname);
352 exit(1);
353 }
354 a->entry = realloc(a->entry, sizeof(a->entry[0]) * nalloc);
355 if (a->entry == NULL) {
356 fprintf(stderr, "%s: malloc failure\n", progname);
357 exit(1);
358 }
359 }
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100360
Thomas Klausnera14fded2020-07-21 16:07:23 +0200361 a->entry[a->nentry].name = strdup(ent->fts_path + prefix_length);
362 a->entry[a->nentry].size = (zip_uint64_t)ent->fts_statp->st_size;
363 if ((crc = compute_crc(ent->fts_accpath)) < 0) {
364 fts_close(fts);
365 return -1;
366 }
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100367
Thomas Klausnera14fded2020-07-21 16:07:23 +0200368 a->entry[a->nentry].crc = (zip_uint32_t)crc;
369 a->nentry++;
370 break;
371 }
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100372 }
373
Thomas Klausnerde610c32014-05-08 12:19:28 +0200374 if (fts_close(fts)) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200375 fprintf(stderr, "%s: error closing directory '%s': %s\n", progname, a->name, strerror(errno));
376 return -1;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100377 }
378
379 return 0;
380}
Thomas Klausnerde610c32014-05-08 12:19:28 +0200381#endif
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100382
383
384static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100385list_zip(const char *name, struct archive *a) {
Dieter Baron1d9dfeb2014-09-28 23:02:54 +0200386 zip_t *za;
Thomas Klausner199a91f2014-03-10 16:17:10 +0100387 int err;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100388 struct zip_stat st;
Thomas Klausner199a91f2014-03-10 16:17:10 +0100389 unsigned int i;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100390
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100391 if ((za = zip_open(name, paranoid ? ZIP_CHECKCONS : 0, &err)) == NULL) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200392 zip_error_t error;
393 zip_error_init_with_code(&error, err);
394 fprintf(stderr, "%s: cannot open zip archive '%s': %s\n", progname, name, zip_error_strerror(&error));
395 zip_error_fini(&error);
396 return -1;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100397 }
398
399 a->za = za;
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200400 a->nentry = (zip_uint64_t)zip_get_num_entries(za, 0);
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100401
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100402 if (a->nentry == 0)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200403 a->entry = NULL;
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100404 else {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200405 if ((a->nentry > SIZE_MAX / sizeof(a->entry[0])) || (a->entry = (struct entry *)malloc(sizeof(a->entry[0]) * a->nentry)) == NULL) {
406 fprintf(stderr, "%s: malloc failure\n", progname);
407 exit(1);
408 }
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100409
Thomas Klausnera14fded2020-07-21 16:07:23 +0200410 for (i = 0; i < a->nentry; i++) {
411 zip_stat_index(za, i, 0, &st);
412 a->entry[i].name = strdup(st.name);
413 a->entry[i].size = st.size;
414 a->entry[i].crc = st.crc;
415 if (test_files)
416 test_file(za, i, st.size, st.crc);
417 if (paranoid) {
418 a->entry[i].comp_method = st.comp_method;
419 ef_read(za, i, a->entry + i);
420 a->entry[i].comment = zip_file_get_comment(za, i, &a->entry[i].comment_length, 0);
421 }
422 else {
423 a->entry[i].comp_method = 0;
424 a->entry[i].n_extra_fields = 0;
425 }
426 }
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100427
Thomas Klausnera14fded2020-07-21 16:07:23 +0200428 if (paranoid) {
429 int length;
430 a->comment = zip_get_archive_comment(za, &length, 0);
431 a->comment_length = (size_t)length;
432 }
433 else {
434 a->comment = NULL;
435 a->comment_length = 0;
436 }
Thomas Klausnera191f7b2014-01-29 15:14:37 +0100437 }
438
439 return 0;
440}
441
Dieter Baron3efab992012-05-04 09:29:29 +0200442
443static int
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200444comment_compare(const char *c1, size_t l1, const char *c2, size_t l2) {
Dieter Baron3efab992012-05-04 09:29:29 +0200445 if (l1 != l2)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200446 return 1;
Dieter Baron3efab992012-05-04 09:29:29 +0200447
448 if (l1 == 0)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200449 return 0;
Dieter Baron3efab992012-05-04 09:29:29 +0200450
Dieter Barona205a4d2012-08-08 16:52:57 +0200451 if (c1 == NULL || c2 == NULL)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200452 return c1 == c2;
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100453
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200454 return memcmp(c1, c2, (size_t)l2);
Dieter Baron392787a2003-03-14 14:53:29 +0000455}
456
Dieter Baron392787a2003-03-14 14:53:29 +0000457
458static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100459compare_list(char *const name[2], const void *l[2], const zip_uint64_t n[2], int size, int (*cmp)(const void *, const void *), int (*check)(char *const name[2], const void *, const void *), void (*print)(const void *)) {
Thomas Klausner199a91f2014-03-10 16:17:10 +0100460 unsigned int i[2];
461 int j, c;
Dieter Baron392787a2003-03-14 14:53:29 +0000462 int diff;
463
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100464#define INC(k) (i[k]++, l[k] = ((const char *)l[k]) + size)
465#define PRINT(k) \
466 do { \
Thomas Klausnera14fded2020-07-21 16:07:23 +0200467 if (header_done == 0 && verbose) { \
468 printf("--- %s\n+++ %s\n", name[0], name[1]); \
469 header_done = 1; \
470 } \
471 if (verbose) { \
472 printf("%c ", (k) ? '+' : '-'); \
473 print(l[k]); \
474 } \
475 diff = 1; \
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100476 } while (0)
Dieter Baron392787a2003-03-14 14:53:29 +0000477
478 i[0] = i[1] = 0;
479 diff = 0;
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100480 while (i[0] < n[0] && i[1] < n[1]) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200481 c = cmp(l[0], l[1]);
Dieter Baron392787a2003-03-14 14:53:29 +0000482
Thomas Klausnera14fded2020-07-21 16:07:23 +0200483 if (c == 0) {
484 if (check)
485 diff |= check(name, l[0], l[1]);
486 INC(0);
487 INC(1);
488 }
489 else if (c < 0) {
490 PRINT(0);
491 INC(0);
492 }
493 else {
494 PRINT(1);
495 INC(1);
496 }
Dieter Baron392787a2003-03-14 14:53:29 +0000497 }
498
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100499 for (j = 0; j < 2; j++) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200500 while (i[j] < n[j]) {
501 PRINT(j);
502 INC(j);
503 }
Dieter Baron392787a2003-03-14 14:53:29 +0000504 }
505
506 return diff;
507}
508
Dieter Baron392787a2003-03-14 14:53:29 +0000509
510static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100511ef_read(zip_t *za, zip_uint64_t idx, struct entry *e) {
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200512 zip_int16_t n_local, n_central;
Thomas Klausner03ca1c12014-09-24 01:02:15 +0200513 zip_uint16_t i;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200514
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100515 if ((n_local = zip_file_extra_fields_count(za, idx, ZIP_FL_LOCAL)) < 0 || (n_central = zip_file_extra_fields_count(za, idx, ZIP_FL_CENTRAL)) < 0) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200516 return -1;
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200517 }
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100518
Thomas Klausner03ca1c12014-09-24 01:02:15 +0200519 e->n_extra_fields = (zip_uint16_t)(n_local + n_central);
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100520
521 if ((e->extra_fields = (struct ef *)malloc(sizeof(e->extra_fields[0]) * e->n_extra_fields)) == NULL)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200522 return -1;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200523
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100524 for (i = 0; i < n_local; i++) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200525 e->extra_fields[i].name = e->name;
526 e->extra_fields[i].data = zip_file_extra_field_get(za, idx, i, &e->extra_fields[i].id, &e->extra_fields[i].size, ZIP_FL_LOCAL);
527 if (e->extra_fields[i].data == NULL)
528 return -1;
529 e->extra_fields[i].flags = ZIP_FL_LOCAL;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200530 }
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100531 for (; i < e->n_extra_fields; i++) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200532 e->extra_fields[i].name = e->name;
533 e->extra_fields[i].data = zip_file_extra_field_get(za, idx, (zip_uint16_t)(i - n_local), &e->extra_fields[i].id, &e->extra_fields[i].size, ZIP_FL_CENTRAL);
534 if (e->extra_fields[i].data == NULL)
535 return -1;
536 e->extra_fields[i].flags = ZIP_FL_CENTRAL;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200537 }
538
539 qsort(e->extra_fields, e->n_extra_fields, sizeof(e->extra_fields[0]), ef_order);
540
541 return 0;
542}
543
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200544
545static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100546ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2) {
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200547 struct ef *ef[2];
Dieter Barona1f8e2c2013-05-14 22:21:34 +0200548 zip_uint64_t n[2];
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200549
550 ef[0] = e1->extra_fields;
551 ef[1] = e2->extra_fields;
552 n[0] = e1->n_extra_fields;
553 n[1] = e2->n_extra_fields;
554
Thomas Klausnerf5d96cb2013-03-20 00:31:18 +0100555 return compare_list(name, (const void **)ef, n, sizeof(struct ef), ef_order, NULL, ef_print);
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200556}
557
558
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200559static int
560ef_order(const void *ap, const void *bp) {
561 const struct ef *a, *b;
562
Thomas Klausnerf5d96cb2013-03-20 00:31:18 +0100563 a = (struct ef *)ap;
564 b = (struct ef *)bp;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200565
566 if (a->flags != b->flags)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200567 return a->flags - b->flags;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200568 if (a->id != b->id)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200569 return a->id - b->id;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200570 if (a->size != b->size)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200571 return a->size - b->size;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200572 return memcmp(a->data, b->data, a->size);
573}
574
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200575
576static void
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100577ef_print(const void *p) {
Thomas Klausnerf5d96cb2013-03-20 00:31:18 +0100578 const struct ef *ef = (struct ef *)p;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200579 int i;
580
581 printf(" %s ", ef->name);
Dieter Baronff40b572012-07-15 15:19:32 +0200582 printf("%04x %c <", ef->id, ef->flags == ZIP_FL_LOCAL ? 'l' : 'c');
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100583 for (i = 0; i < ef->size; i++)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200584 printf("%s%02x", i ? " " : "", ef->data[i]);
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200585 printf(">\n");
586}
587
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200588
589static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100590entry_cmp(const void *p1, const void *p2) {
Dieter Baron392787a2003-03-14 14:53:29 +0000591 const struct entry *e1, *e2;
592 int c;
593
Thomas Klausnerf5d96cb2013-03-20 00:31:18 +0100594 e1 = (struct entry *)p1;
595 e2 = (struct entry *)p2;
Dieter Baron392787a2003-03-14 14:53:29 +0000596
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100597 if ((c = (ignore_case ? strcasecmp : strcmp)(e1->name, e2->name)) != 0)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200598 return c;
Dieter Barona205a4d2012-08-08 16:52:57 +0200599 if (e1->size != e2->size) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200600 if (e1->size > e2->size)
601 return 1;
602 else
603 return -1;
Dieter Barona205a4d2012-08-08 16:52:57 +0200604 }
Dieter Baron392787a2003-03-14 14:53:29 +0000605 if (e1->crc != e2->crc)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200606 return (int)e1->crc - (int)e2->crc;
Dieter Baron392787a2003-03-14 14:53:29 +0000607
608 return 0;
609}
610
Dieter Baron392787a2003-03-14 14:53:29 +0000611
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200612static int
613entry_paranoia_checks(char *const name[2], const void *p1, const void *p2) {
614 const struct entry *e1, *e2;
Dieter Baron3efab992012-05-04 09:29:29 +0200615 int ret;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200616
Thomas Klausnerf5d96cb2013-03-20 00:31:18 +0100617 e1 = (struct entry *)p1;
618 e2 = (struct entry *)p2;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200619
Dieter Baron3efab992012-05-04 09:29:29 +0200620 ret = 0;
621
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200622 if (ef_compare(name, e1, e2) != 0)
Thomas Klausnera14fded2020-07-21 16:07:23 +0200623 ret = 1;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200624
Thomas Klausner563e6f52012-04-24 19:08:41 +0200625 if (e1->comp_method != e2->comp_method) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200626 if (verbose) {
627 if (header_done == 0) {
628 printf("--- %s\n+++ %s\n", name[0], name[1]);
629 header_done = 1;
630 }
631 printf("--- %s ", e1->name);
632 printf("method %u\n", e1->comp_method);
633 printf("+++ %s ", e1->name);
634 printf("method %u\n", e2->comp_method);
635 }
636 ret = 1;
Dieter Baron3efab992012-05-04 09:29:29 +0200637 }
638 if (comment_compare(e1->comment, e1->comment_length, e2->comment, e2->comment_length) != 0) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200639 if (verbose) {
640 if (header_done == 0) {
641 printf("--- %s\n+++ %s\n", name[0], name[1]);
642 header_done = 1;
643 }
644 printf("--- %s ", e1->name);
645 printf("comment %" PRIu32 "\n", e1->comment_length);
646 printf("+++ %s ", e1->name);
647 printf("comment %" PRIu32 "\n", e2->comment_length);
648 }
649 ret = 1;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200650 }
651
Dieter Baron3efab992012-05-04 09:29:29 +0200652 return ret;
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200653}
654
655
Dieter Baron392787a2003-03-14 14:53:29 +0000656static void
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100657entry_print(const void *p) {
Dieter Baron392787a2003-03-14 14:53:29 +0000658 const struct entry *e;
659
Thomas Klausnerf5d96cb2013-03-20 00:31:18 +0100660 e = (struct entry *)p;
Dieter Baron392787a2003-03-14 14:53:29 +0000661
Thomas Klausnerb52bda02013-11-28 18:01:40 +0100662 /* TODO PRId64 */
Dieter Baron0e5eeab2012-04-24 18:47:12 +0200663 printf("%10lu %08x %s\n", (unsigned long)e->size, e->crc, e->name);
Dieter Baron392787a2003-03-14 14:53:29 +0000664}
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000665
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000666
667static int
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100668test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc) {
Dieter Baron1d9dfeb2014-09-28 23:02:54 +0200669 zip_file_t *zf;
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000670 char buf[8192];
Thomas Klausnerea8ba492014-09-23 16:54:47 +0200671 zip_uint64_t nsize;
672 zip_int64_t n;
Dieter Barona205a4d2012-08-08 16:52:57 +0200673 zip_uint32_t ncrc;
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100674
675 if ((zf = zip_fopen_index(za, idx, 0)) == NULL) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200676 fprintf(stderr, "%s: cannot open file %" PRIu64 " in archive: %s\n", progname, idx, zip_strerror(za));
677 return -1;
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000678 }
679
Dieter Barona205a4d2012-08-08 16:52:57 +0200680 ncrc = (zip_uint32_t)crc32(0, NULL, 0);
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000681 nsize = 0;
Thomas Klausner8eab1a22018-01-15 14:10:11 +0100682
683 while ((n = zip_fread(zf, buf, sizeof(buf))) > 0) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200684 nsize += (zip_uint64_t)n;
685 ncrc = (zip_uint32_t)crc32(ncrc, (const Bytef *)buf, (unsigned int)n);
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000686 }
687
688 if (n < 0) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200689 fprintf(stderr, "%s: error reading file %" PRIu64 " in archive: %s\n", progname, idx, zip_file_strerror(zf));
690 zip_fclose(zf);
691 return -1;
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000692 }
693
Thomas Klausner6be59812004-11-18 17:11:24 +0000694 zip_fclose(zf);
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000695
696 if (nsize != size) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200697 fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %" PRId64 " (should be %" PRId64 ")\n", progname, idx, nsize, size);
698 return -2;
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000699 }
700 if (ncrc != crc) {
Thomas Klausnera14fded2020-07-21 16:07:23 +0200701 fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %x (should be %x)\n", progname, idx, ncrc, crc);
702 return -2;
Dieter Baronb2ed74d2004-04-14 14:01:31 +0000703 }
704
705 return 0;
706}