Imported Upstream version 1.3.2
[platform/upstream/libzip.git] / src / zipcmp.c
1 /*
2   zipcmp.c -- compare zip files
3   Copyright (C) 2003-2016 Dieter Baron and Thomas Klausner
4
5   This file is part of libzip, a library to manipulate ZIP archives.
6   The authors can be contacted at <libzip@nih.at>
7
8   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.
20  
21   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.
32 */
33
34
35 #include "config.h"
36
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #ifdef HAVE_STRINGS_H
43 #include <strings.h>
44 #endif
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #ifdef HAVE_FTS_H
49 #include <fts.h>
50 #endif
51 #include <zlib.h>
52
53 #ifndef HAVE_GETOPT
54 #include "getopt.h"
55 #endif
56
57 #include "compat.h"
58 #include "zip.h"
59
60 struct archive {
61     const char *name;
62     zip_t *za;
63     zip_uint64_t nentry;
64     struct entry *entry;
65     const char *comment;
66     size_t comment_length;
67 };
68
69 struct ef {
70     const char *name;
71     zip_uint16_t flags;
72     zip_uint16_t id;
73     zip_uint16_t size;
74     const zip_uint8_t *data;
75 };
76
77 struct entry {
78     char *name;
79     zip_uint64_t size;
80     zip_uint32_t crc;
81     zip_uint32_t comp_method;
82     struct ef *extra_fields;
83     zip_uint16_t n_extra_fields;
84     const char *comment;
85     zip_uint32_t comment_length;
86 };
87
88
89 const char *prg;
90
91 #define PROGRAM "zipcmp"
92
93 #define USAGE "usage: %s [-hipqtVv] archive1 archive2\n"
94
95 char help_head[] =
96     PROGRAM " (" PACKAGE ") by Dieter Baron and Thomas Klausner\n\n";
97
98 char help[] = "\n\
99   -h       display this help message\n\
100   -i       compare names ignoring case distinctions\n\
101   -p       compare as many details as possible\n\
102   -q       be quiet\n\
103   -t       test zip files (compare file contents to checksum)\n\
104   -V       display version number\n\
105   -v       be verbose (print differences, default)\n\
106 \n\
107 Report bugs to <libzip@nih.at>.\n";
108
109 char version_string[] = PROGRAM " (" PACKAGE " " VERSION ")\n\
110 Copyright (C) 2003-2016 Dieter Baron and Thomas Klausner\n\
111 " PACKAGE " comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
112
113 #define OPTIONS "hVipqtv"
114
115
116 #define BOTH_ARE_ZIPS(a)        (a[0].za && a[1].za)
117
118 static int comment_compare(const char *c1, size_t l1, const char *c2, size_t l2);
119 static int compare_list(char * const name[],
120                         const void *l[], const zip_uint64_t n[], int size,
121                         int (*cmp)(const void *, const void *),
122                         int (*checks)(char *const name[2], const void *, const void *),
123                         void (*print)(const void *));
124 static int compare_zip(char * const zn[]);
125 static int ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2);
126 static int ef_order(const void *a, const void *b);
127 static void ef_print(const void *p);
128 static int ef_read(zip_t *za, zip_uint64_t idx, struct entry *e);
129 static int entry_cmp(const void *p1, const void *p2);
130 static int entry_paranoia_checks(char *const name[2], const void *p1, const void *p2);
131 static void entry_print(const void *p);
132 static int is_directory(const char *name);
133 #ifdef HAVE_FTS_H
134 static int list_directory(const char *name, struct archive *a);
135 #endif
136 static int list_zip(const char *name, struct archive *a);
137 static int test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc);
138
139 int ignore_case, test_files, paranoid, verbose;
140 int header_done;
141
142
143 int
144 main(int argc, char * const argv[])
145 {
146     int c;
147
148     prg = argv[0];
149
150     ignore_case = 0;
151     test_files = 0;
152     paranoid = 0;
153     verbose = 1;
154
155     while ((c=getopt(argc, argv, OPTIONS)) != -1) {
156         switch (c) {
157         case 'i':
158             ignore_case = 1;
159             break;
160         case 'p':
161             paranoid = 1;
162             break;
163         case 'q':
164             verbose = 0;
165             break;
166         case 't':
167             test_files = 1;
168             break;
169         case 'v':
170             verbose = 1;
171             break;
172
173         case 'h':
174             fputs(help_head, stdout);
175             printf(USAGE, prg);
176             fputs(help, stdout);
177             exit(0);
178         case 'V':
179             fputs(version_string, stdout);
180             exit(0);
181
182         default:
183             fprintf(stderr, USAGE, prg);
184             exit(2);
185         }
186     }
187
188     if (argc != optind+2) {
189         fprintf(stderr, USAGE, prg);
190         exit(2);
191     }
192
193     exit((compare_zip(argv+optind) == 0) ? 0 : 1);
194 }
195
196
197 static int
198 compare_zip(char * const zn[])
199 {
200     struct archive a[2];
201     struct entry *e[2];
202     zip_uint64_t n[2];
203     int i;
204     int res;
205
206     for (i=0; i<2; i++) {
207         a[i].name = zn[i];
208         a[i].entry = NULL;
209         a[i].nentry = 0;
210         a[i].za = NULL;
211         a[i].comment = NULL;
212         a[i].comment_length =0;
213
214         if (is_directory(zn[i])) {
215 #ifndef HAVE_FTS_H
216             fprintf(stderr, "%s: reading directories not supported\n", prg);
217             exit(2);
218 #else
219             if (list_directory(zn[i], a+i) < 0)
220                 exit(2);
221             paranoid = 0; /* paranoid checks make no sense for directories, since they compare zip metadata */
222 #endif
223         }
224         else {
225             if (list_zip(zn[i], a+i) < 0)
226                 exit(2);
227         }
228         if (a[i].nentry > 0)
229             qsort(a[i].entry, a[i].nentry, sizeof(a[i].entry[0]), entry_cmp);
230     }
231
232     header_done = 0;
233
234     e[0] = a[0].entry;
235     e[1] = a[1].entry;
236     n[0] = a[0].nentry;
237     n[1] = a[1].nentry;
238     res = compare_list(zn, (const void **)e, n, sizeof(e[i][0]), entry_cmp, paranoid ? entry_paranoia_checks : NULL, entry_print);
239
240     if (paranoid) {
241         if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
242             if (verbose) {
243                 printf("--- archive comment (%zd)\n", a[0].comment_length);
244                 printf("+++ archive comment (%zd)\n", a[1].comment_length);
245             }
246             res = 1;
247         }
248     }
249
250     for (i=0; i<2; i++)
251         if (a[i].za)
252             zip_close(a[i].za);
253
254     switch (res) {
255     case 0:
256         exit(0);
257
258     case 1:
259         exit(1);
260
261     default:
262         exit(2);
263     }
264 }
265
266 #ifdef HAVE_FTS_H
267 static zip_int64_t
268 compute_crc(const char *fname)
269 {
270     FILE *f;
271     uLong crc = crc32(0L, Z_NULL, 0);
272     size_t n;
273     Bytef buffer[8192];
274
275
276     if ((f=fopen(fname, "r")) == NULL) {
277         fprintf(stderr, "%s: can't open %s: %s\n", prg, fname, strerror(errno));
278         return -1;
279     }
280
281     while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
282         crc = crc32(crc, buffer, (unsigned int)n);
283     }
284
285     if (ferror(f)) {
286         fprintf(stderr, "%s: read error on %s: %s\n", prg, fname, strerror(errno));
287         fclose(f);
288         return -1;
289     }
290
291     fclose(f);
292
293     return (zip_int64_t)crc;
294 }
295 #endif
296
297
298 static int
299 is_directory(const char *name)
300 {
301     struct stat st;
302
303     if (stat(name, &st) < 0)
304         return 0;
305
306     return S_ISDIR(st.st_mode);
307 }
308
309
310 #ifdef HAVE_FTS_H
311 static int
312 list_directory(const char *name, struct archive *a)
313 {
314     FTS *fts;
315     FTSENT *ent;
316     zip_uint64_t nalloc;
317     size_t prefix_length;
318
319     char * const names[2] = { (char *)name, NULL };
320
321
322     if ((fts = fts_open(names, FTS_NOCHDIR|FTS_LOGICAL, NULL)) == NULL) {
323         fprintf(stderr, "%s: can't open directory '%s': %s\n", prg, name, strerror(errno));
324         return -1;
325     }
326     prefix_length = strlen(name)+1;
327
328     nalloc = 0;
329
330     while ((ent = fts_read(fts))) {
331         zip_int64_t crc;
332
333         switch (ent->fts_info) {
334         case FTS_D:
335         case FTS_DOT:
336         case FTS_DP:
337         case FTS_DEFAULT:
338         case FTS_SL:
339         case FTS_NSOK:
340             break;
341
342         case FTS_DC:
343         case FTS_DNR:
344         case FTS_ERR:
345         case FTS_NS:
346         case FTS_SLNONE:
347             /* TODO: error */
348             fts_close(fts);
349             return -1;
350
351         case FTS_F:
352             if (a->nentry >= nalloc) {
353                 nalloc += 16;
354                 if (nalloc > SIZE_MAX/sizeof(a->entry[0])) {
355                     fprintf(stderr, "%s: malloc failure\n", prg);
356                     exit(1);
357                 }
358                 a->entry = realloc(a->entry, sizeof(a->entry[0])*nalloc);
359                 if (a->entry == NULL) {
360                     fprintf(stderr, "%s: malloc failure\n", prg);
361                     exit(1);
362                 }
363             }
364             
365             a->entry[a->nentry].name = strdup(ent->fts_path+prefix_length);
366             a->entry[a->nentry].size = (zip_uint64_t)ent->fts_statp->st_size;
367             if ((crc = compute_crc(ent->fts_accpath)) < 0) {
368                 fts_close(fts);
369                 return -1;
370             }
371             
372             a->entry[a->nentry].crc = (zip_uint32_t)crc;
373             a->nentry++;
374             break;
375         }
376     }
377
378     if (fts_close(fts)) {
379         fprintf(stderr, "%s: error closing directory '%s': %s\n", prg, a->name, strerror(errno));
380         return -1;
381     }
382
383     return 0;
384 }
385 #endif
386
387
388 static int
389 list_zip(const char *name, struct archive *a)
390 {
391     zip_t *za;
392     int err;
393     struct zip_stat st;
394     unsigned int i;
395
396     if ((za=zip_open(name, paranoid ? ZIP_CHECKCONS : 0, &err)) == NULL) {
397         zip_error_t error;
398         zip_error_init_with_code(&error, err);
399         fprintf(stderr, "%s: cannot open zip archive '%s': %s\n", prg, name, zip_error_strerror(&error));
400         zip_error_fini(&error);
401         return -1;
402     }
403
404     a->za = za;
405     a->nentry = (zip_uint64_t)zip_get_num_entries(za, 0);
406     
407     if (a->nentry == 0)
408         a->entry = NULL;
409     else {
410         if ((a->nentry > SIZE_MAX/sizeof(a->entry[0])) || (a->entry=(struct entry *)malloc(sizeof(a->entry[0]) * a->nentry)) == NULL) {
411             fprintf(stderr, "%s: malloc failure\n", prg);
412             exit(1);
413         }
414
415         for (i=0; i<a->nentry; i++) {
416             zip_stat_index(za, i, 0, &st);
417             a->entry[i].name = strdup(st.name);
418             a->entry[i].size = st.size;
419             a->entry[i].crc = st.crc;
420             if (test_files)
421                 test_file(za, i, st.size, st.crc);
422             if (paranoid) {
423                 a->entry[i].comp_method = st.comp_method;
424                 ef_read(za, i, a->entry+i);
425                 a->entry[i].comment = zip_file_get_comment(za, i, &a->entry[i].comment_length, 0);
426             }
427             else {
428                 a->entry[i].comp_method = 0;
429                 a->entry[i].n_extra_fields = 0;
430             }
431         }
432
433         if (paranoid) {
434             int length;
435             a->comment = zip_get_archive_comment(za, &length, 0);
436             a->comment_length = (size_t)length;
437         }
438         else {
439             a->comment = NULL;
440             a->comment_length = 0;
441         }
442     }
443
444     return 0;
445 }
446
447
448 static int
449 comment_compare(const char *c1, size_t l1, const char *c2, size_t l2) {
450     if (l1 != l2)
451         return 1;
452
453     if (l1 == 0)
454         return 0;
455
456     if (c1 == NULL || c2 == NULL)
457         return c1 == c2;
458     
459     return memcmp(c1, c2, (size_t)l2);
460 }
461
462
463 static int
464 compare_list(char * const name[2],
465              const void *l[2], const zip_uint64_t n[2], int size,
466              int (*cmp)(const void *, const void *),
467              int (*check)(char *const name[2], const void *, const void *),
468              void (*print)(const void *))
469 {
470     unsigned int i[2];
471     int j, c;
472     int diff;
473
474 #define INC(k)  (i[k]++, l[k]=((const char *)l[k])+size)
475 #define PRINT(k)        do {                                            \
476                             if (header_done==0 && verbose) {            \
477                                 printf("--- %s\n+++ %s\n", name[0], name[1]); \
478                                 header_done = 1;                        \
479                             }                                           \
480                             if (verbose) {                              \
481                                 printf("%c ", (k)?'+':'-');             \
482                                 print(l[k]);                            \
483                             }                                           \
484                             diff = 1;                                   \
485                         } while (0)
486
487     i[0] = i[1] = 0;
488     diff = 0;
489     while (i[0]<n[0] && i[1]<n[1]) {
490         c = cmp(l[0], l[1]);
491
492         if (c == 0) {
493             if (check)
494                 diff |= check(name, l[0], l[1]);
495             INC(0);
496             INC(1);
497         }
498         else if (c < 0) {
499             PRINT(0);
500             INC(0);
501         }
502         else {
503             PRINT(1);
504             INC(1);
505         }
506     }
507
508     for (j=0; j<2; j++) {
509         while (i[j]<n[j]) {
510             PRINT(j);
511             INC(j);
512         }
513     }
514
515     return diff;
516 }
517
518
519 static int
520 ef_read(zip_t *za, zip_uint64_t idx, struct entry *e)
521 {
522     zip_int16_t n_local, n_central;
523     zip_uint16_t i;
524
525     if ((n_local = zip_file_extra_fields_count(za, idx, ZIP_FL_LOCAL)) < 0
526         || (n_central = zip_file_extra_fields_count(za, idx, ZIP_FL_CENTRAL)) < 0) {
527         return -1;
528     }
529     
530     e->n_extra_fields = (zip_uint16_t)(n_local + n_central);
531     
532     if ((e->extra_fields=(struct ef *)malloc(sizeof(e->extra_fields[0])*e->n_extra_fields)) == NULL)
533         return -1;
534
535     for (i=0; i<n_local; i++) {
536         e->extra_fields[i].name = e->name;
537         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);
538         if (e->extra_fields[i].data == NULL)
539             return -1;
540         e->extra_fields[i].flags = ZIP_FL_LOCAL;
541     }
542     for (; i<e->n_extra_fields; i++) {
543         e->extra_fields[i].name = e->name;
544         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);
545         if (e->extra_fields[i].data == NULL)
546             return -1;
547         e->extra_fields[i].flags = ZIP_FL_CENTRAL;
548     }
549
550     qsort(e->extra_fields, e->n_extra_fields, sizeof(e->extra_fields[0]), ef_order);
551
552     return 0;
553 }
554
555
556 static int
557 ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2)
558 {
559     struct ef *ef[2];
560     zip_uint64_t n[2];
561
562     ef[0] = e1->extra_fields;
563     ef[1] = e2->extra_fields;
564     n[0] = e1->n_extra_fields;
565     n[1] = e2->n_extra_fields;
566
567     return compare_list(name, (const void **)ef, n, sizeof(struct ef), ef_order, NULL, ef_print);
568 }
569
570
571
572 static int
573 ef_order(const void *ap, const void *bp) {
574     const struct ef *a, *b;
575
576     a = (struct ef *)ap;
577     b = (struct ef *)bp;
578
579     if (a->flags != b->flags)
580         return a->flags - b->flags;
581     if (a->id != b->id)
582         return a->id - b->id;
583     if (a->size != b->size)
584         return a->size - b->size;
585     return memcmp(a->data, b->data, a->size);
586 }
587
588
589 static void
590 ef_print(const void *p)
591 {
592     const struct ef *ef = (struct ef *)p;
593     int i;
594
595     printf("                    %s  ", ef->name);
596     printf("%04x %c <", ef->id, ef->flags == ZIP_FL_LOCAL ? 'l' : 'c');
597     for (i=0; i<ef->size; i++)
598         printf("%s%02x", i ? " " : "", ef->data[i]);
599     printf(">\n");
600 }
601
602
603 static int
604 entry_cmp(const void *p1, const void *p2)
605 {
606     const struct entry *e1, *e2;
607     int c;
608
609     e1 = (struct entry *)p1;
610     e2 = (struct entry *)p2;
611
612     if ((c=(ignore_case ? strcasecmp : strcmp)(e1->name, e2->name)) != 0)
613         return c;
614     if (e1->size != e2->size) {
615         if (e1->size > e2->size)
616             return 1;
617         else
618             return -1;
619     }
620     if (e1->crc != e2->crc)
621         return (int)e1->crc - (int)e2->crc;
622
623     return 0;
624 }
625
626
627 static int
628 entry_paranoia_checks(char *const name[2], const void *p1, const void *p2) {
629     const struct entry *e1, *e2;
630     int ret;
631
632     e1 = (struct entry *)p1;
633     e2 = (struct entry *)p2;
634
635     ret = 0;
636
637     if (ef_compare(name, e1, e2) != 0)
638         ret = 1;
639
640     if (e1->comp_method != e2->comp_method) {
641         if (verbose) {
642             if (header_done==0) {
643                 printf("--- %s\n+++ %s\n", name[0], name[1]);
644                 header_done = 1;
645             }
646             printf("---                     %s  ", e1->name);
647             printf("method %u\n", e1->comp_method);
648             printf("+++                     %s  ", e1->name);
649             printf("method %u\n", e2->comp_method);
650         }
651         ret =  1;
652     }
653     if (comment_compare(e1->comment, e1->comment_length, e2->comment, e2->comment_length) != 0) {
654         if (verbose) {
655             if (header_done==0) {
656                 printf("--- %s\n+++ %s\n", name[0], name[1]);
657                 header_done = 1;
658             }
659             printf("---                     %s  ", e1->name);
660             printf("comment %d\n", e1->comment_length);
661             printf("+++                     %s  ", e1->name);
662             printf("comment %d\n", e2->comment_length);
663         }
664         ret = 1;
665     }
666
667     return ret;
668 }
669
670
671
672 static void
673 entry_print(const void *p)
674 {
675     const struct entry *e;
676
677     e = (struct entry *)p;
678
679     /* TODO PRId64 */
680     printf("%10lu %08x %s\n", (unsigned long)e->size, e->crc, e->name);
681 }
682
683
684 static int
685 test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc)
686 {
687     zip_file_t *zf;
688     char buf[8192];
689     zip_uint64_t nsize;
690     zip_int64_t n;
691     zip_uint32_t ncrc;
692     
693     if ((zf=zip_fopen_index(za, idx, 0)) == NULL) {
694         fprintf(stderr, "%s: cannot open file %" PRIu64 " in archive: %s\n", prg, idx, zip_strerror(za));
695         return -1;
696     }
697
698     ncrc = (zip_uint32_t)crc32(0, NULL, 0);
699     nsize = 0;
700     
701     while ((n=zip_fread(zf, buf, sizeof(buf))) > 0) {
702         nsize += (zip_uint64_t)n;
703         ncrc = (zip_uint32_t)crc32(ncrc, (const Bytef *)buf, (unsigned int)n);
704     }
705
706     if (n < 0) {
707         fprintf(stderr, "%s: error reading file %" PRIu64 " in archive: %s\n",  prg, idx, zip_file_strerror(zf));
708         zip_fclose(zf);
709         return -1;
710     }
711
712     zip_fclose(zf);
713
714     if (nsize != size) {
715         fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %" PRId64 " (should be %" PRId64 ")\n", prg, idx, nsize, size);
716         return -2;
717     }
718     if (ncrc != crc) {
719         fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %x (should be %x)\n", prg, idx, ncrc, crc);
720         return -2;
721     }
722
723     return 0;
724 }