]> granicus.if.org Git - php/commitdiff
split out zip functions
authorGreg Beaver <cellog@php.net>
Tue, 8 Jan 2008 19:40:23 +0000 (19:40 +0000)
committerGreg Beaver <cellog@php.net>
Tue, 8 Jan 2008 19:40:23 +0000 (19:40 +0000)
ext/phar/LICENSE
ext/phar/config.m4
ext/phar/config.w32
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/zip.c [new file with mode: 0644]

index 3e48203d545352e3b12d7bfd4309410193cc1a3a..50770e3a03ea95f248b5b734cab253e6ce459d7e 100644 (file)
@@ -66,3 +66,30 @@ please see <http://www.php.net>.
 
 PHP includes the Zend Engine, freely available at
 <http://www.zend.com>.
+
+---------------------------------------------------------------------
+phar_tar_octal() based on an implementation Copyright (c) 2003-2007 Tim Kientzle
+from libarchive, licensed with this license:
+
+ Copyright (c) 2003-2007 Tim Kientzle
+ All rights reserved.
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
index 2e262516aba561ec559c4e3869e06a0c43e4fa3e..7e35fc6180b66b045c93d529999303c465dfd471 100644 (file)
@@ -32,7 +32,7 @@ if test "$PHP_PHAR" != "no"; then
        else
                AC_MSG_RESULT([no])
        fi
-  PHP_NEW_EXTENSION(phar, tar.c phar.c phar_object.c phar_path_check.c $PHP_PHAR_SOURCES, $ext_shared)
+  PHP_NEW_EXTENSION(phar, tar.c zip.c phar.c phar_object.c phar_path_check.c $PHP_PHAR_SOURCES, $ext_shared)
   PHP_ADD_BUILD_DIR($ext_builddir/lib, 1)
   PHP_SUBST(PHAR_SHARED_LIBADD)
   PHP_ADD_EXTENSION_DEP(phar, zlib, true)
index c83118b35decfe06de53c37cd6f17c612a9886dd..f91cb76b9a693eae6c7702c1202d35a77714d2bb 100644 (file)
@@ -4,7 +4,7 @@
 ARG_ENABLE("phar", "enable phar support", "no");
 
 if (PHP_PHAR != "no") {
-       EXTENSION("phar", "tar.c phar.c phar_object.c phar_path_check.c");
+       EXTENSION("phar", "tar.c zip.c phar.c phar_object.c phar_path_check.c");
                ADD_SOURCES(configure_module_dirname + "/lib", "zip_add.c zip_error.c zip_fclose.c \
                      zip_fread.c zip_open.c zip_source_filep.c  \
                      zip_strerror.c zip_close.c zip_error_get.c \
index f828cb65b55a1301c41a2a5610b3fe36133e642e..8f0310c0a9373cfa62d67dd51b598b182c4563be 100644 (file)
@@ -341,7 +341,7 @@ void destroy_phar_manifest(void *pDest) /* {{{ */
  * Looks up a phar archive in the filename map, connecting it to the alias
  * (if any) or returns null
  */
-static int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, 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) /* {{{ */
 {
        phar_archive_data *fd, **fd_ptr;
        char *my_realpath, *save;
@@ -909,7 +909,7 @@ int phar_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int
  * 
  * data is the serialized zval
  */
-static int phar_parse_metadata(char **buffer, zval **metadata, int is_zip TSRMLS_DC) /* {{{ */
+int phar_parse_metadata(char **buffer, zval **metadata, int is_zip TSRMLS_DC) /* {{{ */
 {
        const unsigned char *p;
        php_uint32 buf_len;
@@ -957,208 +957,6 @@ static int phar_hex_str(const char *digest, size_t digest_len, char ** signature
        return pos;
 }
 
-/**
- * Does not check for a previously opened phar in the cache.
- *
- * Parse a new one and add it to the cache, returning either SUCCESS or 
- * FAILURE, and setting pphar to the pointer to the manifest entry
- * 
- * 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) /* {{{ */
-{
-#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;
-
-       if (error) {
-               *error = NULL;
-       }
-
-       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)) {
-                                       spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
-                               } else {
-                                       spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\": %s", fname, tmp);
-                                       efree(tmp);
-                               }
-                       }
-               }
-               return FAILURE;
-       }
-       phar_archive_data *mydata = NULL;
-       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->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);
-                       }
-                       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 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_TYPE_P(mydata->metadata) = IS_STRING;
-               }
-       } else {
-               mydata->metadata = NULL;
-       }
-       /* set up our manifest */
-       zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
-               zend_get_hash_value, destroy_phar_manifest, 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 !HAVE_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;
-#else
-                                               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;
-                                               }
-#endif
-                                               entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
-                                               break;
-                               }
-                       }
-                       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;
-                       } else {
-                               entry.is_dir = 0;
-                       }
-                       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;
-                       }
-                       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), 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;
-       }
-       if (pphar) {
-               *pphar = mydata;
-       }
-       return SUCCESS;
-#else
-       spprintf(error, 4096, "Error: Cannot open zip-based phar \"%s\"", fname);
-       return FAILURE;
-#endif
-}
-/* }}} */
-
 /**
  * Does not check for a previously opened phar in the cache.
  *
@@ -1624,80 +1422,6 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
 }
 /* }}} */
 
