From 63adb98f781f0b1a6f1a9f19dc84fdd81a0aa3b2 Mon Sep 17 00:00:00 2001 From: Greg Beaver Date: Tue, 8 Jan 2008 19:40:23 +0000 Subject: [PATCH] split out zip functions --- ext/phar/LICENSE | 27 ++ ext/phar/config.m4 | 2 +- ext/phar/config.w32 | 2 +- ext/phar/phar.c | 590 +----------------------------------- ext/phar/phar_internal.h | 13 +- ext/phar/zip.c | 626 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 669 insertions(+), 591 deletions(-) create mode 100644 ext/phar/zip.c diff --git a/ext/phar/LICENSE b/ext/phar/LICENSE index 3e48203d54..50770e3a03 100644 --- a/ext/phar/LICENSE +++ b/ext/phar/LICENSE @@ -66,3 +66,30 @@ please see . PHP includes the Zend Engine, freely available at . + +--------------------------------------------------------------------- +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. diff --git a/ext/phar/config.m4 b/ext/phar/config.m4 index 2e262516ab..7e35fc6180 100644 --- a/ext/phar/config.m4 +++ b/ext/phar/config.m4 @@ -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) diff --git a/ext/phar/config.w32 b/ext/phar/config.w32 index c83118b35d..f91cb76b9a 100644 --- a/ext/phar/config.w32 +++ b/ext/phar/config.w32 @@ -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 \ diff --git a/ext/phar/phar.c b/ext/phar/phar.c index f828cb65b5..8f0310c0a9 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -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[] = "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?"
":"\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(); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index e9161b6c88..ba2cca2a18 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -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 index 0000000000..25859200e7 --- /dev/null +++ b/ext/phar/zip.c @@ -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 | + +----------------------------------------------------------------------+ +*/ + +#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[] = "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 */ -- 2.50.1