Imported Upstream version 1.1.3
[platform/upstream/libzip.git] / src / zipcmp.c
1 /*
2   zipcmp.c -- compare zip files
3   Copyright (C) 2003-2015 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 "zip.h"
58 #include "compat.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-2015 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         qsort(a[i].entry, a[i].nentry, sizeof(a[i].entry[0]), entry_cmp);
229     }
230
231     header_done = 0;
232
233     e[0] = a[0].entry;
234     e[1] = a[1].entry;
235     n[0] = a[0].nentry;
236     n[1] = a[1].nentry;
237     res = compare_list(zn, (const void **)e, n, sizeof(e[i][0]), entry_cmp, paranoid ? entry_paranoia_checks : NULL, entry_print);
238
239     if (paranoid) {
240         if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
241             if (verbose) {
242                 printf("--- archive comment (%zd)\n", a[0].comment_length);
243                 printf("+++ archive comment (%zd)\n", a[1].comment_length);
244             }
245             res = 1;
246         }
247     }
248
249     for (i=0; i<2; i++)
250         if (a[i].za)
251             zip_close(a[i].za);
252
253     switch (res) {
254     case 0:
255         exit(0);
256
257     case 1:
258         exit(1);
259
260     default:
261         exit(2);
262     }
263 }
264
265 #ifdef HAVE_FTS_H
266 static zip_int64_t
267 compute_crc(const char *fname)
268 {
269     FILE *f;
270     uLong crc = crc32(0L, Z_NULL, 0);
271     size_t n;
272     Bytef buffer[8192];
273
274
275     if ((f=fopen(fname, "r")) == NULL) {
276         fprintf(stderr, "%s: can't open %s: %s\n", prg, fname, strerror(errno));
277         return -1;
278     }
279
280     while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
281         crc = crc32(crc, buffer, (unsigned int)n);
282     }
283
284     if (ferror(f)) {
285         fprintf(stderr, "%s: read error on %s: %s\n", prg, fname, strerror(errno));
286         fclose(f);
287         return -1;
288     }
289
290     fclose(f);
291
292     return (zip_int64_t)crc;
293 }
294 #endif
295
296
297 static int
298 is_directory(const char *name)
299 {
300     struct stat st;
301
302     if (stat(name, &st) < 0)
303         return 0;
304
305     return S_ISDIR(st.st_mode);
306 }
307
308
309 #ifdef HAVE_FTS_H
310 static int
311 list_directory(const char *name, struct archive *a)
312 {
313     FTS *fts;
314     FTSENT *ent;
315     zip_uint64_t nalloc;
316
317     char * const names[2] = { (char *)name, NULL };
318
319
320     if ((fts = fts_open(names, FTS_NOCHDIR|FTS_LOGICAL, NULL)) == NULL) {
321         fprintf(stderr, "%s: can't open directory '%s': %s\n", prg, name, strerror(errno));
322         return -1;
323     }
324     size_t prefix_length = strlen(name)+1;
325
326     nalloc = 0;
327
328     while ((ent = fts_read(fts))) {
329         zip_int64_t crc;
330
331         switch (ent->fts_info) {
332         case FTS_D:
333         case FTS_DOT:
334         case FTS_DP:
335         case FTS_DEFAULT:
336         case FTS_SL:
337         case FTS_NSOK:
338             break;
339
340         case FTS_DC:
341         case FTS_DNR:
342         case FTS_ERR:
343         case FTS_NS:
344         case FTS_SLNONE:
345             /* TODO: error */
346             fts_close(fts);
347             return -1;
348
349         case FTS_F:
350             if (a->nentry >= nalloc) {
351                 nalloc += 16;
352                 if (nalloc > SIZE_MAX/sizeof(a->entry[0])) {
353                     fprintf(stderr, "%s: malloc failure\n", prg);
354                     exit(1);
355                 }
356                 a->entry = realloc(a->entry, sizeof(a->entry[0])*nalloc);
357                 if (a->entry == NULL) {
358                     fprintf(stderr, "%s: malloc failure\n", prg);
359                     exit(1);
360                 }
361             }
362             
363             a->entry[a->nentry].name = strdup(ent->fts_path+prefix_length);
364             a->entry[a->nentry].size = (zip_uint64_t)ent->fts_statp->st_size;
365             if ((crc = compute_crc(ent->fts_accpath)) < 0) {
366                 fts_close(fts);
367                 return -1;
368             }
369             
370             a->entry[a->nentry].crc = (zip_uint32_t)crc;
371             a->nentry++;
372             break;
373         }
374     }
375
376     if (fts_close(fts)) {
377         fprintf(stderr, "%s: error closing directory '%s': %s\n", prg, a->name, strerror(errno));
378         return -1;
379     }
380
381     return 0;
382 }
383 #endif
384
385
386 static int
387 list_zip(const char *name, struct archive *a)
388 {
389     zip_t *za;
390     int err;
391     struct zip_stat st;
392     unsigned int i;
393
394     if ((za=zip_open(name, paranoid ? ZIP_CHECKCONS : 0, &err)) == NULL) {
395         zip_error_t error;
396         zip_error_init_with_code(&error, err);
397         fprintf(stderr, "%s: cannot open zip archive '%s': %s\n", prg, name, zip_error_strerror(&error));
398         zip_error_fini(&error);
399         return -1;
400     }
401
402     a->za = za;
403     a->nentry = (zip_uint64_t)zip_get_num_entries(za, 0);
404     
405     if (a->nentry == 0)
406         a->entry = NULL;
407     else {
408         if ((a->nentry > SIZE_MAX/sizeof(a->entry[0])) || (a->entry=(struct entry *)malloc(sizeof(a->entry[0]) * a->nentry)) == NULL) {
409             fprintf(stderr, "%s: malloc failure\n", prg);
410             exit(1);
411         }
412
413         for (i=0; i<a->nentry; i++) {
414             zip_stat_index(za, i, 0, &st);
415             a->entry[i].name = strdup(st.name);
416             a->entry[i].size = st.size;
417             a->entry[i].crc = st.crc;
418             if (test_files)
419                 test_file(za, i, st.size, st.crc);
420             if (paranoid) {
421                 a->entry[i].comp_method = st.comp_method;
422                 ef_read(za, i, a->entry+i);
423                 a->entry[i].comment = zip_file_get_comment(za, i, &a->entry[i].comment_length, 0);
424             }
425             else {
426                 a->entry[i].comp_method = 0;
427                 a->entry[i].n_extra_fields = 0;
428             }
429         }
430
431         if (paranoid) {
432             int length;
433             a->comment = zip_get_archive_comment(za, &length, 0);
434             a->comment_length = (size_t)length;
435         }
436         else {
437             a->comment = NULL;
438             a->comment_length = 0;
439         }
440     }
441
442     return 0;
443 }
444
445
446 static int
447 comment_compare(const char *c1, size_t l1, const char *c2, size_t l2) {
448     if (l1 != l2)
449         return 1;
450
451     if (l1 == 0)
452         return 0;
453
454     if (c1 == NULL || c2 == NULL)
455         return c1 == c2;
456     
457     return memcmp(c1, c2, (size_t)l2);
458 }
459
460
461 static int
462 compare_list(char * const name[2],
463              const void *l[2], const zip_uint64_t n[2], int size,
464              int (*cmp)(const void *, const void *),
465              int (*check)(char *const name[2], const void *, const void *),
466              void (*print)(const void *))
467 {
468     unsigned int i[2];
469     int j, c;
470     int diff;
471
472 #define INC(k)  (i[k]++, l[k]=((const char *)l[k])+size)
473 #define PRINT(k)        do {                                            \
474                             if (header_done==0 && verbose) {            \
475                                 printf("--- %s\n+++ %s\n", name[0], name[1]); \
476                                 header_done = 1;                        \
477                             }                                           \
478                             if (verbose) {                              \
479                                 printf("%c ", (k)?'+':'-');             \
480                                 print(l[k]);                            \
481                             }                                           \
482                             diff = 1;                                   \
483                         } while (0)
484
485     i[0] = i[1] = 0;
486     diff = 0;
487     while (i[0]<n[0] && i[1]<n[1]) {
488         c = cmp(l[0], l[1]);
489
490         if (c == 0) {
491             if (check)
492                 diff |= check(name, l[0], l[1]);
493             INC(0);
494             INC(1);
495         }
496         else if (c < 0) {
497             PRINT(0);
498             INC(0);
499         }
500         else {
501             PRINT(1);
502             INC(1);
503         }
504     }
505
506     for (j=0; j<2; j++) {
507         while (i[j]<n[j]) {
508             PRINT(j);
509             INC(j);
510         }
511     }
512
513     return diff;
514 }
515
516
517 static int
518 ef_read(zip_t *za, zip_uint64_t idx, struct entry *e)
519 {
520     zip_int16_t n_local, n_central;
521     zip_uint16_t i;
522
523     if ((n_local = zip_file_extra_fields_count(za, idx, ZIP_FL_LOCAL)) < 0
524         || (n_central = zip_file_extra_fields_count(za, idx, ZIP_FL_CENTRAL)) < 0) {
525         return -1;
526     }
527     
528     e->n_extra_fields = (zip_uint16_t)(n_local + n_central);
529     
530     if ((e->extra_fields=(struct ef *)malloc(sizeof(e->extra_fields[0])*e->n_extra_fields)) == NULL)
531         return -1;
532
533     for (i=0; i<n_local; i++) {
534         e->extra_fields[i].name = e->name;
535         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);
536         if (e->extra_fields[i].data == NULL)
537             return -1;
538         e->extra_fields[i].flags = ZIP_FL_LOCAL;
539     }
540     for (; i<e->n_extra_fields; i++) {
541         e->extra_fields[i].name = e->name;
542         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);
543         if (e->extra_fields[i].data == NULL)
544             return -1;
545         e->extra_fields[i].flags = ZIP_FL_CENTRAL;
546     }
547
548     qsort(e->extra_fields, e->n_extra_fields, sizeof(e->extra_fields[0]), ef_order);
549
550     return 0;
551 }
552
553
554 static int
555 ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2)
556 {
557     struct ef *ef[2];
558     zip_uint64_t n[2];
559
560     ef[0] = e1->extra_fields;
561     ef[1] = e2->extra_fields;
562     n[0] = e1->n_extra_fields;
563     n[1] = e2->n_extra_fields;
564
565     return compare_list(name, (const void **)ef, n, sizeof(struct ef), ef_order, NULL, ef_print);
566 }
567
568
569
570 static int
571 ef_order(const void *ap, const void *bp) {
572     const struct ef *a, *b;
573
574     a = (struct ef *)ap;
575     b = (struct ef *)bp;
576
577     if (a->flags != b->flags)
578         return a->flags - b->flags;
579     if (a->id != b->id)
580         return a->id - b->id;
581     if (a->size != b->size)
582         return a->size - b->size;
583     return memcmp(a->data, b->data, a->size);
584 }
585
586
587 static void
588 ef_print(const void *p)
589 {
590     const struct ef *ef = (struct ef *)p;
591     int i;
592
593     printf("                    %s  ", ef->name);
594     printf("%04x %c <", ef->id, ef->flags == ZIP_FL_LOCAL ? 'l' : 'c');
595     for (i=0; i<ef->size; i++)
596         printf("%s%02x", i ? " " : "", ef->data[i]);
597     printf(">\n");
598 }
599
600
601 static int
602 entry_cmp(const void *p1, const void *p2)
603 {
604     const struct entry *e1, *e2;
605     int c;
606
607     e1 = (struct entry *)p1;
608     e2 = (struct entry *)p2;
609
610     if ((c=(ignore_case ? strcasecmp : strcmp)(e1->name, e2->name)) != 0)
611         return c;
612     if (e1->size != e2->size) {
613         if (e1->size > e2->size)
614             return 1;
615         else
616             return -1;
617     }
618     if (e1->crc != e2->crc)
619         return (int)e1->crc - (int)e2->crc;
620
621     return 0;
622 }
623
624
625 static int
626 entry_paranoia_checks(char *const name[2], const void *p1, const void *p2) {
627     const struct entry *e1, *e2;
628     int ret;
629
630     e1 = (struct entry *)p1;
631     e2 = (struct entry *)p2;
632
633     ret = 0;
634
635     if (ef_compare(name, e1, e2) != 0)
636         ret = 1;
637
638     if (e1->comp_method != e2->comp_method) {
639         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("method %u\n", e1->comp_method);
646             printf("+++                     %s  ", e1->name);
647             printf("method %u\n", e2->comp_method);
648         }
649         ret =  1;
650     }
651     if (comment_compare(e1->comment, e1->comment_length, e2->comment, e2->comment_length) != 0) {
652         if (verbose) {
653             if (header_done==0) {
654                 printf("--- %s\n+++ %s\n", name[0], name[1]);
655                 header_done = 1;
656             }
657             printf("---                     %s  ", e1->name);
658             printf("comment %d\n", e1->comment_length);
659             printf("+++                     %s  ", e1->name);
660             printf("comment %d\n", e2->comment_length);
661         }
662         ret = 1;
663     }
664
665     return ret;
666 }
667
668
669
670 static void
671 entry_print(const void *p)
672 {
673     const struct entry *e;
674
675     e = (struct entry *)p;
676
677     /* TODO PRId64 */
678     printf("%10lu %08x %s\n", (unsigned long)e->size, e->crc, e->name);
679 }
680
681
682 static int
683 test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc)
684 {
685     zip_file_t *zf;
686     char buf[8192];
687     zip_uint64_t nsize;
688     zip_int64_t n;
689     zip_uint32_t ncrc;
690     
691     if ((zf=zip_fopen_index(za, idx, 0)) == NULL) {
692         fprintf(stderr, "%s: cannot open file %" PRIu64 " in archive: %s\n", prg, idx, zip_strerror(za));
693         return -1;
694     }
695
696     ncrc = (zip_uint32_t)crc32(0, NULL, 0);
697     nsize = 0;
698     
699     while ((n=zip_fread(zf, buf, sizeof(buf))) > 0) {
700         nsize += (zip_uint64_t)n;
701         ncrc = (zip_uint32_t)crc32(ncrc, (const Bytef *)buf, (unsigned int)n);
702     }
703
704     if (n < 0) {
705         fprintf(stderr, "%s: error reading file %" PRIu64 " in archive: %s\n",  prg, idx, zip_file_strerror(zf));
706         zip_fclose(zf);
707         return -1;
708     }
709
710     zip_fclose(zf);
711
712     if (nsize != size) {
713         fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %" PRId64 " (should be %" PRId64 ")\n", prg, idx, nsize, size);
714         return -2;
715     }
716     if (ncrc != crc) {
717         fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %x (should be %x)\n", prg, idx, ncrc, crc);
718         return -2;
719     }
720
721     return 0;
722 }