diff options
Diffstat (limited to 'ext/zip/lib/zip_open.c')
-rw-r--r-- | ext/zip/lib/zip_open.c | 826 |
1 files changed, 446 insertions, 380 deletions
diff --git a/ext/zip/lib/zip_open.c b/ext/zip/lib/zip_open.c index 1acaefa30e..fb9c566cb1 100644 --- a/ext/zip/lib/zip_open.c +++ b/ext/zip/lib/zip_open.c @@ -1,6 +1,6 @@ /* zip_open.c -- open zip archive by name - Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2015 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at <libzip@nih.at> @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -32,7 +32,6 @@ */ - #include <sys/stat.h> #include <errno.h> #include <limits.h> @@ -42,117 +41,181 @@ #include "zipint.h" -static void set_error(int *, const struct zip_error *, int); -static struct zip *_zip_allocate_new(const char *, unsigned int, int *); -static zip_int64_t _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); -static void _zip_check_torrentzip(struct zip *, const struct zip_cdir *); -static struct zip_cdir *_zip_find_central_dir(FILE *, unsigned int, int *, off_t); -static int _zip_file_exists(const char *, unsigned int, int *); -static int _zip_headercomp(const struct zip_dirent *, const struct zip_dirent *); -static unsigned char *_zip_memmem(const unsigned char *, size_t, - const unsigned char *, size_t); -static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, const unsigned char *, - size_t, unsigned int, struct zip_error *); -static struct zip_cdir *_zip_read_eocd(const unsigned char *, const unsigned char *, off_t, - size_t, unsigned int, struct zip_error *); -static struct zip_cdir *_zip_read_eocd64(FILE *, const unsigned char *, const unsigned char *, - off_t, size_t, unsigned int, struct zip_error *); - - - -ZIP_EXTERN struct zip * +typedef enum { + EXISTS_ERROR = -1, + EXISTS_NOT = 0, + EXISTS_EMPTY, + EXISTS_NONEMPTY, +} exists_t; +static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error); +static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error); +static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len); +static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error); +static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *); +static unsigned char *_zip_memmem(const unsigned char *, size_t, const unsigned char *, size_t); +static zip_cdir_t *_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error); +static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); +static zip_cdir_t *_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); + + +ZIP_EXTERN zip_t * zip_open(const char *fn, int _flags, int *zep) { - FILE *fp; - unsigned int flags; + zip_t *za; + zip_source_t *src; + struct zip_error error; + + zip_error_init(&error); + if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) { + _zip_set_open_error(zep, &error, 0); + zip_error_fini(&error); + return NULL; + } + + if ((za = zip_open_from_source(src, _flags, &error)) == NULL) { + zip_source_free(src); + _zip_set_open_error(zep, &error, 0); + zip_error_fini(&error); + return NULL; + } - if (_flags < 0) { - if (zep) - *zep = ZIP_ER_INVAL; + zip_error_fini(&error); + return za; +} + + +ZIP_EXTERN zip_t * +zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) +{ + static zip_int64_t needed_support_read = -1; + static zip_int64_t needed_support_write = -1; + + unsigned int flags; + zip_int64_t supported; + exists_t exists; + + if (_flags < 0 || src == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } flags = (unsigned int)_flags; + + supported = zip_source_supports(src); + if (needed_support_read == -1) { + needed_support_read = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_STAT, -1); + needed_support_write = zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, -1); + } + if ((supported & needed_support_read) != needed_support_read) { + zip_error_set(error, ZIP_ER_OPNOTSUPP, 0); + return NULL; + } + if ((supported & needed_support_write) != needed_support_write) { + flags |= ZIP_RDONLY; + } - switch (_zip_file_exists(fn, flags, zep)) { - case -1: + if ((flags & (ZIP_RDONLY|ZIP_TRUNCATE)) == (ZIP_RDONLY|ZIP_TRUNCATE)) { + zip_error_set(error, ZIP_ER_RDONLY, 0); return NULL; - case 0: - return _zip_allocate_new(fn, flags, zep); - default: - if (flags & ZIP_TRUNCATE) { - FILE *f; - - if ((f = fopen(fn, "rb")) == NULL) { - set_error(zep, NULL, ZIP_ER_OPEN); - return NULL; - } - fclose(f); - return _zip_allocate_new(fn, flags, zep); - } - break; } - if ((fp=fopen(fn, "rb")) == NULL) { - set_error(zep, NULL, ZIP_ER_OPEN); + exists = _zip_file_exists(src, error); + switch (exists) { + case EXISTS_ERROR: return NULL; - } - return _zip_open(fn, fp, flags, zep); -} + case EXISTS_NOT: + if ((flags & ZIP_CREATE) == 0) { + zip_error_set(error, ZIP_ER_NOENT, 0); + return NULL; + } + return _zip_allocate_new(src, flags, error); + + default: { + zip_t *za; + if (flags & ZIP_EXCL) { + zip_error_set(error, ZIP_ER_EXISTS, 0); + return NULL; + } + if (zip_source_open(src) < 0) { + _zip_error_set_from_source(error, src); + return NULL; + } + + if (flags & ZIP_TRUNCATE) { + za = _zip_allocate_new(src, flags, error); + } + else { + /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */ + za = _zip_open(src, flags, error); + } + if (za == NULL) { + zip_source_close(src); + return NULL; + } + return za; + } + } +} ZIP_EXTERN int -zip_archive_set_tempdir(struct zip *za, const char *tempdir) +zip_archive_set_tempdir(zip_t *za, const char *tempdir) { char *new_tempdir; - + if (tempdir) { if ((new_tempdir = strdup(tempdir)) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, errno); + zip_error_set(&za->error, ZIP_ER_MEMORY, errno); return -1; } } else new_tempdir = NULL; - + free(za->tempdir); za->tempdir = new_tempdir; - + return 0; } - -struct zip * -_zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep) +zip_t * +_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { - struct zip *za; - struct zip_cdir *cdir; - off_t len; - - if (fseeko(fp, 0, SEEK_END) < 0) { - *zep = ZIP_ER_SEEK; + zip_t *za; + zip_cdir_t *cdir; + struct zip_stat st; + zip_uint64_t len; + + zip_stat_init(&st); + if (zip_source_stat(src, &st) < 0) { + _zip_error_set_from_source(error, src); + return NULL; + } + if ((st.valid & ZIP_STAT_SIZE) == 0) { + zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP); return NULL; } - len = ftello(fp); + len = st.size; /* treat empty files as empty archives */ if (len == 0) { - if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) - fclose(fp); - else - za->zp = fp; + if ((za=_zip_allocate_new(src, flags, error)) == NULL) { + zip_source_free(src); + return NULL; + } + return za; } - cdir = _zip_find_central_dir(fp, flags, zep, len); - if (cdir == NULL) { - fclose(fp); - return NULL; + if ((za=_zip_allocate_new(src, flags, error)) == NULL) { + return NULL; } - - if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) { - _zip_cdir_free(cdir); - fclose(fp); + + if ((cdir = _zip_find_central_dir(za, len)) == NULL) { + _zip_error_copy(error, &za->error); + /* keep src so discard does not get rid of it */ + zip_source_keep(src); + zip_discard(za); return NULL; } @@ -160,11 +223,7 @@ _zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep) za->nentry = cdir->nentry; za->nentry_alloc = cdir->nentry_alloc; za->comment_orig = cdir->comment; - - za->zp = fp; - - _zip_check_torrentzip(za, cdir); - + za->ch_flags = za->flags; free(cdir); @@ -173,16 +232,14 @@ _zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep) } - -static void -set_error(int *zep, const struct zip_error *err, int ze) +void +_zip_set_open_error(int *zep, const zip_error_t *err, int ze) { - int se; - if (err) { - _zip_error_get(err, &ze, &se); - if (zip_error_get_sys_type(ze) == ZIP_ET_SYS) - errno = se; + ze = zip_error_code_zip(err); + if (zip_error_system_type(err) == ZIP_ET_SYS) { + errno = zip_error_code_system(err); + } } if (zep) @@ -190,90 +247,108 @@ set_error(int *zep, const struct zip_error *err, int ze) } - /* _zip_readcdir: tries to find a valid end-of-central-directory at the beginning of buf, and then the corresponding central directory entries. - Returns a struct zip_cdir which contains the central directory + Returns a struct zip_cdir which contains the central directory entries, or NULL if unsuccessful. */ -static struct zip_cdir * -_zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, const unsigned char *eocd, size_t buflen, - unsigned int flags, struct zip_error *error) +static zip_cdir_t * +_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { - struct zip_cdir *cd; - const unsigned char *cdp; - const unsigned char **bufp; - zip_int64_t tail_len, comment_len; + zip_cdir_t *cd; + zip_uint16_t comment_len; zip_uint64_t i, left; + zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); + zip_buffer_t *cd_buffer; - tail_len = buf + buflen - eocd - EOCDLEN; - if (tail_len < 0) { + if (_zip_buffer_left(buffer) < EOCDLEN) { /* not enough bytes left for comment */ - _zip_error_set(error, ZIP_ER_NOZIP, 0); + zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } - + /* check for end-of-central-dir magic */ - if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { + zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } - if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { - _zip_error_set(error, ZIP_ER_MULTIDISK, 0); + if (_zip_buffer_get_32(buffer) != 0) { + zip_error_set(error, ZIP_ER_MULTIDISK, 0); return NULL; } - if (eocd-EOCD64LOCLEN >= buf && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) - cd = _zip_read_eocd64(fp, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, flags, error); - else - cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, flags, error); + if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { + _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); + cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); + } + else { + _zip_buffer_set_offset(buffer, eocd_offset); + cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); + } if (cd == NULL) return NULL; - cdp = eocd + 20; - comment_len = _zip_read2(&cdp); + _zip_buffer_set_offset(buffer, eocd_offset + 20); + comment_len = _zip_buffer_get_16(buffer); - if ((zip_uint64_t)cd->offset+(zip_uint64_t)cd->size > (zip_uint64_t)buf_offset + (zip_uint64_t)(eocd-buf)) { + if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ - _zip_error_set(error, ZIP_ER_INCONS, 0); + zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } - if (tail_len < comment_len || ((flags & ZIP_CHECKCONS) && tail_len != comment_len)) { - _zip_error_set(error, ZIP_ER_INCONS, 0); - _zip_cdir_free(cd); - return NULL; - } + if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { + zip_uint64_t tail_len; + + _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); + tail_len = _zip_buffer_left(buffer); + + if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_cdir_free(cd); + return NULL; + } - if (comment_len) { - if ((cd->comment=_zip_string_new(eocd+EOCDLEN, (zip_uint16_t)comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { - _zip_cdir_free(cd); - return NULL; - } + if (comment_len) { + if ((cd->comment=_zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { + _zip_cdir_free(cd); + return NULL; + } + } } if (cd->offset >= buf_offset) { + zip_uint8_t *data; /* if buffer already read in, use it */ - cdp = buf + (cd->offset - buf_offset); - bufp = &cdp; + _zip_buffer_set_offset(buffer, cd->offset - buf_offset); + + if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_cdir_free(cd); + return NULL; + } + if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + _zip_cdir_free(cd); + return NULL; + } } else { - /* go to start of cdir and read it entry by entry */ - bufp = NULL; - clearerr(fp); - fseeko(fp, cd->offset, SEEK_SET); - /* possible consistency check: cd->offset = - len-(cd->size+cd->comment_len+EOCDLEN) ? */ - if (ferror(fp) || (ftello(fp) != cd->offset)) { - /* seek error or offset of cdir wrong */ - if (ferror(fp)) - _zip_error_set(error, ZIP_ER_SEEK, errno); - else - _zip_error_set(error, ZIP_ER_NOZIP, 0); + cd_buffer = NULL; + + if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, za->src); + _zip_cdir_free(cd); + return NULL; + } + + /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ + if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { + zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } @@ -282,24 +357,54 @@ _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, const unsigned cha left = (zip_uint64_t)cd->size; i=0; while (i<cd->nentry && left > 0) { - if ((cd->entry[i].orig=_zip_dirent_new()) == NULL - || (_zip_dirent_read(cd->entry[i].orig, fp, bufp, &left, 0, error)) < 0) { + zip_int64_t entry_size; + if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { _zip_cdir_free(cd); + _zip_buffer_free(cd_buffer); return NULL; } i++; + left -= (zip_uint64_t)entry_size; } - if (i != cd->nentry || ((flags & ZIP_CHECKCONS) && left != 0)) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + + if (i != cd->nentry) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } + + if (za->open_flags & ZIP_CHECKCONS) { + bool ok; + + if (cd_buffer) { + ok = _zip_buffer_eof(cd_buffer); + } + else { + zip_int64_t offset = zip_source_tell(za->src); + + if (offset < 0) { + _zip_error_set_from_source(error, za->src); + _zip_buffer_free(cd_buffer); + _zip_cdir_free(cd); + return NULL; + } + ok = ((zip_uint64_t)offset == cd->offset + cd->size); + } + + if (!ok) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(cd_buffer); + _zip_cdir_free(cd); + return NULL; + } + } + _zip_buffer_free(cd_buffer); return cd; } - /* _zip_checkcons: Checks the consistency of the central directory by comparing central directory entries with local headers and checking for plausible @@ -307,12 +412,13 @@ _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, const unsigned cha difference between the lowest and the highest fileposition reached */ static zip_int64_t -_zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) +_zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) { zip_uint64_t i; zip_uint64_t min, max, j; struct zip_dirent temp; + _zip_dirent_init(&temp); if (cd->nentry) { max = cd->entry[0].orig->offset; min = cd->entry[0].orig->offset; @@ -324,37 +430,39 @@ _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) if (cd->entry[i].orig->offset < min) min = cd->entry[i].orig->offset; if (min > (zip_uint64_t)cd->offset) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } - + j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE; if (j > max) max = j; if (max > (zip_uint64_t)cd->offset) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } - - if (fseeko(fp, (off_t)cd->entry[i].orig->offset, SEEK_SET) != 0) { - _zip_error_set(error, ZIP_ER_SEEK, errno); - return -1; + + if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, za->src); + return -1; } - - if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1) + + if (_zip_dirent_read(&temp, za->src, NULL, true, error) == -1) { + _zip_dirent_finalize(&temp); return -1; - + } + if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + zip_error_set(error, ZIP_ER_INCONS, 0); _zip_dirent_finalize(&temp); return -1; } - + cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields); cd->entry[i].orig->local_extra_fields_read = 1; temp.extra_fields = NULL; - + _zip_dirent_finalize(&temp); } @@ -362,47 +470,12 @@ _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) } - -/* _zip_check_torrentzip: - check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ - -static void -_zip_check_torrentzip(struct zip *za, const struct zip_cdir *cdir) -{ - uLong crc_got, crc_should; - char buf[8+1]; - char *end; - - if (za->zp == NULL || cdir == NULL) - return; - - if (_zip_string_length(cdir->comment) != TORRENT_SIG_LEN+8 - || strncmp((const char *)cdir->comment->raw, TORRENT_SIG, TORRENT_SIG_LEN) != 0) - return; - - memcpy(buf, cdir->comment->raw+TORRENT_SIG_LEN, 8); - buf[8] = '\0'; - errno = 0; - crc_should = strtoul(buf, &end, 16); - if ((crc_should == UINT_MAX && errno != 0) || (end && *end)) - return; - - if (_zip_filerange_crc(za->zp, cdir->offset, cdir->size, &crc_got, NULL) < 0) - return; - - if (crc_got == crc_should) - za->flags |= ZIP_AFL_TORRENT; -} - - - - /* _zip_headercomp: compares a central directory entry and a local file header Return 0 if they are consistent, -1 if not. */ static int -_zip_headercomp(const struct zip_dirent *central, const struct zip_dirent *local) +_zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) { if ((central->version_needed != local->version_needed) #if 0 @@ -415,7 +488,6 @@ _zip_headercomp(const struct zip_dirent *central, const struct zip_dirent *local || !_zip_string_equal(central->filename, local->filename)) return -1; - if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) { /* InfoZip stores valid values in local header even when data descriptor is used. @@ -429,157 +501,141 @@ _zip_headercomp(const struct zip_dirent *central, const struct zip_dirent *local } - -static struct zip * -_zip_allocate_new(const char *fn, unsigned int flags, int *zep) +static zip_t * +_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) { - struct zip *za; - struct zip_error error; + zip_t *za; - if ((za=_zip_new(&error)) == NULL) { - set_error(zep, &error, 0); + if ((za = _zip_new(error)) == NULL) { return NULL; } - if (fn == NULL) - za->zn = NULL; - else { - za->zn = strdup(fn); - if (!za->zn) { - zip_discard(za); - set_error(zep, NULL, ZIP_ER_MEMORY); - return NULL; - } - } + za->src = src; za->open_flags = flags; + if (flags & ZIP_RDONLY) { + za->flags |= ZIP_AFL_RDONLY; + za->ch_flags |= ZIP_AFL_RDONLY; + } return za; } - -static int -_zip_file_exists(const char *fn, unsigned int flags, int *zep) +/* + * tests for file existence + */ +static exists_t +_zip_file_exists(zip_source_t *src, zip_error_t *error) { - struct stat st; - - if (fn == NULL) { - set_error(zep, NULL, ZIP_ER_INVAL); - return -1; - } + struct zip_stat st; - if (stat(fn, &st) != 0) { - if (flags & ZIP_CREATE) - return 0; - else { - set_error(zep, NULL, ZIP_ER_OPEN); - return -1; + zip_stat_init(&st); + if (zip_source_stat(src, &st) != 0) { + zip_error_t *src_error = zip_source_error(src); + if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) { + return EXISTS_NOT; } + _zip_error_copy(error, src_error); + return EXISTS_ERROR; } - else if ((flags & ZIP_EXCL)) { - set_error(zep, NULL, ZIP_ER_EXISTS); - return -1; - } - /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, - just like open() */ - return 1; + return (st.valid & ZIP_STAT_SIZE) && st.size == 0 ? EXISTS_EMPTY : EXISTS_NONEMPTY; } - -static struct zip_cdir * -_zip_find_central_dir(FILE *fp, unsigned int flags, int *zep, off_t len) +static zip_cdir_t * +_zip_find_central_dir(zip_t *za, zip_uint64_t len) { - struct zip_cdir *cdir, *cdirnew; - unsigned char *buf, *match; - off_t buf_offset; - size_t buflen; - zip_int64_t a, i; + zip_cdir_t *cdir, *cdirnew; + zip_uint8_t *match; + zip_int64_t buf_offset; + zip_uint64_t buflen; + zip_int64_t a; zip_int64_t best; - struct zip_error zerr; + zip_error_t error; + zip_buffer_t *buffer; - if (len < (off_t)EOCDLEN) { - set_error(zep, NULL, ZIP_ER_NOZIP); + if (len < EOCDLEN) { + zip_error_set(&za->error, ZIP_ER_NOZIP, 0); return NULL; } - i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); - if (i == -1 && errno != EFBIG) { - /* seek before start of file on my machine */ - set_error(zep, NULL, ZIP_ER_SEEK); - return NULL; + buflen = (len < CDBUFSIZE ? len : CDBUFSIZE); + if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) { + zip_error_t *src_error = zip_source_error(za->src); + if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) { + /* seek before start of file on my machine */ + _zip_error_copy(&za->error, src_error); + return NULL; + } } - buf_offset = ftello(fp); - - /* 64k is too much for stack */ - if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { - set_error(zep, NULL, ZIP_ER_MEMORY); - return NULL; + if ((buf_offset = zip_source_tell(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return NULL; } - - clearerr(fp); - buflen = fread(buf, 1, CDBUFSIZE, fp); - - if (ferror(fp)) { - set_error(zep, NULL, ZIP_ER_READ); - free(buf); - return NULL; + + if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) { + return NULL; } best = -1; cdir = NULL; - match = buf+ (buflen < CDBUFSIZE ? 0 : EOCD64LOCLEN); - _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); - - while ((match=_zip_memmem(match, buflen-(size_t)(match-buf)-(EOCDLEN-4), - (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { - /* found match -- check, if good */ - /* to avoid finding the same match all over again */ - match++; - if ((cdirnew=_zip_readcdir(fp, buf_offset, buf, match-1, buflen, flags, - &zerr)) == NULL) - continue; - - if (cdir) { - if (best <= 0) - best = _zip_checkcons(fp, cdir, &zerr); - a = _zip_checkcons(fp, cdirnew, &zerr); - if (best < a) { - _zip_cdir_free(cdir); - cdir = cdirnew; - best = a; - } - else - _zip_cdir_free(cdirnew); - } - else { - cdir = cdirnew; - if (flags & ZIP_CHECKCONS) - best = _zip_checkcons(fp, cdir, &zerr); - else - best = 0; - } - cdirnew = NULL; + if (buflen >= CDBUFSIZE) { + /* EOCD64 locator is before EOCD, so leave place for it */ + _zip_buffer_set_offset(buffer, EOCD64LOCLEN); + } + zip_error_set(&error, ZIP_ER_NOZIP, 0); + + match = _zip_buffer_get(buffer, 0); + while ((match=_zip_memmem(match, _zip_buffer_left(buffer)-(EOCDLEN-4), (const unsigned char *)EOCD_MAGIC, 4)) != NULL) { + _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); + if ((cdirnew = _zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &error)) != NULL) { + if (cdir) { + if (best <= 0) { + best = _zip_checkcons(za, cdir, &error); + } + + a = _zip_checkcons(za, cdirnew, &error); + if (best < a) { + _zip_cdir_free(cdir); + cdir = cdirnew; + best = a; + } + else { + _zip_cdir_free(cdirnew); + } + } + else { + cdir = cdirnew; + if (za->open_flags & ZIP_CHECKCONS) + best = _zip_checkcons(za, cdir, &error); + else { + best = 0; + } + } + cdirnew = NULL; + } + + match++; + _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); } - free(buf); - + _zip_buffer_free(buffer); + if (best < 0) { - set_error(zep, &zerr, 0); - _zip_cdir_free(cdir); - return NULL; + _zip_error_copy(&za->error, &error); + _zip_cdir_free(cdir); + return NULL; } return cdir; } - static unsigned char * _zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little, size_t littlelen) { const unsigned char *p; - + if ((biglen < littlelen) || (littlelen == 0)) return NULL; p = big-1; @@ -593,150 +649,160 @@ _zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little } - -static struct zip_cdir * -_zip_read_eocd(const unsigned char *eocd, const unsigned char *buf, off_t buf_offset, size_t buflen, - unsigned int flags, struct zip_error *error) +static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { - struct zip_cdir *cd; - const unsigned char *cdp; - zip_uint64_t i, nentry, size, offset; + zip_cdir_t *cd; + zip_uint64_t i, nentry, size, offset, eocd_offset; - if (eocd+EOCDLEN > buf+buflen) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + if (_zip_buffer_left(buffer) < EOCDLEN) { + zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } + + eocd_offset = _zip_buffer_offset(buffer); - cdp = eocd + 8; + _zip_buffer_get(buffer, 8); /* magic and number of disks already verified */ /* number of cdir-entries on this disk */ - i = _zip_read2(&cdp); + i = _zip_buffer_get_16(buffer); /* number of cdir-entries */ - nentry = _zip_read2(&cdp); + nentry = _zip_buffer_get_16(buffer); if (nentry != i) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } - size = _zip_read4(&cdp); - offset = _zip_read4(&cdp); + size = _zip_buffer_get_32(buffer); + offset = _zip_buffer_get_32(buffer); - if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) { - _zip_error_set(error, ZIP_ER_SEEK, EFBIG); + if (offset+size < offset) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } - - if (offset+size > (zip_uint64_t)(buf_offset + (eocd-buf))) { + + if (offset+size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ - _zip_error_set(error, ZIP_ER_INCONS, 0); + zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } - if ((flags & ZIP_CHECKCONS) && offset+size != (zip_uint64_t)(buf_offset + (eocd-buf))) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; - cd->size = (off_t)size; - cd->offset = (off_t)offset; - + cd->size = size; + cd->offset = offset; + return cd; } - -static struct zip_cdir * -_zip_read_eocd64(FILE *f, const zip_uint8_t *eocd64loc, const zip_uint8_t *buf, - off_t buf_offset, size_t buflen, unsigned int flags, struct zip_error *error) +static zip_cdir_t * +_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { - struct zip_cdir *cd; + zip_cdir_t *cd; zip_uint64_t offset; - const zip_uint8_t *cdp; zip_uint8_t eocd[EOCD64LEN]; zip_uint64_t eocd_offset; - zip_uint64_t size, nentry, i; - - cdp = eocd64loc+8; - eocd_offset = _zip_read8(&cdp); - - if (eocd_offset > ZIP_OFF_MAX || eocd_offset + EOCD64LEN > ZIP_OFF_MAX) { - _zip_error_set(error, ZIP_ER_SEEK, EFBIG); + zip_uint64_t size, nentry, i, eocdloc_offset; + bool free_buffer; + + eocdloc_offset = _zip_buffer_offset(buffer); + + _zip_buffer_get(buffer, 8); /* magic and single disk already verified */ + eocd_offset = _zip_buffer_get_64(buffer); + + if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } - if (eocd64loc < buf || (off_t)eocd_offset+EOCD64LEN > (buf_offset+(eocd64loc-buf))) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } - if ((off_t)eocd_offset >= buf_offset && (off_t)eocd_offset+EOCD64LEN <= buf_offset+(ssize_t)buflen) - cdp = buf+((off_t)eocd_offset-buf_offset); + if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) { + _zip_buffer_set_offset(buffer, eocd_offset - buf_offset); + free_buffer = false; + } else { - if (fseeko(f, (off_t)eocd_offset, SEEK_SET) != 0) { - _zip_error_set(error, ZIP_ER_SEEK, errno); - return NULL; - } - - clearerr(f); - if (fread(eocd, 1, EOCD64LEN, f) < EOCD64LEN) { - _zip_error_set(error, ZIP_ER_READ, errno); + if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, src); return NULL; } - - if (ferror(f)) { - _zip_error_set(error, ZIP_ER_READ, errno); - return NULL; - } - - cdp = eocd; + if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { + return NULL; + } + free_buffer = true; } - if (memcmp(cdp, EOCD64_MAGIC, 4) != 0) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } return NULL; } - cdp += 4; - - size = _zip_read8(&cdp); + + size = _zip_buffer_get_64(buffer); - if ((flags & ZIP_CHECKCONS) && size+eocd_offset+12 != (zip_uint64_t)(buf_offset+(eocd64loc-buf))) { - _zip_error_set(error, ZIP_ER_INCONS, 0); - return NULL; + if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; } - cdp += 4; /* skip version made by/needed */ - cdp += 8; /* skip num disks */ - - nentry = _zip_read8(&cdp); - i = _zip_read8(&cdp); + _zip_buffer_get(buffer, 12); /* skip version made by/needed and num disks */ + + nentry = _zip_buffer_get_64(buffer); + i = _zip_buffer_get_64(buffer); if (nentry != i) { - _zip_error_set(error, ZIP_ER_MULTIDISK, 0); + zip_error_set(error, ZIP_ER_MULTIDISK, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } return NULL; } - size = _zip_read8(&cdp); - offset = _zip_read8(&cdp); + size = _zip_buffer_get_64(buffer); + offset = _zip_buffer_get_64(buffer); + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } - if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) { - _zip_error_set(error, ZIP_ER_SEEK, EFBIG); + if (free_buffer) { + _zip_buffer_free(buffer); + } + + if (offset > ZIP_INT64_MAX || offset+size < offset) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if ((flags & ZIP_CHECKCONS) && offset+size != eocd_offset) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; - - cd->size = (off_t)size; - cd->offset = (off_t)offset; + + cd->size = size; + cd->offset = offset; return cd; } |