-/**
- * Create or open a phar for writing
- */
-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);
-       int register_alias;
-
-       if (pphar) {
-               *pphar = phar;
-       }
-       if (FAILURE == ret) {
-               return FAILURE;
-       }
-       if (phar->is_zip) {
-               return ret;
-       }
-
-       if (phar->is_brandnew) {
-               int *errorp;
-               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 */
-               if (register_alias) {
-                       zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), alias, 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;
-       }
-
-       /* we've reached here - the phar exists and is a regular phar */
-       if (error) {
-               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", fname);
-       }
-       return FAILURE;
-#endif /* #if HAVE_PHAR_ZIP */
-}
-
 /**
  * Create or open a phar for writing
  */
@@ -3017,315 +2741,6 @@ static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */
 }
 /* }}} */
 
-#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 :
-                       php_stream_seek(entry->fp, 0, 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;
-                       if (entry->fp && entry->fp_refcount == 0) {
-                               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) /* {{{ */
-{
-       phar_entry_info *entry = (phar_entry_info *)data;
-       if (entry->is_deleted) {
-               entry->index = -1;
-               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->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);
-       }
-       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->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;
-                       }
-                       /* now restore fp and is_modified */
-                       entry->fp = fp;
-                       entry->is_modified = 1;
-               }
-
-               /* 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);
-                       if (-1 == zip_set_file_comment(entry->phar->zip, entry->index, entry->metadata_str.c, entry->metadata_str.len)) {
-                               return ZEND_HASH_APPLY_STOP;
-                       }
-               } else {
-                       zip_set_file_comment(entry->phar->zip, entry->index, NULL, 0);
-               }
-       }
-       return ZEND_HASH_APPLY_KEEP;
-}
-/* }}} */
-
-int phar_zip_flush(phar_archive_data *archive, 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_serialize_data_t metadata_hash;
-       int free_user_stub;
-       int phar_stub_index, phar_alias_index;
-       phar_entry_info entry = {0};
-
-       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);
-       }
-
-       /* set alias */
-       if (archive->is_explicit_alias) {
-               phar_alias_index = zip_name_locate(archive->zip, ".phar/alias.txt", 0);
-               entry.fp = php_stream_fopen_tmpfile();
-               if (archive->alias_len != php_stream_write(entry.fp, archive->alias, archive->alias_len)) {
-                       if (error) {
-                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->fname);
-                       }
-                       return EOF;
-               }
-               entry.uncompressed_filesize = sizeof(newstub) - 1;
-               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 (error) {
-                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->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);
-       }
-
-       /* 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);
-                               }
-                               return EOF;
-                       }
-                       if (len == -1) {
-                               len = PHP_STREAM_COPY_ALL;
-                       } else {
-                               len = -len;
-                       }
-                       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);
-                               }
-                               return EOF;
-                       }
-                       free_user_stub = 1;
-               } else {
-                       free_user_stub = 0;
-               }
-               if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL)
-               {
-                       if (error) {
-                               spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", archive->fname);
-                       }
-                       if (free_user_stub) {
-                               efree(user_stub);
-                       }
-                       return EOF;
-               }
-               len = pos - user_stub + 18;
-               entry.fp = php_stream_fopen_tmpfile();
-               entry.uncompressed_filesize = len + 5;
-
-               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);
-                       }
-                       if (free_user_stub) {
-                               efree(user_stub);
-                       }
-                       php_stream_close(entry.fp);
-                       return EOF;
-               }
-               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 (free_user_stub) {
-                               efree(user_stub);
-                       }
-                       if (error) {
-                               spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", archive->fname);
-                       }
-                       return EOF;
-               }
-               if (free_user_stub) {
-                       efree(user_stub);
-               }
-       } else {
-               if (-1 == phar_stub_index) {
-                       /* 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);
-                               }
-                               return EOF;
-                       }
-                       entry.uncompressed_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)) {
-                               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));
-                               }
-                               return EOF;
-                       }
-               }
-       }
-
-       /* save modified files to the zip */
-       zend_hash_apply(&archive->manifest, phar_zip_changed_apply TSRMLS_CC);
-       if (archive->zip->error.str) {
-               if (error) {
-                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
-               }
-               return EOF;
-       }
-
-       /* save zip */
-       if (-1 == zip_close(archive->zip)) {
-               if (error) {
-                       spprintf(error, 4096, "saving of zip-based phar \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
-               }
-               return EOF;
-       }
-
-       /* re-open */
-       archive->zip = zip_open(archive->fname, 0, &ziperrint);
-       if (!archive->zip) {
-               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);
-                               }
-                       }
-               }
-               return EOF;
-       }
-
-       /* reconstruct manifest zip index map */
-       zend_hash_apply(&archive->manifest, phar_zip_reconstruct_apply TSRMLS_CC);
-       return EOF;
-}
-/* }}} */
-#endif /* if HAVE_PHAR_ZIP */
-
 /**
  * Save phar contents to disk
  *
@@ -4948,6 +4363,7 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */
        PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
        PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");       
        PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
