]> granicus.if.org Git - php/commitdiff
remove ext/zip dependency entirely, write better native zip support
authorGreg Beaver <cellog@php.net>
Mon, 28 Jan 2008 08:52:08 +0000 (08:52 +0000)
committerGreg Beaver <cellog@php.net>
Mon, 28 Jan 2008 08:52:08 +0000 (08:52 +0000)
re-organize, create util.c, move entry_info/archive_data/entry_data access methods to this file
refactor entry->fp, now this is abstracted with phar_get_efp() and phar_seek_efp(), fixes all weird dependency issues
permanently solve the "millions of file pointers" issue for read access.  All compressed files are read into a single
temporary stream, and their constraints are controlled by the entry->fp abstraction

Improvements in this zip implementation over ext/zip:
 * full read/write support for bzip2 compressed files
 * much more efficient access for accessing only a few files within large zip files, as crc/header validation is
   done just-in-time
 * full stream support for opendir/rename/rmdir/mkdir as well as all of the other stream funcs
 * full support for setting file perms via Phar::chmod(), stored as zip-standard extra field
 * no problem with large zips and many open file pointers

# TODO: add big-endian system support for tar/zip file format headers, otherwise the implementation is complete
# TODO: test on windows and fix any windows-specific issues
# TODO: verify zips created work with unzip/winzip/windows explorer and so on

19 files changed:
ext/phar/config.m4
ext/phar/config.w32
ext/phar/dirstream.c
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/pharzip.c [deleted file]
ext/phar/pharzip.h
ext/phar/stream.c
ext/phar/stream.h
ext/phar/tar.c
ext/phar/tests/033.phpt
ext/phar/tests/phar_setalias2.phpt
ext/phar/tests/zip/033.phpt
ext/phar/tests/zip/phar_commitwrite.phpt
ext/phar/tests/zip/phar_convert_phar.phpt
ext/phar/tests/zip/phar_copy.phpt
ext/phar/util.c [new file with mode: 0644]
ext/phar/zip.c

index 4aac00d4979a7c4359ec52cffbd7bb74630fd1b5..d3e4d5a54fdb39b42e9c5c609bd0809312993026 100644 (file)
@@ -5,31 +5,9 @@ PHP_ARG_ENABLE(phar, for phar archive support,
 [  --enable-phar           Enable phar support])
 
 if test "$PHP_PHAR" != "no"; then
-    AC_MSG_CHECKING([for ZIP includes])
-    if test -f $abs_srcdir/include/php/ext/zip/lib/zip.h; then
-      zip_inc_path=$abs_srcdir/ext
-      AC_DEFINE(HAVE_PHAR_ZIP,1,[ ])
-      AC_MSG_RESULT($zip_inc_path)
-      PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared,,-I$zip_inc_path $PHAR_DEFS)
-    elif test -f $abs_srcdir/ext/zip/lib/zip.h; then
-      zip_inc_path=$abs_srcdir/ext
-      AC_DEFINE(HAVE_PHAR_ZIP,1,[ ])
-      AC_MSG_RESULT($zip_inc_path)
-      PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared,,-I$zip_inc_path $PHAR_DEFS)
-    elif test -f $prefix/include/php/ext/zip/lib/zip.h; then
-      zip_inc_path=$prefix/include/php/ext
-      AC_DEFINE(HAVE_PHAR_ZIP,1,[ ])
-      AC_MSG_RESULT($zip_inc_path)
-      PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared,,-I$zip_inc_path $PHAR_DEFS)
-    else
-      zip_inc_path=/dev/null
-      AC_DEFINE(HAVE_PHAR_ZIP,0,[ ])
-      AC_MSG_RESULT([not found, disabling ZIP-based phar support])
-      PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared)
-    fi
+  PHP_NEW_EXTENSION(phar, util.c tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared)
   PHP_ADD_BUILD_DIR($ext_builddir/lib, 1)
   PHP_SUBST(PHAR_SHARED_LIBADD)
-  PHP_ADD_EXTENSION_DEP(phar, zip, true)
   PHP_ADD_EXTENSION_DEP(phar, zlib, true)
   PHP_ADD_EXTENSION_DEP(phar, bz2, true)
   PHP_ADD_EXTENSION_DEP(phar, spl, true)
index 7d6bf11d3479b5818fc39f34c84df41a2d96915e..7589a48c4e055cb8c26f579fc00ad88172d40413 100644 (file)
@@ -4,21 +4,10 @@
 ARG_ENABLE("phar", "enable phar support", "no");
 
 if (PHP_PHAR != "no") {
-       EXTENSION("phar", "dirstream.c func_interceptors.c phar.c phar_object.c phar_path_check.c stream.c tar.c zip.c");
+       EXTENSION("phar", "util.c dirstream.c func_interceptors.c phar.c phar_object.c phar_path_check.c stream.c tar.c zip.c");
        if (PHP_PHAR_SHARED) {
                ADD_FLAG("CFLAGS_PHAR", "/D COMPILE_DL_PHAR ");
        }
-       if (PHP_ZIP_SHARED) {
-               if (PHP_PHAR_SHARED) {
-                       ADD_FLAG("CFLAGS_PHAR", "/D COMPILE_DL_ZIP ");
-               } else {
-                       ERROR("Phar cannot be built statically with shared Zip extension");
-               }
-       }
-       if (PHP_ZIP != "no") {
-               ADD_EXTENSION_DEP('phar', 'zip', true);
-               AC_DEFINE('HAVE_PHAR_ZIP', 1);
-       }
        ADD_EXTENSION_DEP('phar', 'bz2', true);
        ADD_EXTENSION_DEP('phar', 'spl', true);
        ADD_EXTENSION_DEP('phar', 'zlib', true);
index 0d43af991c3b79f8da13208e10e6e6227140c7f9..f52c1b2ef4d31ab4c89ac23d539bedd813bd53d2 100644 (file)
@@ -461,13 +461,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in
        memset((void *) &entry, 0, sizeof(phar_entry_info));
 
        /* strip leading "/" */
-#if HAVE_PHAR_ZIP
        if (phar->is_zip) {
                entry.is_zip = 1;
-               /* prevent attempts to check the CRC */
-               entry.index = -1;
        }
-#endif
        entry.filename = estrdup(resource->path + 1);
        if (phar->is_tar) {
                entry.is_tar = 1;
index 3eeecf735bdd21424f280b617b376caa9ef6980c..1c454a1bf01faa3de7899f0d61ccf2c3a463c479 100644 (file)
@@ -184,38 +184,36 @@ PHP_INI_END()
  * When all uses of a phar have been concluded, this frees the manifest
  * and the phar slot
  */
-static void phar_destroy_phar_data(phar_archive_data *data TSRMLS_DC) /* {{{ */
+static void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
 {
-#if HAVE_PHAR_ZIP
-       if (data->zip) {
-               _zip_free(data->zip);
-               data->zip = 0;
+       if (phar->alias && phar->alias != phar->fname) {
+               efree(phar->alias);
+               phar->alias = NULL;
        }
-#endif
-       if (data->alias && data->alias != data->fname) {
-               efree(data->alias);
-               data->alias = NULL;
+       if (phar->fname) {
+               efree(phar->fname);
+               phar->fname = NULL;
        }
-       if (data->fname) {
-               efree(data->fname);
-               data->fname = NULL;
+       if (phar->signature) {
+               efree(phar->signature);
        }
-       if (data->signature) {
-               efree(data->signature);
+       if (phar->manifest.arBuckets) {
+               zend_hash_destroy(&phar->manifest);
+               phar->manifest.arBuckets = NULL;
        }
-       if (data->manifest.arBuckets) {
-               zend_hash_destroy(&data->manifest);
-               data->manifest.arBuckets = NULL;
+       if (phar->metadata) {
+               zval_ptr_dtor(&phar->metadata);
+               phar->metadata = 0;
        }
-       if (data->metadata) {
-               zval_ptr_dtor(&data->metadata);
-               data->metadata = 0;
+       if (phar->fp) {
+               php_stream_close(phar->fp);
+               phar->fp = 0;
        }
-       if (data->fp) {
-               php_stream_close(data->fp);
-               data->fp = 0;
+       if (phar->ufp) {
+               php_stream_close(phar->ufp);
+               phar->fp = 0;
        }
-       efree(data);
+       efree(phar);
 }
 /* }}}*/
 
@@ -290,9 +288,7 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */
                entry->cfp = 0;
        }
        if (entry->fp) {
-               if (entry->fp != entry->phar->fp) {
-                       php_stream_close(entry->fp);
-               }
+               php_stream_close(entry->fp);
                entry->fp = 0;
        }
        if (entry->metadata) {
@@ -304,12 +300,6 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */
                entry->metadata_str.c = 0;
        }
        efree(entry->filename);
-#if HAVE_PHAR_ZIP
-       if (entry->zip) {
-               zip_fclose(entry->zip);
-               entry->zip = 0;
-       }
-#endif
        if (entry->link) {
                efree(entry->link);
                entry->link = 0;
@@ -317,373 +307,6 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */
 }
 /* }}} */
 
-/**
- * Looks up a phar archive in the filename map, connecting it to the alias
- * (if any) or returns null
- */
-int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
-{
-       phar_archive_data *fd, **fd_ptr;
-       char *my_realpath, *save;
-       int save_len;
-
-       phar_request_initialize(TSRMLS_C);
-
-       if (error) {
-               *error = NULL;
-       }
-       *archive = NULL;
-       if (alias && alias_len) {
-               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) {
-                       if (fname && (fname_len != (*fd_ptr)->fname_len || strncmp(fname, (*fd_ptr)->fname, fname_len))) {
-                               if (error) {
-                                       spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname);
-                               }
-                               return FAILURE;
-                       }
-                       *archive = *fd_ptr;
-                       return SUCCESS;
-               }
-       }
-       my_realpath = NULL;
-       save = fname;
-       save_len = fname_len;
-       if (fname && fname_len) {
-               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
-                       *archive = *fd_ptr;
-                       fd = *fd_ptr;
-                       if (alias && alias_len) {
-                               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd,   sizeof(phar_archive_data*), NULL);
-                       }
-                       return SUCCESS;
-               }
-               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, (void**)&fd_ptr)) {
-                       *archive = *fd_ptr;
-                       return SUCCESS;
-               }
-
-               /* not found, try converting \ to / */
-               my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC);
-               if (my_realpath) {
-                       fname_len = strlen(my_realpath);
-                       fname = my_realpath;
-               } else {
-                       return FAILURE;
-               }
-#ifdef PHP_WIN32
-               phar_unixify_path_separators(fname, fname_len);
-#endif
-               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
-                       *archive = *fd_ptr;
-                       fd = *fd_ptr;
-                       if (alias && alias_len) {
-                               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd,   sizeof(phar_archive_data*), NULL);
-                       }
-                       efree(my_realpath);
-                       return SUCCESS;
-               }
-               efree(my_realpath);
-       }
-       return FAILURE;
-}
-/* }}} */
-
-/**
- * retrieve information on a file contained within a phar, or null if it ain't there
- */
-phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len, char **error TSRMLS_DC) /* {{{ */
-{
-       return phar_get_entry_info_dir(phar, path, path_len, 0, error TSRMLS_CC);
-}
-/* }}} */
-/**
- * retrieve information on a file or directory contained within a phar, or null if none found
- * allow_dir is 0 for none, 1 for both empty directories in the phar and temp directories, and 2 for only
- * valid pre-existing empty directory entries
- */
-phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, int path_len, char dir, char **error TSRMLS_DC) /* {{{ */
-{
-       const char *pcr_error;
-       phar_entry_info *entry;
-       char is_dir = (path[path_len - 1] == '/');
-
-       if (error) {
-               *error = NULL;
-       }
-
-       if (!path_len && !dir) {
-               if (error) {
-                       spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path);
-               }
-               return NULL;
-       }
-       if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
-               if (error) {
-                       spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
-               }
-               return NULL;
-       }
-
-       if (!&phar->manifest.arBuckets) {
-               return NULL;
-       }
-       if (is_dir) {
-               path_len--;
-       }
-       if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) {
-               if (entry->is_deleted) {
-                       /* entry is deleted, but has not been flushed to disk yet */
-                       return NULL;
-               }
-               if (entry->is_dir && !dir) {
-                       if (error) {
-                               spprintf(error, 4096, "phar error: path \"%s\" is a directory", path);
-                       }
-                       return NULL;
-               }
-               if (!entry->is_dir && is_dir) {
-                       /* user requested a directory, we must return one */
-                       if (error) {
-                               spprintf(error, 4096, "phar error: path \"%s\" exists and is a not a directory", path);
-                       }
-                       return NULL;
-               }
-               return entry;
-       }
-       if (dir == 1) {
-               /* try to find a directory */
-               HashTable *manifest;
-               char *key;
-               uint keylen;
-               ulong unused;
-
-               if (!path_len) {
-                       path = "/";
-               }
-               manifest = &phar->manifest;
-               zend_hash_internal_pointer_reset(manifest);
-               while (FAILURE != zend_hash_has_more_elements(manifest)) {
-                       if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
-                               break;
-                       }
-                       if (0 != memcmp(key, path, path_len)) {
-                               /* entry in directory not found */
-                               if (SUCCESS != zend_hash_move_forward(manifest)) {
-                                       break;
-                               }
-                               continue;
-                       } else {
-                               if (key[path_len] != '/') {
-                                       if (SUCCESS != zend_hash_move_forward(manifest)) {
-                                               break;
-                                       }
-                                       continue;
-                               }
-                               /* found a file in this path */
-                               entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
-                               /* this next line tells PharFileInfo->__destruct() to efree the filename */
-                               entry->is_temp_dir = entry->is_dir = 1;
-                               entry->filename = (char *) estrndup(path, path_len + 1);
-                               entry->filename_len = path_len;
-                               return entry;
-                       }
-               }
-       }
-       return NULL;
-}
-/* }}} */
-
-#if defined(PHP_VERSION_ID) && PHP_VERSION_ID < 50202
-typedef struct {
-       char        *data;
-       size_t      fpos;
-       size_t      fsize;
-       size_t      smax;
-       int         mode;
-       php_stream  **owner_ptr;
-} php_stream_memory_data;
-#endif
-
-/* this is only called for non-zip/tar-based phars */
-static int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
-{
-       if (error) {
-               *error = NULL;
-       }
-       /* open a new temp file for writing */
-       entry->fp = php_stream_fopen_tmpfile();
-       if (!entry->fp) {
-               if (error) {
-                       spprintf(error, 0, "phar error: unable to create temporary file");
-               }
-               return FAILURE;
-       }
-       entry->old_flags = entry->flags;
-       entry->is_modified = 1;
-       phar->is_modified = 1;
-       /* reset file size */
-       entry->uncompressed_filesize = 0;
-       entry->compressed_filesize = 0;
-       entry->crc32 = 0;
-       entry->flags = PHAR_ENT_PERM_DEF_FILE;
-       return SUCCESS;
-}
-/* }}} */
-
-/**
- * Retrieve a copy of the file information on a single file within a phar, or null.
- * This also transfers the open file pointer, if any, to the entry.
- *
- * If the file does not already exist, this will fail.  Pre-existing files can be
- * appended, truncated, or read.  For read, if the entry is marked unmodified, it is
- * assumed that the file pointer, if present, is opened for reading
- */
-int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
-{
-       phar_archive_data *phar;
-       phar_entry_info *entry;
-       int for_write  = mode[0] != 'r' || mode[1] == '+';
-       int for_append = mode[0] == 'a';
-       int for_create = mode[0] != 'r';
-       int for_trunc  = mode[0] == 'w';
-
-       if (!ret) {
-               return FAILURE;
-       }
-       *ret = NULL;
-       if (error) {
-               *error = NULL;
-       }
-       if (for_write && PHAR_G(readonly)) {
-               if (error) {
-                       spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname);
-               }
-               return FAILURE;
-       }
-       if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
-               return FAILURE;
-       }
-       if (!path_len) {
-               if (error) {
-                       spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname);
-               }
-               return FAILURE;
-       }
-       if (allow_dir) {
-               if ((entry = phar_get_entry_info_dir(phar, path, path_len, 2, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
-                       if (for_create && !PHAR_G(readonly)) {
-                               return SUCCESS;
-                       }
-                       return FAILURE;
-               }
-       } else {
-               if ((entry = phar_get_entry_info(phar, path, path_len, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
-                       if (for_create && !PHAR_G(readonly)) {
-                               return SUCCESS;
-                       }
-                       return FAILURE;
-               }
-       }
-       if (entry->is_modified && !for_write) {
-               if (error) {
-                       spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname);
-               }
-               return FAILURE;
-       }
-       if (entry->fp_refcount && for_write) {
-               if (error) {
-                       spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname);
-               }
-               return FAILURE;
-       }
-       if (entry->is_deleted) {
-               if (!for_create) {
-                       return FAILURE;
-               }
-               entry->is_deleted = 0;
-       }
-       *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
-       (*ret)->position = 0;
-       (*ret)->zero = 0;
-       (*ret)->phar = phar;
-       (*ret)->for_write = for_write;
-       (*ret)->internal_file = entry;
-       (*ret)->is_zip = entry->is_zip;
-       (*ret)->is_tar = entry->is_tar;
-       if (entry->is_dir) {
-               entry->phar->refcount++;
-               entry->fp_refcount++;
-               return SUCCESS;
-       }
-       if (entry->fp) {
-               /* make a copy */
-               if (for_trunc) {
-                       if (entry->fp == phar->fp) {
-                               /* duplicate entry if we are writing and are recycling the phar fp */
-                               if (FAILURE == phar_open_entry_file(phar, entry, error TSRMLS_CC)) {
-                                       return FAILURE;
-                               }
-                               (*ret)->fp = entry->fp;
-                       }
-#if PHP_VERSION_ID >= 50202
-                       php_stream_truncate_set_size(entry->fp, 0);
-#else
-                       if (php_stream_is(entry->fp, PHP_STREAM_IS_TEMP)) {
-                               if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_MEMORY)) {
-                                       php_stream *inner = *(php_stream**)entry->fp->abstract;
-                                       php_stream_memory_data *memfp = (php_stream_memory_data*)inner->abstract;
-                                       memfp->fpos = 0;
-                                       memfp->fsize = 0;
-                               } else if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_STDIO)) {
-                                       php_stream_truncate_set_size(*(php_stream**)entry->fp->abstract, 0);
-                               } else {
-                                       efree(*ret);
-                                       *ret = NULL;
-                                       if (error) {
-                                               spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
-                                       }
-                                       return FAILURE;
-                               }
-                       } else if (php_stream_is(entry->fp, PHP_STREAM_IS_STDIO)) {
-                               php_stream_truncate_set_size(entry->fp, 0);
-                       } else {
-                               efree(*ret);
-                               *ret = NULL;
-                               if (error) {
-                                       spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
-                               }
-                               return FAILURE;
-                       }
-#endif
-                       entry->old_flags = entry->flags;
-                       entry->is_modified = 1;
-                       phar->is_modified = 1;
-                       /* reset file size */
-                       entry->uncompressed_filesize = 0;
-                       entry->compressed_filesize = 0;
-                       entry->crc32 = 0;
-               } else if (for_append) {
-                       php_stream_seek(entry->fp, 0, SEEK_END);
-               }
-               (*ret)->fp = entry->fp;
-               if (entry->fp == phar->fp) {
-                       (*ret)->zero = entry->offset_within_phar + phar->internal_file_start;
-               }
-       } else {
-               (*ret)->fp = 0;
-               if (for_write) {
-                       if (FAILURE == phar_open_entry_file(phar, entry, error TSRMLS_CC)) {
-                               return FAILURE;
-                       }
-                       (*ret)->fp = entry->fp;
-               }
-       }
-       entry->fp_refcount++;
-       entry->phar->refcount++;
-       return SUCCESS;
-}
-/* }}} */
-
 int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
 {
        int ret = 0;
@@ -692,7 +315,7 @@ int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
                if (--idata->internal_file->fp_refcount < 0) {
                        idata->internal_file->fp_refcount = 0;
                }
-               if (idata->fp && idata->fp != idata->internal_file->fp) {
+               if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
                        php_stream_close(idata->fp);
                }
        }
@@ -711,18 +334,9 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
 
        phar = idata->phar;
        if (idata->internal_file->fp_refcount < 2) {
-               if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->internal_file->fp) {
+               if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
                        php_stream_close(idata->fp);
                }
-#if HAVE_PHAR_ZIP
-               if (idata->internal_file->is_zip) {
-                       if (idata->internal_file->zip) {
-                               zip_fclose(idata->internal_file->zip);
-                               idata->internal_file->zip = 0;
-                       }
-                       zip_delete(phar->zip, idata->internal_file->index);
-               }
-#endif
                zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
                idata->phar->refcount--;
                efree(idata);
@@ -736,95 +350,6 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
 }
 /* }}} */
 
