Annotation of embedaddon/php/ext/zip/lib/zip_open.c, revision 1.1.1.2
1.1 misho 1: /*
1.1.1.2 ! misho 2: zip_open.c -- open zip archive by name
! 3: Copyright (C) 1999-2011 Dieter Baron and Thomas Klausner
1.1 misho 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:
36: #include <sys/stat.h>
37: #include <errno.h>
38: #include <limits.h>
39: #include <stdio.h>
40: #include <stdlib.h>
41: #include <string.h>
42:
43: #include "zipint.h"
44:
45: static void set_error(int *, struct zip_error *, int);
46: static struct zip *_zip_allocate_new(const char *, int *);
47: static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *);
48: static void _zip_check_torrentzip(struct zip *);
49: static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t);
50: static int _zip_file_exists(const char *, int, int *);
51: static int _zip_headercomp(struct zip_dirent *, int,
52: struct zip_dirent *, int);
53: static unsigned char *_zip_memmem(const unsigned char *, int,
54: const unsigned char *, int);
1.1.1.2 ! misho 55: static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, unsigned char *,
1.1 misho 56: int, int, struct zip_error *);
57:
58:
59:
60: ZIP_EXTERN(struct zip *)
61: zip_open(const char *fn, int flags, int *zep)
62: {
63: FILE *fp;
64:
65: if (flags & ZIP_OVERWRITE) {
66: return _zip_allocate_new(fn, zep);
67: }
68:
69: switch (_zip_file_exists(fn, flags, zep)) {
70: case -1:
71: if (!(flags & ZIP_OVERWRITE)) {
72: return NULL;
73: }
74: case 0:
75: return _zip_allocate_new(fn, zep);
76: default:
77: break;
78: }
79:
80: if ((fp=fopen(fn, "rb")) == NULL) {
81: set_error(zep, NULL, ZIP_ER_OPEN);
82: return NULL;
83: }
84:
1.1.1.2 ! misho 85: return _zip_open(fn, fp, flags, 0, zep);
! 86: }
! 87:
! 88:
! 89:
! 90: struct zip *
! 91: _zip_open(const char *fn, FILE *fp, int flags, int aflags, int *zep)
! 92: {
! 93: struct zip *za;
! 94: struct zip_cdir *cdir;
! 95: int i;
! 96: off_t len;
! 97:
! 98: if (fseeko(fp, 0, SEEK_END) < 0) {
! 99: *zep = ZIP_ER_SEEK;
! 100: return NULL;
! 101: }
1.1 misho 102: len = ftello(fp);
103:
104: /* treat empty files as empty archives */
105: if (len == 0) {
106: if ((za=_zip_allocate_new(fn, zep)) == NULL)
107: fclose(fp);
108: else
109: za->zp = fp;
110: return za;
111: }
112:
113: cdir = _zip_find_central_dir(fp, flags, zep, len);
114: if (cdir == NULL) {
115: fclose(fp);
116: return NULL;
117: }
118:
119: if ((za=_zip_allocate_new(fn, zep)) == NULL) {
120: _zip_cdir_free(cdir);
121: fclose(fp);
122: return NULL;
123: }
124:
125: za->cdir = cdir;
126: za->zp = fp;
127:
128: if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry))
129: * cdir->nentry)) == NULL) {
130: set_error(zep, NULL, ZIP_ER_MEMORY);
131: _zip_free(za);
132: return NULL;
133: }
134: for (i=0; i<cdir->nentry; i++)
135: _zip_entry_new(za);
136:
137: _zip_check_torrentzip(za);
138: za->ch_flags = za->flags;
139:
140: return za;
141: }
142:
143:
144:
145: static void
146: set_error(int *zep, struct zip_error *err, int ze)
147: {
148: int se;
149:
150: if (err) {
151: _zip_error_get(err, &ze, &se);
152: if (zip_error_get_sys_type(ze) == ZIP_ET_SYS)
153: errno = se;
154: }
155:
156: if (zep)
157: *zep = ze;
158: }
159:
160:
161:
162: /* _zip_readcdir:
163: tries to find a valid end-of-central-directory at the beginning of
164: buf, and then the corresponding central directory entries.
165: Returns a struct zip_cdir which contains the central directory
166: entries, or NULL if unsuccessful. */
167:
168: static struct zip_cdir *
1.1.1.2 ! misho 169: _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, unsigned char *eocd, int buflen,
1.1 misho 170: int flags, struct zip_error *error)
171: {
172: struct zip_cdir *cd;
173: unsigned char *cdp, **bufp;
174: int i, comlen, nentry;
1.1.1.2 ! misho 175: zip_uint32_t left;
1.1 misho 176:
177: comlen = buf + buflen - eocd - EOCDLEN;
178: if (comlen < 0) {
179: /* not enough bytes left for comment */
180: _zip_error_set(error, ZIP_ER_NOZIP, 0);
181: return NULL;
182: }
183:
184: /* check for end-of-central-dir magic */
185: if (memcmp(eocd, EOCD_MAGIC, 4) != 0) {
186: _zip_error_set(error, ZIP_ER_NOZIP, 0);
187: return NULL;
188: }
189:
190: if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) {
191: _zip_error_set(error, ZIP_ER_MULTIDISK, 0);
192: return NULL;
193: }
194:
195: cdp = eocd + 8;
196: /* number of cdir-entries on this disk */
197: i = _zip_read2(&cdp);
198: /* number of cdir-entries */
199: nentry = _zip_read2(&cdp);
200:
201: if ((cd=_zip_cdir_new(nentry, error)) == NULL)
202: return NULL;
203:
204: cd->size = _zip_read4(&cdp);
205: cd->offset = _zip_read4(&cdp);
206: cd->comment = NULL;
207: cd->comment_len = _zip_read2(&cdp);
208:
1.1.1.2 ! misho 209: if (((zip_uint64_t)cd->offset)+cd->size > buf_offset + (eocd-buf)) {
! 210: /* cdir spans past EOCD record */
! 211: _zip_error_set(error, ZIP_ER_INCONS, 0);
! 212: cd->nentry = 0;
! 213: _zip_cdir_free(cd);
! 214: return NULL;
! 215: }
! 216:
1.1 misho 217: if ((comlen < cd->comment_len) || (cd->nentry != i)) {
218: _zip_error_set(error, ZIP_ER_NOZIP, 0);
1.1.1.2 ! misho 219: cd->nentry = 0;
! 220: _zip_cdir_free(cd);
1.1 misho 221: return NULL;
222: }
223: if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) {
224: _zip_error_set(error, ZIP_ER_INCONS, 0);
1.1.1.2 ! misho 225: cd->nentry = 0;
! 226: _zip_cdir_free(cd);
1.1 misho 227: return NULL;
228: }
229:
230: if (cd->comment_len) {
231: if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN,
232: cd->comment_len, error))
233: == NULL) {
1.1.1.2 ! misho 234: cd->nentry = 0;
! 235: _zip_cdir_free(cd);
1.1 misho 236: return NULL;
237: }
238: }
239:
1.1.1.2 ! misho 240: if (cd->offset >= buf_offset) {
1.1 misho 241: /* if buffer already read in, use it */
1.1.1.2 ! misho 242: cdp = buf + (cd->offset - buf_offset);
1.1 misho 243: bufp = &cdp;
244: }
245: else {
246: /* go to start of cdir and read it entry by entry */
247: bufp = NULL;
248: clearerr(fp);
249: fseeko(fp, cd->offset, SEEK_SET);
250: /* possible consistency check: cd->offset =
251: len-(cd->size+cd->comment_len+EOCDLEN) ? */
252: if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) {
253: /* seek error or offset of cdir wrong */
254: if (ferror(fp))
255: _zip_error_set(error, ZIP_ER_SEEK, errno);
256: else
257: _zip_error_set(error, ZIP_ER_NOZIP, 0);
1.1.1.2 ! misho 258: cd->nentry = 0;
! 259: _zip_cdir_free(cd);
1.1 misho 260: return NULL;
261: }
262: }
263:
264: left = cd->size;
265: i=0;
1.1.1.2 ! misho 266: while (i<cd->nentry && left > 0) {
1.1 misho 267: if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) {
268: cd->nentry = i;
269: _zip_cdir_free(cd);
270: return NULL;
271: }
272: i++;
273:
1.1.1.2 ! misho 274: if (i == cd->nentry && left > 0) {
! 275: /* Infozip extension for more than 64k entries:
! 276: nentries wraps around, size indicates correct EOCD */
! 277: if (_zip_cdir_grow(cd, cd->nentry+ZIP_UINT16_MAX, error) < 0) {
! 278: cd->nentry = i;
! 279: _zip_cdir_free(cd);
! 280: return NULL;
! 281: }
! 282: }
! 283: }
! 284:
! 285: cd->nentry = i;
1.1 misho 286:
287: return cd;
288: }
289:
290:
291:
292: /* _zip_checkcons:
293: Checks the consistency of the central directory by comparing central
294: directory entries with local headers and checking for plausible
295: file and header offsets. Returns -1 if not plausible, else the
296: difference between the lowest and the highest fileposition reached */
297:
298: static int
299: _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error)
300: {
301: int i;
302: unsigned int min, max, j;
303: struct zip_dirent temp;
304:
305: if (cd->nentry) {
306: max = cd->entry[0].offset;
307: min = cd->entry[0].offset;
308: }
309: else
310: min = max = 0;
311:
312: for (i=0; i<cd->nentry; i++) {
313: if (cd->entry[i].offset < min)
314: min = cd->entry[i].offset;
315: if (min > cd->offset) {
316: _zip_error_set(error, ZIP_ER_NOZIP, 0);
317: return -1;
318: }
319:
320: j = cd->entry[i].offset + cd->entry[i].comp_size
321: + cd->entry[i].filename_len + LENTRYSIZE;
322: if (j > max)
323: max = j;
324: if (max > cd->offset) {
325: _zip_error_set(error, ZIP_ER_NOZIP, 0);
326: return -1;
327: }
328:
329: if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) {
330: _zip_error_set(error, ZIP_ER_SEEK, 0);
331: return -1;
332: }
333:
334: if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1)
335: return -1;
336:
337: if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) {
338: _zip_error_set(error, ZIP_ER_INCONS, 0);
339: _zip_dirent_finalize(&temp);
340: return -1;
341: }
342: _zip_dirent_finalize(&temp);
343: }
344:
345: return max - min;
346: }
347:
348:
349:
350: /* _zip_check_torrentzip:
1.1.1.2 ! misho 351: check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
1.1 misho 352:
353: static void
354: _zip_check_torrentzip(struct zip *za)
355: {
356: uLong crc_got, crc_should;
357: char buf[8+1];
358: char *end;
359:
360: if (za->zp == NULL || za->cdir == NULL)
361: return;
362:
363: if (za->cdir->comment_len != TORRENT_SIG_LEN+8
364: || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0)
365: return;
1.1.1.2 ! misho 366:
1.1 misho 367: memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8);
368: buf[8] = '\0';
369: errno = 0;
370: crc_should = strtoul(buf, &end, 16);
371: if ((crc_should == UINT_MAX && errno != 0) || (end && *end))
372: return;
1.1.1.2 ! misho 373:
1.1 misho 374: if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size,
375: &crc_got, NULL) < 0)
1.1.1.2 ! misho 376: return;
1.1 misho 377:
378: if (crc_got == crc_should)
379: za->flags |= ZIP_AFL_TORRENT;
380: }
381:
382:
383:
384:
385: /* _zip_headercomp:
386: compares two headers h1 and h2; if they are local headers, set
387: local1p or local2p respectively to 1, else 0. Return 0 if they
388: are identical, -1 if not. */
389:
390: static int
391: _zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2,
392: int local2p)
393: {
394: if ((h1->version_needed != h2->version_needed)
395: #if 0
396: /* some zip-files have different values in local
397: and global headers for the bitflags */
398: || (h1->bitflags != h2->bitflags)
399: #endif
400: || (h1->comp_method != h2->comp_method)
401: || (h1->last_mod != h2->last_mod)
402: || (h1->filename_len != h2->filename_len)
403: || !h1->filename || !h2->filename
404: || strcmp(h1->filename, h2->filename))
405: return -1;
406:
407: /* check that CRC and sizes are zero if data descriptor is used */
408: if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p
409: && (h1->crc != 0
410: || h1->comp_size != 0
411: || h1->uncomp_size != 0))
412: return -1;
413: if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p
414: && (h2->crc != 0
415: || h2->comp_size != 0
416: || h2->uncomp_size != 0))
417: return -1;
418:
419: /* check that CRC and sizes are equal if no data descriptor is used */
420: if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0)
421: && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) {
422: if ((h1->crc != h2->crc)
423: || (h1->comp_size != h2->comp_size)
424: || (h1->uncomp_size != h2->uncomp_size))
425: return -1;
426: }
427:
428: if ((local1p == local2p)
429: && ((h1->extrafield_len != h2->extrafield_len)
430: || (h1->extrafield_len && h2->extrafield
431: && memcmp(h1->extrafield, h2->extrafield,
432: h1->extrafield_len))))
433: return -1;
434:
435: /* if either is local, nothing more to check */
436: if (local1p || local2p)
437: return 0;
438:
439: if ((h1->version_madeby != h2->version_madeby)
440: || (h1->disk_number != h2->disk_number)
441: || (h1->int_attrib != h2->int_attrib)
442: || (h1->ext_attrib != h2->ext_attrib)
443: || (h1->offset != h2->offset)
444: || (h1->comment_len != h2->comment_len)
445: || (h1->comment_len && h2->comment
446: && memcmp(h1->comment, h2->comment, h1->comment_len)))
447: return -1;
448:
449: return 0;
450: }
451:
452:
453:
454: static struct zip *
455: _zip_allocate_new(const char *fn, int *zep)
456: {
457: struct zip *za;
458: struct zip_error error;
459:
460: if ((za=_zip_new(&error)) == NULL) {
461: set_error(zep, &error, 0);
462: return NULL;
463: }
1.1.1.2 ! misho 464:
! 465: if (fn == NULL)
! 466: za->zn = NULL;
! 467: else {
! 468: za->zn = strdup(fn);
! 469: if (!za->zn) {
! 470: _zip_free(za);
! 471: set_error(zep, NULL, ZIP_ER_MEMORY);
! 472: return NULL;
! 473: }
1.1 misho 474: }
475: return za;
476: }
477:
478:
479:
480: static int
481: _zip_file_exists(const char *fn, int flags, int *zep)
482: {
483: struct stat st;
484:
485: if (fn == NULL) {
486: set_error(zep, NULL, ZIP_ER_INVAL);
487: return -1;
488: }
489:
490: if (stat(fn, &st) != 0) {
491: if (flags & ZIP_CREATE || flags & ZIP_OVERWRITE)
492: return 0;
493: else {
494: set_error(zep, NULL, ZIP_ER_OPEN);
495: return -1;
496: }
497: }
498: else if ((flags & ZIP_EXCL)) {
499: set_error(zep, NULL, ZIP_ER_EXISTS);
500: return -1;
501: }
502: /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL,
503: just like open() */
504:
505: return 1;
506: }
507:
508:
509:
510: static struct zip_cdir *
511: _zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len)
512: {
513: struct zip_cdir *cdir, *cdirnew;
514: unsigned char *buf, *match;
1.1.1.2 ! misho 515: off_t buf_offset;
1.1 misho 516: int a, best, buflen, i;
517: struct zip_error zerr;
518:
519: i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END);
520: if (i == -1 && errno != EFBIG) {
521: /* seek before start of file on my machine */
522: set_error(zep, NULL, ZIP_ER_SEEK);
523: return NULL;
524: }
1.1.1.2 ! misho 525: buf_offset = ftello(fp);
! 526:
1.1 misho 527: /* 64k is too much for stack */
528: if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) {
529: set_error(zep, NULL, ZIP_ER_MEMORY);
530: return NULL;
531: }
532:
533: clearerr(fp);
534: buflen = fread(buf, 1, CDBUFSIZE, fp);
535:
536: if (ferror(fp)) {
537: set_error(zep, NULL, ZIP_ER_READ);
538: free(buf);
539: return NULL;
540: }
541:
542: best = -1;
543: cdir = NULL;
544: match = buf;
545: _zip_error_set(&zerr, ZIP_ER_NOZIP, 0);
546:
547: while ((match=_zip_memmem(match, buflen-(match-buf)-18,
548: (const unsigned char *)EOCD_MAGIC, 4))!=NULL) {
549: /* found match -- check, if good */
550: /* to avoid finding the same match all over again */
551: match++;
1.1.1.2 ! misho 552: if ((cdirnew=_zip_readcdir(fp, buf_offset, buf, match-1, buflen, flags,
1.1 misho 553: &zerr)) == NULL)
554: continue;
555:
556: if (cdir) {
557: if (best <= 0)
558: best = _zip_checkcons(fp, cdir, &zerr);
559: a = _zip_checkcons(fp, cdirnew, &zerr);
560: if (best < a) {
561: _zip_cdir_free(cdir);
562: cdir = cdirnew;
563: best = a;
564: }
565: else
566: _zip_cdir_free(cdirnew);
567: }
568: else {
569: cdir = cdirnew;
570: if (flags & ZIP_CHECKCONS)
571: best = _zip_checkcons(fp, cdir, &zerr);
572: else
573: best = 0;
574: }
575: cdirnew = NULL;
576: }
577:
578: free(buf);
579:
580: if (best < 0) {
581: set_error(zep, &zerr, 0);
582: _zip_cdir_free(cdir);
583: return NULL;
584: }
585:
586: return cdir;
587: }
588:
589:
590:
591: static unsigned char *
592: _zip_memmem(const unsigned char *big, int biglen, const unsigned char *little,
593: int littlelen)
594: {
595: const unsigned char *p;
596:
597: if ((biglen < littlelen) || (littlelen == 0))
598: return NULL;
599: p = big-1;
600: while ((p=(const unsigned char *)
601: memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1)))
602: != NULL) {
603: if (memcmp(p+1, little+1, littlelen-1)==0)
604: return (unsigned char *)p;
605: }
606:
607: return NULL;
608: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>