+       PUTS("Portions of tar implementation Copyright (c) 2003-2007 Tim Kientzle.");
        php_info_print_box_end();
 
        DISPLAY_INI_ENTRIES();
index e9161b6c882854d587b3432dda592b864cf52dfe..ba2cca2a185e6d8525118c7bebc839de9b251be5 100755 (executable)
@@ -294,17 +294,26 @@ int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, i
 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);
+
 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_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
+int phar_parse_metadata(char **buffer, zval **metadata, int is_zip TSRMLS_DC);
 void destroy_phar_manifest(void *pDest);
-/* tar functions */
+
+/* tar functions in tar.c */
 int phar_is_tar(char *buf);
 int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
 int phar_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC);
 int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
 int phar_tar_flush(phar_archive_data *archive, 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_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC);
+int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC);
+
 #ifdef PHAR_MAIN
 static void phar_fopen(INTERNAL_FUNCTION_PARAMETERS);
 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);
diff --git a/ext/phar/zip.c b/ext/phar/zip.c
new file mode 100644 (file)
index 0000000..2585920
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+  +----------------------------------------------------------------------+
+  | ZIP archive support for Phar                                         |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2007-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>                             |
+  +----------------------------------------------------------------------+
+*/
+
+#include "phar_internal.h"
+
+#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000)
+
+static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC)
+{
+       php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC);
+
+       if (!wrapper || !wrapper->wops) {
+               return 0;
+       }
+
+       if (!wrapper->wops->unlink) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper");
+               return 0;
+       }
+       return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC);
+}
+
+#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC)
+
+#endif
+
+/**
+ * Does not check for a previously opened phar in the cache.
+ *
+ * Parse a new one and add it to the cache, returning either SUCCESS or 
+ * FAILURE, and setting pphar to the pointer to the manifest entry
+ * 
+ * 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) /* {{{ */
+{
+#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;
+
+       if (error) {
+               *error = NULL;
+       }
+
+       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)) {
+                                       spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
+                               } else {
+                                       spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\": %s", fname, tmp);
+                                       efree(tmp);
+                               }
+                       }
+               }
+               return FAILURE;
+       }
+       phar_archive_data *mydata = NULL;
+       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->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);
+                       }
+                       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 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_TYPE_P(mydata->metadata) = IS_STRING;
+               }
+       } else {
+               mydata->metadata = NULL;
+       }
+       /* set up our manifest */
+       zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
+               zend_get_hash_value, destroy_phar_manifest, 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 !HAVE_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;
+#else
+                                               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;
+                                               }
+#endif
+                                               entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
+                                               break;
+                               }
+                       }
+                       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;
+                       } else {
+                               entry.is_dir = 0;
+                       }
+                       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;
+                       }
+                       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), 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;
+       }
+       if (pphar) {
+               *pphar = mydata;
+       }
+       return SUCCESS;
+#else
+       spprintf(error, 4096, "Error: Cannot open zip-based phar \"%s\"", fname);
+       return FAILURE;
+#endif
+}
+/* }}} */
+
+/**
+ * Create or open a zip-based phar for writing
+ */
+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);
+       int register_alias;
+
+       if (pphar) {
+               *pphar = phar;
+       }
+       if (FAILURE == ret) {
+               return FAILURE;
+       }
+       if (phar->is_zip) {
+               return ret;
+       }
+
+       if (phar->is_brandnew) {
+               int *errorp;
+               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 */
+               if (register_alias) {
+                       zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), alias, 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;
+       }
+
+       /* we've reached here - the phar exists and is a regular phar */
+       if (error) {
+               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", 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 :
+                       php_stream_seek(entry->fp, 0, 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;
+                       if (entry->fp && entry->fp_refcount == 0) {
+                               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) /* {{{ */
+{
+       phar_entry_info *entry = (phar_entry_info *)data;
+       if (entry->is_deleted) {
+               entry->index = -1;
+               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->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);
+       }
+       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->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;
+                       }
+                       /* now restore fp and is_modified */
+                       entry->fp = fp;
+                       entry->is_modified = 1;
+               }
+
+               /* 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);
+                       if (-1 == zip_set_file_comment(entry->phar->zip, entry->index, entry->metadata_str.c, entry->metadata_str.len)) {
+                               return ZEND_HASH_APPLY_STOP;
+                       }
+               } else {
+                       zip_set_file_comment(entry->phar->zip, entry->index, NULL, 0);
+               }
+       }
+       return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+int phar_zip_flush(phar_archive_data *archive, 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_serialize_data_t metadata_hash;
+       int free_user_stub;
+       int phar_stub_index, phar_alias_index;
+       phar_entry_info entry = {0};
+
+       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);
+       }
+
+       /* set alias */
+       if (archive->is_explicit_alias) {
+               phar_alias_index = zip_name_locate(archive->zip, ".phar/alias.txt", 0);
+               entry.fp = php_stream_fopen_tmpfile();
+               if (archive->alias_len != php_stream_write(entry.fp, archive->alias, archive->alias_len)) {
+                       if (error) {
+                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->fname);
+                       }
+                       return EOF;
+               }
+               entry.uncompressed_filesize = sizeof(newstub) - 1;
+               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 (error) {
+                               spprintf(error, 0, "unable to set alias in new zip-based phar \"%s\"", archive->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);
+       }
+
+       /* 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);
+                               }
+                               return EOF;
+                       }
+                       if (len == -1) {
+                               len = PHP_STREAM_COPY_ALL;
+                       } else {
+                               len = -len;
+                       }
+                       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);
+                               }
+                               return EOF;
+                       }
+                       free_user_stub = 1;
+               } else {
+                       free_user_stub = 0;
+               }
+               if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL)
+               {
+                       if (error) {
+                               spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", archive->fname);
+                       }
+                       if (free_user_stub) {
+                               efree(user_stub);
+                       }
+                       return EOF;
+               }
+               len = pos - user_stub + 18;
+               entry.fp = php_stream_fopen_tmpfile();
+               entry.uncompressed_filesize = len + 5;
+
+               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);
+                       }
+                       if (free_user_stub) {
+                               efree(user_stub);
+                       }
+                       php_stream_close(entry.fp);
+                       return EOF;
+               }
+               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 (free_user_stub) {
+                               efree(user_stub);
+                       }
+                       if (error) {
+                               spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", archive->fname);
+                       }
+                       return EOF;
+               }
+               if (free_user_stub) {
+                       efree(user_stub);
+               }
+       } else {
+               if (-1 == phar_stub_index) {
+                       /* 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);
+                               }
+                               return EOF;
+                       }
+                       entry.uncompressed_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)) {
+                               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));
+                               }
+                               return EOF;
+                       }
+               }
+       }
+
+       /* save modified files to the zip */
+       zend_hash_apply(&archive->manifest, phar_zip_changed_apply TSRMLS_CC);
+       if (archive->zip->error.str) {
+               if (error) {
+                       spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
+               }
+               return EOF;
+       }
+
+       /* save zip */
+       if (-1 == zip_close(archive->zip)) {
+               if (error) {
+                       spprintf(error, 4096, "saving of zip-based phar \"%s\" failed: %s", archive->fname, zip_strerror(archive->zip));
+               }
+               return EOF;
+       }
+
+       /* re-open */
+       archive->zip = zip_open(archive->fname, 0, &ziperrint);
+       if (!archive->zip) {
+               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);
+                               }
+                       }
+               }
+               return EOF;
+       }
+
+       /* reconstruct manifest zip index map */
+       zend_hash_apply(&archive->manifest, phar_zip_reconstruct_apply TSRMLS_CC);
+       return EOF;
+}
+/* }}} */
+#endif /* if HAVE_PHAR_ZIP */