-/**
- * Create a new dummy file slot within a writeable phar for a newly created file
- */
-phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
-{
-       phar_archive_data *phar;
-       phar_entry_info *entry, etemp;
-       phar_entry_data *ret;
-       const char *pcr_error;
-       char is_dir = path[path_len - 1] == '/';
-
-       if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
-               return NULL;
-       }
-
-       if (FAILURE == phar_get_entry_data(&ret, fname, fname_len, path, path_len, mode, allow_dir, error TSRMLS_CC)) {
-               return NULL;
-       } else if (ret) {
-               return ret;
-       }
-
-       if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
-               if (error) {
-                       spprintf(error, 0, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
-               }
-               return NULL;
-       }
-
-       /* create a new phar data holder */
-       ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
-
-       /* create an entry, this is a new file */
-       memset(&etemp, 0, sizeof(phar_entry_info));
-       etemp.filename_len = path_len;
-       etemp.fp = php_stream_fopen_tmpfile();
-       if (!etemp.fp) {
-               if (error) {
-                       spprintf(error, 0, "phar error: unable to create temporary file");
-               }
-               return NULL;
-       }
-       if (is_dir) {
-               etemp.fp_refcount = 1;
-               etemp.is_dir = 1;
-               etemp.flags = PHAR_ENT_PERM_DEF_DIR;
-               etemp.old_flags = PHAR_ENT_PERM_DEF_DIR;
-               etemp.filename_len--; /* strip trailing / */
-               path_len--;
-       } else {
-               etemp.fp_refcount = 1;
-               etemp.flags = PHAR_ENT_PERM_DEF_FILE;
-               etemp.old_flags = PHAR_ENT_PERM_DEF_FILE;
-       }
-       etemp.is_modified = 1;
-       etemp.timestamp = time(0);
-       etemp.offset_within_phar = (phar->is_zip ? 0 : -1);
-       etemp.is_crc_checked = 1;
-       etemp.phar = phar;
-#if HAVE_PHAR_ZIP
-       if (phar->is_zip) {
-               etemp.is_zip = 1;
-               /* prevent attempts to check the CRC */
-               etemp.is_crc_checked = 1;
-               etemp.index = -1;
-       }
-#endif
-       etemp.filename = estrndup(path, path_len);
-       if (phar->is_tar) {
-               etemp.is_tar = phar->is_tar;
-               etemp.tar_type = TAR_FILE;
-       }
-       zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry);
-       
-       if (!entry) {
-               return NULL;
-       }
-
-       phar->refcount++;
-       ret->phar = phar;
-       ret->fp = entry->fp;
-       ret->position = 0;
-       ret->for_write = 1;
-       ret->is_zip = entry->is_zip;
-       ret->is_tar = entry->is_tar;
-       ret->internal_file = entry;
-       return ret;
-}
-/* }}} */
-
 #define MAPPHAR_ALLOC_FAIL(msg) \
        php_stream_close(fp);\
        if (error) {\
@@ -1318,12 +843,15 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
        /* set up our manifest */
        zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
                zend_get_hash_value, destroy_phar_manifest_entry, 0);
-       offset = 0;
+       offset = halt_offset + manifest_len + 4;
+       memset(&entry, 0, sizeof(phar_entry_info));
+       entry.phar = mydata;
+       entry.fp_type = PHAR_FP;
+
        for (manifest_index = 0; manifest_index < manifest_count; manifest_index++) {
                if (buffer + 4 > endbuffer) {
                        MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
                }
-               memset(&entry, 0, sizeof(phar_entry_info));
                PHAR_GET_32(buffer, entry.filename_len);
                if (entry.filename_len == 0) {
                        MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
@@ -1333,16 +861,14 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
                }
                if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
                        entry.is_dir = 1;
-                       entry.filename_len--;
-                       entry.flags |= PHAR_ENT_PERM_DEF_DIR;
                } else {
                        entry.is_dir = 0;
                }
                entry.filename = estrndup(buffer, entry.filename_len);
-               buffer += entry.filename_len + (entry.is_dir ? 1 : 0);
+               buffer += entry.filename_len;
                PHAR_GET_32(buffer, entry.uncompressed_filesize);
                PHAR_GET_32(buffer, entry.timestamp);
-               if (offset == 0) {
+               if (offset == halt_offset + manifest_len + 4) {
                        mydata->min_timestamp = entry.timestamp;
                        mydata->max_timestamp = entry.timestamp;
                } else {
@@ -1355,11 +881,15 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
                PHAR_GET_32(buffer, entry.compressed_filesize);
                PHAR_GET_32(buffer, entry.crc32);
                PHAR_GET_32(buffer, entry.flags);
+               if (entry.is_dir) {
+                       entry.filename_len--;
+                       entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+               }
                if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) {
                        efree(entry.filename);
                        MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
                }
-               entry.offset_within_phar = offset;
+               entry.offset = entry.offset_abs = offset;
                offset += entry.compressed_filesize;
                switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
                case PHAR_ENT_COMPRESSED_GZ:
@@ -1393,8 +923,6 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
                manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
                /* if signature matched, no need to check CRC32 for each file */
                entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
-               entry.fp = NULL;
-               entry.phar = mydata;
                zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
        }
 
@@ -1468,6 +996,7 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
        phar_archive_data *mydata;
        int register_alias;
        php_stream *fp;
+       char *actual = NULL;
 
        if (!pphar) {
                pphar = &mydata;
@@ -1483,20 +1012,32 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
        }
 
        /* first open readonly so it won't be created if not present */
-       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
+       if (actual) {
+               fname = actual;
+               fname_len = strlen(actual);
+       }
 
        if (fp) {
                if (phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC) == SUCCESS) {
                        if (!PHAR_G(readonly)) {
                                (*pphar)->is_writeable = 1;
                        }
+                       if (actual) {
+                               efree(actual);
+                       }
                        return SUCCESS;
                } else {
                        /* file exists, but is either corrupt or not a phar archive */
                        php_stream_close(fp);
+                       if (actual) {
+                               efree(actual);
+                       }
                        return FAILURE;
                }
-               php_stream_close(fp);
+       }
+       if (actual) {
+               efree(actual);
        }
 
 
@@ -1512,8 +1053,16 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
        /* set up our manifest */
        mydata = ecalloc(sizeof(phar_archive_data), 1);
 
-       /* re-open for writing */
-       fp = php_stream_open_wrapper(fname, "r+b", IGNORE_URL|STREAM_MUST_SEEK|0, &mydata->fname);
+       /* re-open for writing (we only reach here if the file does not exist) */
+       fp = php_stream_open_wrapper(fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|0, &mydata->fname);
+       if (!fp) {
+               if (options & REPORT_ERRORS) {
+                       if (error) {
+                               spprintf(error, 0, "creating archive \"%s\" failed", fname);
+                       }
+               }
+               return FAILURE;
+       }
        if (mydata->fname) {
                fname = mydata->fname;
 #ifdef PHP_WIN32
@@ -1568,6 +1117,8 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
 int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
 {
        php_stream *fp;
+       char *actual;
+       int ret;
 
        if (error) {
                *error = NULL;
@@ -1589,18 +1140,29 @@ int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, i
                return FAILURE;
        }
 
-       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, NULL);
-       
+       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
        if (!fp) {
                if (options & REPORT_ERRORS) {
                        if (error) {
                                spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
                        }
                }
+               if (actual) {
+                       efree(actual);
+               }
                return FAILURE;
        }
 
-       return phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC);
+       if (actual) {
+               fname = actual;
+               fname_len = strlen(actual);
+       }
+
+       ret =  phar_open_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC);
+       if (actual) {
+               efree(actual);
+       }
+       return ret;
 }
 /* }}}*/
 
@@ -1727,8 +1289,8 @@ static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias,
                                continue;
                        }
                        if (!memcmp(pos, zip_magic, 4)) {
-                               php_stream_close(fp);
-                               return phar_open_zipfile(fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
+                               php_stream_seek(fp, 0, SEEK_END);
+                               return phar_open_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC);
                        }
                        if (got > 512) {
                                if (phar_is_tar(pos)) {
@@ -2059,243 +1621,56 @@ int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC)
 /**
  * Validate the CRC32 of a file opened from within the phar
  */
-int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */
+int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32, char **error TSRMLS_DC) /* {{{ */
 {
        php_uint32 crc = ~0;
        int len = idata->internal_file->uncompressed_filesize;
        php_stream *fp = idata->fp;
-
-       php_stream_seek(fp, 0 + idata->zero, SEEK_SET); 
-       while (len--) { 
-               CRC32(crc, php_stream_getc(fp));
-       }
-       php_stream_seek(fp, 0 + idata->zero, SEEK_SET);
-       if (~crc == crc32) {
-               return SUCCESS;
-       } else {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, idata->internal_file->filename);
-               return FAILURE;
-       }
-}
-/* }}} */
-
-/**
- * Determine which stream compression filter (if any) we need to read this file
- */
-static char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
-{
-       switch (entry->flags & PHAR_ENT_COMPRESSION_MASK) {
-       case PHAR_ENT_COMPRESSED_GZ:
-               return "zlib.deflate";
-       case PHAR_ENT_COMPRESSED_BZ2:
-               return "bzip2.compress";
-       default:
-               return return_unknown ? "unknown" : NULL;
-       }
-}
-/* }}} */
-
-/**
- * Determine which stream decompression filter (if any) we need to read this file
- */
-static char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
-{
-       php_uint32 flags;
-
-       if (entry->is_modified) {
-               flags = entry->old_flags;
-       } else {
-               flags = entry->flags;
-       }
-       switch (flags & PHAR_ENT_COMPRESSION_MASK) {
-       case PHAR_ENT_COMPRESSED_GZ:
-               return "zlib.inflate";
-       case PHAR_ENT_COMPRESSED_BZ2:
-               return "bzip2.decompress";
-       default:
-               return return_unknown ? "unknown" : NULL;
-       }
-}
-/* }}} */
-
-/**
- * helper function to open an internal file's fp just-in-time
- */
-phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp,
-                                     char **error, int for_write TSRMLS_DC)
-{
-       php_uint32 offset, flags;
-       php_stream_filter *filter/*,  *consumed */;
-       char *filter_name;
-       char *buffer;
+       phar_entry_info *entry = idata->internal_file;
 
        if (error) {
                *error = NULL;
        }
-#if HAVE_PHAR_ZIP
        if (entry->is_zip) {
-               char readbuf[8192];
-               int got;
-               if (entry->fp) {
-                       return entry;
-               }
-               if (!entry->zip) {
-                       if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
-                               char *filter_name;
-                               php_stream_filter *filter;
-                               /* we have to decompress this by hand */
-
-                               if (!phar_has_bz2) {
-                                       if (error) {
-                                               spprintf(error, 4096, "phar error, cannot decompress bzip2-compressed entry"); 
-                                       }
-                                       return NULL;
-                               }
-
-                               /* first, read into a temp stream */
-                               fp = php_stream_temp_new();
-                               entry->zip = zip_fopen_index(phar->zip, entry->index, ZIP_FL_COMPRESSED);
-                               if (!entry->zip) {
-                                       if (error) {
-                                               spprintf(error, 4096, "phar error: %s", zip_strerror(phar->zip));
-                                       }
-                                       zip_error_clear(phar->zip);
-                                       return NULL;
-                               }
-                               do {
-                                       size_t check;
-                                       got = zip_fread(entry->zip, (void *)readbuf, 8192);
-                                       if (-1 == got) break;
-                                       check = got;
-                                       if (check != php_stream_write(fp, readbuf, got)) {
-                                               if (error) {
-                                                       spprintf(error, 4096, "phar error: could not copy full zip file contents of entry \"%s\"", entry->filename);
-                                               }
-                                               php_stream_close(fp);
-                                               entry->fp = NULL;
-                                               zip_fclose(entry->zip);
-                                               entry->zip = NULL;
-                                               return NULL;
-                                       }
-                               } while (got == 8192);
-                               zip_fclose(entry->zip);
-                               entry->zip = NULL;
-                               php_stream_seek(fp, 0, SEEK_SET);
-
-                               /* now use a decompression filter to inflate into our temp file */
-                               if ((filter_name = phar_decompress_filter(entry, 0)) != NULL) {
-                                       filter = php_stream_filter_create(filter_name, NULL, php_stream_is_persistent(fp) TSRMLS_CC);
-                               } else {
-                                       filter = NULL;
-                               }
-                               if (!filter) {
-                                       spprintf(error, 0, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", entry->phar->fname, phar_decompress_filter(entry, 1), entry->filename);
-                                       return NULL;                    
-                               }
+               /* verify local file header */
+               phar_zip_file_header local;
 
-                               /* now we can safely use proper decompression */
-                               entry->fp = php_stream_temp_new();
-                               php_stream_filter_append(&entry->fp->writefilters, filter);
-                               if (php_stream_copy_to_stream(fp, entry->fp, entry->compressed_filesize) != entry->compressed_filesize || php_stream_tell(entry->fp) != (off_t) entry->uncompressed_filesize) {
-                                       spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
-                                       php_stream_filter_remove(filter, 1 TSRMLS_CC);
-                                       return NULL;
-                               }
-                               php_stream_filter_flush(filter, 1);
-                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
-                               php_stream_close(fp);
-                               return entry;
-                       } else {
-                               /* uncompressed or zlib-compressed */
-                               entry->zip = zip_fopen_index(phar->zip, entry->index, 0);
-                               if (!entry->zip) {
-                                       if (error) {
-                                               spprintf(error, 4096, "phar error: %s", zip_strerror(phar->zip));
-                                       }
-                                       zip_error_clear(phar->zip);
-                                       return NULL;
-                               }
-                       }
+               if (SUCCESS != phar_open_archive_fp(idata->phar TSRMLS_CC)) {
+                       spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename);
+                       return FAILURE;
                }
+               php_stream_seek(idata->phar->fp, entry->header_offset, SEEK_SET);
 
-               /* load contents of zip file to temp stream */
-               entry->fp = php_stream_temp_new();
-               do {
-                       size_t check;
-                       got = zip_fread(entry->zip, (void *)readbuf, 8192);
-                       if (-1 == got) break;
-                       check = got;
-                       if (check != php_stream_write(entry->fp, readbuf, got)) {
-                               if (error) {
-                                       spprintf(error, 4096, "phar error: could not copy full zip file contents of entry \"%s\"", entry->filename);
-                               }
-                               php_stream_close(entry->fp);
-                               entry->fp = NULL;
-                               zip_fclose(entry->zip);
-                               entry->zip = NULL;
-                               return NULL;
-                       }
-               } while (got == 8192);
-               zip_fclose(entry->zip);
-               entry->zip = NULL;
-               return entry;
-       }
-#endif /* #if HAVE_PHAR_ZIP */
-       /* seek to start of internal file and read it */
-       offset = phar->internal_file_start + entry->offset_within_phar;
-       if (-1 == php_stream_seek(fp, offset, SEEK_SET)) {
-               spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (cannot seek to start of file \"%s\" at offset \"%d\")",
-                       phar->fname, entry->filename, offset);
-               return NULL;
-       }
-
-       if (entry->is_modified) {
-               flags = entry->old_flags;
-       } else {
-               flags = entry->flags;
-       }
+               if (sizeof(local) != php_stream_read(idata->phar->fp, (char *) &local, sizeof(local))) {
 
-       if ((flags & PHAR_ENT_COMPRESSION_MASK) != 0) {
-               if ((filter_name = phar_decompress_filter(entry, 0)) != NULL) {
-                       filter = php_stream_filter_create(phar_decompress_filter(entry, 0), NULL, php_stream_is_persistent(fp) TSRMLS_CC);
-               } else {
-                       filter = NULL;
-               }
-               if (!filter) {
-                       spprintf(error, 0, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename);
-                       return NULL;                    
+                       spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename);
+                       return FAILURE;
                }
-               /* now we can safely use proper decompression */
-               entry->old_flags = entry->flags;
-               buffer = (char *) emalloc(8192);
-
-               entry->fp = php_stream_temp_new();
-               php_stream_filter_append(&entry->fp->writefilters, filter);
-               if (php_stream_copy_to_stream(fp, entry->fp, entry->compressed_filesize) != entry->compressed_filesize || php_stream_tell(entry->fp) != (off_t) entry->uncompressed_filesize) {
-                       efree(buffer);
-                       spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
-                       php_stream_filter_remove(filter, 1 TSRMLS_CC);
-                       return NULL;
+               /* fix up for big-endian systems */
+               /* verify local header if not yet verified */
+               if (entry->filename_len != local.filename_len || entry->crc32 != local.crc32 || entry->uncompressed_filesize != local.uncompsize || entry->compressed_filesize != local.compsize) {
+                       spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local head of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename);
+                       return FAILURE;
                }
-               efree(buffer);
-               php_stream_filter_flush(filter, 1);
-               php_stream_filter_remove(filter, 1 TSRMLS_CC);
-               php_stream_seek(fp, offset + entry->compressed_filesize, SEEK_SET);
-       } else { /* from here is for non-compressed */
-               if (!for_write && !entry->is_modified) {
-                       /* recycle the phar fp */
-                       entry->fp = phar->fp;
-                       return entry;
-               }
-               /* bypass to temp stream */
-               entry->fp = php_stream_temp_new();
-               if (php_stream_copy_to_stream(fp, entry->fp, entry->uncompressed_filesize) != entry->uncompressed_filesize) {
-                       spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
-                       return NULL;
+               if (-1 == php_stream_seek(idata->phar->fp, local.filename_len + local.extra_len, SEEK_CUR)) {
+                       spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot seek to start of file data for file \"%s\")", idata->phar->fname, entry->filename);
+                       return FAILURE;
                }
        }
-       return entry;
+       php_stream_seek(fp, idata->zero, SEEK_SET);     
+       while (len--) {
+               CRC32(crc, php_stream_getc(fp));
+       }
+       php_stream_seek(fp, idata->zero, SEEK_SET);
+       if (~crc == crc32) {
+               entry->is_crc_checked = 1;
+               return SUCCESS;
+       } else {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
+               return FAILURE;
+       }
 }
+/* }}} */
 
 static inline void phar_set_32(char *buffer, int var) /* {{{ */
 {
@@ -2396,7 +1771,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
        char *newstub;
        phar_entry_info *entry, *newentry;
        int halt_offset, restore_alias_len, global_flags = 0, closeoldfile;
-       char *buf, *pos, has_dirs = 0;
+       char *pos, has_dirs = 0;
        char manifest[18], entry_buffer[24];
        off_t manifest_ftell;
        long offset;
@@ -2417,11 +1792,9 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                return EOF;
        }
 
-#if HAVE_PHAR_ZIP
        if (phar->is_zip) {
                return phar_zip_flush(phar, user_stub, len, error TSRMLS_CC);
        }
-#endif
        if (phar->is_tar) {
                return phar_tar_flush(phar, user_stub, len, error TSRMLS_CC);
        }
@@ -2558,7 +1931,6 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
        }
        new_manifest_count = 0;
        offset = 0;
-       buf = (char *) emalloc(8192);
        for (zend_hash_internal_pointer_reset(&phar->manifest);
            zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
            zend_hash_move_forward(&phar->manifest)) {
@@ -2591,6 +1963,9 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                        php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
                        PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
                } else {
+                       if (entry->metadata_str.c) {
+                               smart_str_free(&entry->metadata_str);
+                       }
                        entry->metadata_str.c = 0;
                        entry->metadata_str.len = 0;
                }
@@ -2602,7 +1977,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                if (oldfile && !entry->is_modified) {
                        continue;
                }
-               if (!entry->fp || (entry->is_modified && entry->fp == phar->fp)) {
+               if (!phar_get_efp(entry)) {
                        /* re-open internal file pointer just-in-time */
                        newentry = phar_open_jit(phar, entry, oldfile, error, 0 TSRMLS_CC);
                        if (!newentry) {
@@ -2613,20 +1988,16 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                        }
                        entry = newentry;
                }
-               file = entry->fp;
-               if (file == phar->fp) {
-                       if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
-                               if (closeoldfile) {
-                                       php_stream_close(oldfile);
-                               }
-                               php_stream_close(newfile);
-                               if (error) {
-                                       spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
-                               }
-                               return EOF;
+               file = phar_get_efp(entry);
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+                       if (closeoldfile) {
+                               php_stream_close(oldfile);
                        }
-               } else {
-                       php_stream_rewind(file);
+                       php_stream_close(newfile);
+                       if (error) {
+                               spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+                       }
+                       return EOF;
                }
                newcrc32 = ~0;
                mytime = entry->uncompressed_filesize;
@@ -2655,7 +2026,6 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                                        spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
                                }
                        }
-                       efree(buf);
                        return EOF;
                }
 
@@ -2671,36 +2041,41 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                                php_stream_close(oldfile);
                        }
                        php_stream_close(newfile);
-                       efree(buf);
                        return EOF;
                }
                php_stream_flush(file);
-               if (file == phar->fp) {
-                       if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
-                               if (closeoldfile) {
-                                       php_stream_close(oldfile);
-                               }
-                               php_stream_close(newfile);
-                               if (error) {
-                                       spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
-                               }
-                               return EOF;
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+                       if (closeoldfile) {
+                               php_stream_close(oldfile);
                        }
-               } else {
-                       php_stream_rewind(file);
+                       php_stream_close(newfile);
+                       if (error) {
+                               spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+                       }
+                       return EOF;
+               }
+               php_stream_filter_append((&entry->cfp->writefilters), filter);
+               if (entry->uncompressed_filesize != php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize)) {
+                       if (closeoldfile) {
+                               php_stream_close(oldfile);
+                       }
+                       php_stream_close(newfile);
+                       if (error) {
+                               spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+                       }
+                       return EOF;
                }
-               php_stream_filter_append((&file->readfilters), filter);
-               entry->compressed_filesize = (php_uint32) php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize+8192);
                php_stream_filter_flush(filter, 1);
-               entry->compressed_filesize += (php_uint32) php_stream_copy_to_stream(file, entry->cfp, entry->uncompressed_filesize+8192);
+               php_stream_flush(entry->cfp);
                php_stream_filter_remove(filter, 1 TSRMLS_CC);
+               php_stream_seek(entry->cfp, 0, SEEK_END);
+               entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
                /* generate crc on compressed file */
                php_stream_rewind(entry->cfp);
                entry->old_flags = entry->flags;
                entry->is_modified = 1;
                global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
        }
-       efree(buf);
        global_flags |= PHAR_HDR_SIGNATURE;
 
        /* write out manifest pre-header */
