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