Back to index

php5  5.3.10
zip_open.c
Go to the documentation of this file.
00001 /*
00002   zip_open.c -- open zip archive
00003   Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
00004 
00005   This file is part of libzip, a library to manipulate ZIP archives.
00006   The authors can be contacted at <libzip@nih.at>
00007 
00008   Redistribution and use in source and binary forms, with or without
00009   modification, are permitted provided that the following conditions
00010   are met:
00011   1. Redistributions of source code must retain the above copyright
00012      notice, this list of conditions and the following disclaimer.
00013   2. Redistributions in binary form must reproduce the above copyright
00014      notice, this list of conditions and the following disclaimer in
00015      the documentation and/or other materials provided with the
00016      distribution.
00017   3. The names of the authors may not be used to endorse or promote
00018      products derived from this software without specific prior
00019      written permission.
00020  
00021   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
00022   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
00025   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00026   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00027   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00028   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00029   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00030   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00031   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 */
00033 
00034 
00035 
00036 #include <sys/stat.h>
00037 #include <errno.h>
00038 #include <limits.h>
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 
00043 #include "zipint.h"
00044 
00045 static void set_error(int *, struct zip_error *, int);
00046 static struct zip *_zip_allocate_new(const char *, int *);
00047 static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *);
00048 static void _zip_check_torrentzip(struct zip *);
00049 static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t);
00050 static int _zip_file_exists(const char *, int, int *);
00051 static int _zip_headercomp(struct zip_dirent *, int,
00052                         struct zip_dirent *, int);
00053 static unsigned char *_zip_memmem(const unsigned char *, int,
00054                               const unsigned char *, int);
00055 static struct zip_cdir *_zip_readcdir(FILE *, unsigned char *, unsigned char *,
00056                              int, int, struct zip_error *);
00057 
00058 
00059 
00060 ZIP_EXTERN(struct zip *)
00061 zip_open(const char *fn, int flags, int *zep)
00062 {
00063     FILE *fp;
00064     struct zip *za;
00065     struct zip_cdir *cdir;
00066     int i;
00067     off_t len;
00068 
00069     if (flags & ZIP_OVERWRITE) {
00070        return _zip_allocate_new(fn, zep);
00071     }
00072 
00073     switch (_zip_file_exists(fn, flags, zep)) {
00074     case -1:
00075                      if (!(flags & ZIP_OVERWRITE)) {
00076                             return NULL;
00077                      }
00078 
00079     case 0:
00080        return _zip_allocate_new(fn, zep);
00081 
00082     default:
00083        break;
00084     }
00085 
00086     if ((fp=fopen(fn, "rb")) == NULL) {
00087        set_error(zep, NULL, ZIP_ER_OPEN);
00088        return NULL;
00089     }
00090 
00091     fseeko(fp, 0, SEEK_END);
00092     len = ftello(fp);
00093 
00094     /* treat empty files as empty archives */
00095     if (len == 0) {
00096        if ((za=_zip_allocate_new(fn, zep)) == NULL)
00097            fclose(fp);
00098        else
00099            za->zp = fp;
00100        return za;
00101     }
00102 
00103     cdir = _zip_find_central_dir(fp, flags, zep, len);
00104     if (cdir == NULL) {
00105        fclose(fp);
00106        return NULL;
00107     }
00108 
00109     if ((za=_zip_allocate_new(fn, zep)) == NULL) {
00110        _zip_cdir_free(cdir);
00111        fclose(fp);
00112        return NULL;
00113     }
00114 
00115     za->cdir = cdir;
00116     za->zp = fp;
00117 
00118     if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry))
00119                                          * cdir->nentry)) == NULL) {
00120        set_error(zep, NULL, ZIP_ER_MEMORY);
00121        _zip_free(za);
00122        return NULL;
00123     }
00124     for (i=0; i<cdir->nentry; i++)
00125        _zip_entry_new(za);
00126 
00127     _zip_check_torrentzip(za);
00128     za->ch_flags = za->flags;
00129 
00130     return za;
00131 }
00132 
00133 
00134 
00135 static void
00136 set_error(int *zep, struct zip_error *err, int ze)
00137 {
00138     int se;
00139 
00140     if (err) {
00141        _zip_error_get(err, &ze, &se);
00142        if (zip_error_get_sys_type(ze) == ZIP_ET_SYS)
00143            errno = se;
00144     }
00145 
00146     if (zep)
00147        *zep = ze;
00148 }
00149 
00150 
00151 
00152 /* _zip_readcdir:
00153    tries to find a valid end-of-central-directory at the beginning of
00154    buf, and then the corresponding central directory entries.
00155    Returns a struct zip_cdir which contains the central directory 
00156    entries, or NULL if unsuccessful. */
00157 
00158 static struct zip_cdir *
00159 _zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen,
00160              int flags, struct zip_error *error)
00161 {
00162     struct zip_cdir *cd;
00163     unsigned char *cdp, **bufp;
00164     int i, comlen, nentry;
00165     unsigned int left;
00166 
00167     comlen = buf + buflen - eocd - EOCDLEN;
00168     if (comlen < 0) {
00169        /* not enough bytes left for comment */
00170        _zip_error_set(error, ZIP_ER_NOZIP, 0);
00171        return NULL;
00172     }
00173 
00174     /* check for end-of-central-dir magic */
00175     if (memcmp(eocd, EOCD_MAGIC, 4) != 0) {
00176        _zip_error_set(error, ZIP_ER_NOZIP, 0);
00177        return NULL;
00178     }
00179 
00180     if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) {
00181        _zip_error_set(error, ZIP_ER_MULTIDISK, 0);
00182        return NULL;
00183     }
00184 
00185     cdp = eocd + 8;
00186     /* number of cdir-entries on this disk */
00187     i = _zip_read2(&cdp);
00188     /* number of cdir-entries */
00189     nentry = _zip_read2(&cdp);
00190 
00191     if ((cd=_zip_cdir_new(nentry, error)) == NULL)
00192        return NULL;
00193 
00194     cd->size = _zip_read4(&cdp);
00195     cd->offset = _zip_read4(&cdp);
00196     cd->comment = NULL;
00197     cd->comment_len = _zip_read2(&cdp);
00198 
00199     if ((comlen < cd->comment_len) || (cd->nentry != i)) {
00200        _zip_error_set(error, ZIP_ER_NOZIP, 0);
00201        free(cd);
00202        return NULL;
00203     }
00204     if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) {
00205        _zip_error_set(error, ZIP_ER_INCONS, 0);
00206        free(cd);
00207        return NULL;
00208     }
00209 
00210     if (cd->comment_len) {
00211        if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN,
00212                                         cd->comment_len, error))
00213            == NULL) {
00214            free(cd);
00215            return NULL;
00216        }
00217     }
00218 
00219     if (cd->size < (unsigned int)(eocd-buf)) {
00220        /* if buffer already read in, use it */
00221        cdp = eocd - cd->size;
00222        bufp = &cdp;
00223     }
00224     else {
00225        /* go to start of cdir and read it entry by entry */
00226        bufp = NULL;
00227        clearerr(fp);
00228        fseeko(fp, cd->offset, SEEK_SET);
00229        /* possible consistency check: cd->offset =
00230           len-(cd->size+cd->comment_len+EOCDLEN) ? */
00231        if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) {
00232            /* seek error or offset of cdir wrong */
00233            if (ferror(fp))
00234               _zip_error_set(error, ZIP_ER_SEEK, errno);
00235            else
00236               _zip_error_set(error, ZIP_ER_NOZIP, 0);
00237            free(cd);
00238            return NULL;
00239        }
00240     }
00241 
00242     left = cd->size;
00243     i=0;
00244     do {
00245        if (i == cd->nentry && left > 0) {
00246            /* Infozip extension for more than 64k entries:
00247               nentries wraps around, size indicates correct EOCD */
00248            _zip_cdir_grow(cd, cd->nentry+0x10000, error);
00249        }
00250 
00251        if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) {
00252            cd->nentry = i;
00253            _zip_cdir_free(cd);
00254            return NULL;
00255        }
00256        i++;
00257        
00258     } while (i<cd->nentry);
00259     
00260     return cd;
00261 }
00262 
00263 
00264 
00265 /* _zip_checkcons:
00266    Checks the consistency of the central directory by comparing central
00267    directory entries with local headers and checking for plausible
00268    file and header offsets. Returns -1 if not plausible, else the
00269    difference between the lowest and the highest fileposition reached */
00270 
00271 static int
00272 _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error)
00273 {
00274     int i;
00275     unsigned int min, max, j;
00276     struct zip_dirent temp;
00277 
00278     if (cd->nentry) {
00279        max = cd->entry[0].offset;
00280        min = cd->entry[0].offset;
00281     }
00282     else
00283        min = max = 0;
00284 
00285     for (i=0; i<cd->nentry; i++) {
00286        if (cd->entry[i].offset < min)
00287            min = cd->entry[i].offset;
00288        if (min > cd->offset) {
00289            _zip_error_set(error, ZIP_ER_NOZIP, 0);
00290            return -1;
00291        }
00292        
00293        j = cd->entry[i].offset + cd->entry[i].comp_size
00294            + cd->entry[i].filename_len + LENTRYSIZE;
00295        if (j > max)
00296            max = j;
00297        if (max > cd->offset) {
00298            _zip_error_set(error, ZIP_ER_NOZIP, 0);
00299            return -1;
00300        }
00301        
00302        if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) {
00303            _zip_error_set(error, ZIP_ER_SEEK, 0);
00304            return -1;
00305        }
00306        
00307        if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1)
00308            return -1;
00309        
00310        if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) {
00311            _zip_error_set(error, ZIP_ER_INCONS, 0);
00312            _zip_dirent_finalize(&temp);
00313            return -1;
00314        }
00315        _zip_dirent_finalize(&temp);
00316     }
00317 
00318     return max - min;
00319 }
00320 
00321 
00322 
00323 /* _zip_check_torrentzip:
00324    check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
00325 
00326 static void
00327 _zip_check_torrentzip(struct zip *za)
00328 {
00329     uLong crc_got, crc_should;
00330     char buf[8+1];
00331     char *end;
00332 
00333     if (za->zp == NULL || za->cdir == NULL)
00334        return;
00335 
00336     if (za->cdir->comment_len != TORRENT_SIG_LEN+8
00337        || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0)
00338        return;
00339     
00340     memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8);
00341     buf[8] = '\0';
00342     errno = 0;
00343     crc_should = strtoul(buf, &end, 16);
00344     if ((crc_should == UINT_MAX && errno != 0) || (end && *end))
00345        return;
00346     
00347     if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size,
00348                         &crc_got, NULL) < 0)
00349            return;
00350 
00351     if (crc_got == crc_should)
00352        za->flags |= ZIP_AFL_TORRENT;
00353 }
00354 
00355 
00356 
00357 
00358 /* _zip_headercomp:
00359    compares two headers h1 and h2; if they are local headers, set
00360    local1p or local2p respectively to 1, else 0. Return 0 if they
00361    are identical, -1 if not. */
00362 
00363 static int
00364 _zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2,
00365           int local2p)
00366 {
00367     if ((h1->version_needed != h2->version_needed)
00368 #if 0
00369        /* some zip-files have different values in local
00370           and global headers for the bitflags */
00371        || (h1->bitflags != h2->bitflags)
00372 #endif
00373        || (h1->comp_method != h2->comp_method)
00374        || (h1->last_mod != h2->last_mod)
00375        || (h1->filename_len != h2->filename_len)
00376        || !h1->filename || !h2->filename
00377        || strcmp(h1->filename, h2->filename))
00378        return -1;
00379 
00380     /* check that CRC and sizes are zero if data descriptor is used */
00381     if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p
00382        && (h1->crc != 0
00383            || h1->comp_size != 0
00384            || h1->uncomp_size != 0))
00385        return -1;
00386     if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p
00387        && (h2->crc != 0
00388            || h2->comp_size != 0
00389            || h2->uncomp_size != 0))
00390        return -1;
00391     
00392     /* check that CRC and sizes are equal if no data descriptor is used */
00393     if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0)
00394        && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) {
00395        if ((h1->crc != h2->crc)
00396            || (h1->comp_size != h2->comp_size)
00397            || (h1->uncomp_size != h2->uncomp_size))
00398            return -1;
00399     }
00400     
00401     if ((local1p == local2p)
00402        && ((h1->extrafield_len != h2->extrafield_len)
00403            || (h1->extrafield_len && h2->extrafield
00404               && memcmp(h1->extrafield, h2->extrafield,
00405                        h1->extrafield_len))))
00406        return -1;
00407 
00408     /* if either is local, nothing more to check */
00409     if (local1p || local2p)
00410        return 0;
00411 
00412     if ((h1->version_madeby != h2->version_madeby)
00413        || (h1->disk_number != h2->disk_number)
00414        || (h1->int_attrib != h2->int_attrib)
00415        || (h1->ext_attrib != h2->ext_attrib)
00416        || (h1->offset != h2->offset)
00417        || (h1->comment_len != h2->comment_len)
00418        || (h1->comment_len && h2->comment
00419            && memcmp(h1->comment, h2->comment, h1->comment_len)))
00420        return -1;
00421 
00422     return 0;
00423 }
00424 
00425 
00426 
00427 static struct zip *
00428 _zip_allocate_new(const char *fn, int *zep)
00429 {
00430     struct zip *za;
00431     struct zip_error error;
00432 
00433     if ((za=_zip_new(&error)) == NULL) {
00434        set_error(zep, &error, 0);
00435        return NULL;
00436     }
00437        
00438     za->zn = strdup(fn);
00439     if (!za->zn) {
00440        _zip_free(za);
00441        set_error(zep, NULL, ZIP_ER_MEMORY);
00442        return NULL;
00443     }
00444     return za;
00445 }
00446 
00447 
00448 
00449 static int
00450 _zip_file_exists(const char *fn, int flags, int *zep)
00451 {
00452     struct stat st;
00453 
00454     if (fn == NULL) {
00455        set_error(zep, NULL, ZIP_ER_INVAL);
00456        return -1;
00457     }
00458     
00459     if (stat(fn, &st) != 0) {
00460        if (flags & ZIP_CREATE || flags & ZIP_OVERWRITE)
00461            return 0;
00462        else {
00463            set_error(zep, NULL, ZIP_ER_OPEN);
00464            return -1;
00465        }
00466     }
00467     else if ((flags & ZIP_EXCL)) {
00468        set_error(zep, NULL, ZIP_ER_EXISTS);
00469        return -1;
00470     }
00471     /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL,
00472        just like open() */
00473 
00474     return 1;
00475 }
00476 
00477 
00478 
00479 static struct zip_cdir *
00480 _zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len)
00481 {
00482     struct zip_cdir *cdir, *cdirnew;
00483     unsigned char *buf, *match;
00484     int a, best, buflen, i;
00485     struct zip_error zerr;
00486 
00487     i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END);
00488     if (i == -1 && errno != EFBIG) {
00489        /* seek before start of file on my machine */
00490        set_error(zep, NULL, ZIP_ER_SEEK);
00491        return NULL;
00492     }
00493 
00494     /* 64k is too much for stack */
00495     if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) {
00496        set_error(zep, NULL, ZIP_ER_MEMORY);
00497        return NULL;
00498     }
00499 
00500     clearerr(fp);
00501     buflen = fread(buf, 1, CDBUFSIZE, fp);
00502 
00503     if (ferror(fp)) {
00504        set_error(zep, NULL, ZIP_ER_READ);
00505        free(buf);
00506        return NULL;
00507     }
00508     
00509     best = -1;
00510     cdir = NULL;
00511     match = buf;
00512     _zip_error_set(&zerr, ZIP_ER_NOZIP, 0);
00513 
00514     while ((match=_zip_memmem(match, buflen-(match-buf)-18,
00515                            (const unsigned char *)EOCD_MAGIC, 4))!=NULL) {
00516        /* found match -- check, if good */
00517        /* to avoid finding the same match all over again */
00518        match++;
00519        if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags,
00520                                &zerr)) == NULL)
00521            continue;
00522 
00523        if (cdir) {
00524            if (best <= 0)
00525               best = _zip_checkcons(fp, cdir, &zerr);
00526            a = _zip_checkcons(fp, cdirnew, &zerr);
00527            if (best < a) {
00528               _zip_cdir_free(cdir);
00529               cdir = cdirnew;
00530               best = a;
00531            }
00532            else
00533               _zip_cdir_free(cdirnew);
00534        }
00535        else {
00536            cdir = cdirnew;
00537            if (flags & ZIP_CHECKCONS)
00538               best = _zip_checkcons(fp, cdir, &zerr);
00539            else
00540               best = 0;
00541        }
00542        cdirnew = NULL;
00543     }
00544 
00545     free(buf);
00546     
00547     if (best < 0) {
00548        set_error(zep, &zerr, 0);
00549        _zip_cdir_free(cdir);
00550        return NULL;
00551     }
00552 
00553     return cdir;
00554 }
00555 
00556 
00557 
00558 static unsigned char *
00559 _zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, 
00560        int littlelen)
00561 {
00562     const unsigned char *p;
00563     
00564     if ((biglen < littlelen) || (littlelen == 0))
00565        return NULL;
00566     p = big-1;
00567     while ((p=(const unsigned char *)
00568                memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1)))
00569           != NULL) {
00570        if (memcmp(p+1, little+1, littlelen-1)==0)
00571            return (unsigned char *)p;
00572     }
00573 
00574     return NULL;
00575 }