@@ -2849,7 +2224,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
        }
        
        /* now copy the actual file data to the new phar */
-       offset = 0;
+       offset = php_stream_tell(newfile);
        for (zend_hash_internal_pointer_reset(&phar->manifest);
            zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
            zend_hash_move_forward(&phar->manifest)) {
@@ -2862,24 +2237,9 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                if (entry->cfp) {
                        file = entry->cfp;
                        php_stream_rewind(file);
-               } else if (entry->fp && (entry->is_modified || !oldfile)) {
-                       file = entry->fp;
-                       if (file == phar->fp) {
-                               if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
-                                       if (closeoldfile) {
-                                               php_stream_close(oldfile);
-                                       }
-                                       php_stream_close(newfile);
-                                       if (error) {
-                                               spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
-                                       }
-                                       return EOF;
-                               }
-                       } else {
-                               php_stream_rewind(file);
-                       }
                } else {
-                       if (!oldfile) {
+                       file = phar_get_efp(entry);
+                       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
                                if (closeoldfile) {
                                        php_stream_close(oldfile);
                                }
@@ -2889,21 +2249,20 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                                }
                                return EOF;
                        }
-                       if (-1 == php_stream_seek(oldfile, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) {
-                               if (closeoldfile) {
-                                       php_stream_close(oldfile);
-                               }
-                               php_stream_close(newfile);
-                               if (error) {
-                                       spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
-                               }
-                               return EOF;
+               }
+               if (!file) {
+                       if (closeoldfile) {
+                               php_stream_close(oldfile);
                        }
-                       file = oldfile;
+                       php_stream_close(newfile);
+                       if (error) {
+                               spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
+                       }
+                       return EOF;
                }
                /* this will have changed for all files that have either
                   changed compression or been modified */
-               entry->offset_within_phar = offset;
+               entry->offset = entry->offset_abs = offset;
                offset += entry->compressed_filesize;
                wrote = php_stream_copy_to_stream(file, newfile, entry->compressed_filesize);
                if (entry->compressed_filesize != wrote) {
@@ -2919,13 +2278,18 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
                entry->is_modified = 0;
                if (entry->cfp) {
                        php_stream_close(entry->cfp);
-                       entry->cfp = 0;
+                       entry->cfp = NULL;
                }
-               if (entry->fp && entry->fp_refcount == 0) {
-                       if (entry->fp != phar->fp) {
+               if (entry->fp_type == PHAR_MOD) {
+                       /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed
+                          when the phar_entry_data is phar_entry_delref'ed */
+                       if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
                                php_stream_close(entry->fp);
                        }
-                       entry->fp = 0;
+                       entry->fp = NULL;
+                       entry->fp_type = PHAR_FP;
+               } else if (entry->fp_type == PHAR_UFP) {
+                       entry->fp_type = PHAR_FP;
                }
        }
 
@@ -3027,11 +2391,16 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error
        if (phar->fp) {
                php_stream_close(phar->fp);
        }
+       if (phar->ufp) {
+               php_stream_close(phar->ufp);
+               phar->ufp = NULL;
+       }
        if (closeoldfile) {
                php_stream_close(oldfile);
        }
 
        phar->internal_file_start = halt_offset + manifest_len + 4;
+       phar->halt_offset = halt_offset;
        phar->is_brandnew = 0;
 
        php_stream_rewind(newfile);
@@ -3252,24 +2621,11 @@ skip_phar:
 
 PHP_MINIT_FUNCTION(phar) /* {{{ */
 {
-       zend_module_entry *test;
-
        ZEND_INIT_MODULE_GLOBALS(phar, php_phar_init_globals_module, NULL);
        REGISTER_INI_ENTRIES();
 
        phar_has_bz2 = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
        phar_has_zlib = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
-       if (SUCCESS == zend_hash_find(&module_registry, "zip", sizeof("zip"), (void **) &test)) {
-               if (php_version_compare((const char *) test->version, "1.8.11") != -1) {
-                       phar_has_zip = 1;
-               } else {
-                       phar_has_zip = 0;
-               }
-               phar_zip_ver = (char *) test->version;
-       } else {
-               phar_has_zip = 0;
-               phar_zip_ver = NULL;
-       }
 
        phar_orig_compile_file = zend_compile_file;
        zend_compile_file = phar_compile_file;
@@ -3349,22 +2705,7 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */
        php_info_print_table_row(2, "CVS revision", "$Revision$");
        php_info_print_table_row(2, "Phar-based phar archives", "enabled");
        php_info_print_table_row(2, "Tar-based phar archives", "enabled");
-#if HAVE_PHAR_ZIP
-       if (phar_has_zip) {
-               php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
-       } else {
-               if (phar_zip_ver) {
-                       char *tmp;
-                       spprintf(&tmp, 0, "disabled (pecl/zip version %s installed, we need zip >= 1.8.11)", phar_zip_ver);
-                       php_info_print_table_row(2, "ZIP-based phar archives", tmp);
-                       efree(tmp);
-               } else {
-                       php_info_print_table_row(2, "ZIP-based phar archives", "disabled (install pecl/zip)");
-               }
-       }
-#else
-       php_info_print_table_row(2, "ZIP-based phar archives", "unavailable");
-#endif
+       php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
        if (phar_has_zlib) {
                php_info_print_table_row(2, "gzip compression", "enabled");
        } else {
@@ -3392,9 +2733,6 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */
 /* {{{ phar_module_entry
  */
 static zend_module_dep phar_deps[] = {
-#if HAVE_PHAR_ZIP
-       ZEND_MOD_OPTIONAL("zip")
-#endif
        ZEND_MOD_OPTIONAL("zlib")
        ZEND_MOD_OPTIONAL("bz2")
 #if HAVE_SPL
index 82be02c6a7a400a94c8da187494a8880748970aa..540c8df1c7fdcd7b9903609817eeac518c0e7354 100755 (executable)
 
 #include <time.h>
 #include "php.h"
-#if HAVE_PHAR_ZIP
-#include "ext/zip/lib/zip.h"
-#include "ext/zip/lib/zipint.h"
-#endif
 #include "tar.h"
 #include "php_ini.h"
 #include "zend_constants.h"
@@ -174,8 +170,6 @@ ZEND_EXTERN_MODULE_GLOBALS(phar)
 
 int phar_has_bz2;
 int phar_has_zlib;
-int phar_has_zip;
-char *phar_zip_ver;
 
 #ifdef ZTS
 #      include "TSRM.h"
@@ -193,12 +187,25 @@ char *phar_zip_ver;
 #  define php_uint16 uint16_t
 # endif
 #endif
+#include "pharzip.h"
 
 #if HAVE_SPL
 typedef union _phar_archive_object  phar_archive_object;
 typedef union _phar_entry_object    phar_entry_object;
 #endif
 
+/*
+ * used in phar_entry_info->fp_type to
+ */
+enum phar_fp_type {
+       /* regular file pointer phar_archive_data->fp */
+       PHAR_FP,
+       /* uncompressed file pointer phar_archive_data->uncompressed_fp */
+       PHAR_UFP,
+       /* modified file pointer phar_entry_info->fp */
+       PHAR_MOD
+};
+
 typedef struct _phar_archive_data phar_archive_data;
 /* entry for one file in a phar file */
 typedef struct _phar_entry_info {
@@ -214,7 +221,13 @@ typedef struct _phar_entry_info {
        zval                     *metadata;
        php_uint32               filename_len;
        char                     *filename;
-       long                     offset_within_phar;
+       enum phar_fp_type        fp_type;
+       /* offset within original phar file of the file contents */
+       long                     offset_abs;
+       /* offset within fp of the file contents */
+       long                     offset;
+       /* offset within original phar file of the file header (for zip-based/tar-based) */
+       long                     header_offset;
        php_stream               *fp;
        php_stream               *cfp;
        int                      fp_refcount;
@@ -232,10 +245,6 @@ typedef struct _phar_entry_info {
        char                     tar_type;
        /* zip-based phar file stuff */
        int                      is_zip:1;
-#if HAVE_PHAR_ZIP
-       int                      index;
-       struct zip_file          *zip;
-#endif
 } phar_entry_info;
 
 /* information about a phar file (the archive itself) */
@@ -252,6 +261,8 @@ struct _phar_archive_data {
        php_uint32               min_timestamp;
        php_uint32               max_timestamp;
        php_stream               *fp;
+       /* decompressed file contents are stored here */
+       php_stream               *ufp;
        int                      refcount;
        php_uint32               sig_flags;
        int                      sig_len;
@@ -267,9 +278,6 @@ struct _phar_archive_data {
        int                      is_zip:1;
        /* tar-based phar variables */
        int                      is_tar:1;
-#if HAVE_PHAR_ZIP
-       struct zip               *zip;
-#endif
 };
 
 #define PHAR_MIME_PHP '\0'
@@ -286,8 +294,8 @@ typedef struct _phar_mime_type {
 /* stream access data for one file entry in a phar file */
 typedef struct _phar_entry_data {
        phar_archive_data        *phar;
-       /* stream position proxy, allows multiple open streams referring to the same fp */
        php_stream               *fp;
+       /* stream position proxy, allows multiple open streams referring to the same fp */
        off_t                    position;
        /* for copies of the phar fp, defines where 0 is */
        off_t                    zero;
@@ -330,19 +338,31 @@ void phar_request_initialize(TSRMLS_D);
 
 void phar_object_init(TSRMLS_D);
 
+int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC);
 int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
 int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
 int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
 int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC);
 int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC);
 int phar_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
+
+/* utility functions */
 char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC);
+char * phar_decompress_filter(phar_entry_info * entry, int return_unknown);
+char * phar_compress_filter(phar_entry_info * entry, int return_unknown);
 
 char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC);
 phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp,
                                      char **error, int for_write TSRMLS_DC);
 int phar_parse_metadata(char **buffer, zval **metadata, int is_zip TSRMLS_DC);
 void destroy_phar_manifest_entry(void *pDest);
+int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position TSRMLS_DC);
+php_stream *phar_get_efp(phar_entry_info *entry);
+int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC);
+int phar_open_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC);
 
 /* tar functions in tar.c */
 int phar_is_tar(char *buf);
@@ -352,11 +372,9 @@ int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_l
 int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC);
 
 /* zip functions in zip.c */
-int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC);
+int phar_open_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC);
 int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
-#if HAVE_PHAR_ZIP
 int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC);
-#endif
 
 #ifdef PHAR_MAIN
 static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
index de54d0588a898fe3ffadb92e1617c6369cd7f501..63b479ea742267c44707ef2d9282ccb8b00a607a 100755 (executable)
@@ -243,7 +243,7 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch
                        }
 
                        /* prepare to output  */
-                       if (!phar->fp) {
+                       if (!phar_get_efp(phar->internal_file)) {
                                char *error;
                                if (!phar_open_jit(phar->phar, phar->internal_file, phar->phar->fp, &error, 0 TSRMLS_CC)) {
                                        if (error) {
@@ -252,29 +252,16 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch
                                        }
                                        return -1;
                                }
-                               phar->fp = phar->internal_file->fp;
-                               if (phar->internal_file->fp == phar->phar->fp) {
-                                       phar->zero = phar->internal_file->offset_within_phar;
-                                       if (!phar->is_tar && !phar->is_zip) {
-                                               phar->zero += phar->phar->internal_file_start;
-                                       }
-                               }
+                               phar->fp = phar_get_efp(phar->internal_file);
+                               phar->zero = phar->internal_file->offset;
                        }
-                       php_stream_seek(phar->fp, phar->zero, SEEK_SET);
+                       phar_seek_efp(phar->internal_file, 0, SEEK_SET, 0 TSRMLS_CC);
                        do {
-                               if (!phar->zero) {
-                                       got = php_stream_read(phar->fp, buf, 8192);
-                                       PHPWRITE(buf, got);
-                                       if (phar->fp->eof) {
-                                               break;
-                                       }
-                               } else {
-                                       got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position));
-                                       PHPWRITE(buf, got);
-                                       phar->position = php_stream_tell(phar->fp) - phar->zero;
-                                       if (phar->position == (off_t) phar->internal_file->uncompressed_filesize) {
-                                               break;
-                                       }
+                               got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position));
+                               PHPWRITE(buf, got);
+                               phar->position = php_stream_tell(phar->fp) - phar->zero;
+                               if (phar->position == (off_t) phar->internal_file->uncompressed_filesize) {
+                                       break;
                                }
                        } while (1);
 
@@ -1313,44 +1300,65 @@ PHP_METHOD(Phar, isPhar)
 
 static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */
 {
-       /* if not available, open the file for reading */
-       if (!entry->fp) {
-               char *error;
-               if (!phar_open_jit(entry->phar, entry, entry->phar->fp, &error, 0 TSRMLS_CC)) {
-                       if (error) {
-                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                       "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
-                               efree(error);
-                       } else {
-                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                       "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
-                       }
-                       return FAILURE;
+       char *error;
+       off_t offset;
+
+       if (FAILURE == phar_open_entry_fp(entry, &error TSRMLS_CC)) {
+               if (error) {
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
+                       efree(error);
+               } else {
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
                }
+               return FAILURE;
        }
        /* copy old contents in entirety */
-       php_stream_seek(entry->fp, entry->phar->internal_file_start + entry->offset_within_phar, SEEK_SET);
-       if (entry->uncompressed_filesize != php_stream_copy_to_stream(entry->fp, fp, entry->uncompressed_filesize)) {
+       phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+       offset = php_stream_tell(fp);
+       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), fp, entry->uncompressed_filesize)) {
                zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
                        "Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename);
                return FAILURE;
        }
-       if (entry->fp != entry->phar->fp) {
-               php_stream_close(entry->fp);
+       if (entry->fp_type == PHAR_MOD) {
+               /* save for potential restore on error */
+               entry->cfp = entry->fp;
                entry->fp = NULL;
        }
+       /* set new location of file contents */
+       entry->fp_type = PHAR_FP;
+       entry->offset = offset;
        return SUCCESS;
 }
 /* }}} */
 
+static int phar_restore_apply(void *data TSRMLS_DC) /* {{{ */
+{
+       phar_entry_info *entry = (phar_entry_info *)data;
+
+       if (entry->cfp) {
+               entry->fp_type = PHAR_MOD;
+               entry->fp = entry->cfp;
+               entry->cfp = NULL;
+               entry->offset = 0;
+       } else {
+               entry->fp_type = PHAR_FP;
+               entry->fp = NULL;
+               entry->offset = entry->offset_abs;
+       }
+       return ZEND_HASH_APPLY_KEEP;
+}
+
 static void phar_convert_to_other(phar_archive_data *source, int convert, php_uint32 flags TSRMLS_DC) /* {{{ */
 {
        phar_archive_data phar = {0};
-       long offset = 0;
-       char *error, *opened_path = NULL;
-#if HAVE_PHAR_ZIP
-       int fd, ziperror;
-#endif
+       char *error;
+       zval *userz;
+       long tmp;
+       php_stream *fp;
+       phar_entry_info *entry, newentry;
 
        /* set whole-archive compression from parameter */
        phar.flags = flags;
@@ -1360,39 +1368,6 @@ static void phar_convert_to_other(phar_archive_data *source, int convert, php_ui
                        break;
                case 2 :
                        phar.is_zip = 1;
-#if HAVE_PHAR_ZIP
-                       if (!((fd = php_open_temporary_fd(NULL, "pharzip", &opened_path TSRMLS_CC)) >= 0)) {
-                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                       "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
-                               return;
-                       }
-                       close(fd);
-                       unlink(opened_path);
-                       phar.zip = zip_open(opened_path, ZIP_CREATE, &ziperror);
-                       if (!phar.zip) {
-                               /* now for the stupid hoops libzip forces... */
-                               char *tmp;
-                               int tmp_len;
-
-                               efree(opened_path);
-                               tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
-                               if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
-                                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                               "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
-                               } else {
-                                       if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
-                                               efree(tmp);
-                                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                                       "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
-                                       } else {
-                                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, 
-                                                       "Cannot convert phar archive \"%s\", unable to open temporary zip archive: %s", source->fname, tmp);
-                                               efree(tmp);
-                                       }
-                               }
-                               return;
-                       }
-#endif
                        break;
        }
 
@@ -1403,67 +1378,51 @@ static void phar_convert_to_other(phar_archive_data *source, int convert, php_ui
        phar.fname = source->fname;
        /* first copy each file's uncompressed contents to a temporary file and set per-file flags */
        for (zend_hash_internal_pointer_reset(&source->manifest); SUCCESS == zend_hash_has_more_elements(&source->manifest); zend_hash_move_forward(&source->manifest)) {
-               phar_entry_info *entry, newentry;
+
                if (FAILURE == zend_hash_get_current_data(&source->manifest, (void **) &entry)) {
                        zend_hash_destroy(&(phar.manifest));
                        php_stream_close(phar.fp);
                        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
                                "Cannot convert phar archive \"%s\"", source->fname);
-                       if (opened_path) {
-                               efree(opened_path);
+                       zend_hash_apply(&source->manifest, phar_restore_apply TSRMLS_CC);
+                       return;
+               }
+               if (!convert) {
+                       /* converting to Phar */
+                       if (entry->filename_len == sizeof(".phar/stub.php")-1 && !strncmp(entry->filename, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
+                               /* do not copy stub file */
+                               continue;
                        }
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
+                       if (entry->filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry->filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
+                               /* do not copy alias file */
+                               continue;
                        }
-#endif
-                       return;
                }
                if (entry->fp_refcount > 0) {
                        zend_hash_destroy(&(phar.manifest));
                        php_stream_close(phar.fp);
                        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
                                "Cannot convert phar archive \"%s\", open file pointers for entry \"%s\"", source->fname, entry->filename);
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
-                       }
-#endif
-                       if (opened_path) {
-                               efree(opened_path);
-                       }
+                       zend_hash_apply(&source->manifest, phar_restore_apply TSRMLS_CC);
                        return;
                }
                newentry = *entry;
-               if (FAILURE == phar_copy_file_contents(entry, phar.fp TSRMLS_CC)) {
+               if (FAILURE == phar_copy_file_contents(&newentry, phar.fp TSRMLS_CC)) {
                        zend_hash_destroy(&(phar.manifest));
                        php_stream_close(phar.fp);
                        /* exception already thrown */
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
-                       }
-#endif
-                       if (opened_path) {
-                               efree(opened_path);
-                       }
+                       zend_hash_apply(&source->manifest, phar_restore_apply TSRMLS_CC);
                        return;
                }
                newentry.filename = estrndup(newentry.filename, newentry.filename_len);
                if (newentry.metadata) {
                        SEPARATE_ZVAL(&(newentry.metadata));
                }
-               newentry.offset_within_phar = offset;
-               offset += newentry.uncompressed_filesize;
                newentry.is_zip = phar.is_zip;
                newentry.is_tar = phar.is_tar;
                if (newentry.is_tar) {
                        newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE);
                }
-               newentry.fp = phar.fp;
-#if HAVE_PHAR_ZIP
-               newentry.index = -1;
-#endif
                newentry.is_modified = 1;
                newentry.phar = &phar;
                newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */
@@ -1471,90 +1430,28 @@ static void phar_convert_to_other(phar_archive_data *source, int convert, php_ui
        }
 
        /* next copy the stub and flush */
-       if (phar.is_zip) {
-               phar.fname = opened_path;
-       }
-
-       if (source->is_zip) {
-               phar_entry_info *entry;
-               long tmp = 0;
-               zval *userz;
 
+       if (source->is_tar || source->is_zip) {
                if (FAILURE == (zend_hash_find(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&entry))) {
                        /* use default stub - the existing one in the manifest will be used if present */
                        phar_flush(&phar, NULL, 0, &error TSRMLS_CC);
                        goto finalize;
                }
-               if (!entry->fp && !phar_open_jit(source, entry, NULL, &error, 0 TSRMLS_CC)) {
-                       if (error) {
-                               efree(error);
-                       }
-                       php_stream_close(phar.fp);
-                       zend_hash_destroy(&(phar.manifest));
-                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                               "Cannot convert phar archive \"%s\": unable to retrieve stub", source->fname, error);
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
-                       }
-#endif
-                       if (opened_path) {
-                               efree(opened_path);
-                       }
-                       return;
-               }
-               /* copy the other phar's stub */
-               php_stream_seek(entry->fp, 0, SEEK_SET);
-               ALLOC_ZVAL(userz);
-               php_stream_to_zval(entry->fp, userz);
-               tmp = entry->uncompressed_filesize;
-               phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
-               efree(userz);
-               php_stream_close(entry->fp);
-               if (error) {
-                       zend_hash_destroy(&(phar.manifest));
-                       php_stream_close(phar.fp);
-                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                               "Cannot convert phar archive \"%s\": %s", source->fname, error);
-                       efree(error);
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
-                       }
-#endif
-                       if (opened_path) {
-                               efree(opened_path);
-                       }
-                       return;
-               }
+               fp = php_stream_open_wrapper(source->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+               php_stream_seek(fp, entry->offset, SEEK_SET);
+               /* use this unused value to set the stub size */
+               source->internal_file_start = entry->uncompressed_filesize;
        } else {
-               zval *userz;
-               long tmp;
-
-               if (source->is_tar) {
-                       phar_entry_info *entry;
-
-                       if (FAILURE == (zend_hash_find(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&entry))) {
-                               /* use default stub - the existing one in the manifest will be used if present */
-                               phar_flush(&phar, NULL, 0, &error TSRMLS_CC);
-                               goto finalize;
-                       }
-                       php_stream_seek(source->fp, entry->offset_within_phar, SEEK_SET);
-                       /* use this unused value to set the stub size */
-                       source->internal_file_start = entry->uncompressed_filesize;
-               } else {
-                       php_stream_seek(source->fp, 0, SEEK_SET);
-               }
-               /* copy the other phar's stub */
-               ALLOC_ZVAL(userz);
-               php_stream_to_zval(source->fp, userz);
-               tmp = source->internal_file_start;
-               phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
-               efree(userz);
-               if (source->is_tar) {
-                       source->internal_file_start = 0;
-               }
+               fp = php_stream_open_wrapper(source->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
        }
+       /* copy the other phar's stub */
+       ALLOC_ZVAL(userz);
+       php_stream_to_zval(fp, userz);
+       tmp = source->internal_file_start;
+       source->internal_file_start = 0;
+       phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
+       php_stream_close(fp);
+       efree(userz);
 
        if (error) {
                zend_hash_destroy(&(phar.manifest));
@@ -1562,14 +1459,6 @@ static void phar_convert_to_other(phar_archive_data *source, int convert, php_ui
                        "Cannot convert phar archive \"%s\": %s", source->fname, error);
                efree(error);
                php_stream_close(phar.fp);
-#if HAVE_PHAR_ZIP
-               if (phar.is_zip) {
-                       _zip_free(phar.zip);
-               }
-#endif
-               if (opened_path) {
-                       efree(opened_path);
-               }
                return;
        }
 
@@ -1582,14 +1471,6 @@ finalize:
                        php_stream_close(phar.fp);
                        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
                                "Cannot convert phar archive \"%s\"", source->fname);
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
-                       }
-#endif
-                       if (opened_path) {
-                               efree(opened_path);
-                       }
                        /* we can't throw an exception or bad crap will happen */
                        zend_error(E_ERROR, "Error: could not convert phar archive \"%s\", phar is in unstable state, shutting down", source->fname);
                        return;
@@ -1600,6 +1481,7 @@ finalize:
                                        /* a little hack to prevent destruction of data */
                                        dtor_func_t save;
 
+                                       entry->phar = source;
                                        save = phar.manifest.pDestructor;
                                        phar.manifest.pDestructor = NULL;
                                        zend_hash_add(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void*)entry, sizeof(phar_entry_info), NULL);
@@ -1611,6 +1493,7 @@ finalize:
                                        /* a little hack to prevent destruction of data */
                                        dtor_func_t save;
 
+                                       entry->phar = source;
                                        save = phar.manifest.pDestructor;
                                        phar.manifest.pDestructor = NULL;
                                        zend_hash_add(&source->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1, (void*)entry, sizeof(phar_entry_info), NULL);
@@ -1621,14 +1504,6 @@ finalize:
                        }
                        zend_hash_destroy(&(phar.manifest));
                        php_stream_close(phar.fp);
-#if HAVE_PHAR_ZIP
-                       if (phar.is_zip) {
-                               _zip_free(phar.zip);
-                       }
-#endif
-                       if (opened_path) {
-                               efree(opened_path);
-                       }
                        /* we can't throw an exception or bad crap will happen */
                        zend_error(E_ERROR, "Error: could not convert phar archive \"%s\", phar is in unstable state, shutting down", source->fname);
                        return;
@@ -1639,53 +1514,32 @@ finalize:
                        zval_dtor(mine->metadata);
                        efree(mine->metadata);
                }
+               if (mine->fp_type == PHAR_MOD && mine->fp != source->fp && mine->fp != source->ufp) {
+                       php_stream_close(mine->fp);
+               }
                *mine = *entry;
                mine->phar = source;
        }
+       if (source->fp && source->refcount == 1) {
+               php_stream_close(source->fp);
+       }
        source->fp = phar.fp;
        /* don't free stuff */
        phar.manifest.pDestructor = NULL;
        zend_hash_destroy(&(phar.manifest));
        source->is_zip = phar.is_zip;
+       if (phar.is_zip || phar.is_tar) {
+               source->internal_file_start = source->halt_offset = 0;
+       }
        source->is_tar = phar.is_tar;
+       if (source->signature) {
+               efree(source->signature);
+       }
        if (phar.signature) {
-               if (source->signature) {
-                       efree(source->signature);
-               }
                source->signature = phar.signature;
+       } else {
+               source->signature = 0;
        }
-#if HAVE_PHAR_ZIP
-       if (phar.is_zip) {
-               _zip_free(phar.zip);
-               _zip_free(source->zip);
-               if (FAILURE == php_copy_file(opened_path, source->fname TSRMLS_CC)) {
-                       /* we can't throw an exception or bad crap will happen */
-                       zend_error(E_ERROR, "Error: could not copy newly created zip to \"%s\", phar is in unstable state, shutting down", source->fname);
-               }
-               source->zip = zip_open(source->fname, 0, &ziperror);
-               if (!source->zip) {
-                       /* now for the stupid hoops libzip forces... */
-                       char *tmp;
-                       int tmp_len;
-
-                       efree(opened_path);
-                       tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
-                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
-                               zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\", phar is in unstable state, shutting down", source->fname);
-                       } else {
-                               if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
-                                       efree(tmp);
-                                       zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\", phar is in unstable state, shutting down", source->fname);
-                               } else {
-                                       zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\" (%s), phar is in unstable state, shutting down", source->fname, tmp);
-                                       efree(tmp);
-                               }
-                       }
-                       return;
-               }
-               efree(opened_path);
-       }
-#endif
 }
 
 /* {{{ proto bool Phar::convertToTar([int compression])
@@ -1781,44 +1635,6 @@ PHP_METHOD(Phar, convertToZip)
                return;
        }
 
-#if HAVE_PHAR_ZIP
-       if (!phar_has_zip) {
-               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
-                       "Cannot convert phar archive to zip format, zip-based phar archives are disabled (enable ext/zip in php.ini)");
-               return;
-       }
-
-       if (!zend_hash_num_elements(&phar_obj->arc.archive->manifest)) {
-               int ziperror;
-               /* nothing need be done specially, this has no files in it */
-               phar_obj->arc.archive->is_zip = 1;
-               phar_obj->arc.archive->internal_file_start = 0;
-               phar_obj->arc.archive->is_modified = 1;
-               phar_obj->arc.archive->zip = zip_open(phar_obj->arc.archive->fname, ZIP_CREATE, &ziperror);
-               if (!phar_obj->arc.archive->zip) {
-                       /* now for the stupid hoops libzip forces... */
-                       char *tmp;
-                       int tmp_len;
-                       tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
-                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
-                               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
-                                       "Cannot convert phar archive to zip format, unable to open", phar_obj->arc.archive->fname);
-                       } else {
-                               if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
-                                       efree(tmp);
-                                       zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
-                                               "Cannot convert phar archive to zip format, unable to open", phar_obj->arc.archive->fname);
-                               } else {
-                               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
-                                       "Cannot convert phar archive to zip format, unable to open: %s", phar_obj->arc.archive->fname, tmp);
-                                       efree(tmp);
-                               }
-                       }
-                       return;
-               }
-               RETURN_TRUE;
-       }
-
        if (phar_obj->arc.archive->donotflush) {
                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
                        "Cannot convert phar archive to zip format, call stopBuffering() first");
@@ -1833,11 +1649,6 @@ PHP_METHOD(Phar, convertToZip)
        }
        phar_convert_to_other(phar_obj->arc.archive, 2, 0 TSRMLS_CC);
        RETURN_TRUE;
-#else
-       zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
-               "Cannot convert phar archive to zip format, zip-based phar archives are unavailable");
-       return;
-#endif
 }
 /* }}} */
 
@@ -2359,12 +2170,6 @@ PHP_METHOD(Phar, compressAllFilesBZIP2)
        char *error;
        PHAR_ARCHIVE_OBJECT();
 
-       if (phar_obj->arc.archive->is_zip) {
-               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
-                       "Cannot compress all files as Bzip2, not possible with zip-based phar archives");
-               return;
-       }
-
        if (PHAR_G(readonly)) {
                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
                        "Phar is readonly, cannot change compression");
@@ -2438,7 +2243,6 @@ PHP_METHOD(Phar, copy)
        const char *pcr_error;
        int oldfile_len, newfile_len;
        phar_entry_info *oldentry, newentry = {0}, *temp;
-       php_stream *fp;
        PHAR_ARCHIVE_OBJECT();
 
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) {
@@ -2477,67 +2281,28 @@ PHP_METHOD(Phar, copy)
                RETURN_FALSE;
        }
 
-       fp = oldentry->fp;
-       if (fp && fp != phar_obj->arc.archive->fp) {
-               /* new file */
-               newentry.fp = php_stream_temp_new();
-               fp = newentry.fp;
-               php_stream_seek(oldentry->fp, 0, SEEK_SET);
-               if (oldentry->uncompressed_filesize != php_stream_copy_to_stream(oldentry->fp, fp, oldentry->uncompressed_filesize)) {
-                       php_stream_close(fp);
-                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                               "file \"%s\" could not be copied to file \"%s\" in %s, copy failed", oldfile, newfile, phar_obj->arc.archive->fname);
-                       return;
-               }
-       } else {
-               newentry.fp = NULL;
-       }
-
        memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info));
        if (newentry.metadata) {
                SEPARATE_ZVAL(&(newentry.metadata));
                newentry.metadata_str.c = NULL;
                newentry.metadata_str.len = 0;
        }
-#if HAVE_PHAR_ZIP
-       if (oldentry->is_zip) {
-               int zindex;
-               /* for new files, start with an empty string */
-               struct zip_source *s;
-               if (!newentry.fp) {
-                       if (!phar_open_jit(phar_obj->arc.archive, oldentry, NULL, &error, 0 TSRMLS_CC)) {
-                               php_stream_close(fp);
-                               if (error) {
-                                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                               "file \"%s\" could not be copied to file \"%s\" in %s, open of source file failed: %s", oldfile, newfile, phar_obj->arc.archive->fname, error);
-                                       efree(error);
-                               } else {
-                                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                                               "file \"%s\" could not be copied to file \"%s\" in %s, open of source file failed", oldfile, newfile, phar_obj->arc.archive->fname);
-                               }
-                               return;
-                       }
-                       fp = oldentry->fp;
-                       oldentry->fp = NULL;
-               }
-               s = zip_source_buffer(phar_obj->arc.archive->zip, (void *)"", 0, 0);
-               if (-1 == (zindex = zip_add(phar_obj->arc.archive->zip, newfile, s))) {
-                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
-                               "file \"%s\" could not be copied to file \"%s\" in %s, creation of destination file failed: %s", oldfile, newfile, phar_obj->arc.archive->fname, zip_strerror(phar_obj->arc.archive->zip));
-                       zip_error_clear(phar_obj->arc.archive->zip);
-                       return;
-               }
-               newentry.index = zindex;
-               newentry.zip = NULL;
-       }
-#endif
-       newentry.fp = fp;
        newentry.filename = estrndup(newfile, newfile_len);
        newentry.filename_len = newfile_len;
-       newentry.is_modified = 1;
        newentry.fp_refcount = 0;
+
+       if (oldentry->fp_type != PHAR_FP) {
+               if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error TSRMLS_CC)) {
+                       efree(newentry.filename);
+                       php_stream_close(newentry.fp);
+                       zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error);
+                       efree(error);
+                       return;
+               }
+       }
+
+       zend_hash_add(&oldentry->phar->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
        phar_obj->arc.archive->is_modified = 1;
-       zend_hash_add(&phar_obj->arc.archive->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
 
        phar_flush(phar_obj->arc.archive, 0, 0, &error TSRMLS_CC);
        if (error) {
@@ -2691,15 +2456,6 @@ PHP_METHOD(Phar, offsetUnset)
                        }
                        entry->is_modified = 0;
                        entry->is_deleted = 1;
-#if HAVE_PHAR_ZIP
-                       if (entry->is_zip) {
-                               if (entry->zip) {
-                                       zip_fclose(entry->zip);
-                                       entry->zip = 0;
-                               }
-                               zip_delete(phar_obj->arc.archive->zip, entry->index);
-                       }
-#endif
                        /* we need to "flush" the stream to save the newly deleted file on disk */
                        phar_flush(phar_obj->arc.archive, 0, 0, &error TSRMLS_CC);
                        if (error) {
@@ -2719,49 +2475,34 @@ PHP_METHOD(Phar, offsetUnset)
  */
 PHP_METHOD(Phar, getStub)
 {
-       char *buf;
        size_t len;
+       char *buf;
        php_stream *fp;
+       php_stream_filter *filter = NULL;
+       phar_entry_info *stub;
        PHAR_ARCHIVE_OBJECT();
 
-#if HAVE_PHAR_ZIP
-       if (phar_obj->arc.archive->is_zip) {
-               struct zip_stat zs;
-               struct zip_file *zf;
-               int index;
-
-               if (-1 == zip_stat(phar_obj->arc.archive->zip, ".phar/stub.php", 0, &zs)) {
-                       zip_error_clear(phar_obj->arc.archive->zip);
-                       RETURN_STRINGL("", 0, 1);
-               }
-               index = zs.index;
-               len = zs.size;
-               zf = zip_fopen_index(phar_obj->arc.archive->zip, index, 0);
-               if (!zf) {
-                       zip_error_clear(phar_obj->arc.archive->zip);
-                       RETURN_STRINGL("", 0, 1);
-               }
-               buf = safe_emalloc(len, 1, 1);
-               if (len != zip_fread(zf, buf, len)) {
-                       zip_fclose(zf);
-                       efree(buf);
-                       zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
-                               "Unable to read stub");
-                       return;
-               }
-               buf[len] = '\0';
-
-               RETURN_STRINGL(buf, len, 0);
-       }
-#endif
-       if (phar_obj->arc.archive->is_tar) {
-               phar_entry_info *stub;
+       if (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) {
 
                if (SUCCESS == zend_hash_find(&(phar_obj->arc.archive->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
-                       if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew) {
+                       if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew && !(stub->flags & PHAR_ENT_COMPRESSION_MASK)) {
                                fp = phar_obj->arc.archive->fp;
                        } else {
                                fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", 0, NULL);
+                               if (stub->flags & PHAR_ENT_COMPRESSION_MASK) {
+                                       char *filter_name;
+
+                                       if ((filter_name = phar_decompress_filter(stub, 0)) != NULL) {
+                                               filter = php_stream_filter_create(phar_decompress_filter(stub, 0), NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+                                       } else {
+                                               filter = NULL;
+                                       }
+                                       if (!filter) {
+                                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "phar error: unable to read stub of phar \"%s\" (cannot create %s filter)", phar_obj->arc.archive->fname, phar_decompress_filter(stub, 1));
+                                               return;
+                                       }
+                                       php_stream_filter_append(&fp->readfilters, filter);
+                               }
                        }
 
                        if (!fp)  {
@@ -2770,7 +2511,7 @@ PHP_METHOD(Phar, getStub)
                                return;
                        }
 
-                       php_stream_seek(fp, stub->offset_within_phar, SEEK_SET);
+                       php_stream_seek(fp, stub->offset_abs, SEEK_SET);
                        len = stub->uncompressed_filesize;
                        goto carry_on;
                } else {
@@ -2803,6 +2544,10 @@ carry_on:
                efree(buf);
                return;
        }
+       if (filter) {
+               php_stream_filter_flush(filter, 1);
+               php_stream_filter_remove(filter, 1 TSRMLS_CC);
+       }
        if (fp != phar_obj->arc.archive->fp) {
                php_stream_close(fp);
        }
@@ -3101,12 +2846,6 @@ PHP_METHOD(PharFileInfo, chmod)
                zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Cannot modify permissions for file \"%s\" in phar \"%s\", write operations are prohibited", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
                return;
        }
-#if HAVE_PHAR_ZIP
-       if (entry_obj->ent.entry->is_zip) {
-               zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Cannot modify permissions for file \"%s\" in phar \"%s\", not supported for zip-based phars", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
-               return;
-       }
-#endif
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perms) == FAILURE) {
                return;
        }       
diff --git a/ext/phar/pharzip.c b/ext/phar/pharzip.c
deleted file mode 100644 (file)
index ed21c1d..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
-  +----------------------------------------------------------------------+
-  | phar php single-file executable PHP extension, zip implementation    |
-  +----------------------------------------------------------------------+
-  | Copyright (c) 2005-2008 The PHP Group                                |
-  +----------------------------------------------------------------------+
-  | This source file is subject to version 3.01 of the PHP license,      |
-  | that is bundled with this package in the file LICENSE, and is        |
-  | available through the world-wide-web at the following url:           |
-  | http://www.php.net/license/3_01.txt.                                 |
-  | If you did not receive a copy of the PHP license and are unable to   |
-  | obtain it through the world-wide-web, please send a note to          |
-  | license@php.net so we can mail you a copy immediately.               |
-  +----------------------------------------------------------------------+
-  | Authors: Gregory Beaver <cellog@php.net>                             |
-  |          Marcus Boerger <helly@php.net>                              |
-  +----------------------------------------------------------------------+
-*/
-
-/* $Id$ */
-
-#include "phar_internal.h"
-
-#ifdef PHP_WIN32
-static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{ */
-{
-       char *s;
-
-       /* unixify win paths */
-       for (s = path; s - path < path_len; s++) {
-               if (*s == '\\') {
-                       *s = '/';
-               }
-       }
-}
-/* }}} */
-#endif
-
-static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC)
-{
-       php_uint16 left = len;
-       union {
-               phar_zip_extra_field_header header;
-               phar_zip_unix3 unix3;
-       } h;
-
-       do {
-               if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
-                       return FAILURE;
-               }
-               /* clean up header for big-endian systems */
-               if (h.header.tag != "\x75\x6e") {
-                       /* skip to next header */
-                       php_stream_seek(fp, h.header.size, SEEK_CUR);
-                       len -= h.header.size;
-                       continue;
-               }
-               /* unix3 header found */
-               /* clean up header for big-endian systems */
-               if (sizeof(h.unix3) != php_stream_read(fp, (char *) &h.unix3, sizeof(h.unix3))) {
-                       if (h.unix3.size > sizeof(h.unix3) - 4) {
-                               /* skip symlink filename - we may add this support in later */
-                               php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR);
-                       }
-                       /* set permissions */
-                       entry->flags &= PHAR_ENT_COMPRESSION_MASK;
-                       if (entry->is_dir) {
-                               entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_DIR;
-                       } else {
-                               entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_FILE;
-                       }
-               }
-       } while (len);
-       return SUCCESS;
-}
-
-/*
-  extracted from libzip
-  zip_dirent.c -- read directory entry (local or central), clean dirent
-  Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
-
-  This function is part of libzip, a library to manipulate ZIP archives.
-  The authors can be contacted at <nih@giga.or.at>
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in
-     the documentation and/or other materials provided with the
-     distribution.
-  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
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-static time_t phar_zip_d2u_time(int dtime, int ddate)
-{
-    struct tm *tm, tmbuf;
-    time_t now;
-
-    now = time(NULL);
-    tm = php_localtime_r(&now, &tmbuf);
-    
-    tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
-    tm->tm_mon = ((ddate>>5)&15) - 1;
-    tm->tm_mday = ddate&31;
-
-    tm->tm_hour = (dtime>>11)&31;
-    tm->tm_min = (dtime>>5)&63;
-    tm->tm_sec = (dtime<<1)&62;
-
-    return mktime(tm);
-}
-
-int phar_zip_parse(char *fname, int fname_len, char **error TSRMLS_DC)
-{
-       php_stream *fp;
-       char buf[8192], *metadata;
-       phar_zip_dir_end locator;
-       long size;
-       size_t read;
-       php_uint16 i;
-       phar_archive_data *mydata = NULL;
-       phar_entry_info entry = {0};
-
-       fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
-
-       php_stream_seek(fp, 0, SEEK_END);
-       size = php_stream_tell(fp);
-       if (size > sizeof(locator) + 65536) {
-               /* seek to max comment length + end of central directory record */
-               size = sizeof(locator) + 65536;
-               if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
-                       php_stream_close(fp);
-                       return FAILURE;
-               }
-       } else {
-               php_stream_seek(fp, 0, SEEK_SET);
-       }
-       do {
-               char *p = buf;
-               if (!(read = php_stream_read(fp, buf, 8192))) {
-                       php_stream_close(fp);
-                       return FAILURE;
-               }
-               while ((p=(char *) memchr(p + 1, 'P', (size_t)(buf - (p+1) + 8192 - 4 + 1))) != NULL) {
-                       if (!memcmp(p + 1, "K\5\6", 3)) {
-                               if (p - buf < sizeof(locator)) {
-                                       /* didn't read in the whole thing, back up */
-                                       php_stream_seek(fp, 8192 - (p - buf), SEEK_CUR);
-                                       if (sizeof(locator) != php_stream_read(fp, (char *) &locator, sizeof(locator))) {
-                                               php_stream_close(fp);
-                                               return FAILURE;
-                                       }
-                               } else {
-                                       memcpy((void *)&locator, (void *) p, sizeof(locator));
-                               }
-                               goto foundit;
-                       }
-               }
-       } while (read == 8192);
-       php_stream_close(fp);
-       return FAILURE;
-foundit:
-       if (locator.centraldisk != 0 || locator.disknumber != 0) {
-               /* split archives not handled */
-               php_stream_close(fp);
-               return FAILURE;
-       }
-       mydata = ecalloc(sizeof(phar_archive_data), 1);
-       mydata->fname = estrndup(fname, fname_len);
-#ifdef PHP_WIN32
-       phar_unixify_path_separators(mydata->fname, fname_len);
-#endif
-       mydata->is_zip = 1;
-       mydata->fname_len = fname_len;
-       /* clean up on big-endian systems */
-       /* read in archive comment, if any */
-       if (locator.comment_len) {
-               metadata = (char *) emalloc(locator.comment_len);
-               if (locator.comment_len != php_stream_read(fp, metadata, locator.comment_len)) {
-                       php_stream_close(fp);
-                       efree(mydata->fname);
-                       efree(mydata);
-                       return FAILURE;
-               }
-               if (phar_parse_metadata(&metadata, &mydata->metadata, locator.comment_len TSRMLS_CC) == FAILURE) {
-                       /* if not valid serialized data, it is a regular string */
-                       ALLOC_INIT_ZVAL(mydata->metadata);
-                       Z_STRVAL_P(mydata->metadata) = metadata;
-                       Z_STRLEN_P(mydata->metadata) = locator.comment_len;
-                       Z_TYPE_P(mydata->metadata) = IS_STRING;
-               }
-       } else {
-               mydata->metadata = NULL;
-       }
-       /* seek to central directory */
-       php_stream_seek(fp, locator.cdir_offset, SEEK_SET);
-       /* read in central directory */
-       zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
-               zend_get_hash_value, destroy_phar_manifest_entry, 0);
-       entry.phar = mydata;
-       entry.is_zip = 1;
-#define PHAR_ZIP_FAIL \
-                       zend_hash_destroy(&mydata->manifest); \
-                       php_stream_close(fp); \
-                       efree(mydata->fname); \
-                       if (mydata->metadata) { \
-                               zval_dtor(mydata->metadata); \
-                       } \
-                       efree(mydata); \
-                       return FAILURE
-
-       /* add each central directory item to the manifest */
-       for (i = 0; i < locator.cdir_size; i++) {
-               phar_zip_central_dir_file zipentry;
-
-               /* sizeof(zipentry) reports size of contents + 2, no idea why */
-               if (sizeof(zipentry)-2 != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry)-2)) {
-                       PHAR_ZIP_FAIL;
-               }
-               /* clean up for bigendian systems */
-               if (memcmp("PK\1\2", zipentry.signature, 4)) {
-                       /* corrupted entry */
-                       PHAR_ZIP_FAIL;
-               }
-               entry.compressed_filesize = zipentry.compsize;
-               entry.uncompressed_filesize = zipentry.uncompsize;
-               entry.crc32 = zipentry.crc32;
-               entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
-               entry.flags = PHAR_ENT_PERM_DEF_FILE;
-               if (zipentry.flags & PHAR_ZIP_FLAG_ENCRYPTED) {
-                       PHAR_ZIP_FAIL;
-               }
-               if (!zipentry.filename_len) {
-                       PHAR_ZIP_FAIL;
-               }
-               entry.filename_len = zipentry.filename_len;
-               entry.filename = (char *) emalloc(zipentry.filename_len+1);
-               if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
-                       efree(entry.filename);
-                       PHAR_ZIP_FAIL;
-               }
-               entry.filename[entry.filename_len] = '\0';
-               if (entry.filename[entry.filename_len - 1] == '/') {
-                       entry.is_dir = 1;
-                       entry.filename_len--;
-                       entry.flags |= PHAR_ENT_PERM_DEF_DIR;
-               } else {
-                       entry.is_dir = 0;
-               }
-               if (zipentry.extra_len) {
-                       if (FAILURE == phar_zip_process_extra(fp, &entry, zipentry.extra_len TSRMLS_CC)) {
-                               PHAR_ZIP_FAIL;
-                       }
-               }
-               switch (zipentry.compressed) {
-                       case PHAR_ZIP_COMP_NONE :
-                               /* compression flag already set */
-                               break;
-                       case PHAR_ZIP_COMP_DEFLATE :
-                               entry.flags |= PHAR_ENT_COMPRESSED_GZ;
-                               if (!phar_has_zlib) {
-                                       if (error) {
-                                               spprintf(error, 0, "zlib extension is required for zlib compressed zip-based .phar file \"%s\"", fname);
-                                       }
-                                       PHAR_ZIP_FAIL;
-                               }
-                               break;
-                       case PHAR_ZIP_COMP_BZIP2 :
-                               entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
-                               if (!phar_has_bz2) {
-                                       if (error) {
-                                               spprintf(error, 0, "bz2 extension is required for Bzip2 compressed zip-based .phar file \"%s\"", fname);
-                                       }
-                                       PHAR_ZIP_FAIL;
-                               }
-                               break;
-                       default :
-                               PHAR_ZIP_FAIL;
-               }
-               /* get file metadata */
-               if (zipentry.comment_len) {
-                       metadata = (char *) emalloc(zipentry.comment_len);
-                       if (zipentry.comment_len != php_stream_read(fp, metadata, zipentry.comment_len)) {
-                               PHAR_ZIP_FAIL;
-                       }
-                       if (phar_parse_metadata(&metadata, &(entry.metadata), zipentry.comment_len TSRMLS_CC) == FAILURE) {
-                               /* if not valid serialized data, it is a regular string */
-                               ALLOC_INIT_ZVAL(entry.metadata);
-                               Z_STRVAL_P(entry.metadata) = metadata;
-                               Z_STRLEN_P(entry.metadata) = zipentry.comment_len;
-                               Z_TYPE_P(entry.metadata) = IS_STRING;
-                       } else {
-                               efree(metadata);
-                       }
-               } else {
-                       entry.metadata = NULL;
-               }
-               if (entry.filename_len == sizeof(".phar/alias.txt")-1 && strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
-                       size_t remember = php_stream_tell(fp);
-                       php_stream_filter *filter;
-                       /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
-                       zval filterparams;
-
-                       /* archive alias found, seek to file contents, do not validate local header.  Potentially risky, but
-                          not very. */
-                       php_stream_seek(fp, zipentry.offset + sizeof(phar_zip_file_header) + entry.filename_len + zipentry.extra_len, SEEK_SET);
-                       if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
-/* this is defined in zlib's zconf.h */
-#ifndef MAX_WBITS
-#define MAX_WBITS 15
-#endif
-                               array_init(&filterparams);
-                               add_assoc_long(&filterparams, "window", MAX_WBITS + 32);
-                               filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
-                               if (!filter) {
-                                       add_assoc_long(&filterparams, "window", MAX_WBITS);
-                                       filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC);
-                                       if (!filter) {
-                                               PHAR_ZIP_FAIL;
-                                       }
-                               }
-                               zval_dtor(&filterparams);
-                               php_stream_filter_append(&fp->readfilters, filter);
-                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
-                                       PHAR_ZIP_FAIL;
-                               }
-                               php_stream_filter_flush(filter, 1);
-                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
-                       } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
-                               php_stream_filter *filter;
-                               filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
-                               if (!filter) {
-                                       PHAR_ZIP_FAIL;
-                               }
-                               php_stream_filter_append(&fp->readfilters, filter);
-                               php_stream_filter_append(&fp->readfilters, filter);
-                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
-                                       PHAR_ZIP_FAIL;
-                               }
-                               php_stream_filter_flush(filter, 1);
-                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
-                       }
-
-                       mydata->is_explicit_alias = 1;
-                       mydata->alias_len = zipentry.uncompsize;
-                       zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), mydata->alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
-               }
-               zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
-       }
-       zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
-       if (pphar) {
-               *pphar = mydata;
-       }
-       return SUCCESS;
-}
index 066c167cbfea043a58bee8aedff524f87dd67ded..e74b5ba47fd36b1501973e0776d21df066967dc2 100644 (file)
 
 /* $Id$ */
 
+#ifdef PHP_WIN32
+#pragma pack(1)
+# define PHAR_ZIP_PACK
+#else
+# define PHAR_ZIP_PACK __attribute__((__packed__))
+#endif
 typedef struct _phar_zip_file_header {
-       char magic[4];           /* local file header signature     4 bytes  (0x04034b50) */
+       char signature[4];       /* local file header signature     4 bytes  (0x04034b50) */
        char zipversion[2];      /* version needed to extract       2 bytes */
        php_uint16 flags;        /* general purpose bit flag        2 bytes */
        php_uint16 compressed;   /* compression method              2 bytes */
@@ -33,13 +39,13 @@ typedef struct _phar_zip_file_header {
        php_uint16 extra_len;    /* extra field length              2 bytes */
 /* file name (variable size) */
 /* extra field (variable size) */
-} phar_zip_file_header;
+} PHAR_ZIP_PACK phar_zip_file_header;
 
 typedef struct _phar_zip_file_datadesc {
        php_uint32 crc32;      /* crc-32                          4 bytes */
        php_uint32 compsize;   /* compressed size                 4 bytes */
        php_uint32 uncompsize; /* uncompressed size               4 bytes */
-} phar_zip_data_desc;
+} PHAR_ZIP_PACK phar_zip_data_desc;
 
 typedef struct _phar_zip_file_datadesc_zip64 {
        php_uint32 crc32;       /* crc-32                          4 bytes */
@@ -47,13 +53,13 @@ typedef struct _phar_zip_file_datadesc_zip64 {
        php_uint32 compsize2;
        php_uint32 uncompsize;  /* uncompressed size               8 bytes */
        php_uint32 uncompsize2;
-} phar_zip_data_desc_zip64;
+} PHAR_ZIP_PACK phar_zip_data_desc_zip64;
 
 typedef struct _phar_zip_archive_extra_data_record {
        char signature[4];   /* archive extra data signature    4 bytes  (0x08064b50) */
        php_uint32 len;      /* extra field length              4 bytes */
 /* extra field data                (variable size) */
-} phar_zip_archive_extra_data_record;
+} PHAR_ZIP_PACK phar_zip_archive_extra_data_record;
 
 /* madeby/extractneeded value if bzip2 compression is used */
 #define PHAR_ZIP_BZIP2 "46"
@@ -132,18 +138,18 @@ typedef struct _phar_zip_archive_extra_data_record {
 typedef struct _phar_zip_extra_field_header {
        char tag[2];
        php_uint16 size;
-} phar_zip_extra_field_header;
+} PHAR_ZIP_PACK phar_zip_extra_field_header;
 
 typedef struct _phar_zip_unix3 {
        char tag[2];            /* 0x756e        Short       tag for this extra block type ("nu") */
        php_uint16 size;        /* TSize         Short       total data size for this block */
        php_uint32 crc32;       /* CRC           Long        CRC-32 of the remaining data */
        php_uint16 perms;       /* Mode          Short       file permissions */
-       php_uint32 smylinksize; /* SizDev        Long        symlink'd size OR major/minor dev num */
+       php_uint32 symlinksize; /* SizDev        Long        symlink'd size OR major/minor dev num */
        php_uint16 uid;         /* UID           Short       user ID */
        php_uint16 gid;         /* GID           Short       group ID */
 /* (var.)        variable    symbolic link filename */
-} phar_zip_unix3;
+} PHAR_ZIP_PACK phar_zip_unix3;
 
 typedef struct _phar_zip_central_dir_file {
        char signature[4];            /* central file header signature   4 bytes  (0x02014b50) */
@@ -167,12 +173,12 @@ typedef struct _phar_zip_central_dir_file {
 /* file name (variable size) */
 /* extra field (variable size) */
 /* file comment (variable size) */
-} phar_zip_central_dir_file;
+} PHAR_ZIP_PACK phar_zip_central_dir_file;
 
 typedef struct _phar_zip_dir_signature {
        char signature[4];  /* header signature                4 bytes  (0x05054b50) */
        php_uint16 size;    /* size of data                    2 bytes */
-} phar_zip_dir_signature;
+} PHAR_ZIP_PACK phar_zip_dir_signature;
 
 typedef struct _phar_zip64_dir_end {
        char signature[4];        /* zip64 end of central dir 
@@ -198,7 +204,7 @@ typedef struct _phar_zip64_dir_end {
                                  the starting disk number        8 bytes */
        php_uint32 offset2;
 /* zip64 extensible data sector    (variable size) */
-} phar_zip64_dir_end;
+} PHAR_ZIP_PACK phar_zip64_dir_end;
 
 typedef struct _phar_zip64_dir_locator {
        char signature[4];     /* zip64 end of central dir locator 
@@ -210,7 +216,7 @@ typedef struct _phar_zip64_dir_locator {
                               end of central directory record 8 bytes */
        php_uint32 diroffset2;
        php_uint32 totaldisks; /* total number of disks           4 bytes */
-} phar_zip64_dir_locator;
+} PHAR_ZIP_PACK phar_zip64_dir_locator;
 
 typedef struct _phar_zip_dir_end {
        char signature[4];           /* end of central dir signature    4 bytes  (0x06054b50) */
@@ -227,11 +233,10 @@ typedef struct _phar_zip_dir_end {
                                     the starting disk number        4 bytes */
        php_uint16 comment_len;      /* .ZIP file comment length        2 bytes */
 /* .ZIP file comment       (variable size) */
-} phar_zip_dir_end;
-
-BEGIN_EXTERN_C()
-int phar_zip_parse(char *fname, int fname_len, char **error TSRMLS_DC);
-END_EXTERN_C()
+} PHAR_ZIP_PACK phar_zip_dir_end;
+#ifdef PHP_WIN32
+#pragma pack()
+#endif
 /*
  * Local variables:
  * tab-width: 4
index cf24b5045d0ad2fd5220403a26280b1033298c61..61ec675b79b899781e7a3f349c30633e91f2965c 100644 (file)
@@ -241,8 +241,9 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat
                        if (error) {
                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
                                efree(error);
+                       } else {
+                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
                        }
-                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
                        efree(internal_file);
                        php_url_free(resource);
                        return NULL;
@@ -260,56 +261,13 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat
                fprintf(stderr, "Cached:     %s\n", idata->internal_file->filedata ? "yes" : "no");
 #endif
 
-       /* do we have the data already? */
-       if (idata->fp) {
-               fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
-               efree(internal_file);
-               return fpf;
-       }
-
-#if PHP_MAJOR_VERSION < 6
-       if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-#endif
-       
-       if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) {
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-
-       fp = idata->phar->fp;
-
-       if (!idata->phar->is_zip && !fp) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot open phar \"%s\"", idata->phar->fname);
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-
-       if (!phar_open_jit(idata->phar, idata->internal_file, fp, &error, idata->for_write TSRMLS_CC)) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
-               efree(error);
-               phar_entry_delref(idata TSRMLS_CC);
-               efree(internal_file);
-               return NULL;
-       }
-       idata->fp = idata->internal_file->fp;
-       if (idata->fp == idata->phar->fp) {
-               idata->zero = idata->internal_file->offset_within_phar + idata->phar->internal_file_start;
-       }
-
        /* check length, crc32 */
-       if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) {
+       if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32, &error TSRMLS_CC) != SUCCESS) {
                /* already issued the error */
                phar_entry_delref(idata TSRMLS_CC);
                efree(internal_file);
                return NULL;
        }
-       idata->internal_file->is_crc_checked = 1;
 
        fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
        efree(internal_file);
@@ -344,18 +302,9 @@ static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRML
        /* use our proxy position */
        php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
 
-       if (!data->zero) {
-               got = php_stream_read(data->fp, buf, count);
-               if (data->fp->eof) {
-                       stream->eof = 1;
-               }
-               /* note the position, and restore the stream for the next fp */
-               data->position = php_stream_tell(data->fp);
-       } else {
-               got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position));
-               data->position = php_stream_tell(data->fp) - data->zero;
-               stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize);
-       }
+       got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position));
+       data->position = php_stream_tell(data->fp) - data->zero;
+       stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize);
 
 
        return got;
@@ -370,39 +319,28 @@ static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t
        phar_entry_data *data = (phar_entry_data *)stream->abstract;
 
        int res;
-       if (data->zero) {
-               off_t temp;
-               switch (whence) {
-                       case SEEK_END :
-                               temp = data->zero + data->internal_file->uncompressed_filesize + offset;
-                               break;
-                       case SEEK_CUR :
-                               temp = data->zero + data->position + offset;
-                               break;
-                       case SEEK_SET :
-                               temp = data->zero + offset;
-                               break;
-               }
-               if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) {
-                       *newoffset = -1;
-                       return -1;
-               }
-               if (temp < data->zero) {
-                       *newoffset = -1;
-                       return -1;
-               }
-               res = php_stream_seek(data->fp, temp, SEEK_SET);
-               *newoffset = php_stream_tell(data->fp) - data->zero;
-               data->position = *newoffset;
-               return res;
-       }
-       if (whence != SEEK_SET) {
-               /* use our proxy position, so the relative stuff works */
-               php_stream_seek(data->fp, data->position, SEEK_SET);
-       }
-       /* now do the actual seek */
-       res = php_stream_seek(data->fp, offset, whence);
-       *newoffset = php_stream_tell(data->fp);
+       off_t temp;
+       switch (whence) {
+               case SEEK_END :
+                       temp = data->zero + data->internal_file->uncompressed_filesize + offset;
+                       break;
+               case SEEK_CUR :
+                       temp = data->zero + data->position + offset;
+                       break;
+               case SEEK_SET :
+                       temp = data->zero + offset;
+                       break;
+       }
+       if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) {
+               *newoffset = -1;
+               return -1;
+       }
+       if (temp < data->zero) {
+               *newoffset = -1;
+               return -1;
+       }
+       res = php_stream_seek(data->fp, temp, SEEK_SET);
+       *newoffset = php_stream_tell(data->fp) - data->zero;
        data->position = *newoffset;
        return res;
 }
@@ -817,7 +755,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
        }
 
        if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
-               phar_entry_info new;
+               phar_entry_info new, *source;
 
                /* perform rename magic */
                if (entry->is_deleted) {
@@ -833,23 +771,21 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
                entry->fp = NULL;
                entry->metadata = 0;
                entry->link = NULL;
-#if HAVE_PHAR_ZIP
-               entry->zip = NULL;
-#endif
+               source = entry;
 
+               /* add to the manifest, and then store the pointer to the new guy in entry */
                zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
-               if (!entry->is_modified) {
-                       /* copy file contents into a new temp stream */
-                       if (!phar_open_jit(phar, entry, phar->fp, &error, 1 TSRMLS_CC)) {
-                               php_url_free(resource_from);
-                               php_url_free(resource_to);
-                               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
-                               efree(error);
-                               return 0;
-                       }
+
+               entry->filename = estrdup(resource_to->path+1);
+               if (FAILURE == phar_copy_entry_fp(source, entry, &error TSRMLS_CC)) {
+                       php_url_free(resource_from);
+                       php_url_free(resource_to);
+                       php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
+                       efree(error);
+                       zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
+                       return 0;
                }
                entry->is_modified = 1;
-               entry->filename = estrdup(resource_to->path+1);
                entry->filename_len = strlen(entry->filename);
                phar_flush(phar, 0, 0, &error TSRMLS_CC);
                if (error) {
@@ -857,6 +793,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
                        php_url_free(resource_to);
                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
                        efree(error);
+                       zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
                        return 0;
                }
        }
index 7eb39fa3bf9dd348a648ea35764e06d32fd5add3..f273acb0aaf95f61ad30d9e3050f1685401ab9b3 100644 (file)
@@ -20,7 +20,7 @@
 /* $Id$ */
 
 BEGIN_EXTERN_C()
-int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC);
+int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32, char **error TSRMLS_DC);
 
 php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC);
 void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC);
index e1b371bc9af0b4501ef37f89776994fe7928f3ff..8c395761e17e2d18b072f3879010a8a55d5c2c79 100644 (file)
@@ -235,7 +235,8 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i
                }
 
                entry.tar_type = ((old & (hdr->typeflag == 0))?'0':hdr->typeflag);
-               entry.offset_within_phar = pos;
+               entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
+               entry.fp_type = PHAR_FP;
                entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK;
                entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime));
 
@@ -337,7 +338,6 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC)
        size_t pos;
        phar_entry_info *entry = (phar_entry_info *) pDest;
        struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument;
-       php_stream *file;
        char padding[512];
 
        if (entry->is_deleted) {
@@ -387,6 +387,7 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC)
        }
 
        /* write header */
+       entry->header_offset = php_stream_tell(fp->new);
        if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) {
                if (fp->error) {
                        spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for  file \"%s\" could not be written", entry->phar->fname, entry->filename);
@@ -396,19 +397,16 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC)
        pos = php_stream_tell(fp->new); /* save start of file within tar */
 
        /* write contents */
-       if (entry->fp) {
-               /* new file */
-               file = entry->fp;
-               if (file == entry->phar->fp) {
-                       php_stream_seek(file, entry->offset_within_phar, SEEK_SET);
-               } else {
-                       php_stream_seek(file, 0, SEEK_SET);
+       if (FAILURE == phar_open_entry_fp(entry, fp->error TSRMLS_CC)) {
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+               if (fp->error) {
+                       spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename);
                }
-       } else {
-               file = fp->old;
-               php_stream_seek(file, entry->offset_within_phar, SEEK_SET);
+               return ZEND_HASH_APPLY_STOP;
        }
-       if (entry->uncompressed_filesize != php_stream_copy_to_stream(file, fp->new, entry->uncompressed_filesize)) {
+       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), fp->new, entry->uncompressed_filesize)) {
                if (fp->error) {
                        spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
                }
@@ -419,15 +417,14 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC)
        php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
 
        entry->is_modified = 0;
-       if (entry->fp && entry->fp_refcount == 0) {
-               if (entry->fp != entry->phar->fp) {
-                       php_stream_close(entry->fp);
-               }
+       if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
+               php_stream_close(entry->fp);
                entry->fp = NULL;
        }
+       entry->fp_type = PHAR_FP;
 
        /* note new location within tar */
-       entry->offset_within_phar = pos;
+       entry->offset = entry->offset_abs = pos;
        return ZEND_HASH_APPLY_KEEP;
 }
 
@@ -447,6 +444,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **er
        entry.is_tar = 1;
        entry.tar_type = '0';
        entry.phar = phar;
+       entry.fp_type = PHAR_MOD;
        /* set alias */
        if (phar->is_explicit_alias) {
                entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
@@ -574,6 +572,10 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **er
        if (phar->fp) {
                php_stream_close(phar->fp);
        }
+       if (phar->ufp) {
+               php_stream_close(phar->ufp);
+               phar->ufp = NULL;
+       }
 
        phar->is_brandnew = 0;
 
index 9620fdaaa3aecea7b7f5b450390569255f64920c..c57e4dbeaded95d37699f65d033b9313b7d021b6 100644 (file)
@@ -35,7 +35,7 @@ var_dump($a['dir']->isReadable());
 <?php 
 unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.1.phar.php');
 ?>
---EXPECTF--
+--EXPECT--
 bool(false)
 bool(true)
 bool(false)
index 631a8e016c319ed739b5cc5c68958c78ac8e8362..3cca4202031c7f6af6d2dedb44c5f4bad5eab6ef 100644 (file)
@@ -34,6 +34,7 @@ try {
 <?php 
 unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
 unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phartmp.php');
+unlink(dirname(__FILE__) . '/notphar.phar');
 __HALT_COMPILER();
 ?>
 --EXPECTF--
index 3d0e584e64044badf46e6099bfa07f5a28998836..b2855368bd1b7131249ad2220793fb0adee6f78d 100644 (file)
@@ -42,7 +42,13 @@ try {
 <?php 
 unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.1.phar.php');
 ?>
---EXPECTF--
+--EXPECT--
 bool(false)
-Cannot modify permissions for file "a.php" in phar "%s033.1.phar.php", not supported for zip-based phars
+bool(true)
+bool(false)
+test dir
+bool(true)
+bool(true)
+bool(false)
+bool(true)
 ===DONE===
index 9d560e438399801d3fb98f634e24fe0858ac5339..9a371eaac55719dccca479c7a0e55a95a48c32a3 100644 (file)
@@ -1,8 +1,7 @@
 --TEST--
-Phar::setStub()/stopBuffering() tar-based
+Phar::setStub()/stopBuffering() zip-based
 --SKIPIF--
 <?php if (!extension_loaded("phar")) die("skip"); ?>
-<?php if (!extension_loaded("zip")) die("skip"); ?>
 --INI--
 phar.require_hash=0
 phar.readonly=0
index f0dede60c9a15b81380b0caa573e87d807344f42..337682e7d8c6f0ece31da819ad2a62ed9acfc509 100644 (file)
@@ -2,7 +2,6 @@
 Phar::convertToPhar() from zip
 --SKIPIF--
 <?php if (!extension_loaded("phar")) die("skip"); ?>
-<?php if (!extension_loaded("zip")) die("skip"); ?>
 --INI--
 phar.require_hash=0
 phar.readonly=0
index 3053c8bbe244789b29290c6a72d80b5861f65567..199abc83054d4e7ab59553d18d8b8b655def8c96 100644 (file)
@@ -3,7 +3,6 @@ Phar: copy() zip-based
 --SKIPIF--
 <?php if (!extension_loaded("phar")) die("skip"); ?>
 <?php if (!extension_loaded("spl")) die("skip SPL not available"); ?>
-<?php if (!extension_loaded("zip")) die("skip"); ?>
 --INI--
 phar.readonly=0
 phar.require_hash=1
diff --git a/ext/phar/util.c b/ext/phar/util.c
new file mode 100644 (file)
index 0000000..4205ae2
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+  +----------------------------------------------------------------------+
+  | phar php single-file executable PHP extension                        |
+  | utility functions                                                    |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2005-2008 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt.                                 |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Gregory Beaver <cellog@php.net>                             |
+  |          Marcus Boerger <helly@php.net>                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "phar_internal.h"
+
+/* retrieve a phar_entry_info's current file pointer for reading contents */
+php_stream *phar_get_efp(phar_entry_info *entry)
+{
+       if (entry->fp_type == PHAR_FP) {
+               return entry->phar->fp;
+       } else if (entry->fp_type == PHAR_UFP) {
+               return entry->phar->ufp;
+       } else {
+               return entry->fp;
+       }
+}
+
+int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position TSRMLS_DC)
+{
+       php_stream *fp = phar_get_efp(entry);
+       off_t temp;
+
+       switch (whence) {
+               case SEEK_END :
+                       temp = entry->offset + entry->uncompressed_filesize + offset;
+                       break;
+               case SEEK_CUR :
+                       temp = entry->offset + position + offset;
+                       break;
+               case SEEK_SET :
+                       temp = entry->offset + offset;
+                       break;
+       }
+       if (temp > entry->offset + (off_t) entry->uncompressed_filesize) {
+               return -1;
+       }
+       if (temp < entry->offset) {
+               return -1;
+       }
+       return php_stream_seek(fp, temp, SEEK_SET);
+}
+
+/**
+ * Retrieve a copy of the file information on a single file within a phar, or null.
+ * This also transfers the open file pointer, if any, to the entry.
+ *
+ * If the file does not already exist, this will fail.  Pre-existing files can be
+ * appended, truncated, or read.  For read, if the entry is marked unmodified, it is
+ * assumed that the file pointer, if present, is opened for reading
+ */
+int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
+{
+       phar_archive_data *phar;
+       phar_entry_info *entry;
+       int for_write  = mode[0] != 'r' || mode[1] == '+';
+       int for_append = mode[0] == 'a';
+       int for_create = mode[0] != 'r';
+       int for_trunc  = mode[0] == 'w';
+
+       if (!ret) {
+               return FAILURE;
+       }
+       *ret = NULL;
+       if (error) {
+               *error = NULL;
+       }
+       if (for_write && PHAR_G(readonly)) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname);
+               }
+               return FAILURE;
+       }
+       if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
+               return FAILURE;
+       }
+       if (!path_len) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname);
+               }
+               return FAILURE;
+       }
+       if (allow_dir) {
+               if ((entry = phar_get_entry_info_dir(phar, path, path_len, 2, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
+                       if (for_create && !PHAR_G(readonly)) {
+                               return SUCCESS;
+                       }
+                       return FAILURE;
+               }
+       } else {
+               if ((entry = phar_get_entry_info(phar, path, path_len, for_create && !PHAR_G(readonly) ? NULL : error TSRMLS_CC)) == NULL) {
+                       if (for_create && !PHAR_G(readonly)) {
+                               return SUCCESS;
+                       }
+                       return FAILURE;
+               }
+       }
+       if (entry->is_modified && !for_write) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname);
+               }
+               return FAILURE;
+       }
+       if (entry->fp_refcount && for_write) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname);
+               }
+               return FAILURE;
+       }
+       if (entry->is_deleted) {
+               if (!for_create) {
+                       return FAILURE;
+               }
+               entry->is_deleted = 0;
+       }
+       if (entry->is_dir) {
+               *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
+               (*ret)->position = 0;
+               (*ret)->phar = phar;
+               (*ret)->for_write = for_write;
+               (*ret)->internal_file = entry;
+               (*ret)->is_zip = entry->is_zip;
+               (*ret)->is_tar = entry->is_tar;
+               entry->phar->refcount++;
+               entry->fp_refcount++;
+               return SUCCESS;
+       }
+       if (entry->fp_type == PHAR_MOD) {
+               if (for_trunc) {
+                       if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) {
+                               return FAILURE;
+                       }
+               } else if (for_append) {
+                       phar_seek_efp(entry, 0, SEEK_END, 0 TSRMLS_CC);
+               }
+       } else {
+               if (for_write) {
+                       if (for_trunc) {
+                               if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) {
+                                       return FAILURE;
+                               }
+                       } else {
+                               if (FAILURE == phar_separate_entry_fp(entry, error TSRMLS_CC)) {
+                                       return FAILURE;
+                               }
+                       }
+               } else {
+                       if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+                               return FAILURE;
+                       }
+               }
+       }
+       *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
+       (*ret)->position = 0;
+       (*ret)->phar = phar;
+       (*ret)->for_write = for_write;
+       (*ret)->internal_file = entry;
+       (*ret)->is_zip = entry->is_zip;
+       (*ret)->is_tar = entry->is_tar;
+       (*ret)->fp = phar_get_efp(entry);
+       (*ret)->zero = entry->offset;
+       entry->fp_refcount++;
+       entry->phar->refcount++;
+       return SUCCESS;
+}
+/* }}} */
+
+/**
+ * Create a new dummy file slot within a writeable phar for a newly created file
+ */
+phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC) /* {{{ */
+{
+       phar_archive_data *phar;
+       phar_entry_info *entry, etemp;
+       phar_entry_data *ret;
+       const char *pcr_error;
+       char is_dir = path[path_len - 1] == '/';
+
+       if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) {
+               return NULL;
+       }
+
+       if (FAILURE == phar_get_entry_data(&ret, fname, fname_len, path, path_len, mode, allow_dir, error TSRMLS_CC)) {
+               return NULL;
+       } else if (ret) {
+               return ret;
+       }
+
+       if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
+               if (error) {
+                       spprintf(error, 0, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
+               }
+               return NULL;
+       }
+
+       /* create a new phar data holder */
+       ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data));
+
+       /* create an entry, this is a new file */
+       memset(&etemp, 0, sizeof(phar_entry_info));
+       etemp.filename_len = path_len;
+       etemp.fp_type = PHAR_MOD;
+       etemp.fp = php_stream_fopen_tmpfile();
+       if (!etemp.fp) {
+               if (error) {
+                       spprintf(error, 0, "phar error: unable to create temporary file");
+               }
+               return NULL;
+       }
+       etemp.fp_refcount = 1;
+
+       if (is_dir) {
+               etemp.is_dir = 1;
+               etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_DIR;
+               etemp.filename_len--; /* strip trailing / */
+               path_len--;
+       } else {
+               etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE;
+       }
+       etemp.is_modified = 1;
+       etemp.timestamp = time(0);
+       etemp.is_crc_checked = 1;
+       etemp.phar = phar;
+       etemp.filename = estrndup(path, path_len);
+       etemp.is_zip = phar->is_zip;
+       if (phar->is_tar) {
+               etemp.is_tar = phar->is_tar;
+               etemp.tar_type = TAR_FILE;
+       }
+       zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry);
+       
+       if (!entry) {
+               php_stream_close(etemp.fp);
+               efree(etemp.filename);
+               return NULL;
+       }
+
+       phar->refcount++;
+       ret->phar = phar;
+       ret->fp = entry->fp;
+       ret->position = ret->zero = 0;
+       ret->for_write = 1;
+       ret->is_zip = entry->is_zip;
+       ret->is_tar = entry->is_tar;
+       ret->internal_file = entry;
+       return ret;
+}
+/* }}} */
+
+/* initialize a phar_archive_data's read-only fp for existing phar data */
+int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) 
+{
+       if (phar->fp) {
+               return SUCCESS;
+       }
+
+#if PHP_MAJOR_VERSION < 6
+       if (PG(safe_mode) && (!php_checkuid(phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
+               return FAILURE;
+       }
+#endif
+       
+       if (php_check_open_basedir(phar->fname TSRMLS_CC)) {
+               return FAILURE;
+       }
+
+       phar->fp = php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL);
+       if (!phar->fp) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+/* copy file data from an existing to a new phar_entry_info that is not in the manifest */
+int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC)
+{
+       if (FAILURE == phar_open_entry_fp(source, error TSRMLS_CC)) {
+               return FAILURE;
+       }
+       dest->fp_type = PHAR_MOD;
+       dest->offset = 0;
+       dest->is_modified = 1;
+       dest->fp = php_stream_fopen_tmpfile();
+
+       phar_seek_efp(source, 0, SEEK_SET, 0 TSRMLS_CC);
+       if (source->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(source), dest->fp, source->uncompressed_filesize)) {
+               php_stream_close(dest->fp);
+               dest->fp_type = PHAR_FP;
+               if (error) {
+                       spprintf(error, 4096, "phar error: unable to copy contents of file \"%s\" to \"%s\" in phar archive \"%s\"", source->filename, dest->filename, source->phar->fname);
+               }
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+/* open and decompress a compressed phar entry
+ */
+int phar_open_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) 
+{
+       php_stream_filter *filter;
+       phar_archive_data *phar = entry->phar;
+       char *filtername;
+       off_t loc;
+
+       if (entry->fp_type != PHAR_FP) {
+               /* either newly created or already modified */
+               return SUCCESS;
+       }
+       if (!phar->fp) {
+               if (FAILURE == phar_open_archive_fp(phar TSRMLS_CC)) {
+                       spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname);
+                       return FAILURE;
+               }
+       }
+       if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
+               return SUCCESS;
+       }
+       if (!phar->ufp) {
+               phar->ufp = php_stream_fopen_tmpfile();
+               if (!phar->ufp) {
+                       spprintf(error, 4096, "phar error: Cannot open temporary file for decompressing phar archive \"%s\" file \"%s\"", phar->fname, entry->filename);
+                       return FAILURE;
+               }
+       }
+
+       if ((filtername = phar_decompress_filter(entry, 0)) != NULL) {
+               filter = php_stream_filter_create(filtername, NULL, php_stream_is_persistent(phar->ufp) TSRMLS_CC);
+       } else {
+               filter = NULL;
+       }
+       if (!filter) {
+               spprintf(error, 4096, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename);
+               return FAILURE;
+       }
+       /* now we can safely use proper decompression */
+       /* save the new offset location within ufp */
+       php_stream_seek(phar->ufp, 0, SEEK_END);
+       loc = php_stream_tell(phar->ufp);
+       php_stream_filter_append(&phar->ufp->writefilters, filter);
+       php_stream_seek(phar->fp, entry->offset, SEEK_SET);
+       if (php_stream_copy_to_stream(phar->fp, phar->ufp, entry->compressed_filesize) != entry->compressed_filesize) {
+               spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
+               php_stream_filter_remove(filter, 1 TSRMLS_CC);
+               return FAILURE;
+       }
+       php_stream_filter_flush(filter, 1);
+       php_stream_flush(phar->ufp);
+       php_stream_filter_remove(filter, 1 TSRMLS_CC);
+       if (php_stream_tell(phar->ufp) - loc != (off_t) entry->uncompressed_filesize) {
+               spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename);
+               return FAILURE;
+       }
+
+       entry->old_flags = entry->flags;
+       entry->fp_type = PHAR_UFP;
+       /* this is now the new location of the file contents within this fp */
+       entry->offset = loc;
+
+       return SUCCESS;
+}
+
+#if defined(PHP_VERSION_ID) && PHP_VERSION_ID < 50202
+typedef struct {
+       char        *data;
+       size_t      fpos;
+       size_t      fsize;
+       size_t      smax;
+       int         mode;
+       php_stream  **owner_ptr;
+} php_stream_memory_data;
+#endif
+
+int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */
+{
+       if (entry->fp_type == PHAR_MOD) {
+               /* already newly created, truncate */
+#if PHP_VERSION_ID >= 50202
+               php_stream_truncate_set_size(entry->fp, 0);
+#else
+               if (php_stream_is(entry->fp, PHP_STREAM_IS_TEMP)) {
+                       if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_MEMORY)) {
+                               php_stream *inner = *(php_stream**)entry->fp->abstract;
+                               php_stream_memory_data *memfp = (php_stream_memory_data*)inner->abstract;
+                               memfp->fpos = 0;
+                               memfp->fsize = 0;
+                       } else if (php_stream_is(*(php_stream**)entry->fp->abstract, PHP_STREAM_IS_STDIO)) {
+                               php_stream_truncate_set_size(*(php_stream**)entry->fp->abstract, 0);
+                       } else {
+                               efree(*ret);
+                               *ret = NULL;
+                               if (error) {
+                                       spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
+                               }
+                               return FAILURE;
+                       }
+               } else if (php_stream_is(entry->fp, PHP_STREAM_IS_STDIO)) {
+                       php_stream_truncate_set_size(entry->fp, 0);
+               } else {
+                       efree(*ret);
+                       *ret = NULL;
+                       if (error) {
+                               spprintf(error, 0, "phar error: file \"%s\" cannot be opened for writing, no truncate support", fname);
+                       }
+                       return FAILURE;
+               }
+#endif
+               entry->old_flags = entry->flags;
+               entry->is_modified = 1;
+               phar->is_modified = 1;
+               /* reset file size */
+               entry->uncompressed_filesize = 0;
+               entry->compressed_filesize = 0;
+               entry->crc32 = 0;
+               entry->flags = PHAR_ENT_PERM_DEF_FILE;
+               entry->fp_type = PHAR_MOD;
+               entry->offset = 0;
+               return SUCCESS;
+       }
+       if (error) {
+               *error = NULL;
+       }
+       /* open a new temp file for writing */
+       entry->fp = php_stream_fopen_tmpfile();
+       if (!entry->fp) {
+               if (error) {
+                       spprintf(error, 0, "phar error: unable to create temporary file");
+               }
+               return FAILURE;
+       }
+       entry->old_flags = entry->flags;
+       entry->is_modified = 1;
+       phar->is_modified = 1;
+       /* reset file size */
+       entry->uncompressed_filesize = 0;
+       entry->compressed_filesize = 0;
+       entry->crc32 = 0;
+       entry->flags = PHAR_ENT_PERM_DEF_FILE;
+       entry->fp_type = PHAR_MOD;
+       entry->offset = 0;
+       return SUCCESS;
+}
+/* }}} */
+
+int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC)
+{
+       php_stream *fp;
+
+       if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+               return FAILURE;
+       }
+
+       if (entry->fp_type == PHAR_MOD) {
+               return SUCCESS;
+       }
+
+       fp = php_stream_fopen_tmpfile();
+       phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), fp, entry->uncompressed_filesize)) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: cannot separate entry file \"%s\" contents in phar archive \"%s\" for write access", entry->filename, entry->phar->fname);
+               }
+               return FAILURE;
+       }
+
+       entry->offset = 0;
+       entry->fp = fp;
+       entry->fp_type = PHAR_MOD;
+       entry->is_modified = 1;
+       return SUCCESS;
+}
+
+/**
+ * helper function to open an internal file's fp just-in-time
+ */
+phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp,
+                                     char **error, int for_write TSRMLS_DC)
+{
+       if (error) {
+               *error = NULL;
+       }
+       /* seek to start of internal file and read it */
+       if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+               return NULL;
+       }
+       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+               spprintf(error, 4096, "phar error: cannot seek to start of file \"%s\" in phar \"%s\"", entry->filename, phar->fname);
+               return NULL;
+       }
+       return entry;
+}
+
+/**
+ * Looks up a phar archive in the filename map, connecting it to the alias
+ * (if any) or returns null
+ */
+int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC) /* {{{ */
+{
+       phar_archive_data *fd, **fd_ptr;
+       char *my_realpath, *save;
+       int save_len;
+
+       phar_request_initialize(TSRMLS_C);
+
+       if (error) {
+               *error = NULL;
+       }
+       *archive = NULL;
+       if (alias && alias_len) {
+               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) {
+                       if (fname && (fname_len != (*fd_ptr)->fname_len || strncmp(fname, (*fd_ptr)->fname, fname_len))) {
+                               if (error) {
+                                       spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname);
+                               }
+                               return FAILURE;
+                       }
+                       *archive = *fd_ptr;
+                       return SUCCESS;
+               }
+       }
+       my_realpath = NULL;
+       save = fname;
+       save_len = fname_len;
+       if (fname && fname_len) {
+               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
+                       *archive = *fd_ptr;
+                       fd = *fd_ptr;
+                       if (alias && alias_len) {
+                               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd,   sizeof(phar_archive_data*), NULL);
+                       }
+                       return SUCCESS;
+               }
+               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, (void**)&fd_ptr)) {
+                       *archive = *fd_ptr;
+                       return SUCCESS;
+               }
+
+               /* not found, try converting \ to / */
+               my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC);
+               if (my_realpath) {
+                       fname_len = strlen(my_realpath);
+                       fname = my_realpath;
+               } else {
+                       return FAILURE;
+               }
+#ifdef PHP_WIN32
+               phar_unixify_path_separators(fname, fname_len);
+#endif
+               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) {
+                       *archive = *fd_ptr;
+                       fd = *fd_ptr;
+                       if (alias && alias_len) {
+                               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd,   sizeof(phar_archive_data*), NULL);
+                       }
+                       efree(my_realpath);
+                       return SUCCESS;
+               }
+               efree(my_realpath);
+       }
+       return FAILURE;
+}
+/* }}} */
+
+/**
+ * Determine which stream compression filter (if any) we need to read this file
+ */
+char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
+{
+       switch (entry->flags & PHAR_ENT_COMPRESSION_MASK) {
+       case PHAR_ENT_COMPRESSED_GZ:
+               return "zlib.deflate";
+       case PHAR_ENT_COMPRESSED_BZ2:
+               return "bzip2.compress";
+       default:
+               return return_unknown ? "unknown" : NULL;
+       }
+}
+/* }}} */
+
+/**
+ * Determine which stream decompression filter (if any) we need to read this file
+ */
+char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
+{
+       php_uint32 flags;
+
+       if (entry->is_modified) {
+               flags = entry->old_flags;
+       } else {
+               flags = entry->flags;
+       }
+       switch (flags & PHAR_ENT_COMPRESSION_MASK) {
+       case PHAR_ENT_COMPRESSED_GZ:
+               return "zlib.inflate";
+       case PHAR_ENT_COMPRESSED_BZ2:
+               return "bzip2.decompress";
+       default:
+               return return_unknown ? "unknown" : NULL;
+       }
+}
+/* }}} */
+
+/**
+ * retrieve information on a file contained within a phar, or null if it ain't there
+ */
+phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, int path_len, char **error TSRMLS_DC) /* {{{ */
+{
+       return phar_get_entry_info_dir(phar, path, path_len, 0, error TSRMLS_CC);
+}
+/* }}} */
+/**
+ * retrieve information on a file or directory contained within a phar, or null if none found
+ * allow_dir is 0 for none, 1 for both empty directories in the phar and temp directories, and 2 for only
+ * valid pre-existing empty directory entries
+ */
+phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, int path_len, char dir, char **error TSRMLS_DC) /* {{{ */
+{
+       const char *pcr_error;
+       phar_entry_info *entry;
+       char is_dir = (path[path_len - 1] == '/');
+
+       if (error) {
+               *error = NULL;
+       }
+
+       if (!path_len && !dir) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path);
+               }
+               return NULL;
+       }
+       if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) {
+               if (error) {
+                       spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error);
+               }
+               return NULL;
+       }
+
+       if (!&phar->manifest.arBuckets) {
+               return NULL;
+       }
+       if (is_dir) {
+               path_len--;
+       }
+       if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) {
+               if (entry->is_deleted) {
+                       /* entry is deleted, but has not been flushed to disk yet */
+                       return NULL;
+               }
+               if (entry->is_dir && !dir) {
+                       if (error) {
+                               spprintf(error, 4096, "phar error: path \"%s\" is a directory", path);
+                       }
+                       return NULL;
+               }
+               if (!entry->is_dir && is_dir) {
+                       /* user requested a directory, we must return one */
+                       if (error) {
+                               spprintf(error, 4096, "phar error: path \"%s\" exists and is a not a directory", path);
+                       }
+                       return NULL;
+               }
+               return entry;
+       }
+       if (dir == 1) {
+               /* try to find a directory */
+               HashTable *manifest;
+               char *key;
+               uint keylen;
+               ulong unused;
+
+               if (!path_len) {
+                       path = "/";
+               }
+               manifest = &phar->manifest;
+               zend_hash_internal_pointer_reset(manifest);
+               while (FAILURE != zend_hash_has_more_elements(manifest)) {
+                       if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
+                               break;
+                       }
+                       if (0 != memcmp(key, path, path_len)) {
+                               /* entry in directory not found */
+                               if (SUCCESS != zend_hash_move_forward(manifest)) {
+                                       break;
+                               }
+                               continue;
+                       } else {
+                               if (key[path_len] != '/') {
+                                       if (SUCCESS != zend_hash_move_forward(manifest)) {
+                                               break;
+                                       }
+                                       continue;
+                               }
+                               /* found a file in this path */
+                               entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
+                               /* this next line tells PharFileInfo->__destruct() to efree the filename */
+                               entry->is_temp_dir = entry->is_dir = 1;
+                               entry->filename = (char *) estrndup(path, path_len + 1);
+                               entry->filename_len = path_len;
+                               return entry;
+                       }
+               }
+       }
+       return NULL;
+}
+/* }}} */
index 0f7881c6ae90b39fe9c9a5c79776ebe434f6e85b..b7c35acdcd8fcd603332037ec9d1989b70174c3a 100644 (file)
@@ -17,7 +17,6 @@
 */
 
 #include "phar_internal.h"
-#include "php_stream_unlink.h"
 
 #ifdef PHP_WIN32
 static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{ */
@@ -34,6 +33,104 @@ static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{
 /* }}} */
 #endif
 
+static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC)
+{
+       union {
+               phar_zip_extra_field_header header;
+               phar_zip_unix3 unix3;
+       } h;
+
+       do {
+               if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
+                       return FAILURE;
+               }
+               /* clean up header for big-endian systems */
+               if (h.header.tag != "nu") {
+                       /* skip to next header */
+                       php_stream_seek(fp, h.header.size, SEEK_CUR);
+                       len -= h.header.size + 4;
+                       continue;
+               }
+               /* unix3 header found */
+               /* clean up header for big-endian systems */
+               if (sizeof(h.unix3) != php_stream_read(fp, (char *) &h.unix3, sizeof(h.unix3))) {
+                       if (h.unix3.size > sizeof(h.unix3) - 4) {
+                               /* skip symlink filename - we may add this support in later */
+                               php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR);
+                       }
+                       /* set permissions */
+                       entry->flags &= PHAR_ENT_COMPRESSION_MASK;
+                       if (entry->is_dir) {
+                               entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_DIR;
+                       } else {
+                               entry->flags |= h.unix3.perms & PHAR_ENT_PERM_DEF_FILE;
+                       }
+               }
+       } while (len);
+       return SUCCESS;
+}
+
+/*
+  extracted from libzip
+  zip_dirent.c -- read directory entry (local or central), clean dirent
+  Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
+
+  This function is part of libzip, a library to manipulate ZIP archives.
+  The authors can be contacted at <nih@giga.or.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  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
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+static time_t phar_zip_d2u_time(int dtime, int ddate)
+{
+    struct tm *tm, tmbuf;
+    time_t now;
+
+    now = time(NULL);
+    tm = php_localtime_r(&now, &tmbuf);
+    
+    tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
+    tm->tm_mon = ((ddate>>5)&15) - 1;
+    tm->tm_mday = ddate&31;
+
+    tm->tm_hour = (dtime>>11)&31;
+    tm->tm_min = (dtime>>5)&63;
+    tm->tm_sec = (dtime<<1)&62;
+
+    return mktime(tm);
+}
+
+static void phar_zip_u2d_time(time_t time, php_uint16 *dtime, php_uint16 *ddate)
+{
+    struct tm *tm, tmbuf;
+
+    tm = php_localtime_r(&time, &tmbuf);
+    *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday;
+    *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1);
+}
+
 /**
  * Does not check for a previously opened phar in the cache.
  *
@@ -43,44 +140,55 @@ static inline void phar_unixify_path_separators(char *path, int path_len) /* {{{
  * This is used by phar_open_fp to process a zip-based phar, but can be called
  * directly.
  */
-int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
+int phar_open_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
 {
-#if HAVE_PHAR_ZIP
-       struct zip *zip;
-       int ziperror, i, phar_alias_index, register_alias, metadata_len;
-       phar_entry_info entry = {0};
-       struct zip_stat zs;
-       char tmp_buf[1024], *metadata;
+       char buf[8192], *metadata;
+       phar_zip_dir_end locator;
+       long size;
+       size_t read;
+       php_uint16 i;
        phar_archive_data *mydata = NULL;
+       phar_entry_info entry = {0};
 
-       if (error) {
-               *error = NULL;
-       }
-
-       if (!phar_has_zip) {
-               spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\", ext/zip is not enabled", fname);
-               return FAILURE;
+       size = php_stream_tell(fp);
+       if (size > sizeof(locator) + 65536) {
+               /* seek to max comment length + end of central directory record */
+               size = sizeof(locator) + 65536;
+               if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
+                       php_stream_close(fp);
+                       return FAILURE;
+               }
+       } else {
+               php_stream_seek(fp, 0, SEEK_SET);
        }
-
-       zip = zip_open(fname, 0, &ziperror);
-       if (!zip) {
-               if (error) {
-                       /* now for the stupid hoops libzip forces... */
-                       char *tmp;
-                       int tmp_len;
-                       tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
-                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
-                               spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
-                       } else {
-                               if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
-                                       efree(tmp);
-                                       spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
+       do {
+               char *p = buf;
+               if (!(read = php_stream_read(fp, buf, 8192))) {
+                       php_stream_close(fp);
+                       return FAILURE;
+               }
+               while ((p=(char *) memchr(p + 1, 'P', (size_t)(buf - (p+1) + 8192 - 4 + 1))) != NULL) {
+                       if (!memcmp(p + 1, "K\5\6", 3)) {
+                               if (p - buf < sizeof(locator)) {
+                                       /* didn't read in the whole thing, back up */
+                                       php_stream_seek(fp, 8192 - (p - buf), SEEK_CUR);
+                                       if (sizeof(locator) != php_stream_read(fp, (char *) &locator, sizeof(locator))) {
+                                               php_stream_close(fp);
+                                               return FAILURE;
+                                       }
                                } else {
-                                       spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\": %s", fname, tmp);
-                                       efree(tmp);
+                                       memcpy((void *)&locator, (void *) p, sizeof(locator));
                                }
+                               goto foundit;
                        }
                }
+       } while (read == 8192);
+       php_stream_close(fp);
+       return FAILURE;
+foundit:
+       if (locator.centraldisk != 0 || locator.disknumber != 0) {
+               /* split archives not handled */
+               php_stream_close(fp);
                return FAILURE;
        }
        mydata = ecalloc(sizeof(phar_archive_data), 1);
@@ -88,139 +196,188 @@ int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, ph
 #ifdef PHP_WIN32
        phar_unixify_path_separators(mydata->fname, fname_len);
 #endif
+       mydata->is_zip = 1;
        mydata->fname_len = fname_len;
-       if (-1 != (phar_alias_index = zip_name_locate(zip, ".phar/alias.txt", 0))) {
-               struct zip_file *zf = zip_fopen_index(zip, phar_alias_index, 0);
-               int tmp_len;
-
-               tmp_len = zip_fread(zf, tmp_buf, 1024);
-               zip_fclose(zf);
-               /* if the alias is stored we enforce it (implicit overrides explicit) */
-               if (tmp_len != -1 && alias && alias_len && (alias_len != tmp_len || strncmp(alias, tmp_buf, tmp_len)))
-               {
-                       if (error) {
-                               spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, tmp_buf, alias);
-                       }
+       /* clean up on big-endian systems */
+       /* read in archive comment, if any */
+       if (locator.comment_len) {
+               metadata = (char *) emalloc(locator.comment_len);
+               if (locator.comment_len != php_stream_read(fp, metadata, locator.comment_len)) {
+                       php_stream_close(fp);
+                       efree(mydata->fname);
+                       efree(mydata);
                        return FAILURE;
                }
-               if (tmp_len != -1) {
-                       /* use implicit alias */
-                       alias = tmp_buf;
-                       alias_len = tmp_len;
-                       register_alias = 0;
-               }
-       } else {
-               register_alias = alias ? 1 : 0;
-       }
-       mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, alias_len);
-       mydata->alias_len = alias ? alias_len : fname_len;
-       mydata->is_zip = 1;
-       mydata->zip = zip;
-
-       phar_request_initialize(TSRMLS_C);
-
-       /* read in phar metadata (zip file comment) */
-       metadata = (char *) zip_get_archive_comment(zip, &metadata_len, 0);
-       if (metadata) {
-               if (phar_parse_metadata(&metadata, &mydata->metadata, metadata_len TSRMLS_CC) == FAILURE) {
+               if (phar_parse_metadata(&metadata, &mydata->metadata, locator.comment_len TSRMLS_CC) == FAILURE) {
                        /* if not valid serialized data, it is a regular string */
                        ALLOC_INIT_ZVAL(mydata->metadata);
-                       Z_STRVAL_P(mydata->metadata) = estrndup(metadata, metadata_len);
-                       Z_STRLEN_P(mydata->metadata) = metadata_len;
+                       Z_STRVAL_P(mydata->metadata) = metadata;
+                       Z_STRLEN_P(mydata->metadata) = locator.comment_len;
                        Z_TYPE_P(mydata->metadata) = IS_STRING;
                }
        } else {
                mydata->metadata = NULL;
        }
-       /* set up our manifest */
+       /* seek to central directory */
+       php_stream_seek(fp, locator.cdir_offset, SEEK_SET);
+       /* read in central directory */
        zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
                zend_get_hash_value, destroy_phar_manifest_entry, 0);
        entry.phar = mydata;
        entry.is_zip = 1;
-       /* prevent CRC checking */
-       entry.is_crc_checked = 1;
-       for (i = 0; i < zip_get_num_files(zip); i++) {
-               char *name;
-               name = (char *) zip_get_name(zip, i, 0);
-               if (name) {
-                       /* get file stat */
-                       if (-1 != zip_stat_index(zip, i, 0, &zs)) {
-                               entry.compressed_filesize = zs.comp_size;
-                               entry.uncompressed_filesize = zs.size;
-                               entry.crc32 = zs.crc;
-                               entry.timestamp = (php_uint32) zs.mtime;
-                               entry.flags = PHAR_ENT_PERM_DEF_FILE;
-                               switch (zs.comp_method) {
-                                       case ZIP_CM_DEFLATE :
-                                               /* if we have zip, we have zlib decompression */
-                                               entry.flags |= PHAR_ENT_COMPRESSED_GZ;
-                                               break;
-                                       case ZIP_CM_BZIP2 :
-                                               if (!phar_has_bz2) {
-                                                       if (mydata->metadata) {
-                                                               zval_dtor(mydata->metadata);
-                                                       }
-                                                       efree(mydata->fname);
-                                                       if (mydata->alias) {
-                                                               efree(mydata->alias);
-                                                       }
-                                                       zip_close(zip);
-                                                       zend_hash_destroy(&(mydata->manifest));
-                                                       mydata->manifest.arBuckets = NULL;
-                                                       efree(mydata);
-                                                       if (error) {
-                                                               spprintf(error, 0, "bz2 extension is required for Bzip2 compressed zip-based .phar file \"%s\"", fname);
-                                                       }
-                                                       return FAILURE;
-                                               }
-                                               entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
-                                               break;
+       entry.fp_type = PHAR_FP;
+#define PHAR_ZIP_FAIL(errmsg) \
+                       zend_hash_destroy(&mydata->manifest); \
+                       php_stream_close(fp); \
+                       if (mydata->metadata) { \
+                               zval_dtor(mydata->metadata); \
+                       } \
+                       if (error) { \
+                               spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
+                       } \
+                       efree(mydata->fname); \
+                       if (mydata->alias) { \
+                               efree(mydata->alias); \
+                       } \
+                       efree(mydata); \
+                       return FAILURE
+
+       /* add each central directory item to the manifest */
+       for (i = 0; i < locator.count; i++) {
+               phar_zip_central_dir_file zipentry;
+
+               if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
+                       PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
+               }
+               /* clean up for bigendian systems */
+               if (memcmp("PK\1\2", zipentry.signature, 4)) {
+                       /* corrupted entry */
+                       PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
+               }
+               entry.compressed_filesize = zipentry.compsize;
+               entry.uncompressed_filesize = zipentry.uncompsize;
+               entry.crc32 = zipentry.crc32;
+               entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
+               entry.flags = PHAR_ENT_PERM_DEF_FILE;
+               entry.header_offset = zipentry.offset;
+               entry.offset = entry.offset_abs = zipentry.offset + sizeof(phar_zip_file_header) + zipentry.filename_len +
+                       zipentry.extra_len;
+               if (zipentry.flags & PHAR_ZIP_FLAG_ENCRYPTED) {
+                       PHAR_ZIP_FAIL("Cannot process encrypted zip files");
+               }
+               if (!zipentry.filename_len) {
+                       PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
+               }
+               entry.filename_len = zipentry.filename_len;
+               entry.filename = (char *) emalloc(zipentry.filename_len+1);
+               if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
+                       efree(entry.filename);
+                       PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
+               }
+               entry.filename[entry.filename_len] = '\0';
+               if (entry.filename[entry.filename_len - 1] == '/') {
+                       entry.is_dir = 1;
+                       entry.filename_len--;
+                       entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+               } else {
+                       entry.is_dir = 0;
+               }
+               if (zipentry.extra_len) {
+                       off_t loc = php_stream_tell(fp);
+                       if (FAILURE == phar_zip_process_extra(fp, &entry, zipentry.extra_len TSRMLS_CC)) {
+                               PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
+                       }
+                       php_stream_seek(fp, loc + zipentry.extra_len, SEEK_SET);
+               }
+               switch (zipentry.compressed) {
+                       case PHAR_ZIP_COMP_NONE :
+                               /* compression flag already set */
+                               break;
+                       case PHAR_ZIP_COMP_DEFLATE :
+                               entry.flags |= PHAR_ENT_COMPRESSED_GZ;
+                               if (!phar_has_zlib) {
+                                       PHAR_ZIP_FAIL("zlib extension is required");
+                               }
+                               break;
+                       case PHAR_ZIP_COMP_BZIP2 :
+                               entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
+                               if (!phar_has_bz2) {
+                                       PHAR_ZIP_FAIL("bzip2 extension is required");
                                }
+                               break;
+                       default :
+                               PHAR_ZIP_FAIL("unsupported compression method used in this zip");
+               }
+               /* get file metadata */
+               if (zipentry.comment_len) {
+                       metadata = (char *) emalloc(zipentry.comment_len);
+                       if (zipentry.comment_len != php_stream_read(fp, metadata, zipentry.comment_len)) {
+                               PHAR_ZIP_FAIL("unable to read in file comment, truncated");
                        }
-                       entry.index = i;
-                       entry.filename_len = strlen(name);
-                       if (name[entry.filename_len - 1] == '/') {
-                               entry.is_dir = 1;
-                               entry.filename_len--;
-                               entry.flags |= PHAR_ENT_PERM_DEF_DIR;
+                       if (phar_parse_metadata(&metadata, &(entry.metadata), zipentry.comment_len TSRMLS_CC) == FAILURE) {
+                               /* if not valid serialized data, it is a regular string */
+                               ALLOC_INIT_ZVAL(entry.metadata);
+                               Z_STRVAL_P(entry.metadata) = metadata;
+                               Z_STRLEN_P(entry.metadata) = zipentry.comment_len;
+                               Z_TYPE_P(entry.metadata) = IS_STRING;
                        } else {
-                               entry.is_dir = 0;
+                               efree(metadata);
                        }
-                       entry.filename = estrndup(name, entry.filename_len);
-                       /* get file metadata */
-                       metadata = (char *) zip_get_file_comment(zip, i, &metadata_len, 0);
-                       if (metadata) {
-                               if (phar_parse_metadata(&metadata, &(entry.metadata), metadata_len TSRMLS_CC) == FAILURE) {
-                                       /* if not valid serialized data, it is a regular string */
-                                       ALLOC_INIT_ZVAL(entry.metadata);
-                                       Z_STRVAL_P(entry.metadata) = estrndup(metadata, metadata_len);
-                                       Z_STRLEN_P(entry.metadata) = metadata_len;
-                                       Z_TYPE_P(entry.metadata) = IS_STRING;
+               } else {
+                       entry.metadata = NULL;
+               }
+               if (entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
+                       php_stream_filter *filter;
+                       off_t saveloc;
+
+                       /* archive alias found, seek to file contents, do not validate local header.  Potentially risky, but
+                          not very. */
+                       saveloc = php_stream_tell(fp);
+                       php_stream_seek(fp, zipentry.offset + sizeof(phar_zip_file_header) + entry.filename_len + zipentry.extra_len, SEEK_SET);
+                       if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
+                               filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+                               if (!filter) {
+                                       PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
                                }
+                               php_stream_filter_append(&fp->readfilters, filter);
+                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+                                       PHAR_ZIP_FAIL("unable to read in alias, truncated");
+                               }
+                               php_stream_filter_flush(filter, 1);
+                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
+                       } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
+                               php_stream_filter *filter;
+                               filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
+                               if (!filter) {
+                                       PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
+                               }
+                               php_stream_filter_append(&fp->readfilters, filter);
+                               php_stream_filter_append(&fp->readfilters, filter);
+                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+                                       PHAR_ZIP_FAIL("unable to read in alias, truncated");
+                               }
+                               php_stream_filter_flush(filter, 1);
+                               php_stream_filter_remove(filter, 1 TSRMLS_CC);
                        } else {
-                               entry.metadata = NULL;
+                               if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) {
+                                       PHAR_ZIP_FAIL("unable to read in alias, truncated");
+                               }
                        }
-                       zend_hash_add(&mydata->manifest, name, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
-               } 
-       }
-       /* ignore all errors in loading up manifest */
-       zip_error_clear(zip);
 
-       zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
-       if (register_alias) {
-               mydata->is_explicit_alias = 1;
-               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
-       } else {
-               mydata->is_explicit_alias = 0;
+                       mydata->is_explicit_alias = 1;
+                       mydata->alias_len = zipentry.uncompsize;
+                       zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), mydata->alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
+                       /* return to central directory parsing */
+                       php_stream_seek(fp, saveloc, SEEK_SET);
+               }
+               zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
        }
+       mydata->fp = fp;
+       zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
        if (pphar) {
                *pphar = mydata;
        }
        return SUCCESS;
-#else
-       spprintf(error, 4096, "phar zip error: Cannot open zip-based phar \"%s\", phar not compiled with zip enabled", fname);
-       return FAILURE;
-#endif
 }
 /* }}} */
 
@@ -229,7 +386,6 @@ int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, ph
  */
 int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
 {
-#if HAVE_PHAR_ZIP
        phar_archive_data *phar;
        int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, options, &phar, error TSRMLS_CC);
 
@@ -243,49 +399,10 @@ int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_l
                return ret;
        }
 
-       if (!phar_has_zip) {
-               if (error) {
-                       spprintf(error, 4096, "phar zip error: phar \"%s\" cannot be created as zip-based phar, zip-based phars are disabled (enable ext/zip)", fname);
-               }
-               return FAILURE;
-       }
        if (phar->is_brandnew) {
-               int *errorp = NULL;
+               phar->internal_file_start = 0;
                phar->is_zip = 1;
-               if (phar->fp) {
-                       php_stream_close(phar->fp);
-                       phar->fp = NULL;
-                       php_stream_unlink(phar->fname, 0, NULL);
-               }
-               phar->zip = zip_open(fname, 0 | ZIP_CREATE | ZIP_EXCL, errorp);
-               if (NULL != phar->zip) {
-                       return SUCCESS;
-               }
-               /* fail - free newly created manifest entry */
-               zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), phar->alias, phar->alias_len);
-               zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len);
-               efree(phar->fname);
-               efree(phar->alias);
-               efree(phar);
-               *pphar = NULL;
-
-               if (error) {
-                       /* now for the stupid hoops libzip forces... */
-                       char *tmp;
-                       int tmp_len;
-                       tmp_len = zip_error_to_str(NULL, 0, *errorp, *errorp);
-                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
-                               spprintf(error, 4096, "phar zip error: cannot create zip-based phar \"%s\"", fname);
-                       } else {
-                               if (!zip_error_to_str(tmp, tmp_len + 1, *errorp, *errorp)) {
-                                       spprintf(error, 4096, "phar zip error: cannot create zip-based phar \"%s\"", fname);
-                               } else {
-                                       spprintf(error, 4096, "phar zip error: cannot create zip-based phar \"%s\": %s", fname, tmp);
-                                       efree(tmp);
-                               }
-                       }
-               }
-               return FAILURE;
+               return SUCCESS;
        }
 
        /* we've reached here - the phar exists and is a regular phar */
@@ -293,56 +410,29 @@ int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_l
                spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname);
        }
        return FAILURE;
-#else
-       if (error) {
-               spprintf(error, 4096, "phar zip error: phar \"%s\" cannot be created as zip-based phar, zip-based phars are disabled and cannot be enabled", fname);
-       }
-       return FAILURE;
-#endif /* #if HAVE_PHAR_ZIP */
 }
 /* }}} */
 
-#if HAVE_PHAR_ZIP
-static ssize_t phar_zip_source(void *state, void *data, size_t len, enum zip_source_cmd cmd)
-{
-       char *buf = (char *) data;
-       phar_entry_info *entry = (phar_entry_info *) state;
-       size_t read;
-       struct zip_stat *sb = (struct zip_stat *) data;
-       TSRMLS_FETCH();
-
-       switch (cmd) {
-               case ZIP_SOURCE_OPEN :
-                       /* offset_within_phar is only non-zero when converting from tar/phar-based to zip-based */
-                       php_stream_seek(entry->fp, entry->offset_within_phar, SEEK_SET);
-                       return 0;
-               case ZIP_SOURCE_READ :
-                       read = php_stream_read(entry->fp, buf, len);
-                       if (read < 0) return 0;
-                       return read;
-               case ZIP_SOURCE_STAT :
-                       zip_stat_init(sb);
-                       sb->mtime = time(NULL);
-                       sb->size = entry->uncompressed_filesize;
-                       return sizeof(struct zip_stat);
-               case ZIP_SOURCE_FREE:
-                       entry->is_modified = 0;
-                       /* phar->fp is set only if we're converting from a tar/phar-based archive */
-                       if (entry->fp && entry->fp_refcount == 0 && entry->fp != entry->phar->fp) {
-                               php_stream_close(entry->fp);
-                               entry->fp = NULL;
-                       }
-               default:
-                       return len;
-       }
-}
-
-/* reconstruct the zip index of each manifest entry */
-static int phar_zip_reconstruct_apply(void *data TSRMLS_DC) /* {{{ */
+struct _phar_zip_pass {
+       php_stream *filefp;
+       php_stream *centralfp;
+       php_stream *old;
+       char **error;
+};
+/* perform final modification of zip contents for each file in the manifest before saving */
+static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
 {
-       phar_entry_info *entry = (phar_entry_info *)data;
+       phar_entry_info *entry;
+       phar_zip_file_header local;
+       phar_zip_unix3 perms;
+       phar_zip_central_dir_file central;
+       struct _phar_zip_pass *p;
+       php_uint32 newcrc32;
+       off_t offset;
+
+       entry = (phar_entry_info *)data;
+       p = (struct _phar_zip_pass*) arg;
        if (entry->is_deleted) {
-               entry->index = -1;
                if (entry->fp_refcount <= 0) {
                        return ZEND_HASH_APPLY_REMOVE;
                } else {
@@ -350,137 +440,251 @@ static int phar_zip_reconstruct_apply(void *data TSRMLS_DC) /* {{{ */
                        return ZEND_HASH_APPLY_KEEP;
                }
        }
-       if (entry->is_dir) {
-               char *myname = estrndup(entry->filename, entry->filename_len+2);
-               myname[entry->filename_len] = '/';
-               myname[entry->filename_len+1] = '\0';
-               entry->index = zip_name_locate(entry->phar->zip, entry->filename, ZIP_FL_UNCHANGED);
-               efree(myname);
-       } else {
-               entry->index = zip_name_locate(entry->phar->zip, entry->filename, ZIP_FL_UNCHANGED);
+       memset(&local, 0, sizeof(local));
+       memset(&central, 0, sizeof(central));
+       memset(&perms, 0, sizeof(perms));
+       strncpy(local.signature, "PK\3\4", 4);
+       strncpy(central.signature, "PK\1\2", 4);
+       central.extra_len = local.extra_len = sizeof(perms);
+       perms.tag[0] = 'n';
+       perms.tag[1] = 'u';
+       perms.size = sizeof(perms) - 4;
+       perms.perms = entry->flags & PHAR_ENT_PERM_MASK;
+       perms.crc32 = ~0;
+       CRC32(perms.crc32, (char)perms.perms & 0xFF);
+       CRC32(perms.crc32, (char)perms.perms & 0xFF00 >> 8);
+       perms.crc32 = ~(perms.crc32);
+       if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
+               local.compressed = central.compressed = PHAR_ZIP_COMP_DEFLATE;
        }
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-/* perform final modification of zip contents for each file in the manifest before saving */
-static int phar_zip_changed_apply(void *data TSRMLS_DC) /* {{{ */
-{
-       phar_entry_info *entry = (phar_entry_info *)data;
-
-       if (entry->is_deleted) {
-               if (entry->fp_refcount <= 0) {
-                       return ZEND_HASH_APPLY_REMOVE;
-               } else {
-                       /* we can't delete this in-memory until it is closed */
-                       return ZEND_HASH_APPLY_KEEP;
-               }
+       if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
+               local.compressed = central.compressed = PHAR_ZIP_COMP_BZIP2;
        }
+       phar_zip_u2d_time(entry->timestamp, &local.timestamp, &local.datestamp);
+       central.timestamp = local.timestamp;
+       central.datestamp = local.datestamp;
+       central.filename_len = local.filename_len = entry->filename_len;
+       central.offset = php_stream_tell(p->filefp);
+       /* do extra field for perms later */
        if (entry->is_modified) {
-               ssize_t (*cb)(void *state, void *data, size_t len, enum zip_source_cmd cmd) = phar_zip_source;
-               if (entry->fp) {
-                       php_stream *fp = entry->fp;
-                       struct zip_source *s = zip_source_function(entry->phar->zip, cb, entry);
-
-                       /* we have to prevent free of this fp by mistake */
-                       entry->fp = NULL;
-                       if (-1 == (entry->index = _zip_replace(entry->phar->zip, entry->index, (const char *) entry->filename, s))) {
-                               zip_error_clear(entry->phar->zip);
-                               return ZEND_HASH_APPLY_REMOVE;
+               php_uint32 loc;
+               php_stream_filter *filter;
+               php_stream *efp;
+
+               if (entry->is_dir) {
+                       entry->is_modified = 0;
+                       if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
+                               php_stream_close(entry->fp);
+                               entry->fp = NULL;
+                               entry->fp_type = PHAR_FP;
                        }
-                       /* now restore fp and is_modified */
-                       entry->fp = fp;
-                       entry->is_modified = 1;
+                       goto continue_dir;
+               }
+               if (FAILURE == phar_open_entry_fp(entry, p->error TSRMLS_CC)) {
+                       spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
                }
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+                       spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
+               }
+               efp = phar_get_efp(entry);
 
-               /* set file metadata */
-               if (entry->metadata) {
-                       php_serialize_data_t metadata_hash;
-                       if (entry->metadata_str.c) {
-                               smart_str_free(&entry->metadata_str);
+               newcrc32 = ~0;
+               for (loc = 0;loc < entry->uncompressed_filesize; loc++) {
+                       CRC32(newcrc32, php_stream_getc(efp));
+               }
+               entry->crc32 = ~newcrc32;
+               central.uncompsize = local.uncompsize = entry->uncompressed_filesize;
+               if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
+                       /* not compressed */
+                       entry->compressed_filesize = entry->uncompressed_filesize;
+                       central.compsize = local.compsize = entry->compressed_filesize;
+                       goto not_compressed;
+               }
+               filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
+               if (!filter) {
+                       if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
+                               spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       } else {
+                               spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                        }
-                       entry->metadata_str.c = 0;
-                       entry->metadata_str.len = 0;
-                       PHP_VAR_SERIALIZE_INIT(metadata_hash);
-                       php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
-                       PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
-                       if (-1 == zip_set_file_comment(entry->phar->zip, entry->index, entry->metadata_str.c, entry->metadata_str.len)) {
+                       return ZEND_HASH_APPLY_STOP;
+               }
+
+               /* create new file that holds the compressed version */
+               /* work around inability to specify freedom in write and strictness
+               in read count */
+               entry->cfp = php_stream_fopen_tmpfile();
+               if (!entry->cfp) {
+                       spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
+               }
+               php_stream_flush(efp);
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+                       spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
+               }
+               php_stream_filter_append((&entry->cfp->writefilters), filter);
+               if (entry->uncompressed_filesize != php_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize)) {
+                       spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
+               }
+               php_stream_filter_flush(filter, 1);
+               php_stream_flush(entry->cfp);
+               php_stream_filter_remove(filter, 1 TSRMLS_CC);
+               php_stream_seek(entry->cfp, 0, SEEK_END);
+               entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
+               /* generate crc on compressed file */
+               php_stream_rewind(entry->cfp);
+               entry->old_flags = entry->flags;
+               entry->is_modified = 1;
+       } else {
+               central.uncompsize = local.uncompsize = entry->uncompressed_filesize;
+               central.compsize = local.compsize = entry->compressed_filesize;
+               if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
+                       spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
+               }
+       }
+not_compressed:
+       central.crc32 = local.crc32 = entry->crc32;
+continue_dir:
+       /* set file metadata */
+       if (entry->metadata) {
+               php_serialize_data_t metadata_hash;
+               if (entry->metadata_str.c) {
+                       smart_str_free(&entry->metadata_str);
+               }
+               entry->metadata_str.c = 0;
+               entry->metadata_str.len = 0;
+               PHP_VAR_SERIALIZE_INIT(metadata_hash);
+               php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
+               PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
+               central.comment_len = entry->metadata_str.len;
+       }
+       entry->header_offset = php_stream_tell(p->filefp);
+       offset = entry->header_offset + sizeof(local) + entry->filename_len + sizeof(perms);
+       if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
+               spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (sizeof(central) != php_stream_write(p->centralfp, (char *)&central, sizeof(central))) {
+               spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
+               spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
+               spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
+               spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
+               spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (entry->is_modified) {
+               if (entry->cfp) {
+                       if (entry->compressed_filesize != php_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize)) {
+                               spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                                return ZEND_HASH_APPLY_STOP;
                        }
+                       php_stream_close(entry->cfp);
+                       entry->cfp = NULL;
                } else {
-                       zip_set_file_comment(entry->phar->zip, entry->index, NULL, 0);
+                       if (FAILURE == phar_open_entry_fp(entry, p->error TSRMLS_CC)) {
+                               return ZEND_HASH_APPLY_STOP;
+                       }
+                       phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+                       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry), p->filefp, entry->uncompressed_filesize)) {
+                               spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                               return ZEND_HASH_APPLY_STOP;
+                       }
+               }
+               if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
+                       php_stream_close(entry->fp);
+               }
+               entry->is_modified = 0;
+       } else {
+               if (!entry->is_dir && entry->compressed_filesize && entry->compressed_filesize != php_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize)) {
+                       spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       return ZEND_HASH_APPLY_STOP;
                }
        }
+       entry->fp = NULL;
+       entry->offset = entry->offset_abs = offset;
+       entry->fp_type = PHAR_FP;
+       if (entry->metadata_str.c) {
+               if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) {
+                       spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
+                       smart_str_free(&entry->metadata_str);
+                       return ZEND_HASH_APPLY_STOP;
+               }
+               smart_str_free(&entry->metadata_str);
+       }
        return ZEND_HASH_APPLY_KEEP;
 }
 /* }}} */
 
-int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */
+int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */
 {
        char *pos;
        smart_str main_metadata_str = {0};
-       int ziperrint;
        static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
-       php_stream *stubfile;
+       php_stream *stubfile, *oldfile;
        php_serialize_data_t metadata_hash;
-       int free_user_stub;
-       int phar_stub_index, phar_alias_index;
+       int free_user_stub, closeoldfile = 0;
        phar_entry_info entry = {0};
+       char *temperr = NULL;
+       struct _phar_zip_pass pass;
+       phar_zip_dir_end eocd;
 
+       pass.error = &temperr;
        entry.flags = PHAR_ENT_PERM_DEF_FILE;
        entry.timestamp = time(NULL);
        entry.is_modified = 1;
-       entry.is_crc_checked = 1;
        entry.is_zip = 1;
-       entry.phar = archive;
-
-       /* set phar metadata */
-       main_metadata_str.c = 0;
-       if (archive->metadata) {
-               PHP_VAR_SERIALIZE_INIT(metadata_hash);
-               php_var_serialize(&main_metadata_str, &archive->metadata, &metadata_hash TSRMLS_CC);
-               PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
-               zip_set_archive_comment(archive->zip, main_metadata_str.c, main_metadata_str.len);
-               smart_str_free(&main_metadata_str);
-       } else {
-               zip_set_archive_comment(archive->zip, NULL, 0);
-       }
+       entry.phar = phar;
+       entry.fp_type = PHAR_MOD;
 
        /* set alias */
-       if (archive->is_explicit_alias) {
-               phar_alias_index = zip_name_locate(archive->zip, ".phar/alias.txt", 0);
+       if (phar->is_explicit_alias) {
                entry.fp = php_stream_fopen_tmpfile();
-               if (archive->alias_len != php_stream_write(entry.fp, archive->alias, archive->alias_len)) {
+               if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
                        if (error) {
-                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->fname);
+                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", phar->fname);
                        }
                        return EOF;
                }
-               entry.uncompressed_filesize = sizeof(newstub) - 1;
+               entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
                entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
                entry.filename_len = sizeof(".phar/alias.txt")-1;
                entry.is_modified = 1;
-               entry.index = phar_alias_index;
-               if (SUCCESS != zend_hash_update(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
+               if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
                        if (error) {
-                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->fname);
+                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", phar->fname);
                        }
                        return EOF;
                }
        }
        /* register alias */
-       if (archive->alias_len) {
-               phar_get_archive(&archive, archive->fname, archive->fname_len, archive->alias, archive->alias_len, NULL TSRMLS_CC);
+       if (phar->alias_len) {
+               phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, NULL TSRMLS_CC);
        }
 
        /* set stub */
-       phar_stub_index = zip_name_locate(archive->zip, ".phar/stub.php", 0);
-       zip_error_clear(archive->zip);
        if (user_stub) {
                if (len < 0) {
                        /* resource passed in */
                        if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
                                if (error) {
-                                       spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", archive->fname);
+                                       spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
                                }
                                return EOF;
                        }
@@ -492,7 +696,7 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char *
                        user_stub = 0;
                        if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
                                if (error) {
-                                       spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", archive->fname);
+                                       spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
                                }
                                return EOF;
                        }
@@ -503,7 +707,7 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char *
                if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL)
                {
                        if (error) {
-                               spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", archive->fname);
+                               spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
                        }
                        if (free_user_stub) {
                                efree(user_stub);
@@ -517,7 +721,7 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char *
                if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
                ||            5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
                        if (error) {
-                               spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", archive->fname);
+                               spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
                        }
                        if (free_user_stub) {
                                efree(user_stub);
@@ -528,13 +732,12 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char *
                entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
                entry.filename_len = sizeof(".phar/stub.php")-1;
                entry.is_modified = 1;
-               entry.index = phar_stub_index;
-               if (SUCCESS != zend_hash_update(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
+               if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
                        if (free_user_stub) {
                                efree(user_stub);
                        }
                        if (error) {
-                               spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", archive->fname);
+                               spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
                        }
                        return EOF;
                }
@@ -542,73 +745,156 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char *
                        efree(user_stub);
                }
        } else {
-               if (-1 == phar_stub_index) {
+               if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
                        /* this is a brand new phar, add the stub */
                        entry.fp = php_stream_fopen_tmpfile();
                        if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
                                if (error) {
-                                       spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\"", archive->fname);
+                                       spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\"", phar->fname);
                                }
                                return EOF;
                        }
-                       entry.uncompressed_filesize = sizeof(newstub) - 1;
+                       entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
                        entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
                        entry.filename_len = sizeof(".phar/stub.php")-1;
                        entry.is_modified = 1;
-                       entry.index = -1;
-                       if (SUCCESS != zend_hash_add(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
+                       if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
                                php_stream_close(entry.fp);
                                efree(entry.filename);
                                if (error) {
-                                       spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\": %s", archive->fname, zip_strerror(archive->zip));
+                                       spprintf(error, 0, "unable to create stub in new zip-based phar \"%s\"", phar->fname);
                                }
                                return EOF;
                        }
                }
        }
 
+       if (phar->fp && !phar->is_brandnew) {
+               oldfile = phar->fp;
+               closeoldfile = 0;
+               php_stream_rewind(oldfile);
+       } else {
+               oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
+               closeoldfile = oldfile != NULL;
+       }
+
        /* save modified files to the zip */
-       zend_hash_apply(&archive->manifest, phar_zip_changed_apply TSRMLS_CC);
-       if (archive->zip->error.str) {
+       pass.old = oldfile;
+       pass.filefp = php_stream_fopen_tmpfile();
+       if (!pass.filefp) {
+               if (closeoldfile) {
+                       php_stream_close(oldfile);
+               }
                if (error) {
-                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
+                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
                }
                return EOF;
        }
+       pass.centralfp = php_stream_fopen_tmpfile();
+       if (!pass.centralfp) {
+               if (closeoldfile) {
+                       php_stream_close(oldfile);
+               }
+               if (error) {
+                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
+               }
+               return EOF;
+       }
+       memset(&eocd, 0, sizeof(eocd));
+
+       strncpy(eocd.signature, "PK\5\6", 4);
+       eocd.counthere = eocd.count = zend_hash_num_elements(&phar->manifest);
+       zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
+       if (temperr) {
+               php_stream_close(pass.filefp);
+               php_stream_close(pass.centralfp);
+               if (closeoldfile) {
+                       php_stream_close(oldfile);
+               }
+               if (error) {
+                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
+               }
+               efree(temperr);
+               return EOF;
+       }
 
        /* save zip */
-       if (-1 == zip_close(archive->zip)) {
+       eocd.cdir_size = php_stream_tell(pass.centralfp);
+       eocd.cdir_offset = php_stream_tell(pass.filefp);
+       /* implement metadata here */
+       php_stream_seek(pass.centralfp, 0, SEEK_SET);
+       if (eocd.cdir_size != php_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL)) {
+               php_stream_close(pass.filefp);
+               php_stream_close(pass.centralfp);
                if (error) {
-                       spprintf(error, 4096, "saving of zip-based phar \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
+                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
+               }
+               if (closeoldfile) {
+                       php_stream_close(oldfile);
                }
                return EOF;
        }
-
-       /* re-open */
-       archive->zip = zip_open(archive->fname, 0, &ziperrint);
-       if (!archive->zip) {
+       php_stream_close(pass.centralfp);
+       if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
+               php_stream_close(pass.filefp);
                if (error) {
-                       /* now for the stupid hoops libzip forces... */
-                       char *tmp;
-                       int tmp_len;
-                       tmp_len = zip_error_to_str(NULL, 0, ziperrint, ziperrint);
-                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
-                               spprintf(error, 4096, "phar zip error: cannot re-open zip-based phar \"%s\"", archive->fname);
-                       } else {
-                               if (!zip_error_to_str(tmp, tmp_len + 1, ziperrint, ziperrint)) {
-                                       spprintf(error, 4096, "phar zip error: cannot re-open zip-based phar \"%s\"", archive->fname);
-                               } else {
-                                       spprintf(error, 4096, "phar zip error: cannot re-open zip-based phar \"%s\": %s", archive->fname, tmp);
-                                       efree(tmp);
-                               }
-                       }
+                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
+               }
+               if (closeoldfile) {
+                       php_stream_close(oldfile);
                }
                return EOF;
        }
+       if (phar->metadata) {
+               /* set phar metadata */
+               PHP_VAR_SERIALIZE_INIT(metadata_hash);
+               php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
+               PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
+               if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) {
+                       php_stream_close(pass.filefp);
+                       if (error) {
+                               spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
+                       }
+                       if (closeoldfile) {
+                               php_stream_close(oldfile);
+                       }
+                       return EOF;
+               }
+               smart_str_free(&main_metadata_str);
+       }
+       if (phar->fp) {
+               php_stream_close(phar->fp);
+       }
+       if (phar->ufp) {
+               php_stream_close(phar->ufp);
+               phar->ufp = NULL;
+       }
+       /* re-open */
+       phar->is_brandnew = 0;
+       if (phar->donotflush) {
+               /* deferred flush */
+               phar->fp = pass.filefp;
+       } else {
+               phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
+               if (!phar->fp) {
+                       if (closeoldfile) {
+                               php_stream_close(oldfile);
+                       }
+                       phar->fp = pass.filefp;
+                       if (error) {
+                               spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
+                       }
+                       return EOF;
+               }
+               php_stream_rewind(pass.filefp);
+               php_stream_copy_to_stream(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL);
+               /* we could also reopen the file in "rb" mode but there is no need for that */
+               php_stream_close(pass.filefp);
+       }
 
-       /* reconstruct manifest zip index map */
-       zend_hash_apply(&archive->manifest, phar_zip_reconstruct_apply TSRMLS_CC);
+       if (closeoldfile) {
+               php_stream_close(oldfile);
+       }
        return EOF;
 }
 /* }}} */
-#endif /* if HAVE_PHAR_ZIP */