From: Steph Fox Date: Fri, 1 Aug 2008 13:45:06 +0000 (+0000) Subject: - This is as good as it gets in HEAD at present (~30% tests fail here) X-Git-Tag: BEFORE_HEAD_NS_CHANGE~922 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c6aa379d74535bf5e245fa9941b235498f987196;p=php - This is as good as it gets in HEAD at present (~30% tests fail here) --- diff --git a/ext/phar/Makefile.frag b/ext/phar/Makefile.frag index d693e8dd86..32b5d04104 100755 --- a/ext/phar/Makefile.frag +++ b/ext/phar/Makefile.frag @@ -4,22 +4,42 @@ $(srcdir)/phar_path_check.c: $(srcdir)/phar_path_check.re pharcmd: $(builddir)/phar.php $(builddir)/phar.phar -$(builddir)/phar.php: $(srcdir)/build_precommand.php $(srcdir)/phar/*.inc $(srcdir)/phar/*.php $(SAPI_CLI_PATH) - if test -x "$(PHP_EXECUTABLE)"; then \ - export PHP="$(PHP_EXECUTABLE)"; \ +PHP_PHARCMD_SETTINGS = -d 'open_basedir=' -d 'output_buffering=0' -d 'memory_limit=-1' -d phar.readonly=0 +PHP_PHARCMD_EXECUTABLE = ` \ + if test -x "$(top_builddir)/$(SAPI_CLI_PATH)"; then \ + $(top_srcdir)/build/shtool echo -n -- "$(top_builddir)/$(SAPI_CLI_PATH) -n"; \ + if test "x$(PHP_MODULES)" != "x"; then \ + $(top_srcdir)/build/shtool echo -n -- " -d extension_dir=$(top_builddir)/modules"; \ + for i in bz2 zlib phar; do \ + if test -f "$(top_builddir)/modules/$$i.la"; then \ + . $(top_builddir)/modules/$$i.la; $(top_srcdir)/build/shtool echo -n -- " -d extension=$$dlname"; \ + fi; \ + done; \ + fi; \ else \ - export PHP="$(top_builddir)/$(SAPI_CLI_PATH)"; \ - fi; \ - $$PHP $(srcdir)/build_precommand.php > $(builddir)/phar.php - -$(builddir)/phar.phar: $(builddir)/phar.php $(srcdir)/phar/*.inc $(srcdir)/phar/*.php $(SAPI_CLI_PATH) - if test -x "$(PHP_EXECUTABLE)"; then \ - export PHP="$(PHP_EXECUTABLE)"; \ - export BANG="$(PHP_EXECUTABLE)"; \ + $(top_srcdir)/build/shtool echo -n -- "$(PHP_EXECUTABLE)"; \ + fi;` +PHP_PHARCMD_BANG = `if test -x "$(PHP_EXECUTABLE)"; then \ + $(top_srcdir)/build/shtool echo -n -- "$(PHP_EXECUTABLE)"; \ else \ - export PHP="$(top_builddir)/$(SAPI_CLI_PATH)"; \ - export BANG="$(INSTALL_ROOT)$(bindir)/$(program_prefix)php$(program_suffix)$(EXEEXT)"; \ - fi; \ - $$PHP -d phar.readonly=0 $(srcdir)/phar.php pack -f $(builddir)/phar.phar -a pharcommand -c auto -x CVS -p 0 -s $(srcdir)/phar/phar.php -h sha1 -b "$$BANG" $(srcdir)/phar/ - @chmod +x $(builddir)/phar.phar + $(top_srcdir)/build/shtool echo -n -- "$(INSTALL_ROOT)$(bindir)/$(program_prefix)php$(program_suffix)$(EXEEXT)"; \ + fi; ` + +$(builddir)/phar/phar.inc: $(srcdir)/phar/phar.inc + -@test -d $(builddir)/phar || mkdir $(builddir)/phar + -@test -f $(builddir)/phar/phar.inc || cp $(srcdir)/phar/phar.inc $(builddir)/phar/phar.inc + +$(builddir)/phar.php: $(srcdir)/build_precommand.php $(srcdir)/phar/*.inc $(srcdir)/phar/*.php $(SAPI_CLI_PATH) + -@echo "Generating phar.php" + @$(PHP_PHARCMD_EXECUTABLE) $(PHP_PHARCMD_SETTINGS) $(srcdir)/build_precommand.php > $(builddir)/phar.php + +$(builddir)/phar.phar: $(builddir)/phar.php $(builddir)/phar/phar.inc $(srcdir)/phar/*.inc $(srcdir)/phar/*.php $(SAPI_CLI_PATH) + -@echo "Generating phar.phar" + -@rm -f $(builddir)/phar.phar + -@rm -f $(srcdir)/phar.phar + @$(PHP_PHARCMD_EXECUTABLE) $(PHP_PHARCMD_SETTINGS) $(builddir)/phar.php pack -f $(builddir)/phar.phar -a pharcommand -c auto -x CVS -p 0 -s $(srcdir)/phar/phar.php -h sha1 -b "$(PHP_PHARCMD_BANG)" $(srcdir)/phar/ + -@chmod +x $(builddir)/phar.phar +install-pharcmd: pharcmd + -@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir) + $(INSTALL) $(builddir)/phar.phar $(INSTALL_ROOT)$(bindir) diff --git a/ext/phar/build_precommand.php b/ext/phar/build_precommand.php index 8904afdf05..c2fe9c8e52 100755 --- a/ext/phar/build_precommand.php +++ b/ext/phar/build_precommand.php @@ -5,14 +5,12 @@ * @ingroup Phar * @brief class Phar Pre Command * @author Marcus Boerger - * @date 2007 - 2007 + * @date 2007 - 2008 * * Phar Command */ -foreach(array("SPL", "Reflection", "Phar") as $ext) -{ - if (!extension_loaded($ext)) - { +foreach(array("SPL", "Reflection", "Phar") as $ext) { + if (!extension_loaded($ext)) { echo "$argv[0] requires PHP extension $ext.\n"; exit(1); } @@ -28,14 +26,12 @@ $classes = array( 'PharCommand', ); -foreach($classes as $name) -{ +foreach($classes as $name) { echo "if (!class_exists('$name', 0))\n{\n"; $f = file(dirname(__FILE__) . '/phar/' . strtolower($name) . '.inc'); unset($f[0]); $c = count($f); - while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) - { + while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) { unset($f[$c--]); } if (substr($f[$c], -2) == "\r\n") { @@ -47,8 +43,7 @@ foreach($classes as $name) if (substr($f[$c], -2) == '?>') { $f[$c] = substr($f[$c], 0,-2); } - while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) - { + while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) { unset($f[$c--]); } echo join('', $f); diff --git a/ext/phar/cgidebug b/ext/phar/cgidebug index c0d89d7d25..1907581900 100755 --- a/ext/phar/cgidebug +++ b/ext/phar/cgidebug @@ -1,11 +1,11 @@ #!/bin/sh export SCRIPT_NAME=/front.phar.php -export PATH_INFO= +export PATH_INFO=/index.php export SCRIPT_FILENAME=/home/cellog/workspace/php5/ext/phar/tests/front.phar.php export PATH_TRANSLATED=/home/cellog/workspace/php5/ext/phar/tests/front.phar.php export REDIRECT_STATUS=1 export REQUEST_METHOD=GET -export REQUEST_URI=/front.phar.php -cd /home/cellog/workspace/php5/ +export REQUEST_URI=/front.phar.php/index.php +cd /home/cellog/workspace/php5 ddd sapi/cgi/php-cgi & cd /home/cellog/workspace/php5/ext/phar diff --git a/ext/phar/config.m4 b/ext/phar/config.m4 index 78b4fa30c7..32337ec007 100644 --- a/ext/phar/config.m4 +++ b/ext/phar/config.m4 @@ -2,12 +2,23 @@ dnl $Id$ dnl config.m4 for extension phar PHP_ARG_ENABLE(phar, for phar archive support, -[ --enable-phar Enable phar support]) +[ --enable-phar Enable phar support]) if test "$PHP_PHAR" != "no"; then PHP_NEW_EXTENSION(phar, util.c tar.c zip.c stream.c func_interceptors.c dirstream.c phar.c phar_object.c phar_path_check.c, $ext_shared) PHP_ADD_BUILD_DIR($ext_builddir/lib, 1) PHP_SUBST(PHAR_SHARED_LIBADD) + AC_MSG_CHECKING([for phar openssl support]) + if test "$PHP_OPENSSL_SHARED" = "yes"; then + AC_MSG_RESULT([no (shared openssl)]) + else + if test "$PHP_OPENSSL" = "yes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE(PHAR_HAVE_OPENSSL,1,[ ]) + else + AC_MSG_RESULT([no]) + fi + fi PHP_ADD_EXTENSION_DEP(phar, spl, true) PHP_ADD_MAKEFILE_FRAGMENT fi diff --git a/ext/phar/config.w32 b/ext/phar/config.w32 index cbc68ca2e5..6632180649 100644 --- a/ext/phar/config.w32 +++ b/ext/phar/config.w32 @@ -2,11 +2,37 @@ // vim:ft=javascript ARG_ENABLE("phar", "enable phar support", "no"); +ARG_ENABLE("phar-native-ssl", "enable phar with native OpenSSL support", "no"); + +if (PHP_PHAR_NATIVE_SSL != "no") { + PHP_PHAR = PHP_PHAR_NATIVE_SSL; + PHP_PHAR_SHARED = PHP_PHAR_NATIVE_SSL_SHARED; +} if (PHP_PHAR != "no") { EXTENSION("phar", "dirstream.c func_interceptors.c phar.c phar_object.c phar_path_check.c stream.c tar.c util.c zip.c"); if (PHP_PHAR_SHARED) { ADD_FLAG("CFLAGS_PHAR", "/D COMPILE_DL_PHAR "); } + if (PHP_PHAR_NATIVE_SSL != "no") { + if (CHECK_LIB("libeay32st.lib", "phar")) { + /* We don't really need GDI for this, but there's no + way to avoid linking it in the static openssl build */ + ADD_FLAG("LIBS_PHAR", "libeay32st.lib gdi32.lib"); + if (PHP_DEBUG == "no") { + /* Silence irrelevant-to-us warning in release builds */ + ADD_FLAG("LDFLAGS_PHAR", "/IGNORE:4089 "); + } + AC_DEFINE('PHAR_HAVE_OPENSSL', 1); + STDOUT.WriteLine(' Native OpenSSL support in Phar enabled'); + } else { + WARNING('Could not enable native OpenSSL support in Phar'); + } + } else { + /* If ext/openssl is built-in we can at least use the API directly */ + if (PHP_OPENSSL != "no" && !PHP_OPENSSL_SHARED) { + AC_DEFINE('PHAR_HAVE_OPENSSL', 1); + } + } ADD_EXTENSION_DEP('phar', 'spl', true); } diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 90f02fc2a1..e94a11a596 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -45,13 +45,13 @@ static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{ { HashTable *data = (HashTable *)stream->abstract; - if (data && data->arBuckets) - { + if (data && data->arBuckets) { zend_hash_destroy(data); data->arBuckets = 0; FREE_HASHTABLE(data); stream->abstract = NULL; } + return 0; } /* }}} */ @@ -63,14 +63,15 @@ static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *ne { HashTable *data = (HashTable *)stream->abstract; - if (!data) - { + if (!data) { return -1; } + if (whence == SEEK_END) { whence = SEEK_SET; offset = zend_hash_num_elements(data) + offset; } + if (whence == SEEK_SET) { zend_hash_internal_pointer_reset(data); } @@ -102,15 +103,19 @@ static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_D if (FAILURE == zend_hash_has_more_elements(data)) { return 0; } + if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(data, &key, &keylen, &unused, 0, NULL)) { return 0; } + PHAR_STR(key, str_key); zend_hash_move_forward(data); to_read = MIN(keylen, count); + if (to_read == 0 || count < keylen) { return 0; } + memset(buf, 0, sizeof(php_stream_dirent)); memcpy(((php_stream_dirent *) buf)->d_name, str_key, to_read); ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; @@ -145,9 +150,9 @@ static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */ */ static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength) /* {{{ */ { - void *dummy = (void *) 1; + void *dummy = (char *) 1; - return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL); + return zend_hash_update(ht, arKey, nKeyLength, (void *) &dummy, sizeof(void *), NULL); } /* }}} */ @@ -159,10 +164,9 @@ static int phar_compare_dir_name(const void *a, const void *b TSRMLS_DC) /* {{{ Bucket *f; Bucket *s; int result; - + f = *((Bucket **) a); s = *((Bucket **) b); - #if (PHP_MAJOR_VERSION < 6) result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength); #else @@ -202,12 +206,16 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) efree(dir); return php_stream_alloc(&phar_dir_ops, data, NULL, "r"); } + zend_hash_internal_pointer_reset(manifest); + while (FAILURE != zend_hash_has_more_elements(manifest)) { if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) { break; } + PHAR_STR(key, str_key); + if (keylen <= (uint)dirlen) { if (keylen < (uint)dirlen || !strncmp(str_key, dir, dirlen)) { if (SUCCESS != zend_hash_move_forward(manifest)) { @@ -216,6 +224,7 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) continue; } } + if (*dir == '/') { /* root directory */ if (keylen >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) { @@ -225,6 +234,7 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) } continue; } + if (NULL != (found = (char *) memchr(str_key, '/', keylen))) { /* the entry has a path separator and is a subdirectory */ entry = (char *) safe_emalloc(found - str_key, 1, 1); @@ -236,6 +246,7 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) memcpy(entry, str_key, keylen); entry[keylen] = '\0'; } + goto PHAR_ADD_ENTRY; } else { if (0 != memcmp(str_key, dir, dirlen)) { @@ -253,8 +264,10 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) } } } + save = str_key; save += dirlen + 1; /* seek to just past the path separator */ + if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) { /* is subdirectory */ save -= dirlen + 1; @@ -274,11 +287,14 @@ PHAR_ADD_ENTRY: if (keylen) { phar_add_empty(data, entry, keylen); } + efree(entry); + if (SUCCESS != zend_hash_move_forward(manifest)) { break; } } + if (FAILURE != zend_hash_has_more_elements(data)) { efree(dir); if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) { @@ -296,8 +312,7 @@ PHAR_ADD_ENTRY: /** * Open a directory handle within a phar archive */ -php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, - int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ +php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ { php_url *resource = NULL; php_stream *ret; @@ -334,8 +349,8 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char host_len = strlen(resource->host); phar_request_initialize(TSRMLS_C); - internal_file = resource->path + 1; /* strip leading "/" */ + if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) { if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); @@ -346,9 +361,11 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char php_url_free(resource); return NULL; } + if (error) { efree(error); } + if (*internal_file == '\0') { /* root directory requested */ internal_file = estrndup(internal_file - 1, 1); @@ -356,10 +373,12 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char php_url_free(resource); return ret; } + if (!phar->manifest.arBuckets) { php_url_free(resource); return NULL; } + if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry) && !entry->is_dir) { php_url_free(resource); return NULL; @@ -389,6 +408,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC); } } + if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { break; } @@ -415,43 +435,45 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); - return FAILURE; + return 0; } + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { phar = NULL; } + efree(arch); efree(entry2); + if (PHAR_G(readonly) && (!phar || !phar->is_data)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", write operations disabled", url_from); - return FAILURE; + return 0; } if ((resource = phar_parse_url(wrapper, url_from, "w", options TSRMLS_CC)) == NULL) { - return FAILURE; + return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from); - return FAILURE; + return 0; } if (strcasecmp("phar", resource->scheme)) { php_url_free(resource); php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from); - return FAILURE; + return 0; } host_len = strlen(resource->host); - phar_request_initialize(TSRMLS_C); if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", resource->path+1, resource->host, error); efree(error); php_url_free(resource); - return FAILURE; + return 0; } if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 2, &error, 1 TSRMLS_CC))) { @@ -462,25 +484,34 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in } php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", resource->path+1, resource->host); php_url_free(resource); - return FAILURE; + return 0; } + if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); efree(error); php_url_free(resource); - return FAILURE; + return 0; } + if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC))) { /* entry exists as a file */ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", resource->path+1, resource->host); php_url_free(resource); - return FAILURE; + return 0; } + if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); efree(error); php_url_free(resource); - return FAILURE; + return 0; + } + + if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", unable to make cached phar writeable", resource->path+1, resource->host); + php_url_free(resource); + return 0; } memset((void *) &entry, 0, sizeof(phar_entry_info)); @@ -489,11 +520,14 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in if (phar->is_zip) { entry.is_zip = 1; } + entry.filename = estrdup(resource->path + 1); + if (phar->is_tar) { entry.is_tar = 1; entry.tar_type = TAR_DIR; } + entry.filename_len = strlen(resource->path + 1); php_url_free(resource); entry.is_dir = 1; @@ -502,20 +536,25 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in entry.is_crc_checked = 1; entry.flags = PHAR_ENT_PERM_DEF_DIR; entry.old_flags = PHAR_ENT_PERM_DEF_DIR; + if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", entry.filename, phar->fname); efree(error); efree(entry.filename); - return FAILURE; + return 0; } + phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry.filename, phar->fname, error); zend_hash_del(&phar->manifest, entry.filename, entry.filename_len); efree(error); - return FAILURE; + return 0; } - return SUCCESS; + + phar_add_virtual_dirs(phar, entry.filename, entry.filename_len TSRMLS_CC); + return 1; } /* }}} */ @@ -530,50 +569,60 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ int arch_len, entry_len; php_url *resource = NULL; uint host_len; + int key_type; + phar_zstr key; + char *str_key; + uint key_len; + ulong unused; + uint path_len; /* pre-readonly check, we need to know if this is a data phar */ if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); - return FAILURE; + return 0; } + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { phar = NULL; } + efree(arch); efree(entry2); + if (PHAR_G(readonly) && (!phar || !phar->is_data)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); - return FAILURE; + return 0; } if ((resource = phar_parse_url(wrapper, url, "w", options TSRMLS_CC)) == NULL) { - return FAILURE; + return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url); - return FAILURE; + return 0; } if (strcasecmp("phar", resource->scheme)) { php_url_free(resource); php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url); - return FAILURE; + return 0; } host_len = strlen(resource->host); - phar_request_initialize(TSRMLS_C); if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", resource->path+1, resource->host, error); efree(error); php_url_free(resource); - return FAILURE; + return 0; } - if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path) - 1, 2, &error, 1 TSRMLS_CC))) { + path_len = strlen(resource->path+1); + + if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, path_len, 2, &error, 1 TSRMLS_CC))) { if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); efree(error); @@ -581,21 +630,83 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", resource->path+1, resource->host); } php_url_free(resource); - return FAILURE; + return 0; } /* now for the easy part */ - entry->is_deleted = 1; - entry->is_modified = 1; - phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error); - zend_hash_del(&phar->manifest, entry->filename, entry->filename_len); + if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", unable to make cached phar writeable", resource->path+1, resource->host); php_url_free(resource); - efree(error); - return FAILURE; + return 0; } + + for (zend_hash_internal_pointer_reset(&phar->manifest); + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)); + zend_hash_move_forward(&phar->manifest)) { + + PHAR_STR(key, str_key); + + if (!entry->is_deleted && + key_len > path_len && + memcmp(str_key, resource->path+1, path_len) == 0 && + IS_SLASH(str_key[path_len])) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty"); + if (entry->is_temp_dir) { + efree(entry->filename); + efree(entry); + } + php_url_free(resource); + return 0; + } + } + + for (zend_hash_internal_pointer_reset(&phar->virtual_dirs); + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL)); + zend_hash_move_forward(&phar->virtual_dirs)) { + + PHAR_STR(key, str_key); + + if (!entry->is_deleted && + key_len > path_len && + memcmp(str_key, resource->path+1, path_len) == 0 && + IS_SLASH(str_key[path_len])) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty"); + if (entry->is_temp_dir) { + efree(entry->filename); + efree(entry); + } + php_url_free(resource); + return 0; + } + } + + if (entry->is_temp_dir) { + zend_hash_del(&phar->virtual_dirs, resource->path+1, path_len); + efree(entry->filename); + efree(entry); + } else { + entry->is_deleted = 1; + entry->is_modified = 1; + phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error); + php_url_free(resource); + efree(error); + return 0; + } + } + php_url_free(resource); - return SUCCESS; + return 1; } /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h index 6467b55bce..b6b49699b9 100644 --- a/ext/phar/dirstream.h +++ b/ext/phar/dirstream.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-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 | diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index 853cf1477d..d3308b571a 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -29,9 +29,15 @@ PHAR_FUNC(phar_opendir) /* {{{ */ int filename_len; zval *zcontext = NULL; - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (!PHAR_G(intercepted)) { goto skip_phar; } + + if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) + && !cached_phars.arBuckets) { + goto skip_phar; + } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &filename, &filename_len, &zcontext) == FAILURE) { return; } @@ -97,13 +103,20 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */ long maxlen = PHP_STREAM_COPY_ALL; zval *zcontext = NULL; - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (!PHAR_G(intercepted)) { goto skip_phar; } + + if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) + && !cached_phars.arBuckets) { + goto skip_phar; + } + /* Parse arguments */ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|br!ll", &filename, &filename_len, &use_include_path, &zcontext, &offset, &maxlen) == FAILURE) { - return; + goto skip_phar; } + if (use_include_path || (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://"))) { char *arch, *entry, *fname; int arch_len, entry_len, fname_len; @@ -117,7 +130,7 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */ fname_len = strlen(fname); if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { char *name; - phar_archive_data **pphar; + phar_archive_data *phar; efree(entry); entry = filename; @@ -131,7 +144,7 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */ } /* retrieving a file defaults to within the current directory, so use this if possible */ - if (FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { efree(arch); goto skip_phar; } @@ -147,7 +160,7 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */ } else { entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC); if (entry[0] == '/') { - if (!zend_hash_exists(&((*pphar)->manifest), entry + 1, entry_len - 1)) { + if (!zend_hash_exists(&(phar->manifest), entry + 1, entry_len - 1)) { /* this file is not in the phar, use the original path */ notfound: efree(arch); @@ -155,7 +168,7 @@ notfound: goto skip_phar; } } else { - if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { + if (!zend_hash_exists(&(phar->manifest), entry, entry_len)) { goto notfound; } } @@ -222,7 +235,12 @@ PHAR_FUNC(phar_readfile) /* {{{ */ zval *zcontext = NULL; php_stream *stream; - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (!PHAR_G(intercepted)) { + goto skip_phar; + } + + if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) + && !cached_phars.arBuckets) { goto skip_phar; } if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|br!", &filename, &filename_len, &use_include_path, &zcontext) == FAILURE) { @@ -233,7 +251,7 @@ PHAR_FUNC(phar_readfile) /* {{{ */ int arch_len, entry_len, fname_len; php_stream_context *context = NULL; char *name; - phar_archive_data **pphar; + phar_archive_data *phar; fname = zend_get_executed_filename(TSRMLS_C); if (strncasecmp(fname, "phar://", 7)) { @@ -249,7 +267,7 @@ PHAR_FUNC(phar_readfile) /* {{{ */ /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file defaults to within the current directory, so use this if possible */ - if (FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { efree(arch); goto skip_phar; } @@ -264,7 +282,7 @@ PHAR_FUNC(phar_readfile) /* {{{ */ } else { entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC); if (entry[0] == '/') { - if (!zend_hash_exists(&((*pphar)->manifest), entry + 1, entry_len - 1)) { + if (!zend_hash_exists(&(phar->manifest), entry + 1, entry_len - 1)) { /* this file is not in the phar, use the original path */ notfound: efree(entry); @@ -272,7 +290,7 @@ notfound: goto skip_phar; } } else { - if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { + if (!zend_hash_exists(&(phar->manifest), entry, entry_len)) { goto notfound; } } @@ -312,7 +330,12 @@ PHAR_FUNC(phar_fopen) /* {{{ */ zval *zcontext = NULL; php_stream *stream; - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (!PHAR_G(intercepted)) { + goto skip_phar; + } + + if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) + && !cached_phars.arBuckets) { /* no need to check, include_path not even specified in fopen/ no active phars */ goto skip_phar; } @@ -324,7 +347,7 @@ PHAR_FUNC(phar_fopen) /* {{{ */ int arch_len, entry_len, fname_len; php_stream_context *context = NULL; char *name; - phar_archive_data **pphar; + phar_archive_data *phar; fname = zend_get_executed_filename(TSRMLS_C); if (strncasecmp(fname, "phar://", 7)) { @@ -340,7 +363,7 @@ PHAR_FUNC(phar_fopen) /* {{{ */ /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file defaults to within the current directory, so use this if possible */ - if (FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { efree(arch); goto skip_phar; } @@ -355,7 +378,7 @@ PHAR_FUNC(phar_fopen) /* {{{ */ } else { entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC); if (entry[0] == '/') { - if (!zend_hash_exists(&((*pphar)->manifest), entry + 1, entry_len - 1)) { + if (!zend_hash_exists(&(phar->manifest), entry + 1, entry_len - 1)) { /* this file is not in the phar, use the original path */ notfound: efree(entry); @@ -363,7 +386,7 @@ notfound: goto skip_phar; } } else { - if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { + if (!zend_hash_exists(&(phar->manifest), entry, entry_len)) { /* this file is not in the phar, use the original path */ goto notfound; } @@ -595,8 +618,7 @@ void phar_file_stat(const char *filename, php_stat_len filename_length, int type int arch_len, entry_len, fname_len; struct stat sb = {0}; phar_entry_info *data = NULL; - char *tmp; - int tmp_len; + phar_archive_data *phar; fname = zend_get_executed_filename(TSRMLS_C); @@ -607,28 +629,57 @@ void phar_file_stat(const char *filename, php_stat_len filename_length, int type goto skip_phar; } fname_len = strlen(fname); + if (PHAR_G(last_phar) && fname_len - 7 >= PHAR_G(last_phar_name_len) && !memcmp(fname + 7, PHAR_G(last_phar_name), PHAR_G(last_phar_name_len))) { + arch = estrndup(PHAR_G(last_phar_name), PHAR_G(last_phar_name_len)); + arch_len = PHAR_G(last_phar_name_len); + entry = estrndup(filename, filename_length); + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = (int) filename_length; + phar = PHAR_G(last_phar); + goto splitted; + } if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { - phar_archive_data **pphar; efree(entry); entry = estrndup(filename, filename_length); /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = (int) filename_length; - if (FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { efree(arch); goto skip_phar; } +splitted: entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); if (entry[0] == '/') { - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry + 1, entry_len - 1, (void **) &data)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry + 1, entry_len - 1, (void **) &data)) { efree(entry); goto stat_entry; } goto notfound; } - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &data)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry, entry_len, (void **) &data)) { efree(entry); goto stat_entry; + } + if (zend_hash_exists(&(phar->virtual_dirs), entry, entry_len)) { + efree(entry); + efree(arch); + if (IS_EXISTS_CHECK(type)) { + RETURN_TRUE; + } + sb.st_size = 0; + sb.st_mode = 0777; + sb.st_mode |= S_IFDIR; /* regular directory */ +#ifdef NETWARE + sb.st_mtime.tv_sec = phar->max_timestamp; + sb.st_atime.tv_sec = phar->max_timestamp; + sb.st_ctime.tv_sec = phar->max_timestamp; +#else + sb.st_mtime = phar->max_timestamp; + sb.st_atime = phar->max_timestamp; + sb.st_ctime = phar->max_timestamp; +#endif + goto statme_baby; } else { char *save, *save2, *actual; int save_len, save2_len, actual_len; @@ -647,54 +698,42 @@ notfound: PHAR_G(cwd_len) = 0; /* clean path without cwd */ entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry + 1, entry_len - 1, (void **) &data)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry + 1, entry_len - 1, (void **) &data)) { PHAR_G(cwd) = save; PHAR_G(cwd_len) = save_len; efree(entry); efree(save2); + if (IS_EXISTS_CHECK(type)) { + efree(arch); + RETURN_TRUE; + } goto stat_entry; - } else { - phar_archive_data *phar = *pphar; - phar_zstr key; - char *str_key; - uint keylen; - ulong unused; - + } + if (zend_hash_exists(&(phar->virtual_dirs), entry + 1, entry_len - 1)) { PHAR_G(cwd) = save; PHAR_G(cwd_len) = save_len; - /* original not found either, this is possibly a directory relative to cwd */ - zend_hash_internal_pointer_reset(&phar->manifest); - while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) { - if (HASH_KEY_NON_EXISTANT != - zend_hash_get_current_key_ex( - &phar->manifest, &key, &keylen, &unused, 0, NULL)) { - PHAR_STR(key, str_key); - if (!memcmp(actual, str_key, actual_len)) { - efree(save2); - efree(entry); - /* directory found, all dirs have the same stat */ - if (str_key[actual_len] == '/') { - sb.st_size = 0; - sb.st_mode = 0777; - sb.st_mode |= S_IFDIR; /* regular directory */ + efree(entry); + efree(save2); + efree(arch); + if (IS_EXISTS_CHECK(type)) { + RETURN_TRUE; + } + sb.st_size = 0; + sb.st_mode = 0777; + sb.st_mode |= S_IFDIR; /* regular directory */ #ifdef NETWARE - sb.st_mtime.tv_sec = phar->max_timestamp; - sb.st_atime.tv_sec = phar->max_timestamp; - sb.st_ctime.tv_sec = phar->max_timestamp; + sb.st_mtime.tv_sec = phar->max_timestamp; + sb.st_atime.tv_sec = phar->max_timestamp; + sb.st_ctime.tv_sec = phar->max_timestamp; #else - sb.st_mtime = phar->max_timestamp; - sb.st_atime = phar->max_timestamp; - sb.st_ctime = phar->max_timestamp; + sb.st_mtime = phar->max_timestamp; + sb.st_atime = phar->max_timestamp; + sb.st_ctime = phar->max_timestamp; #endif - goto statme_baby; - } - } - } - if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { - break; - } - } + goto statme_baby; } + PHAR_G(cwd) = save; + PHAR_G(cwd_len) = save_len; efree(entry); efree(save2); efree(arch); @@ -705,6 +744,7 @@ notfound: RETURN_FALSE; } stat_entry: + efree(arch); if (!data->is_dir) { sb.st_size = data->uncompressed_filesize; sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; @@ -743,30 +783,18 @@ stat_entry: } statme_baby: - efree(arch); - if (!(*pphar)->is_writeable) { + if (!phar->is_writeable) { sb.st_mode = (sb.st_mode & 0555) | (sb.st_mode & ~0777); } - + sb.st_nlink = 1; sb.st_rdev = -1; - if (data) { - tmp_len = data->filename_len + (*pphar)->alias_len; - } else { - tmp_len = (*pphar)->alias_len + 1; - } - tmp = (char *) emalloc(tmp_len); - memcpy(tmp, (*pphar)->alias, (*pphar)->alias_len); - if (data) { - memcpy(tmp + (*pphar)->alias_len, data->filename, data->filename_len); - } else { - *(tmp + (*pphar)->alias_len) = '/'; - } /* this is only for APC, so use /dev/null device - no chance of conflict there! */ sb.st_dev = 0xc; /* generate unique inode number for alias/filename, so no phars will conflict */ - sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len); - efree(tmp); + if (data) { + sb.st_ino = data->inode; + } #ifndef PHP_WIN32 sb.st_blksize = -1; sb.st_blocks = -1; @@ -783,14 +811,18 @@ skip_phar: #define PharFileFunction(fname, funcnum, orig) \ void fname(INTERNAL_FUNCTION_PARAMETERS) { \ - char *filename; \ - int filename_len; \ - \ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { \ - return; \ + if (!PHAR_G(intercepted)) { \ + PHAR_G(orig)(INTERNAL_FUNCTION_PARAM_PASSTHRU); \ + } else { \ + char *filename; \ + int filename_len; \ + \ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { \ + return; \ + } \ + \ + phar_file_stat(filename, (php_stat_len) filename_len, funcnum, PHAR_G(orig), INTERNAL_FUNCTION_PARAM_PASSTHRU); \ } \ - \ - phar_file_stat(filename, (php_stat_len) filename_len, funcnum, PHAR_G(orig), INTERNAL_FUNCTION_PARAM_PASSTHRU); \ } /* }}} */ @@ -854,13 +886,13 @@ PharFileFunction(phar_is_readable, FS_IS_R, orig_is_readable) PharFileFunction(phar_is_executable, FS_IS_X, orig_is_executable) /* }}} */ -/* {{{ proto bool is_executable(string filename) - Returns true if file is executable */ +/* {{{ proto bool file_exists(string filename) + Returns true if filename exists */ PharFileFunction(phar_file_exists, FS_EXISTS, orig_file_exists) /* }}} */ -/* {{{ proto bool is_executable(string filename) - Returns true if file is executable */ +/* {{{ proto bool is_dir(string filename) + Returns true if file is directory */ PharFileFunction(phar_is_dir, FS_IS_DIR, orig_is_dir) /* }}} */ @@ -869,7 +901,12 @@ PHAR_FUNC(phar_is_file) /* {{{ */ char *filename; int filename_len; - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (!PHAR_G(intercepted)) { + goto skip_phar; + } + + if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) + && !cached_phars.arBuckets) { goto skip_phar; } if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { @@ -888,19 +925,19 @@ PHAR_FUNC(phar_is_file) /* {{{ */ } fname_len = strlen(fname); if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { - phar_archive_data **pphar; + phar_archive_data *phar; efree(entry); entry = filename; /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file within the current directory, so use this if possible */ - if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (SUCCESS == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { phar_entry_info *etemp; entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC); if (entry[0] == '/') { - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry + 1, entry_len - 1, (void **) &etemp)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry + 1, entry_len - 1, (void **) &etemp)) { /* this file is not in the current directory, use the original path */ found_it: efree(entry); @@ -908,7 +945,7 @@ found_it: RETURN_BOOL(!etemp->is_dir); } } else { - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &etemp)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry, entry_len, (void **) &etemp)) { goto found_it; } } @@ -929,7 +966,12 @@ PHAR_FUNC(phar_is_link) /* {{{ */ char *filename; int filename_len; - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (!PHAR_G(intercepted)) { + goto skip_phar; + } + + if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) + && !cached_phars.arBuckets) { goto skip_phar; } if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { @@ -948,19 +990,19 @@ PHAR_FUNC(phar_is_link) /* {{{ */ } fname_len = strlen(fname); if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { - phar_archive_data **pphar; + phar_archive_data *phar; efree(entry); entry = filename; /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file within the current directory, so use this if possible */ - if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (SUCCESS == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { phar_entry_info *etemp; entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC); if (entry[0] == '/') { - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry + 1, entry_len - 1, (void **) &etemp)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry + 1, entry_len - 1, (void **) &etemp)) { /* this file is not in the current directory, use the original path */ found_it: efree(entry); @@ -968,7 +1010,7 @@ found_it: RETURN_BOOL(etemp->link); } } else { - if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &etemp)) { + if (SUCCESS == zend_hash_find(&(phar->manifest), entry, entry_len, (void **) &etemp)) { goto found_it; } } @@ -979,7 +1021,7 @@ found_it: } } skip_phar: - PHAR_G(orig_file_exists)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + PHAR_G(orig_is_link)(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } /* }}} */ @@ -995,6 +1037,24 @@ PharFileFunction(phar_stat, FS_STAT, orig_stat) /* }}} */ /* {{{ void phar_intercept_functions(TSRMLS_D) */ +void phar_intercept_functions(TSRMLS_D) +{ + if (!PHAR_G(request_init)) { + PHAR_G(cwd) = NULL; + PHAR_G(cwd_len) = 0; + } + PHAR_G(intercepted) = 1; +} +/* }}} */ + +/* {{{ void phar_release_functions(TSRMLS_D) */ +void phar_release_functions(TSRMLS_D) +{ + PHAR_G(intercepted) = 0; +} +/* }}} */ + +/* {{{ void phar_intercept_functions_init(TSRMLS_D) */ #define PHAR_INTERCEPT(func) \ PHAR_G(orig_##func) = NULL; \ if (SUCCESS == zend_hash_find(CG(function_table), #func, sizeof(#func), (void **)&orig)) { \ @@ -1002,17 +1062,10 @@ PharFileFunction(phar_stat, FS_STAT, orig_stat) orig->internal_function.handler = phar_##func; \ } -void phar_intercept_functions(TSRMLS_D) +void phar_intercept_functions_init(TSRMLS_D) { zend_function *orig; - if (!PHAR_G(request_init)) { - PHAR_G(cwd) = NULL; - PHAR_G(cwd_len) = 0; - } else if (PHAR_G(orig_fopen)) { - /* don't double-intercept */ - return; - } PHAR_INTERCEPT(fopen); PHAR_INTERCEPT(file_get_contents); PHAR_INTERCEPT(is_file); @@ -1035,17 +1088,18 @@ void phar_intercept_functions(TSRMLS_D) PHAR_INTERCEPT(lstat); PHAR_INTERCEPT(stat); PHAR_INTERCEPT(readfile); + PHAR_G(intercepted) = 0; } /* }}} */ -/* {{{ void phar_release_functions(TSRMLS_D) */ +/* {{{ void phar_intercept_functions_shutdown(TSRMLS_D) */ #define PHAR_RELEASE(func) \ if (PHAR_G(orig_##func) && SUCCESS == zend_hash_find(CG(function_table), #func, sizeof(#func), (void **)&orig)) { \ orig->internal_function.handler = PHAR_G(orig_##func); \ } \ PHAR_G(orig_##func) = NULL; -void phar_release_functions(TSRMLS_D) +void phar_intercept_functions_shutdown(TSRMLS_D) { zend_function *orig; @@ -1070,6 +1124,7 @@ void phar_release_functions(TSRMLS_D) PHAR_RELEASE(lstat); PHAR_RELEASE(stat); PHAR_RELEASE(readfile); + PHAR_G(intercepted) = 0; } /* }}} */ diff --git a/ext/phar/func_interceptors.h b/ext/phar/func_interceptors.h index b48be591ed..d67d5f6380 100644 --- a/ext/phar/func_interceptors.h +++ b/ext/phar/func_interceptors.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-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 | @@ -22,6 +22,8 @@ BEGIN_EXTERN_C() void phar_intercept_functions(TSRMLS_D); void phar_release_functions(TSRMLS_D); +void phar_intercept_functions_init(TSRMLS_D); +void phar_intercept_functions_shutdown(TSRMLS_D); END_EXTERN_C() /* diff --git a/ext/phar/makestub.php b/ext/phar/makestub.php index 44e0b96337..2bb30e96d8 100644 --- a/ext/phar/makestub.php +++ b/ext/phar/makestub.php @@ -4,18 +4,18 @@ $s = str_replace("\r", '', file_get_contents(dirname(__FILE__) . '/shortarc.php' $s .= "\nExtract_Phar::go();\n__HALT_COMPILER();"; $news = ''; foreach (token_get_all($s) as $token) { - if (is_array($token)) { - if ($token[0] == T_COMMENT) { - $token[1] = ''; - } - if ($token[0] == T_WHITESPACE) { - $n = str_repeat("\n", substr_count($token[1], "\n")); - $token[1] = strlen($n) ? $n : ' '; - } - $news .= $token[1]; - } else { - $news .= $token; - } + if (is_array($token)) { + if ($token[0] == T_COMMENT) { + $token[1] = ''; + } + if ($token[0] == T_WHITESPACE) { + $n = str_repeat("\n", substr_count($token[1], "\n")); + $token[1] = strlen($n) ? $n : ' '; + } + $news .= $token[1]; + } else { + $news .= $token; + } } $s = $news . ' ?>'; $slen = strlen($s) - strlen('index.php') - strlen("000"); @@ -34,7 +34,7 @@ $stub = '/* +----------------------------------------------------------------------+ | phar php single-file executable PHP extension generated stub | +----------------------------------------------------------------------+ - | Copyright (c) 2005-' . date('Y') . ' The PHP Group | + | Copyright (c) 2005-' . date('Y') . ' 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 | @@ -57,50 +57,50 @@ $s1split = str_split($s1, 2046); $s3split = str_split($s3, 2046); $took = false; foreach ($s1split as $i => $chunk) { - if ($took) { - $s1split[$i] = substr($chunk, 1); - $took = false; - } - if ($chunk[strlen($chunk) - 1] == '\\') { - $s1split[$i] .= $s1split[$i + 1][0]; - $took = true; - } + if ($took) { + $s1split[$i] = substr($chunk, 1); + $took = false; + } + if ($chunk[strlen($chunk) - 1] == '\\') { + $s1split[$i] .= $s1split[$i + 1][0]; + $took = true; + } } foreach ($s3split as $i => $chunk) { - if ($took) { - $s3split[$i] = substr($chunk, 1); - $took = false; - } - if ($chunk[strlen($chunk) - 1] == '\\') { - $s3split[$i] .= $s3split[$i + 1][0]; - $took = true; - } + if ($took) { + $s3split[$i] = substr($chunk, 1); + $took = false; + } + if ($chunk[strlen($chunk) - 1] == '\\') { + $s3split[$i] .= $s3split[$i + 1][0]; + $took = true; + } } $stub .= "\tstatic const char newstub0[] = \"" . $webs . '"; '; foreach ($s1split as $i => $chunk) { - $s1count = $i + 1; - $stub .= "\tstatic const char newstub1_" . $i . '[] = "' . $chunk . '"; + $s1count = $i + 1; + $stub .= "\tstatic const char newstub1_" . $i . '[] = "' . $chunk . '"; '; } $stub .= "\tstatic const char newstub2[] = \"" . $s2 . "\"; "; foreach ($s3split as $i => $chunk) { - $s3count = $i + 1; - $stub .= "\tstatic const char newstub3_" . $i . '[] = "' . $chunk . '"; + $s3count = $i + 1; + $stub .= "\tstatic const char newstub3_" . $i . '[] = "' . $chunk . '"; '; } $stub .= "\n\tstatic const int newstub_len = " . $slen . "; \t*len = spprintf(stub, name_len + web_len + newstub_len, \"%s%s" . str_repeat('%s', $s1count) . '%s%s%d' - . str_repeat('%s', $s3count) . '", newstub0, web'; + . str_repeat('%s', $s3count) . '", newstub0, web'; foreach ($s1split as $i => $unused) { - $stub .= ', newstub1_' . $i; + $stub .= ', newstub1_' . $i; } $stub .= ', index_php, newstub2'; $stub .= ", name_len + web_len + newstub_len"; foreach ($s3split as $i => $unused) { - $stub .= ', newstub3_' . $i; + $stub .= ', newstub3_' . $i; } $stub .= "); }"; diff --git a/ext/phar/package.php b/ext/phar/package.php index f4d63ae21d..d7d81e2e6a 100644 --- a/ext/phar/package.php +++ b/ext/phar/package.php @@ -53,29 +53,29 @@ require_once 'PEAR/PackageFileManager2.php'; PEAR::setErrorHandling(PEAR_ERROR_DIE); $options = array( - 'filelistgenerator' => 'CVS', - 'changelogoldtonew' => false, - 'simpleoutput' => true, - 'baseinstalldir' => '/', - 'packagedirectory' => dirname(__FILE__), - 'packagefile' => 'package.xml', - 'clearcontents' => true, - 'ignore' => array('package*.php', 'package*.xml'), - 'dir_roles' => array( - 'docs' => 'doc', - 'examples' => 'doc', - 'tests' => 'test', - 'phar' => 'src', - ), - 'exceptions' => array( - 'CREDITS' => 'doc', - 'EXPERIMENTAL' => 'doc', - 'LICENSE' => 'doc', - 'Makefile.frag' => 'src', - 'phar_path_check.re' => 'src', - 'TODO' => 'doc', - 'phar.phar' => 'script', - ), + 'filelistgenerator' => 'CVS', + 'changelogoldtonew' => false, + 'simpleoutput' => true, + 'baseinstalldir' => '/', + 'packagedirectory' => dirname(__FILE__), + 'packagefile' => 'package.xml', + 'clearcontents' => true, + 'ignore' => array('package*.php', 'package*.xml'), + 'dir_roles' => array( + 'docs' => 'doc', + 'examples' => 'doc', + 'tests' => 'test', + 'phar' => 'src', + ), + 'exceptions' => array( + 'CREDITS' => 'doc', + 'EXPERIMENTAL' => 'doc', + 'LICENSE' => 'doc', + 'Makefile.frag' => 'src', + 'phar_path_check.re' => 'src', + 'TODO' => 'doc', + 'phar.phar' => 'script', + ), ); $package = PEAR_PackageFileManager2::importOptions(dirname(__FILE__) . '/package.xml', $options); @@ -100,9 +100,9 @@ $package->setNotes("\n$notes\n"); $package->generateContents(); if (isset($_GET['make']) || (isset($_SERVER['argv']) && @$_SERVER['argv'][1] == 'make')) { - $package->writePackageFile(); + $package->writePackageFile(); } else { - $package->debugPackageFile(); + $package->debugPackageFile(); } ?> diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 27afc67dc7..9cb0b74916 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -24,6 +24,8 @@ #include "SAPI.h" #include "func_interceptors.h" +static void destroy_phar_data(void *pDest); + ZEND_DECLARE_MODULE_GLOBALS(phar) #if PHP_VERSION_ID >= 50300 char *(*phar_save_resolve_path)(const char *filename, int filename_len TSRMLS_DC); @@ -40,12 +42,12 @@ static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC) /* {{{ if (!phar->is_data) { phar->is_writeable = !keep; } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ -/* if the original value is 0 (disabled), then allow setting/unsetting at will - otherwise, only allow 1 (enabled), and error on disabling */ +/* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ { zend_bool old, ini; @@ -54,7 +56,7 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ old = PHAR_G(readonly_orig); } else { old = PHAR_G(require_hash_orig); - } + } if (new_value_length == 2 && !strcasecmp("on", new_value)) { ini = (zend_bool) 1; @@ -72,10 +74,10 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ /* do not allow unsetting in runtime */ if (stage == ZEND_INI_STAGE_STARTUP) { if (entry->name_length == 14) { - PHAR_G(readonly_orig) = ini; + PHAR_G(readonly_orig) = ini; } else { PHAR_G(require_hash_orig) = ini; - } + } } else if (old && !ini) { return FAILURE; } @@ -87,14 +89,117 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ } } else { PHAR_G(require_hash) = ini; - } + } + return SUCCESS; } /* }}}*/ +/* this global stores the global cached pre-parsed manifests */ +HashTable cached_phars; +HashTable cached_alias; + +static void phar_split_cache_list(TSRMLS_D) /* {{{ */ +{ + char *tmp; + char *key, *lasts, *end; + char ds[2]; + phar_archive_data *phar; + uint i = 0; + + if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) { + return; + } + + ds[0] = DEFAULT_DIR_SEPARATOR; + ds[1] = '\0'; + tmp = estrdup(PHAR_GLOBALS->cache_list); + + /* fake request startup */ + PHAR_GLOBALS->request_init = 1; + if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) { + EG(regular_list).nNextFreeElement=1; /* we don't want resource id 0 */ + } + + PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2")); + PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib")); + /* these two are dummies and will be destroyed later */ + zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1); + zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1); + /* these two are real and will be copied over cached_phars/cached_alias later */ + zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1); + zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1); + PHAR_GLOBALS->manifest_cached = 1; + PHAR_GLOBALS->persist = 1; + + for (key = php_strtok_r(tmp, ds, &lasts); + key; + key = php_strtok_r(NULL, ds, &lasts)) { + end = strchr(key, DEFAULT_DIR_SEPARATOR); + + if (end) { + if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) { +finish_up: + phar->phar_pos = i++; + php_stream_close(phar->fp); + phar->fp = NULL; + } else { +finish_error: + PHAR_GLOBALS->persist = 0; + PHAR_GLOBALS->manifest_cached = 0; + efree(tmp); + zend_hash_destroy(&(PHAR_G(phar_fname_map))); + PHAR_GLOBALS->phar_fname_map.arBuckets = 0; + zend_hash_destroy(&(PHAR_G(phar_alias_map))); + PHAR_GLOBALS->phar_alias_map.arBuckets = 0; + zend_hash_destroy(&cached_phars); + zend_hash_destroy(&cached_alias); + zend_hash_graceful_reverse_destroy(&EG(regular_list)); + memset(&EG(regular_list), 0, sizeof(HashTable)); + /* free cached manifests */ + PHAR_GLOBALS->request_init = 0; + return; + } + } else { + if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL TSRMLS_CC)) { + goto finish_up; + } else { + goto finish_error; + } + } + } + + PHAR_GLOBALS->persist = 0; + PHAR_GLOBALS->request_init = 0; + /* destroy dummy values from before */ + zend_hash_destroy(&cached_phars); + zend_hash_destroy(&cached_alias); + cached_phars = PHAR_GLOBALS->phar_fname_map; + cached_alias = PHAR_GLOBALS->phar_alias_map; + PHAR_GLOBALS->phar_fname_map.arBuckets = 0; + PHAR_GLOBALS->phar_alias_map.arBuckets = 0; + zend_hash_graceful_reverse_destroy(&EG(regular_list)); + memset(&EG(regular_list), 0, sizeof(HashTable)); + efree(tmp); +} +/* }}} */ + +ZEND_INI_MH(phar_ini_cache_list) /* {{{ */ +{ + PHAR_G(cache_list) = new_value; + + if (stage == ZEND_INI_STAGE_STARTUP) { + phar_split_cache_list(TSRMLS_C); + } + + return SUCCESS; +} +/* }}} */ + PHP_INI_BEGIN() - STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals) + STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals) STD_PHP_INI_BOOLEAN( "phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals) + STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals) PHP_INI_END() /** @@ -104,37 +209,61 @@ PHP_INI_END() void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */ { if (phar->alias && phar->alias != phar->fname) { - efree(phar->alias); + pefree(phar->alias, phar->is_persistent); phar->alias = NULL; } + if (phar->fname) { - efree(phar->fname); + pefree(phar->fname, phar->is_persistent); phar->fname = NULL; } + if (phar->signature) { - efree(phar->signature); + pefree(phar->signature, phar->is_persistent); + phar->signature = NULL; } + if (phar->manifest.arBuckets) { zend_hash_destroy(&phar->manifest); phar->manifest.arBuckets = NULL; } + if (phar->mounted_dirs.arBuckets) { zend_hash_destroy(&phar->mounted_dirs); phar->mounted_dirs.arBuckets = NULL; } + + if (phar->virtual_dirs.arBuckets) { + zend_hash_destroy(&phar->virtual_dirs); + phar->virtual_dirs.arBuckets = NULL; + } + if (phar->metadata) { - zval_ptr_dtor(&phar->metadata); + if (phar->is_persistent) { + if (phar->metadata_len) { + /* for zip comments that are strings */ + free(phar->metadata); + } else { + zval_internal_ptr_dtor(&phar->metadata); + } + } else { + zval_ptr_dtor(&phar->metadata); + } + phar->metadata_len = 0; phar->metadata = 0; } + if (phar->fp) { php_stream_close(phar->fp); phar->fp = 0; } + if (phar->ufp) { php_stream_close(phar->ufp); - phar->fp = 0; + phar->ufp = 0; } - efree(phar); + + pefree(phar, phar->is_persistent); } /* }}}*/ @@ -143,6 +272,10 @@ void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */ */ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ { + if (phar->is_persistent) { + return 0; + } + if (--phar->refcount < 0) { if (PHAR_GLOBALS->request_done || zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) { @@ -150,6 +283,10 @@ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ } return 1; } else if (!phar->refcount) { + /* invalidate phar cache */ + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) { /* close open file handle - allows removal or rename of the file on windows, which has greedy locking @@ -158,6 +295,7 @@ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ php_stream_close(phar->fp); phar->fp = NULL; } + if (!zend_hash_num_elements(&phar->manifest)) { /* this is a new phar that has perhaps had an alias/metadata set, but has never been flushed */ @@ -204,10 +342,12 @@ static int phar_tmpclose_apply(void *pDest TSRMLS_DC) /* {{{ */ if (entry->fp_type != PHAR_TMP) { return ZEND_HASH_APPLY_KEEP; } + if (entry->fp && !entry->fp_refcount) { php_stream_close(entry->fp); entry->fp = NULL; } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -227,7 +367,9 @@ static void destroy_phar_data(void *pDest) /* {{{ */ destroy_phar_data_only(pDest); return; } + zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, phar_data TSRMLS_CC); + if (--phar_data->refcount < 0) { phar_destroy_phar_data(phar_data TSRMLS_CC); } @@ -246,25 +388,41 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */ php_stream_close(entry->cfp); entry->cfp = 0; } + if (entry->fp) { php_stream_close(entry->fp); entry->fp = 0; } + if (entry->metadata) { - zval_ptr_dtor(&entry->metadata); + if (entry->is_persistent) { + if (entry->metadata_len) { + /* for zip comments that are strings */ + free(entry->metadata); + } else { + zval_internal_ptr_dtor(&entry->metadata); + } + } else { + zval_ptr_dtor(&entry->metadata); + } + entry->metadata_len = 0; entry->metadata = 0; } + if (entry->metadata_str.c) { smart_str_free(&entry->metadata_str); entry->metadata_str.c = 0; } - efree(entry->filename); + + pefree(entry->filename, entry->is_persistent); + if (entry->link) { - efree(entry->link); + pefree(entry->link, entry->is_persistent); entry->link = 0; } + if (entry->tmp) { - efree(entry->tmp); + pefree(entry->tmp, entry->is_persistent); entry->tmp = 0; } } @@ -274,14 +432,16 @@ int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */ { int ret = 0; - if (idata->internal_file) { + if (idata->internal_file && !idata->internal_file->is_persistent) { if (--idata->internal_file->fp_refcount < 0) { idata->internal_file->fp_refcount = 0; } + if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) { php_stream_close(idata->fp); } } + phar_archive_delref(idata->phar TSRMLS_CC); efree(idata); return ret; @@ -296,6 +456,7 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */ phar_archive_data *phar; phar = idata->phar; + if (idata->internal_file->fp_refcount < 2) { if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) { php_stream_close(idata->fp); @@ -307,6 +468,7 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */ idata->internal_file->is_deleted = 1; phar_entry_delref(idata TSRMLS_CC); } + if (!phar->donotflush) { phar_flush(phar, 0, 0, 0, error TSRMLS_CC); } @@ -328,7 +490,7 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */ phar_destroy_phar_data(mydata TSRMLS_CC);\ }\ if (signature) {\ - efree(signature);\ + pefree(signature, PHAR_G(persist));\ }\ MAPPHAR_ALLOC_FAIL(msg) @@ -376,6 +538,7 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len #ifdef PHP_WIN32 unixfname = estrndup(fname, fname_len); phar_unixify_path_separators(unixfname, fname_len); + if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error TSRMLS_CC) && ((alias && fname_len == phar->fname_len && !strncmp(unixfname, phar->fname, fname_len)) || !alias) @@ -406,9 +569,11 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len } } } + if (pphar) { *pphar = phar; } + return SUCCESS; } else { #ifdef PHP_WIN32 @@ -417,9 +582,11 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len if (pphar) { *pphar = NULL; } + if (phar && error && !(options & REPORT_ERRORS)) { efree(error); } + return FAILURE; } } @@ -444,55 +611,54 @@ int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSR } else { buf_len = zip_metadata_len; } - + if (buf_len) { - ALLOC_INIT_ZVAL(*metadata); + ALLOC_ZVAL(*metadata); + INIT_ZVAL(**metadata); p = (const unsigned char*) *buffer; PHP_VAR_UNSERIALIZE_INIT(var_hash); - if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) { + + if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); zval_ptr_dtor(metadata); *metadata = NULL; return FAILURE; } + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + + if (PHAR_G(persist)) { + /* lazy init metadata */ + zval_ptr_dtor(metadata); + *metadata = (zval *) pemalloc(buf_len, 1); + memcpy(*metadata, *buffer, buf_len); + if (!zip_metadata_len) { + *buffer += buf_len; + } + return SUCCESS; + } } else { *metadata = NULL; } + if (!zip_metadata_len) { *buffer += buf_len; } + return SUCCESS; } /* }}}*/ -static const char hexChars[] = "0123456789ABCDEF"; - -static int phar_hex_str(const char *digest, size_t digest_len, char ** signature) -{ - int pos = -1; - size_t len; - - *signature = (char*)safe_emalloc(digest_len, 2, 1); - - for(len = 0; len < digest_len; ++len) { - (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] >> 4]; - (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] & 0x0F]; - } - (*signature)[++pos] = '\0'; - 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 + * 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_from_filename to process the manifest, but can be called * directly. */ -int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ +static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ { char b32[4], *buffer, *endbuffer, *savebuf; phar_archive_data *mydata = NULL; @@ -506,6 +672,7 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, if (pphar) { *pphar = NULL; } + if (error) { *error = NULL; } @@ -516,15 +683,18 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, } buffer = b32; + if (3 != php_stream_read(fp, buffer, 3)) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)") } + if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') { int nextchar; halt_offset += 3; if (EOF == (nextchar = php_stream_getc(fp))) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)") } + if ((char) nextchar == '\r') { /* if we have an \r we require an \n as well */ if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') { @@ -532,10 +702,12 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, } ++halt_offset; } + if ((char) nextchar == '\n') { ++halt_offset; } } + /* make sure we are at the right location to read the manifest */ if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) { MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"") @@ -543,31 +715,38 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, /* read in manifest */ buffer = b32; + if (4 != php_stream_read(fp, buffer, 4)) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)") } + PHAR_GET_32(buffer, manifest_len); + if (manifest_len > 1048576 * 100) { /* prevent serious memory issues by limiting manifest to at most 100 MB in length */ MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"") } + buffer = (char *)emalloc(manifest_len); savebuf = buffer; endbuffer = buffer + manifest_len; + if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)") } /* extract the number of entries */ PHAR_GET_32(buffer, manifest_count); + if (manifest_count == 0) { MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries. Phars must have at least 1 entry"); } /* extract API version, lowest nibble currently unused */ - manifest_ver = (((unsigned char)buffer[0]) << 8) - + ((unsigned char)buffer[1]); + manifest_ver = (((unsigned char)buffer[0]) << 8) + + ((unsigned char)buffer[1]); buffer += 2; + if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) { efree(savebuf); php_stream_close(fp); @@ -580,7 +759,6 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, PHAR_GET_32(buffer, manifest_flags); manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK; - manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK; /* remember whether this entire phar was compressed with gz/bzip2 */ manifest_flags |= compression; @@ -588,14 +766,13 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, /* The lowest nibble contains the phar wide flags. The compression flags can */ /* be ignored on reading because it is being generated anyways. */ if (manifest_flags & PHAR_HDR_SIGNATURE) { - unsigned char buf[1024]; - int read_size, len; char sig_buf[8], *sig_ptr = sig_buf; off_t read_len; + size_t end_of_phar; if (-1 == php_stream_seek(fp, -8, SEEK_END) || (read_len = php_stream_tell(fp)) < 20 - || 8 != php_stream_read(fp, sig_buf, 8) + || 8 != php_stream_read(fp, sig_buf, 8) || memcmp(sig_buf+4, "GBMB", 4)) { efree(savebuf); php_stream_close(fp); @@ -604,170 +781,191 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, } return FAILURE; } + PHAR_GET_32(sig_ptr, sig_flags); + switch(sig_flags) { -#if HAVE_HASH_EXT - case PHAR_SIG_SHA512: { - unsigned char digest[64], saved[64]; - PHP_SHA512_CTX context; - - php_stream_rewind(fp); - PHP_SHA512Init(&context); - read_len -= sizeof(digest); - if (read_len > sizeof(buf)) { - read_size = sizeof(buf); - } else { - read_size = (int)read_len; - } - while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { - PHP_SHA512Update(&context, buf, len); - read_len -= (off_t)len; - if (read_len < read_size) { - read_size = (int)read_len; + case PHAR_SIG_OPENSSL: { + php_uint32 signature_len; + char *sig; + off_t whence; + + /* we store the signature followed by the signature length */ + if (-1 == php_stream_seek(fp, -12, SEEK_CUR) + || 4 != php_stream_read(fp, sig_buf, 4)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname); + } + return FAILURE; } - } - PHP_SHA512Final(digest, &context); - if (read_len > 0 - || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved) - || memcmp(digest, saved, sizeof(digest))) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + sig_ptr = sig_buf; + PHAR_GET_32(sig_ptr, signature_len); + sig = (char *) emalloc(signature_len); + whence = signature_len + 4; + whence = -whence; + + if (-1 == php_stream_seek(fp, whence, SEEK_CUR) + || !(end_of_phar = php_stream_tell(fp)) + || signature_len != php_stream_read(fp, sig, signature_len)) { + efree(savebuf); + efree(sig); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname); + } + return FAILURE; } - return FAILURE; - } - sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature); + if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + efree(sig); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; + } + efree(sig); + } break; - } - case PHAR_SIG_SHA256: { - unsigned char digest[32], saved[32]; - PHP_SHA256_CTX context; +#if HAVE_HASH_EXT + case PHAR_SIG_SHA512: { + unsigned char digest[64]; - php_stream_rewind(fp); - PHP_SHA256Init(&context); - read_len -= sizeof(digest); - if (read_len > sizeof(buf)) { - read_size = sizeof(buf); - } else { - read_size = (int)read_len; - } - while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { - PHP_SHA256Update(&context, buf, len); - read_len -= (off_t)len; - if (read_len < read_size) { - read_size = (int)read_len; + php_stream_seek(fp, -(8 + 64), SEEK_END); + read_len = php_stream_tell(fp); + + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } - } - PHP_SHA256Final(digest, &context); - if (read_len > 0 - || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved) - || memcmp(digest, saved, sizeof(digest))) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; } - return FAILURE; + break; } + case PHAR_SIG_SHA256: { + unsigned char digest[32]; - sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature); - break; - } -#else - case PHAR_SIG_SHA512: - case PHAR_SIG_SHA256: - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname); - } - return FAILURE; -#endif - case PHAR_SIG_SHA1: { - unsigned char digest[20], saved[20]; - PHP_SHA1_CTX context; - - php_stream_rewind(fp); - PHP_SHA1Init(&context); - read_len -= sizeof(digest); - if (read_len > sizeof(buf)) { - read_size = sizeof(buf); - } else { - read_size = (int)read_len; - } - while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { - PHP_SHA1Update(&context, buf, len); - read_len -= (off_t)len; - if (read_len < read_size) { - read_size = (int)read_len; + php_stream_seek(fp, -(8 + 32), SEEK_END); + read_len = php_stream_tell(fp); + + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } - } - PHP_SHA1Final(digest, &context); - if (read_len > 0 - || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved) - || memcmp(digest, saved, sizeof(digest))) { + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; + } + break; + } +#else + case PHAR_SIG_SHA512: + case PHAR_SIG_SHA256: efree(savebuf); php_stream_close(fp); + if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname); } return FAILURE; - } +#endif + case PHAR_SIG_SHA1: { + unsigned char digest[20]; - sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature); - break; - } - case PHAR_SIG_MD5: { - unsigned char digest[16], saved[16]; - PHP_MD5_CTX context; + php_stream_seek(fp, -(8 + 20), SEEK_END); + read_len = php_stream_tell(fp); - php_stream_rewind(fp); - PHP_MD5Init(&context); - read_len -= sizeof(digest); - if (read_len > sizeof(buf)) { - read_size = sizeof(buf); - } else { - read_size = (int)read_len; - } - while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { - PHP_MD5Update(&context, buf, len); - read_len -= (off_t)len; - if (read_len < read_size) { - read_size = (int)read_len; + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } + + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; + } + break; } - PHP_MD5Final(digest, &context); + case PHAR_SIG_MD5: { + unsigned char digest[16]; + + php_stream_seek(fp, -(8 + 16), SEEK_END); + read_len = php_stream_tell(fp); + + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; + } - if (read_len > 0 - || php_stream_read(fp, (char*)saved, sizeof(saved)) != sizeof(saved) - || memcmp(digest, saved, sizeof(digest))) { + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; + } + break; + } + default: efree(savebuf); php_stream_close(fp); + if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname); } return FAILURE; - } - - sig_len = phar_hex_str((const char*)digest, sizeof(digest), &signature); - break; - } - default: - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname); - } - return FAILURE; } } else if (PHAR_G(require_hash)) { efree(savebuf); php_stream_close(fp); + if (error) { spprintf(error, 0, "phar \"%s\" does not have a signature", fname); } @@ -779,12 +977,15 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, /* extract alias */ PHAR_GET_32(buffer, tmp_len); + if (buffer + tmp_len > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)"); } + if (manifest_len < 10 + tmp_len) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)") } + /* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */ if (tmp_len) { /* if the alias is stored we enforce it (implicit overrides explicit) */ @@ -792,15 +993,19 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, { buffer[tmp_len] = '\0'; php_stream_close(fp); + if (signature) { efree(signature); } + if (error) { spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias); } + efree(savebuf); return FAILURE; } + alias_len = tmp_len; alias = buffer; buffer += tmp_len; @@ -821,43 +1026,72 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)") } - mydata = ecalloc(sizeof(phar_archive_data), 1); + mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist)); + mydata->is_persistent = PHAR_G(persist); /* check whether we have meta data, zero check works regardless of byte order */ - if (phar_parse_metadata(&buffer, &mydata->metadata, 0 TSRMLS_CC) == FAILURE) { - MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); + if (mydata->is_persistent) { + char *mysave = buffer; + PHAR_GET_32(buffer, mydata->metadata_len); + buffer = mysave; + if (phar_parse_metadata(&buffer, &mydata->metadata, mydata->metadata_len TSRMLS_CC) == FAILURE) { + MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); + } + } else { + if (phar_parse_metadata(&buffer, &mydata->metadata, 0 TSRMLS_CC) == FAILURE) { + MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\""); + } } /* set up our manifest */ - zend_hash_init(&mydata->manifest, sizeof(phar_entry_info), - zend_get_hash_value, destroy_phar_manifest_entry, 0); - zend_hash_init(&mydata->mounted_dirs, sizeof(char *), - zend_get_hash_value, NULL, 0); + zend_hash_init(&mydata->manifest, manifest_count, + zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent); + zend_hash_init(&mydata->mounted_dirs, 5, + zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent); + zend_hash_init(&mydata->virtual_dirs, manifest_count * 2, + zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent); + mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent); +#ifdef PHP_WIN32 + phar_unixify_path_separators(mydata->fname, fname_len); +#endif + mydata->fname_len = fname_len; offset = halt_offset + manifest_len + 4; memset(&entry, 0, sizeof(phar_entry_info)); entry.phar = mydata; entry.fp_type = PHAR_FP; + entry.is_persistent = mydata->is_persistent; for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) { if (buffer + 4 > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)") } + PHAR_GET_32(buffer, entry.filename_len); + if (entry.filename_len == 0) { MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\""); } + + if (entry.is_persistent) { + entry.manifest_pos = manifest_index; + } + if (buffer + entry.filename_len + 20 > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)"); } + if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') { entry.is_dir = 1; } else { entry.is_dir = 0; } - entry.filename = estrndup(buffer, entry.filename_len); + + phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC); + entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent); buffer += entry.filename_len; PHAR_GET_32(buffer, entry.uncompressed_filesize); PHAR_GET_32(buffer, entry.timestamp); + if (offset == halt_offset + (int)manifest_len + 4) { mydata->min_timestamp = entry.timestamp; mydata->max_timestamp = entry.timestamp; @@ -868,51 +1102,77 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, mydata->max_timestamp = entry.timestamp; } } + PHAR_GET_32(buffer, entry.compressed_filesize); PHAR_GET_32(buffer, entry.crc32); PHAR_GET_32(buffer, entry.flags); + if (entry.is_dir) { entry.filename_len--; entry.flags |= PHAR_ENT_PERM_DEF_DIR; } - if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) { - efree(entry.filename); - MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); + + if (entry.is_persistent) { + if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) { + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); + } + } else { + if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) { + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); + } } + entry.offset = entry.offset_abs = offset; offset += entry.compressed_filesize; + switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) { - case PHAR_ENT_COMPRESSED_GZ: - if (!PHAR_G(has_zlib)) { - if (entry.metadata) { - zval_ptr_dtor(&entry.metadata); + case PHAR_ENT_COMPRESSED_GZ: + if (!PHAR_G(has_zlib)) { + if (entry.metadata) { + if (entry.is_persistent) { + free(entry.metadata); + } else { + zval_ptr_dtor(&entry.metadata); + } + } + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\""); } - efree(entry.filename); - MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\""); - } - break; - case PHAR_ENT_COMPRESSED_BZ2: - if (!PHAR_G(has_bz2)) { - if (entry.metadata) { - zval_ptr_dtor(&entry.metadata); + break; + case PHAR_ENT_COMPRESSED_BZ2: + if (!PHAR_G(has_bz2)) { + if (entry.metadata) { + if (entry.is_persistent) { + free(entry.metadata); + } else { + zval_ptr_dtor(&entry.metadata); + } + } + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\""); } - efree(entry.filename); - MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\""); - } - break; - default: - if (entry.uncompressed_filesize != entry.compressed_filesize) { - if (entry.metadata) { - zval_ptr_dtor(&entry.metadata); + break; + default: + if (entry.uncompressed_filesize != entry.compressed_filesize) { + if (entry.metadata) { + if (entry.is_persistent) { + free(entry.metadata); + } else { + zval_ptr_dtor(&entry.metadata); + } + } + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)"); } - efree(entry.filename); - MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)"); - } - break; + break; } + manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK); /* if signature matched, no need to check CRC32 for each file */ entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0); + phar_set_inode(&entry TSRMLS_CC); zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); } @@ -920,13 +1180,8 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, mydata->internal_file_start = halt_offset + manifest_len + 4; mydata->halt_offset = halt_offset; mydata->flags = manifest_flags; - mydata->fp = fp; - mydata->fname = estrndup(fname, fname_len); -#ifdef PHP_WIN32 - phar_unixify_path_separators(mydata->fname, fname_len); -#endif - mydata->fname_len = fname_len; endbuffer = strrchr(mydata->fname, '/'); + if (endbuffer) { mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer); if (mydata->ext == endbuffer) { @@ -936,21 +1191,28 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext; } } - mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len); + + mydata->alias = alias ? + pestrndup(alias, alias_len, mydata->is_persistent) : + pestrndup(mydata->fname, fname_len, mydata->is_persistent); mydata->alias_len = alias ? alias_len : fname_len; mydata->sig_flags = sig_flags; + mydata->fp = fp; mydata->sig_len = sig_len; mydata->signature = signature; phar_request_initialize(TSRMLS_C); + if (register_alias) { phar_archive_data **fd_ptr; mydata->is_temporary_alias = temp_alias; + if (!phar_validate_alias(mydata->alias, mydata->alias_len)) { signature = NULL; fp = NULL; MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias"); } + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { signature = NULL; @@ -958,13 +1220,15 @@ int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive"); } } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); } else { mydata->is_temporary_alias = 1; } + zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); efree(savebuf); - + if (pphar) { *pphar = mydata; } @@ -984,33 +1248,36 @@ int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int al phar_archive_data **test, *unused = NULL; test = &unused; + if (error) { *error = NULL; } /* first try to open an existing file */ - if (phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) { + if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) { goto check_file; } + /* next try to create a new file */ - if (FAILURE == phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) { + if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) { if (error) { spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised", fname); } return FAILURE; } - check_file: if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) { if (pphar) { *pphar = *test; } + if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) { if (error) { spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname); } return FAILURE; } + if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) { phar_entry_info *stub; if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) { @@ -1018,6 +1285,7 @@ check_file: return FAILURE; } } + if (!PHAR_G(readonly) || (*test)->is_data) { (*test)->is_writeable = 1; } @@ -1042,8 +1310,8 @@ check_file: } return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC); - } +/* }}} */ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ { @@ -1059,13 +1327,13 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a return FAILURE; } #endif - if (php_check_open_basedir(fname TSRMLS_CC)) { return FAILURE; } /* first open readonly so it won't be created if not present */ fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual); + if (actual) { fname = actual; fname_len = strlen(actual); @@ -1088,6 +1356,7 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a return FAILURE; } } + if (actual) { efree(actual); } @@ -1102,14 +1371,14 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a } /* set up our manifest */ - mydata = ecalloc(sizeof(phar_archive_data), 1); - + mydata = ecalloc(1, sizeof(phar_archive_data)); mydata->fname = expand_filepath(fname, NULL TSRMLS_CC); fname_len = strlen(mydata->fname); #ifdef PHP_WIN32 phar_unixify_path_separators(mydata->fname, fname_len); #endif p = strrchr(mydata->fname, '/'); + if (p) { mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p); if (mydata->ext == p) { @@ -1123,10 +1392,13 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a if (pphar) { *pphar = mydata; } + zend_hash_init(&mydata->manifest, sizeof(phar_entry_info), zend_get_hash_value, destroy_phar_manifest_entry, 0); zend_hash_init(&mydata->mounted_dirs, sizeof(char *), zend_get_hash_value, NULL, 0); + zend_hash_init(&mydata->virtual_dirs, sizeof(char *), + zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent); mydata->fname_len = fname_len; snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION); mydata->is_temporary_alias = alias ? 0 : 1; @@ -1136,6 +1408,7 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a mydata->is_brandnew = 1; phar_request_initialize(TSRMLS_C); zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (is_data) { alias = NULL; alias_len = 0; @@ -1150,16 +1423,21 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a if (error) { spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias); } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + if (pphar) { *pphar = NULL; } + return FAILURE; } } + mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len); mydata->alias_len = alias ? alias_len : fname_len; } + if (alias_len && alias) { if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) { if (options & REPORT_ERRORS) { @@ -1167,13 +1445,17 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias); } } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + if (pphar) { *pphar = NULL; } + return FAILURE; } } + return SUCCESS; } /* }}}*/ @@ -1204,18 +1486,17 @@ int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_l } else if (error && *error) { return FAILURE; } - #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { return FAILURE; } #endif - if (php_check_open_basedir(fname TSRMLS_CC)) { return FAILURE; } fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual); + if (!fp) { if (options & REPORT_ERRORS) { if (error) { @@ -1234,33 +1515,43 @@ int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_l } ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC); + if (actual) { efree(actual); } + return ret; } /* }}}*/ -static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) +static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */ { const char *c; int so_far = 0; - /* this assumes buf_len > search_len */ + if (buf_len < search_len) { + return NULL; + } + c = buf - 1; + do { if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) { return (char *) NULL; } + so_far = c - buf; + if (so_far >= (buf_len - search_len)) { return (char *) NULL; } + if (!memcmp(c, search, search_len)) { return (char *) c; } } while (1); } +/* }}} */ /** * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify @@ -1283,6 +1574,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a if (error) { *error = NULL; } + if (-1 == php_stream_rewind(fp)) { MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"") } @@ -1297,6 +1589,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") } + if (!test) { test = '\1'; pos = buffer+tokenlen; @@ -1311,23 +1604,26 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini") } array_init(&filterparams); - /* this is defined in zlib's zconf.h */ #ifndef MAX_WBITS #define MAX_WBITS 15 #endif add_assoc_long(&filterparams, "window", MAX_WBITS + 32); + /* entire file is gzip-compressed, uncompress to temporary file */ if (!(temp = php_stream_fopen_tmpfile())) { MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"") } + php_stream_rewind(fp); filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { err = 1; add_assoc_long(&filterparams, "window", MAX_WBITS); filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC); zval_dtor(&filterparams); + if (!filter) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6") @@ -1335,7 +1631,9 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a } else { zval_dtor(&filterparams); } + php_stream_filter_append(&temp->writefilters, filter); + if (0 == php_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL)) { if (err) { php_stream_close(temp); @@ -1344,6 +1642,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file") } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); php_stream_close(fp); @@ -1361,21 +1660,27 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a if (!PHAR_G(has_bz2)) { MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini") } + /* entire file is bzip-compressed, uncompress to temporary file */ if (!(temp = php_stream_fopen_tmpfile())) { MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"") } + php_stream_rewind(fp); filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed") } + php_stream_filter_append(&temp->writefilters, filter); + if (0 == php_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL)) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file") } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); php_stream_close(fp); @@ -1387,10 +1692,12 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a test = '\0'; continue; } + if (!memcmp(pos, zip_magic, 4)) { php_stream_seek(fp, 0, SEEK_END); return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC); } + if (got > 512) { if (phar_is_tar(pos, fname)) { php_stream_rewind(fp); @@ -1398,7 +1705,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a } } } - if ((pos = phar_strnstr(buffer, 1024 + sizeof(token), token, sizeof(token)-1)) != NULL) { + + if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) { halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */ return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC); } @@ -1406,7 +1714,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a halt_offset += got; memmove(buffer, buffer + tokenlen, got + 1); } - + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)") } /* }}} */ @@ -1420,13 +1728,14 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory * succeed if we are creating the file, otherwise fail. */ -static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC) +static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC) /* {{{ */ { php_stream_statbuf ssb; char *realpath, old, *a = (char *)(ext + ext_len); old = *a; *a = '\0'; + if ((realpath = expand_filepath(fname, NULL TSRMLS_CC))) { #ifdef PHP_WIN32 phar_unixify_path_separators(realpath, strlen(realpath)); @@ -1436,16 +1745,26 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in efree(realpath); return SUCCESS; } + + if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) { + *a = old; + efree(realpath); + return SUCCESS; + } efree(realpath); } + if (SUCCESS == php_stream_stat_path((char *) fname, &ssb)) { *a = old; + if (ssb.sb.st_mode & S_IFDIR) { return FAILURE; } + if (for_create == 1) { return FAILURE; } + return SUCCESS; } else { char *slash; @@ -1454,12 +1773,15 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in *a = old; return FAILURE; } + slash = (char *) strrchr(fname, '/'); *a = old; + if (slash) { old = *slash; *slash = '\0'; } + if (SUCCESS != php_stream_stat_path((char *) fname, &ssb)) { if (slash) { *slash = old; @@ -1473,35 +1795,44 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in a = strstr(realpath, fname) + ((ext - fname) + ext_len); *a = '\0'; slash = strrchr(realpath, '/'); + if (slash) { *slash = '\0'; } else { efree(realpath); return FAILURE; } + if (SUCCESS != php_stream_stat_path(realpath, &ssb)) { efree(realpath); return FAILURE; } + efree(realpath); + if (ssb.sb.st_mode & S_IFDIR) { return SUCCESS; } } + return FAILURE; } + if (slash) { *slash = old; } + if (ssb.sb.st_mode & S_IFDIR) { return SUCCESS; } + return FAILURE; } } +/* }}} */ /* check for ".phar" in extension */ -static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) +static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) /* {{{ */ { char test[51]; const char *pos; @@ -1509,6 +1840,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i if (ext_len >= 50) { return FAILURE; } + if (executable == 1) { /* copy "." as well */ memcpy(test, ext_str - 1, ext_len + 1); @@ -1516,6 +1848,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */ /* (phar://hi/there/.phar/oops is also invalid) */ pos = strstr(test, ".phar"); + if (pos && (*(pos - 1) != '/') && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) { return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC); @@ -1523,6 +1856,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i return FAILURE; } } + /* data phars need only contain a single non-"." to be valid */ if (!executable) { pos = strstr(ext_str, ".phar"); @@ -1535,8 +1869,10 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC); } } + return FAILURE; } +/* }}} */ /* * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions @@ -1551,28 +1887,35 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the * extension rules, not to iterate. */ -int phar_detect_phar_fname_ext(const char *filename, int check_length, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC) /* {{{ */ +int phar_detect_phar_fname_ext(const char *filename, int filename_len, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC) /* {{{ */ { const char *pos, *slash; - int filename_len = strlen(filename); *ext_str = NULL; if (!filename_len || filename_len == 1) { return FAILURE; } + phar_request_initialize(TSRMLS_C); /* first check for alias in first segment */ - pos = strchr(filename, '/'); + pos = memchr(filename, '/', filename_len); + if (pos && pos != filename) { if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) { *ext_str = pos; *ext_len = -1; return FAILURE; } + + if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) { + *ext_str = pos; + *ext_len = -1; + return FAILURE; + } } - if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)) || PHAR_G(manifest_cached)) { phar_archive_data **pphar; if (is_complete) { @@ -1580,17 +1923,26 @@ int phar_detect_phar_fname_ext(const char *filename, int check_length, const cha *ext_str = filename + (filename_len - (*pphar)->ext_len); woohoo: *ext_len = (*pphar)->ext_len; + if (executable == 2) { return SUCCESS; } + if (executable == 1 && !(*pphar)->is_data) { return SUCCESS; } + if (!executable && (*pphar)->is_data) { return SUCCESS; } + return FAILURE; } + + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) { + *ext_str = filename + (filename_len - (*pphar)->ext_len); + goto woohoo; + } } else { phar_zstr key; char *str_key; @@ -1598,6 +1950,7 @@ woohoo: ulong unused; zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map)); + while (FAILURE != zend_hash_has_more_elements(&(PHAR_GLOBALS->phar_fname_map))) { if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &key, &keylen, &unused, 0, NULL)) { break; @@ -1609,6 +1962,7 @@ woohoo: zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map)); continue; } + if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen || filename[keylen] == '/' || filename[keylen] == '\0')) { if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) { @@ -1617,44 +1971,77 @@ woohoo: *ext_str = filename + (keylen - (*pphar)->ext_len); goto woohoo; } + zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map)); } + + if (PHAR_G(manifest_cached)) { + zend_hash_internal_pointer_reset(&cached_phars); + + while (FAILURE != zend_hash_has_more_elements(&cached_phars)) { + if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&cached_phars, &key, &keylen, &unused, 0, NULL)) { + break; + } + + PHAR_STR(key, str_key); + + if (keylen > (uint) filename_len) { + zend_hash_move_forward(&cached_phars); + continue; + } + + if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen + || filename[keylen] == '/' || filename[keylen] == '\0')) { + if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) { + break; + } + *ext_str = filename + (keylen - (*pphar)->ext_len); + goto woohoo; + } + zend_hash_move_forward(&cached_phars); + } + } } } - pos = strchr(filename + 1, '.'); + pos = memchr(filename + 1, '.', filename_len); next_extension: if (!pos) { return FAILURE; } + while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) { - pos = strchr(pos + 1, '.'); + pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1); if (!pos) { return FAILURE; } } - slash = strchr(pos, '/'); + slash = memchr(pos, '/', filename_len - (pos - filename)); + if (!slash) { /* this is a url like "phar://blah.phar" with no directory */ *ext_str = pos; *ext_len = strlen(pos); + /* file extension must contain "phar" */ switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) { - case SUCCESS : + case SUCCESS: return SUCCESS; - case FAILURE : + case FAILURE: /* we are at the end of the string, so we fail */ return FAILURE; } } + /* we've found an extension that ends at a directory separator */ *ext_str = pos; *ext_len = slash - pos; + switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) { - case SUCCESS : + case SUCCESS: return SUCCESS; - case FAILURE : + case FAILURE: /* look for more extensions */ pos = strchr(pos + 1, '.'); if (pos) { @@ -1663,11 +2050,12 @@ next_extension: } goto next_extension; } + return FAILURE; } /* }}} */ -static int php_check_dots(const char *element, int n) +static int php_check_dots(const char *element, int n) /* {{{ */ { for(n--; n >= 0; --n) { if (element[n] != '.') { @@ -1676,6 +2064,7 @@ static int php_check_dots(const char *element, int n) } return 0; } +/* }}} */ #define IS_DIRECTORY_UP(element, len) \ (len >= 2 && !php_check_dots(element, len)) @@ -1683,11 +2072,11 @@ static int php_check_dots(const char *element, int n) #define IS_DIRECTORY_CURRENT(element, len) \ (len == 1 && element[0] == '.') -#define IS_BACKSLASH(c) ((c) == '/') +#define IS_BACKSLASH(c) ((c) == '/') #ifdef COMPILE_DL_PHAR /* stupid-ass non-extern declaration in tsrm_strtok.h breaks dumbass MS compiler */ -static inline int in_character_class(char ch, const char *delim) +static inline int in_character_class(char ch, const char *delim) /* {{{ */ { while (*delim) { if (*delim == ch) { @@ -1697,8 +2086,9 @@ static inline int in_character_class(char ch, const char *delim) } return 0; } +/* }}} */ -char *tsrm_strtok_r(char *s, const char *delim, char **last) +char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */ { char *token; @@ -1709,6 +2099,7 @@ char *tsrm_strtok_r(char *s, const char *delim, char **last) while (*s && in_character_class(*s, delim)) { ++s; } + if (!*s) { return NULL; } @@ -1718,14 +2109,17 @@ char *tsrm_strtok_r(char *s, const char *delim, char **last) while (*s && !in_character_class(*s, delim)) { ++s; } + if (!*s) { *last = s; } else { *s = '\0'; *last = s + 1; } + return token; } +/* }}} */ #endif /** @@ -1733,74 +2127,97 @@ char *tsrm_strtok_r(char *s, const char *delim, char **last) */ char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ */ { - char *ptr, *free_path, *new_phar; + char newpath[MAXPATHLEN]; + int newpath_len; + char *ptr; char *tok; - int ptr_length, new_phar_len = 1, path_length = *new_len; + int ptr_length, path_length = *new_len; if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') { - free_path = path; - new_phar_len = PHAR_G(cwd_len); - new_phar = estrndup(PHAR_G(cwd), new_phar_len); + newpath_len = PHAR_G(cwd_len); + memcpy(newpath, PHAR_G(cwd), newpath_len); } else { - free_path = path; - new_phar = estrndup("/\0", 2); + newpath[0] = '/'; + newpath_len = 1; } - tok = NULL; - ptr = tsrm_strtok_r(path, "/", &tok); - while (ptr) { - ptr_length = strlen(ptr); - if (IS_DIRECTORY_UP(ptr, ptr_length)) { - char save; + ptr = path; + + if (*ptr == '/') { + ++ptr; + } + + tok = ptr; - save = '/'; + do { + ptr = memchr(ptr, '/', path_length - (ptr - path)); + } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok); + + if (!ptr && (path_length - (tok - path))) { + switch (path_length - (tok - path)) { + case 1: + if (*tok == '.') { + efree(path); + *new_len = 1; + return estrndup("/", 1); + } + break; + case 2: + if (tok[0] == '.' && tok[1] == '.') { + efree(path); + *new_len = 1; + return estrndup("/", 1); + } + } + return path; + } -#define PREVIOUS new_phar[new_phar_len - 1] + while (ptr) { + ptr_length = ptr - tok; +last_time: + if (IS_DIRECTORY_UP(tok, ptr_length)) { +#define PREVIOUS newpath[newpath_len - 1] - while (new_phar_len > 1 && - !IS_BACKSLASH(PREVIOUS)) { - save = PREVIOUS; - PREVIOUS = '\0'; - new_phar_len--; + while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) { + newpath_len--; } - if (new_phar[0] != '/') { - new_phar[new_phar_len++] = save; - new_phar[new_phar_len] = '\0'; - } else if (new_phar_len > 1) { - PREVIOUS = '\0'; - new_phar_len--; + if (newpath[0] != '/') { + newpath[newpath_len] = '\0'; + } else if (newpath_len > 1) { + --newpath_len; } - } else if (!IS_DIRECTORY_CURRENT(ptr, ptr_length)) { - if (new_phar_len > 1) { - new_phar = (char *) erealloc(new_phar, new_phar_len+ptr_length+1+1); - new_phar[new_phar_len++] = '/'; - memcpy(&new_phar[new_phar_len], ptr, ptr_length+1); + } else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) { + if (newpath_len > 1) { + newpath[newpath_len++] = '/'; + memcpy(newpath + newpath_len, tok, ptr_length+1); } else { - new_phar = (char *) erealloc(new_phar, new_phar_len+ptr_length+1); - memcpy(&new_phar[new_phar_len], ptr, ptr_length+1); + memcpy(newpath + newpath_len, tok, ptr_length+1); } - new_phar_len += ptr_length; + newpath_len += ptr_length; } - ptr = tsrm_strtok_r(NULL, "/", &tok); - } - if (path[path_length-1] == '/' && new_phar_len > 1) { - new_phar = (char*)erealloc(new_phar, new_phar_len + 2); - new_phar[new_phar_len++] = '/'; - new_phar[new_phar_len] = 0; - } + if (ptr == path + path_length) { + break; + } - efree(free_path); + tok = ++ptr; - if (new_phar_len == 0) { - new_phar = (char *) erealloc(new_phar, new_phar_len+1+1); - new_phar[new_phar_len] = '/'; - new_phar[++new_phar_len] = '\0'; + do { + ptr = memchr(ptr, '/', path_length - (ptr - path)); + } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok); + + if (!ptr && (path_length - (tok - path))) { + ptr_length = path_length - (tok - path); + ptr = path + path_length; + goto last_time; + } } - *new_len = new_phar_len; - return new_phar; + + efree(path); + *new_len = newpath_len; + return estrndup(newpath, newpath_len); } /* }}} */ @@ -1836,7 +2253,7 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le filename = estrndup(filename, filename_len); phar_unixify_path_separators(filename, filename_len); #endif - if (phar_detect_phar_fname_ext(filename, 0, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) { + if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0 TSRMLS_CC) == FAILURE) { if (ext_len != -1) { if (!ext_str) { /* no / detected, restore arch for error message */ @@ -1846,17 +2263,21 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le *arch = filename; #endif } + if (free_filename) { efree(filename); } + return FAILURE; } + ext_len = 0; /* no extension detected - instead we are dealing with an alias */ } *arch_len = ext_str - filename + ext_len; *arch = estrndup(filename, *arch_len); + if (ext_str[ext_len]) { *entry_len = filename_len - *arch_len; *entry = estrndup(ext_str+ext_len, *entry_len); @@ -1868,9 +2289,11 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le *entry_len = 1; *entry = estrndup("/", 1); } + if (free_filename) { efree(filename); } + return SUCCESS; } /* }}} */ @@ -1892,6 +2315,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ if (error) { *error = NULL; } + fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); @@ -1907,6 +2331,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ } MAKE_STD_ZVAL(halt_constant); + if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) { FREE_ZVAL(halt_constant); if (error) { @@ -1914,41 +2339,44 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ } return FAILURE; } + halt_offset = Z_LVAL(*halt_constant); FREE_ZVAL(halt_constant); - #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { - return FAILURE; - } + return FAILURE; + } #endif if (php_check_open_basedir(fname TSRMLS_CC)) { return FAILURE; - } + } fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual); + if (!fp) { - if (error) { + if (error) { spprintf(error, 0, "unable to open phar for reading \"%s\"", fname); - } + } if (actual) { - efree(actual); - } + efree(actual); + } return FAILURE; - } + } if (actual) { - fname = actual; + fname = actual; fname_len = strlen(actual); - } + } ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, error TSRMLS_CC); - if (actual) { + + if (actual) { efree(actual); - } - return ret; + } + + return ret; } /* }}} */ @@ -1965,6 +2393,7 @@ int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_d if (error) { *error = NULL; } + if (entry->is_zip) { /* verify local file header */ phar_zip_file_header local; @@ -1973,30 +2402,37 @@ int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_d spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename); return FAILURE; } - php_stream_seek(idata->phar->fp, entry->header_offset, SEEK_SET); + php_stream_seek(phar_get_entrypfp(idata->internal_file TSRMLS_CC), entry->header_offset, SEEK_SET); - if (sizeof(local) != php_stream_read(idata->phar->fp, (char *) &local, sizeof(local))) { + if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file TSRMLS_CC), (char *) &local, sizeof(local))) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename); return FAILURE; } + /* verify local header */ if (entry->filename_len != PHAR_ZIP_16(local.filename_len) || entry->crc32 != PHAR_ZIP_32(local.crc32) || entry->uncompressed_filesize != PHAR_ZIP_32(local.uncompsize) || entry->compressed_filesize != PHAR_ZIP_32(local.compsize)) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local head of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename); return FAILURE; } + /* construct actual offset to file start - local extra_len can be different from central extra_len */ entry->offset = entry->offset_abs = sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len); + if (idata->zero && idata->zero != entry->offset_abs) { idata->zero = entry->offset_abs; } } - php_stream_seek(fp, idata->zero, SEEK_SET); + + php_stream_seek(fp, idata->zero, SEEK_SET); + while (len--) { CRC32(crc, php_stream_getc(fp)); } + php_stream_seek(fp, idata->zero, SEEK_SET); + if (~crc == crc32) { entry->is_crc_checked = 1; return SUCCESS; @@ -2033,7 +2469,7 @@ static int phar_flush_clean_deleted_apply(void *data TSRMLS_DC) /* {{{ */ #include "stub.h" -char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC) +char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC) /* {{{ */ { char *stub = NULL; int index_len, web_len; @@ -2077,6 +2513,126 @@ char *phar_create_default_stub(const char *index_php, const char *web_index, siz phar_get_stub(index_php, web_index, len, &stub, index_len+1, web_len+1 TSRMLS_CC); return stub; } +/* }}} */ + +#ifndef PHAR_HAVE_OPENSSL +static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, char *key, int key_len, char **signature, int *signature_len TSRMLS_DC) /* {{{ */ +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + zval *zdata, *zsig, *zkey, *retval_ptr, **zp[3], *openssl; + + MAKE_STD_ZVAL(zdata); + MAKE_STD_ZVAL(openssl); + ZVAL_STRINGL(openssl, is_sign ? "openssl_sign" : "openssl_verify", is_sign ? sizeof("openssl_sign")-1 : sizeof("openssl_verify")-1, 1); + MAKE_STD_ZVAL(zsig); + ZVAL_STRINGL(zsig, *signature, *signature_len, 1); + MAKE_STD_ZVAL(zkey); + ZVAL_STRINGL(zkey, key, key_len, 1); + zp[0] = &zdata; + zp[1] = &zsig; + zp[2] = &zkey; + + php_stream_rewind(fp); + Z_TYPE_P(zdata) = IS_STRING; + Z_STRLEN_P(zdata) = end; + if (end != (off_t) php_stream_copy_to_mem(fp, &(Z_STRVAL_P(zdata)), (size_t) end, 0)) { + zval_dtor(zdata); + zval_dtor(zsig); + zval_dtor(zkey); + return FAILURE; + } + +#if PHP_VERSION_ID < 50300 + if (FAILURE == zend_fcall_info_init(openssl, &fci, &fcc TSRMLS_CC)) { +#else + if (FAILURE == zend_fcall_info_init(openssl, 0, &fci, &fcc, NULL, NULL TSRMLS_CC)) { +#endif + zval_dtor(zdata); + zval_dtor(zsig); + zval_dtor(zkey); + zval_dtor(openssl); + return FAILURE; + } + + zval_dtor(openssl); + efree(openssl); + + fci.param_count = 3; + fci.params = zp; +#if PHP_VERSION_ID < 50300 + ++(zdata->refcount); + ++(zsig->refcount); + ++(zkey->refcount); +#else + Z_ADDREF_P(zdata); + + if (is_sign) { + Z_SET_ISREF_P(zsig); + } else { + Z_ADDREF_P(zsig); + } + + Z_ADDREF_P(zkey); +#endif + fci.retval_ptr_ptr = &retval_ptr; + + if (FAILURE == zend_call_function(&fci, &fcc TSRMLS_CC)) { + zval_dtor(zdata); + zval_dtor(zsig); + zval_dtor(zkey); + efree(zdata); + efree(zkey); + efree(zsig); + return FAILURE; + } +#if PHP_VERSION_ID < 50300 + --(zdata->refcount); + --(zsig->refcount); + --(zkey->refcount); +#else + Z_DELREF_P(zdata); + + if (is_sign) { + Z_UNSET_ISREF_P(zsig); + } else { + Z_DELREF_P(zsig); + } + + Z_DELREF_P(zkey); +#endif + zval_dtor(zdata); + efree(zdata); + zval_dtor(zkey); + efree(zkey); + + switch (Z_TYPE_P(retval_ptr)) { + default: + case IS_LONG: + zval_dtor(zsig); + efree(zsig); + if (1 == Z_LVAL_P(retval_ptr)) { + efree(retval_ptr); + return SUCCESS; + } + efree(retval_ptr); + return FAILURE; + case IS_BOOL: + efree(retval_ptr); + if (Z_BVAL_P(retval_ptr)) { + *signature = estrndup(Z_STRVAL_P(zsig), Z_STRLEN_P(zsig)); + *signature_len = Z_STRLEN_P(zsig); + zval_dtor(zsig); + efree(zsig); + return SUCCESS; + } + zval_dtor(zsig); + efree(zsig); + return FAILURE; + } +} +/* }}} */ +#endif /* #ifndef PHAR_HAVE_OPENSSL */ /** * Save phar contents to disk @@ -2103,12 +2659,15 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, smart_str main_metadata_str = {0}; int free_user_stub, free_fp = 1, free_ufp = 1; - if (error) { - *error = NULL; + if (phar->is_persistent) { + if (error) { + spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname); + } + return EOF; } - if (PHAR_G(readonly) && !phar->is_data) { - return EOF; + if (error) { + *error = NULL; } if (!zend_hash_num_elements(&phar->manifest) && !user_stub) { @@ -2118,10 +2677,15 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, if (phar->is_zip) { return phar_zip_flush(phar, user_stub, len, convert, error TSRMLS_CC); } + if (phar->is_tar) { return phar_tar_flush(phar, user_stub, len, convert, error TSRMLS_CC); } + if (PHAR_G(readonly)) { + return EOF; + } + if (phar->fp && !phar->is_brandnew) { oldfile = phar->fp; closeoldfile = 0; @@ -2190,7 +2754,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } len = pos - user_stub + 18; if ((size_t)len != php_stream_write(newfile, user_stub, len) - || 5 != php_stream_write(newfile, " ?>\r\n", 5)) { + || 5 != php_stream_write(newfile, " ?>\r\n", 5)) { if (closeoldfile) { php_stream_close(oldfile); } @@ -2259,8 +2823,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, new_manifest_count = 0; offset = 0; for (zend_hash_internal_pointer_reset(&phar->manifest); - zend_hash_has_more_elements(&phar->manifest) == SUCCESS; - zend_hash_move_forward(&phar->manifest)) { + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } @@ -2452,55 +3016,67 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, /* write the manifest header */ if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest)) || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) { + if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); phar->alias_len = restore_alias_len; + if (error) { spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname); } + return EOF; } - + phar->alias_len = restore_alias_len; - + phar_set_32(manifest, main_metadata_str.len); if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len && main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len))) { smart_str_free(&main_metadata_str); + if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); phar->alias_len = restore_alias_len; + if (error) { spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname); } + return EOF; - } + } smart_str_free(&main_metadata_str); - + /* re-calculate the manifest location to simplify later code */ manifest_ftell = php_stream_tell(newfile); - + /* now write the manifest */ for (zend_hash_internal_pointer_reset(&phar->manifest); - zend_hash_has_more_elements(&phar->manifest) == SUCCESS; - zend_hash_move_forward(&phar->manifest)) { + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } + if (entry->is_deleted || entry->is_mounted) { /* remove this from the new phar if deleted, ignore if mounted */ continue; } + if (entry->is_dir) { /* add 1 for trailing slash */ phar_set_32(entry_buffer, entry->filename_len + 1); } else { phar_set_32(entry_buffer, entry->filename_len); } + if (4 != php_stream_write(newfile, entry_buffer, 4) || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len) || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) { @@ -2517,6 +3093,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } return EOF; } + /* set the manifest meta-data: 4: uncompressed filesize 4: creation timestamp @@ -2533,30 +3110,37 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, phar_set_32(entry_buffer+12, entry->crc32); phar_set_32(entry_buffer+16, entry->flags); phar_set_32(entry_buffer+20, entry->metadata_str.len); + if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer)) || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) { if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); + if (error) { spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname); } + return EOF; } } - + /* now copy the actual file data to the new phar */ offset = php_stream_tell(newfile); for (zend_hash_internal_pointer_reset(&phar->manifest); - zend_hash_has_more_elements(&phar->manifest) == SUCCESS; - zend_hash_move_forward(&phar->manifest)) { + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } + if (entry->is_deleted || entry->is_dir || entry->is_mounted) { continue; } + if (entry->cfp) { file = entry->cfp; php_stream_rewind(file); @@ -2573,6 +3157,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, return EOF; } } + if (!file) { if (closeoldfile) { php_stream_close(oldfile); @@ -2583,32 +3168,39 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } return EOF; } - /* this will have changed for all files that have either - changed compression or been modified */ + + /* this will have changed for all files that have either changed compression or been modified */ entry->offset = entry->offset_abs = offset; offset += entry->compressed_filesize; wrote = php_stream_copy_to_stream(file, newfile, entry->compressed_filesize); + if (entry->compressed_filesize != wrote) { if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); + if (error) { spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname); } + return EOF; } + entry->is_modified = 0; + if (entry->cfp) { php_stream_close(entry->cfp); entry->cfp = NULL; } + if (entry->fp_type == PHAR_MOD) { - /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed - when the phar_entry_data is phar_entry_delref'ed */ + /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed when the phar_entry_data is phar_entry_delref'ed */ if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) { php_stream_close(entry->fp); } + entry->fp = NULL; entry->fp_type = PHAR_FP; } else if (entry->fp_type == PHAR_UFP) { @@ -2618,94 +3210,58 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, /* append signature */ if (global_flags & PHAR_HDR_SIGNATURE) { - unsigned char buf[1024]; - int sig_flags = 0, sig_len; char sig_buf[4]; php_stream_rewind(newfile); - + if (phar->signature) { efree(phar->signature); + phar->signature = NULL; } - - switch(phar->sig_flags) { -#if HAVE_HASH_EXT - case PHAR_SIG_SHA512: { - unsigned char digest[64]; - PHP_SHA512_CTX context; - - PHP_SHA512Init(&context); - while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) { - PHP_SHA512Update(&context, buf, sig_len); - } - PHP_SHA512Final(digest, &context); - php_stream_write(newfile, (char *) digest, sizeof(digest)); - sig_flags |= PHAR_SIG_SHA512; - phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); - break; - } - case PHAR_SIG_SHA256: { - unsigned char digest[32]; - PHP_SHA256_CTX context; - PHP_SHA256Init(&context); - while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) { - PHP_SHA256Update(&context, buf, sig_len); - } - PHP_SHA256Final(digest, &context); - php_stream_write(newfile, (char *) digest, sizeof(digest)); - sig_flags |= PHAR_SIG_SHA256; - phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); - break; - } -#else - case PHAR_SIG_SHA512: - case PHAR_SIG_SHA256: - if (closeoldfile) { - php_stream_close(oldfile); - } - php_stream_close(newfile); - if (error) { - spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname); - } - return EOF; + switch(phar->sig_flags) { +#if !HAVE_HASH_EXT + case PHAR_SIG_SHA512: + case PHAR_SIG_SHA256: + if (closeoldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + if (error) { + spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname); + } + return EOF; #endif - case PHAR_SIG_PGP: - /* TODO: currently fall back to sha1,later do both */ - default: - case PHAR_SIG_SHA1: { - unsigned char digest[20]; - PHP_SHA1_CTX context; - - PHP_SHA1Init(&context); - while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) { - PHP_SHA1Update(&context, buf, sig_len); - } - PHP_SHA1Final(digest, &context); - php_stream_write(newfile, (char *) digest, sizeof(digest)); - sig_flags |= PHAR_SIG_SHA1; - phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); - break; - } - case PHAR_SIG_MD5: { - unsigned char digest[16]; - PHP_MD5_CTX context; + default: { + char *digest; + int digest_len; - PHP_MD5Init(&context); - while ((sig_len = php_stream_read(newfile, (char*)buf, sizeof(buf))) > 0) { - PHP_MD5Update(&context, buf, sig_len); + if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 0, "phar error: unable to write signature: %s", save); + efree(save); + } + efree(digest); + if (closeoldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + return EOF; + } + + php_stream_write(newfile, digest, digest_len); + efree(digest); + if (phar->sig_flags == PHAR_SIG_OPENSSL) { + phar_set_32(sig_buf, digest_len); + php_stream_write(newfile, sig_buf, 4); + } + break; } - PHP_MD5Final(digest, &context); - php_stream_write(newfile, (char *) digest, sizeof(digest)); - sig_flags |= PHAR_SIG_MD5; - phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); - break; - } } - phar_set_32(sig_buf, sig_flags); + phar_set_32(sig_buf, phar->sig_flags); php_stream_write(newfile, sig_buf, 4); php_stream_write(newfile, "GBMB", 4); - phar->sig_flags = sig_flags; } /* finally, close the temp file, rename the original phar, @@ -2714,12 +3270,14 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, if (phar->fp && free_fp) { php_stream_close(phar->fp); } + if (phar->ufp) { if (free_ufp) { php_stream_close(phar->ufp); } phar->ufp = NULL; } + if (closeoldfile) { php_stream_close(oldfile); } @@ -2742,6 +3300,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } return EOF; } + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { php_stream_filter *filter; /* to properly compress, we have to tell zlib to add a zlib header */ @@ -2751,12 +3310,14 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, add_assoc_long(&filterparams, "window", MAX_WBITS+16); filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); zval_dtor(&filterparams); + if (!filter) { if (error) { spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); } return EOF; } + php_stream_filter_append(&phar->fp->writefilters, filter); php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); php_stream_filter_flush(filter, 1); @@ -2802,7 +3363,7 @@ ZEND_GET_MODULE(phar) * Every user visible function must have an entry in phar_functions[]. */ function_entry phar_functions[] = { - {NULL, NULL, NULL} /* Must be the last line in phar_functions[] */ + {NULL, NULL, NULL} /* Must be the last line in phar_functions[] */ }; /* }}}*/ @@ -2815,13 +3376,13 @@ static void php_phar_init_globals_module(zend_phar_globals *phar_globals) } /* }}} */ -#if PHP_VERSION_ID >= 50300 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */ { - return php_stream_read(((phar_archive_data*)handle)->fp, buf, len); + return php_stream_read(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC), buf, len); } /* }}} */ +#if PHP_VERSION_ID >= 50300 static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */ { return ((phar_archive_data*)handle)->halt_offset + 32; @@ -2829,9 +3390,9 @@ static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */ #else /* PHP_VERSION_ID */ -static long stream_fteller_for_zend(void *handle TSRMLS_DC) /* {{{ */ +static long phar_stream_fteller_for_zend(void *handle TSRMLS_DC) /* {{{ */ { - return (long)php_stream_tell((php_stream*)handle); + return (long)php_stream_tell(phar_get_pharfp((phar_archive_data*)handle TSRMLS_CC)); } /* }}} */ #endif @@ -2877,39 +3438,47 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type /* compressed phar */ #if PHP_VERSION_ID >= 50300 file_handle->type = ZEND_HANDLE_STREAM; - file_handle->free_filename = 0; + /* we do our own reading directly from the phar, don't change the next line */ file_handle->handle.stream.handle = phar; file_handle->handle.stream.reader = phar_zend_stream_reader; file_handle->handle.stream.closer = NULL; file_handle->handle.stream.fsizer = phar_zend_stream_fsizer; file_handle->handle.stream.isatty = 0; - php_stream_rewind(phar->fp); + phar->is_persistent ? + php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) : + php_stream_rewind(phar->fp); memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap)); #else /* PHP_VERSION_ID */ file_handle->type = ZEND_HANDLE_STREAM; - file_handle->free_filename = 0; - file_handle->handle.stream.handle = phar->fp; - file_handle->handle.stream.reader = (zend_stream_reader_t)_php_stream_read; + /* we do our own reading directly from the phar, don't change the next line */ + file_handle->handle.stream.handle = phar; + file_handle->handle.stream.reader = phar_zend_stream_reader; file_handle->handle.stream.closer = NULL; /* don't close - let phar handle this one */ - file_handle->handle.stream.fteller = stream_fteller_for_zend; + file_handle->handle.stream.fteller = phar_stream_fteller_for_zend; file_handle->handle.stream.interactive = 0; - php_stream_rewind(phar->fp); + phar->is_persistent ? + php_stream_rewind(PHAR_GLOBALS->cached_fp[phar->phar_pos].fp) : + php_stream_rewind(phar->fp); #endif } } } + zend_try { failed = 0; res = phar_orig_compile_file(file_handle, type TSRMLS_CC); } zend_catch { failed = 1; } zend_end_try(); + if (name) { efree(name); } + if (failed) { zend_bailout(); } + return res; } /* }}} */ @@ -2929,18 +3498,24 @@ int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); + if (fname_len > 7 && !strncasecmp(fname, "phar://", 7)) { if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + if (!pphar && PHAR_G(manifest_cached)) { + zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); + } efree(arch); efree(entry); } } + /* retrieving an include within the current directory, so use this if possible */ if (!(entry = phar_find_in_include_path((char *) filename, strlen(filename), NULL TSRMLS_CC))) { /* this file is not in the phar, use the original path */ goto skip_phar; } + if (SUCCESS == phar_orig_zend_open(entry, handle TSRMLS_CC)) { if (!handle->opened_path) { handle->opened_path = entry; @@ -2950,9 +3525,11 @@ int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* } return SUCCESS; } + if (entry != filename) { efree(entry); } + return FAILURE; } skip_phar: @@ -2965,6 +3542,8 @@ typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr); PHP_MINIT_FUNCTION(phar) /* {{{ */ { + phar_mime_type mime; + ZEND_INIT_MODULE_GLOBALS(phar, php_phar_init_globals_module, NULL); REGISTER_INI_ENTRIES(); @@ -2981,13 +3560,69 @@ PHP_MINIT_FUNCTION(phar) /* {{{ */ phar_object_init(TSRMLS_C); + phar_intercept_functions_init(TSRMLS_C); + + zend_hash_init(&PHAR_G(mime_types), 0, NULL, NULL, 1); + +#define PHAR_SET_MIME(mimetype, ret, fileext) \ + mime.mime = mimetype; \ + mime.len = sizeof((mimetype))+1; \ + mime.type = ret; \ + zend_hash_add(&PHAR_G(mime_types), fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \ + + PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt") + PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd") + PHAR_SET_MIME("", PHAR_MIME_PHP, "php") + PHAR_SET_MIME("", PHAR_MIME_PHP, "inc") + PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi") + PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp") + PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css") + PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif") + PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm") + PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html") + PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls") + PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico") + PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe") + PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg") + PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg") + PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js") + PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi") + PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid") + PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod") + PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov") + PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3") + PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg") + PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg") + PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf") + PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png") + PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf") + PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif") + PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff") + PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav") + PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm") + PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml") + return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper TSRMLS_CC); } /* }}} */ PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */ { - return php_unregister_url_stream_wrapper("phar" TSRMLS_CC); + php_unregister_url_stream_wrapper("phar" TSRMLS_CC); + + zend_hash_destroy(&PHAR_G(mime_types)); + + phar_intercept_functions_shutdown(TSRMLS_C); + if (zend_compile_file == phar_compile_file) { zend_compile_file = phar_orig_compile_file; } @@ -2997,6 +3632,12 @@ PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */ zend_stream_open_function = phar_orig_zend_open; } #endif + if (PHAR_G(manifest_cached)) { + zend_hash_destroy(&(cached_phars)); + zend_hash_destroy(&(cached_alias)); + } + + return SUCCESS; } /* }}} */ @@ -3004,14 +3645,30 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */ { if (!PHAR_GLOBALS->request_init) { + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2")); PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib")); PHAR_GLOBALS->request_init = 1; PHAR_GLOBALS->request_ends = 0; PHAR_GLOBALS->request_done = 0; - zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 0); - zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 0); - zend_hash_init(&(PHAR_GLOBALS->phar_SERVER_mung_list), sizeof(const char *), zend_get_hash_value, NULL, 0); + zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data, 0); + zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0); + + if (PHAR_G(manifest_cached)) { + phar_archive_data **pphar; + phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp)); + + for (zend_hash_internal_pointer_reset(&cached_phars); + zend_hash_get_current_data(&cached_phars, (void **)&pphar) == SUCCESS; + zend_hash_move_forward(&cached_phars)) { + stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info)); + } + + PHAR_GLOBALS->cached_fp = stuff; + } + + PHAR_GLOBALS->phar_SERVER_mung_list = 0; PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; PHAR_G(cwd_init) = 0; @@ -3022,7 +3679,10 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ { + int i; + PHAR_GLOBALS->request_ends = 1; + if (PHAR_GLOBALS->request_init) { phar_release_functions(TSRMLS_C); @@ -3030,16 +3690,33 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ PHAR_GLOBALS->phar_alias_map.arBuckets = NULL; zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map)); PHAR_GLOBALS->phar_fname_map.arBuckets = NULL; - zend_hash_destroy(&(PHAR_GLOBALS->phar_SERVER_mung_list)); - PHAR_GLOBALS->phar_SERVER_mung_list.arBuckets = NULL; + PHAR_GLOBALS->phar_SERVER_mung_list = 0; + + if (PHAR_GLOBALS->cached_fp) { + for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) { + if (PHAR_GLOBALS->cached_fp[i].fp) { + php_stream_close(PHAR_GLOBALS->cached_fp[i].fp); + } + if (PHAR_GLOBALS->cached_fp[i].ufp) { + php_stream_close(PHAR_GLOBALS->cached_fp[i].ufp); + } + efree(PHAR_GLOBALS->cached_fp[i].manifest); + } + efree(PHAR_GLOBALS->cached_fp); + PHAR_GLOBALS->cached_fp = 0; + } + PHAR_GLOBALS->request_init = 0; + if (PHAR_G(cwd)) { efree(PHAR_G(cwd)); } + PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; PHAR_G(cwd_init) = 0; } + PHAR_GLOBALS->request_done = 1; return SUCCESS; } @@ -3056,24 +3733,35 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */ php_info_print_table_row(2, "Phar-based phar archives", "enabled"); php_info_print_table_row(2, "Tar-based phar archives", "enabled"); php_info_print_table_row(2, "ZIP-based phar archives", "enabled"); + if (PHAR_G(has_zlib)) { php_info_print_table_row(2, "gzip compression", "enabled"); } else { php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)"); } + if (PHAR_G(has_bz2)) { php_info_print_table_row(2, "bzip2 compression", "enabled"); } else { php_info_print_table_row(2, "bzip2 compression", "disabled (install pecl/bz2)"); } +#ifdef PHAR_HAVE_OPENSSL + php_info_print_table_row(2, "Native OpenSSL support", "enabled"); +#else + if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) { + php_info_print_table_row(2, "OpenSSL support", "enabled"); + } else { + php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)"); + } +#endif php_info_print_table_end(); php_info_print_box_start(0); PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik."); - PUTS(!sapi_module.phpinfo_as_text?"
":"\n"); + PUTS(!sapi_module.phpinfo_as_text?"
":"\n"); PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger."); - PUTS(!sapi_module.phpinfo_as_text?"
":"\n"); - PUTS("Portions of tar implementation Copyright (c) 2003-2007 Tim Kientzle."); + PUTS(!sapi_module.phpinfo_as_text?"
":"\n"); + PUTS("Portions of tar implementation Copyright (c) 2003-2008 Tim Kientzle."); php_info_print_box_end(); DISPLAY_INI_ENTRIES(); @@ -3084,6 +3772,10 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */ */ static zend_module_dep phar_deps[] = { ZEND_MOD_OPTIONAL("apc") + ZEND_MOD_OPTIONAL("bz2") + ZEND_MOD_OPTIONAL("openssl") + ZEND_MOD_OPTIONAL("zlib") + ZEND_MOD_OPTIONAL("standard") #if HAVE_SPL ZEND_MOD_REQUIRED("spl") #endif diff --git a/ext/phar/phar.phar b/ext/phar/phar.phar index f26ce2b756..30ad018170 100755 Binary files a/ext/phar/phar.phar and b/ext/phar/phar.phar differ diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 9005ca84eb..d97dee0adb 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-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 | @@ -72,7 +72,16 @@ #endif #ifndef E_RECOVERABLE_ERROR -#define E_RECOVERABLE_ERROR E_ERROR +# define E_RECOVERABLE_ERROR E_ERROR +#endif + +#ifndef pestrndup +# define pestrndup(s, length, persistent) ((persistent)?zend_strndup((s),(length)):estrndup((s),(length))) +#endif + +#ifndef ALLOC_PERMANENT_ZVAL +# define ALLOC_PERMANENT_ZVAL(z) \ + (z) = (zval*)malloc(sizeof(zval)) #endif /* PHP_ because this is public information via MINFO */ @@ -103,7 +112,7 @@ #define PHAR_SIG_SHA1 0x0002 #define PHAR_SIG_SHA256 0x0003 #define PHAR_SIG_SHA512 0x0004 -#define PHAR_SIG_PGP 0x0010 +#define PHAR_SIG_OPENSSL 0x0010 /* flags byte for each file adheres to these bitmasks. All unused values are reserved */ @@ -132,15 +141,29 @@ #define TAR_DIR '5' #define TAR_NEW '8' +#define PHAR_MUNG_PHP_SELF (1<<0) +#define PHAR_MUNG_REQUEST_URI (1<<1) +#define PHAR_MUNG_SCRIPT_NAME (1<<2) +#define PHAR_MUNG_SCRIPT_FILENAME (1<<3) + +typedef struct _phar_entry_fp phar_entry_fp; +typedef struct _phar_archive_data phar_archive_data; + ZEND_BEGIN_MODULE_GLOBALS(phar) HashTable phar_fname_map; + /* for cached phars, this is a per-process store of fp/ufp */ + phar_entry_fp *cached_fp; HashTable phar_alias_map; - HashTable phar_SERVER_mung_list; + int phar_SERVER_mung_list; int readonly; + char* cache_list; + int manifest_cached; + int persist; int has_zlib; int has_bz2; zend_bool readonly_orig; zend_bool require_hash_orig; + zend_bool intercepted; int request_init; int require_hash; int request_done; @@ -171,6 +194,15 @@ ZEND_BEGIN_MODULE_GLOBALS(phar) char* cwd; int cwd_len; int cwd_init; + char *openssl_privatekey; + int openssl_privatekey_len; + /* phar_get_archive cache */ + char* last_phar_name; + int last_phar_name_len; + char* last_alias; + int last_alias_len; + phar_archive_data* last_phar; + HashTable mime_types; ZEND_END_MODULE_GLOBALS(phar) ZEND_EXTERN_MODULE_GLOBALS(phar) @@ -213,7 +245,6 @@ enum phar_fp_type { PHAR_TMP }; -typedef struct _phar_archive_data phar_archive_data; /* entry for one file in a phar file */ typedef struct _phar_entry_info { /* first bytes are exactly as in file */ @@ -226,6 +257,7 @@ typedef struct _phar_entry_info { /* when changing compression, save old flags in case fp is NULL */ php_uint32 old_flags; zval *metadata; + int metadata_len; /* only used for cached manifests */ php_uint32 filename_len; char *filename; enum phar_fp_type fp_type; @@ -256,6 +288,12 @@ typedef struct _phar_entry_info { char tar_type; /* zip-based phar file stuff */ int is_zip:1; + /* for cached phar entries */ + int is_persistent:1; + /* position in the manifest */ + uint manifest_pos; + /* for stat */ + unsigned short inode; } phar_entry_info; /* information about a phar file (the archive itself) */ @@ -271,6 +309,8 @@ struct _phar_archive_data { size_t internal_file_start; size_t halt_offset; HashTable manifest; + /* hash of virtual directories, as in path/to/file.txt has path/to and path as virtual directories */ + HashTable virtual_dirs; /* hash of mounted directory paths */ HashTable mounted_dirs; php_uint32 flags; @@ -284,6 +324,7 @@ struct _phar_archive_data { int sig_len; char *signature; zval *metadata; + int metadata_len; /* only used for cached manifests */ /* if 1, then this alias was manually specified by the user and is not a permanent alias */ int is_temporary_alias:1; int is_modified:1; @@ -297,8 +338,130 @@ struct _phar_archive_data { int is_tar:1; /* PharData variables */ int is_data:1; + /* for cached phar manifests */ + int is_persistent:1; + uint phar_pos; +}; + +typedef struct _phar_entry_fp_info { + enum phar_fp_type fp_type; + /* offset within fp of the file contents */ + long offset; +} phar_entry_fp_info; + +struct _phar_entry_fp { + php_stream *fp; + php_stream *ufp; + phar_entry_fp_info *manifest; }; +static inline php_stream *phar_get_entrypfp(phar_entry_info *entry TSRMLS_DC) +{ + if (!entry->is_persistent) { + return entry->phar->fp; + } + return PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].fp; +} + +static inline php_stream *phar_get_entrypufp(phar_entry_info *entry TSRMLS_DC) +{ + if (!entry->is_persistent) { + return entry->phar->ufp; + } + return PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].ufp; +} + +static inline void phar_set_entrypfp(phar_entry_info *entry, php_stream *fp TSRMLS_DC) +{ + if (!entry->phar->is_persistent) { + entry->phar->fp = fp; + return; + } + + PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].fp = fp; +} + +static inline void phar_set_entrypufp(phar_entry_info *entry, php_stream *fp TSRMLS_DC) +{ + if (!entry->phar->is_persistent) { + entry->phar->ufp = fp; + return; + } + + PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].ufp = fp; +} + +static inline php_stream *phar_get_pharfp(phar_archive_data *phar TSRMLS_DC) +{ + if (!phar->is_persistent) { + return phar->fp; + } + return PHAR_GLOBALS->cached_fp[phar->phar_pos].fp; +} + +static inline php_stream *phar_get_pharufp(phar_archive_data *phar TSRMLS_DC) +{ + if (!phar->is_persistent) { + return phar->ufp; + } + return PHAR_GLOBALS->cached_fp[phar->phar_pos].ufp; +} + +static inline void phar_set_pharfp(phar_archive_data *phar, php_stream *fp TSRMLS_DC) +{ + if (!phar->is_persistent) { + phar->fp = fp; + return; + } + + PHAR_GLOBALS->cached_fp[phar->phar_pos].fp = fp; +} + +static inline void phar_set_pharufp(phar_archive_data *phar, php_stream *fp TSRMLS_DC) +{ + if (!phar->is_persistent) { + phar->ufp = fp; + return; + } + + PHAR_GLOBALS->cached_fp[phar->phar_pos].ufp = fp; +} + +static inline void phar_set_fp_type(phar_entry_info *entry, enum phar_fp_type type, off_t offset TSRMLS_DC) +{ + phar_entry_fp_info *data; + + if (!entry->is_persistent) { + entry->fp_type = type; + entry->offset = offset; + return; + } + data = &(PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].manifest[entry->manifest_pos]); + data->fp_type = type; + data->offset = offset; +} + +static inline enum phar_fp_type phar_get_fp_type(phar_entry_info *entry TSRMLS_DC) +{ + if (!entry->is_persistent) { + return entry->fp_type; + } + return PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].manifest[entry->manifest_pos].fp_type; +} + +static inline off_t phar_get_fp_offset(phar_entry_info *entry TSRMLS_DC) +{ + if (!entry->is_persistent) { + return entry->offset; + } + if (PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].manifest[entry->manifest_pos].fp_type == PHAR_FP) { + if (!PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].manifest[entry->manifest_pos].offset) { + PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].manifest[entry->manifest_pos].offset = entry->offset; + } + } + return PHAR_GLOBALS->cached_fp[entry->phar->phar_pos].manifest[entry->manifest_pos].offset; +} + #define PHAR_MIME_PHP '\0' #define PHAR_MIME_PHPS '\1' #define PHAR_MIME_OTHER '\2' @@ -330,8 +493,8 @@ union _phar_archive_object { zend_object std; spl_filesystem_object spl; struct { - zend_object std; - phar_archive_data *archive; + zend_object std; + phar_archive_data *archive; } arc; }; #endif @@ -342,8 +505,8 @@ union _phar_entry_object { zend_object std; spl_filesystem_object spl; struct { - zend_object std; - phar_entry_info *entry; + zend_object std; + phar_entry_info *entry; } ent; }; #endif @@ -357,11 +520,15 @@ extern char *(*phar_save_resolve_path)(const char *filename, int filename_len TS #if PHP_VERSION_ID >= 60000 typedef zstr phar_zstr; #define PHAR_STR(a, b) \ - spprintf(&b, 0, "%r", a.s); + spprintf(&b, 0, "%s", a.s); +#define PHAR_ZSTR(a, b) \ + b = ZSTR(a); #else typedef char *phar_zstr; #define PHAR_STR(a, b) \ b = a; +#define PHAR_ZSTR(a, b) \ + b = a; #endif BEGIN_EXTERN_C() @@ -391,6 +558,17 @@ static inline int phar_validate_alias(const char *alias, int alias_len) /* {{{ * } /* }}} */ +static inline void phar_set_inode(phar_entry_info *entry TSRMLS_DC) /* {{{ */ +{ + char tmp[MAXPATHLEN]; + int tmp_len; + + tmp_len = entry->filename_len + entry->phar->fname_len; + memcpy(tmp, entry->phar->fname, entry->phar->fname_len); + memcpy(tmp + entry->phar->fname_len, entry->filename, entry->filename_len); + entry->inode = (unsigned short)zend_get_hash_value(tmp, tmp_len); +} +/* }}} */ void phar_request_initialize(TSRMLS_D); @@ -405,12 +583,15 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len 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_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC); +int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error TSRMLS_DC); +int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, int *signature_length, char **error TSRMLS_DC); /* utility functions */ char *phar_create_default_stub(const char *index_php, const char *web_index, size_t *len, char **error TSRMLS_DC); char *phar_decompress_filter(phar_entry_info * entry, int return_unknown); char *phar_compress_filter(phar_entry_info * entry, int return_unknown); +void phar_add_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC); int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, char *path, int path_len TSRMLS_DC); char *phar_find_in_include_path(char *file, int file_len, phar_archive_data **pphar TSRMLS_DC); char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC); @@ -426,6 +607,7 @@ phar_entry_info *phar_get_link_source(phar_entry_info *entry TSRMLS_DC); int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC); int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC); int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC); +int phar_copy_on_write(phar_archive_data **pphar TSRMLS_DC); /* tar functions in tar.c */ int phar_is_tar(char *buf, char *fname); @@ -441,6 +623,9 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, int de #ifdef PHAR_MAIN static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC); extern php_stream_wrapper php_stream_phar_wrapper; +#else +extern HashTable cached_phars; +extern HashTable cached_alias; #endif int phar_archive_delref(phar_archive_data *phar TSRMLS_DC); @@ -451,7 +636,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error, int security TSRMLS_DC); int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error, int security TSRMLS_DC); int phar_flush(phar_archive_data *archive, char *user_stub, long len, int convert, char **error TSRMLS_DC); -int phar_detect_phar_fname_ext(const char *filename, int check_length, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC); +int phar_detect_phar_fname_ext(const char *filename, int filename_len, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC); int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC); typedef enum { diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index eab95dd1aa..fe8eff2661 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2005-2007 The PHP Group | + | Copyright (c) 2005-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -50,127 +50,134 @@ static int phar_file_type(HashTable *mimes, char *file, char **mime_type TSRMLS_ } /* }}} */ -static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char *basename, int basename_len, char *request_uri, int request_uri_len TSRMLS_DC) /* {{{ */ +static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char *basename, char *request_uri, int request_uri_len TSRMLS_DC) /* {{{ */ { - zval **_SERVER, **stuff; + HashTable *_SERVER; + zval **stuff; char *path_info; + int basename_len = strlen(basename); /* "tweak" $_SERVER variables requested in earlier call to Phar::mungServer() */ - if (SUCCESS != zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &_SERVER)) { + if (!PG(http_globals)[TRACK_VARS_SERVER]) { return; } + _SERVER = Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]); + /* PATH_INFO and PATH_TRANSLATED should always be munged */ - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "PATH_INFO", sizeof("PATH_INFO"), (void **) &stuff)) { - int code; - zval *temp; - char newname[] = "PHAR_PATH_INFO"; + if (SUCCESS == zend_hash_find(_SERVER, "PATH_INFO", sizeof("PATH_INFO"), (void **) &stuff)) { + int code; + zval *temp; path_info = Z_STRVAL_PP(stuff); code = Z_STRLEN_PP(stuff); + if (Z_STRLEN_PP(stuff) > entry_len && !memcmp(Z_STRVAL_PP(stuff), entry, entry_len)) { ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + entry_len, request_uri_len, 1); - MAKE_STD_ZVAL(temp); + MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(Z_ARRVAL_PP(_SERVER), newname, sizeof(newname), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_PATH_INFO", sizeof("PHAR_PATH_INFO"), (void *) &temp, sizeof(zval **), NULL); } } - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"), (void **) &stuff)) { - int code; - zval *temp; - char newname[] = "PHAR_PATH_TRANSLATED"; - path_info = Z_STRVAL_PP(stuff); - code = Z_STRLEN_PP(stuff); + if (SUCCESS == zend_hash_find(_SERVER, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"), (void **) &stuff)) { + int code; + zval *temp; + + path_info = Z_STRVAL_PP(stuff); + code = Z_STRLEN_PP(stuff); Z_STRLEN_PP(stuff) = spprintf(&(Z_STRVAL_PP(stuff)), 4096, "phar://%s%s", fname, entry); MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(Z_ARRVAL_PP(_SERVER), newname, sizeof(newname), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_PATH_TRANSLATED", sizeof("PHAR_PATH_TRANSLATED"), (void *) &temp, sizeof(zval **), NULL); } - if (!PHAR_GLOBALS->phar_SERVER_mung_list.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_SERVER_mung_list))) { + + if (!PHAR_GLOBALS->phar_SERVER_mung_list) { return; } - if (zend_hash_exists(&(PHAR_GLOBALS->phar_SERVER_mung_list), "REQUEST_URI", sizeof("REQUEST_URI")-1)) { - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &stuff)) { + + if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_REQUEST_URI) { + if (SUCCESS == zend_hash_find(_SERVER, "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &stuff)) { int code; zval *temp; - char newname[] = "PHAR_REQUEST_URI"; path_info = Z_STRVAL_PP(stuff); code = Z_STRLEN_PP(stuff); + if (Z_STRLEN_PP(stuff) > basename_len && !memcmp(Z_STRVAL_PP(stuff), basename, basename_len)) { ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + basename_len, Z_STRLEN_PP(stuff) - basename_len, 1); MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(Z_ARRVAL_PP(_SERVER), newname, sizeof(newname), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_REQUEST_URI", sizeof("PHAR_REQUEST_URI"), (void *) &temp, sizeof(zval **), NULL); } } } - if (zend_hash_exists(&(PHAR_GLOBALS->phar_SERVER_mung_list), "PHP_SELF", sizeof("PHP_SELF")-1)) { - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "PHP_SELF", sizeof("PHP_SELF"), (void **) &stuff)) { + + if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_PHP_SELF) { + if (SUCCESS == zend_hash_find(_SERVER, "PHP_SELF", sizeof("PHP_SELF"), (void **) &stuff)) { int code; zval *temp; - char newname[] = "PHAR_PHP_SELF"; path_info = Z_STRVAL_PP(stuff); code = Z_STRLEN_PP(stuff); + if (Z_STRLEN_PP(stuff) > basename_len && !memcmp(Z_STRVAL_PP(stuff), basename, basename_len)) { ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + basename_len, Z_STRLEN_PP(stuff) - basename_len, 1); MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(Z_ARRVAL_PP(_SERVER), newname, sizeof(newname), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_PHP_SELF", sizeof("PHAR_PHP_SELF"), (void *) &temp, sizeof(zval **), NULL); } } } - if (zend_hash_exists(&(PHAR_GLOBALS->phar_SERVER_mung_list), "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1)) { - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void **) &stuff)) { - int code; - zval *temp; - char newname[] = "PHAR_SCRIPT_NAME"; - - path_info = Z_STRVAL_PP(stuff); - code = Z_STRLEN_PP(stuff); + if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_SCRIPT_NAME) { + if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void **) &stuff)) { + int code; + zval *temp; + + path_info = Z_STRVAL_PP(stuff); + code = Z_STRLEN_PP(stuff); ZVAL_STRINGL(*stuff, entry, entry_len, 1); - + MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(Z_ARRVAL_PP(_SERVER), newname, sizeof(newname), (void *) &temp, sizeof(zval **), NULL); - } + zend_hash_update(_SERVER, "PHAR_SCRIPT_NAME", sizeof("PHAR_SCRIPT_NAME"), (void *) &temp, sizeof(zval **), NULL); + } } - if (zend_hash_exists(&(PHAR_GLOBALS->phar_SERVER_mung_list), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1)) { - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(_SERVER), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &stuff)) { - int code; - zval *temp; - char newname[] = "PHAR_SCRIPT_FILENAME"; + if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_SCRIPT_FILENAME) { + if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &stuff)) { + int code; + zval *temp; - path_info = Z_STRVAL_PP(stuff); - code = Z_STRLEN_PP(stuff); + path_info = Z_STRVAL_PP(stuff); + code = Z_STRLEN_PP(stuff); Z_STRLEN_PP(stuff) = spprintf(&(Z_STRVAL_PP(stuff)), 4096, "phar://%s%s", fname, entry); MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(Z_ARRVAL_PP(_SERVER), newname, sizeof(newname), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_SCRIPT_FILENAME", sizeof("PHAR_SCRIPT_FILENAME"), (void *) &temp, sizeof(zval **), NULL); } } } /* }}} */ -static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, char *entry, int entry_len, char *arch, int arch_len, char *basename, int basename_len, char *ru, int ru_len TSRMLS_DC) /* {{{ */ +static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char *mime_type, int code, char *entry, int entry_len, char *arch, int arch_len, char *basename, char *ru, int ru_len TSRMLS_DC) /* {{{ */ { char *name = NULL, buf[8192], *cwd; zend_syntax_highlighter_ini syntax_highlighter_ini; sapi_header_line ctr = {0}; size_t got; - int dummy = 1, name_len, ret; + int dummy = 1, name_len; zend_file_handle file_handle; zend_op_array *new_op_array; zval *result = NULL; + php_stream *fp; + off_t position; switch (code) { case PHAR_MIME_PHPS: @@ -185,7 +192,6 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch highlight_file(name, &syntax_highlighter_ini TSRMLS_CC); - phar_entry_delref(phar TSRMLS_CC); efree(name); #ifdef PHP_WIN32 efree(arch); @@ -197,61 +203,66 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch ctr.line_len = spprintf(&(ctr.line), 0, "Content-type: %s", mime_type); sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); efree(ctr.line); - ctr.line_len = spprintf(&(ctr.line), 0, "Content-length: %d", phar->internal_file->uncompressed_filesize); + ctr.line_len = spprintf(&(ctr.line), 0, "Content-length: %d", info->uncompressed_filesize); sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); efree(ctr.line); + if (FAILURE == sapi_send_headers(TSRMLS_C)) { - phar_entry_delref(phar TSRMLS_CC); zend_bailout(); } /* prepare to output */ - if (!phar_get_efp(phar->internal_file, 1 TSRMLS_CC)) { + fp = phar_get_efp(info, 1 TSRMLS_CC); + + if (!fp) { char *error; - if (!phar_open_jit(phar->phar, phar->internal_file, phar->phar->fp, &error, 0 TSRMLS_CC)) { + if (!phar_open_jit(phar, info, phar_get_pharfp(phar TSRMLS_CC), &error, 0 TSRMLS_CC)) { if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } return -1; } - phar->fp = phar_get_efp(phar->internal_file, 1 TSRMLS_CC); - phar->zero = phar->internal_file->offset; + fp = phar_get_efp(info, 1 TSRMLS_CC); } - phar_seek_efp(phar->internal_file, 0, SEEK_SET, 0, 1 TSRMLS_CC); + position = 0; + phar_seek_efp(info, 0, SEEK_SET, 0, 1 TSRMLS_CC); + do { - got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position)); - PHPWRITE(buf, got); - phar->position = php_stream_tell(phar->fp) - phar->zero; - if (phar->position == (off_t) phar->internal_file->uncompressed_filesize) { - break; + got = php_stream_read(fp, buf, MIN(8192, info->uncompressed_filesize - position)); + if (got > 0) { + PHPWRITE(buf, got); + position += got; + if (position == (off_t) info->uncompressed_filesize) { + break; + } } } while (1); - phar_entry_delref(phar TSRMLS_CC); zend_bailout(); case PHAR_MIME_PHP: if (basename) { - phar_mung_server_vars(arch, entry, entry_len, basename, basename_len, ru, ru_len TSRMLS_CC); + phar_mung_server_vars(arch, entry, entry_len, basename, ru, ru_len TSRMLS_CC); efree(basename); } - phar_entry_delref(phar TSRMLS_CC); + if (entry[0] == '/') { name_len = spprintf(&name, 4096, "phar://%s%s", arch, entry); } else { name_len = spprintf(&name, 4096, "phar://%s/%s", arch, entry); } - ret = php_stream_open_for_zend_ex(name, &file_handle, ENFORCE_SAFE_MODE|USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); - - if (ret != SUCCESS) { - efree(name); - return -1; - } + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.handle.fd = 0; + file_handle.filename = name; + file_handle.opened_path = NULL; + file_handle.free_filename = 0; + PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; - if (zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL)==SUCCESS) { - if ((cwd = strrchr(entry, '/'))) { + + if (zend_hash_add(&EG(included_files), name, name_len+1, (void *)&dummy, sizeof(int), NULL) == SUCCESS) { + if ((cwd = zend_memrchr(entry, '/', entry_len))) { PHAR_G(cwd_init) = 1; if (entry == cwd) { /* root directory */ @@ -265,15 +276,18 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len)); } } + new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC); + + if (!new_op_array) { + zend_hash_del(&EG(included_files), name, name_len+1); + } + zend_destroy_file_handle(&file_handle TSRMLS_CC); + } else { + efree(name); new_op_array = NULL; -#if PHP_VERSION_ID >= 50300 - zend_file_handle_dtor(&file_handle TSRMLS_CC); -#else - zend_file_handle_dtor(&file_handle); -#endif } #ifdef PHP_WIN32 efree(arch); @@ -288,18 +302,23 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch } zend_end_try(); destroy_op_array(new_op_array TSRMLS_CC); efree(new_op_array); + if (PHAR_G(cwd)) { efree(PHAR_G(cwd)); PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; } + PHAR_G(cwd_init) = 0; efree(name); + if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { zval_ptr_dtor(EG(return_value_ptr_ptr)); } + zend_bailout(); } + return PHAR_MIME_PHP; } return -1; @@ -321,31 +340,28 @@ static void phar_do_403(char *entry, int entry_len TSRMLS_DC) /* {{{ */ } /* }}} */ -static void phar_do_404(char *fname, int fname_len, char *f404, int f404_len, char *entry, int entry_len TSRMLS_DC) /* {{{ */ +static void phar_do_404(phar_archive_data *phar, char *fname, int fname_len, char *f404, int f404_len, char *entry, int entry_len TSRMLS_DC) /* {{{ */ { - int hi; - phar_entry_data *phar; - char *error; - if (f404_len) { - if (FAILURE == phar_get_entry_data(&phar, fname, fname_len, f404, f404_len, "r", 0, &error, 1 TSRMLS_CC)) { - if (error) { - efree(error); - } - goto nofile; + sapi_header_line ctr = {0}; + phar_entry_info *info; + + if (phar && f404_len) { + info = phar_get_entry_info(phar, f404, f404_len, NULL, 1 TSRMLS_CC); + + if (info) { + phar_file_action(phar, info, "text/html", PHAR_MIME_PHP, f404, f404_len, fname, fname_len, NULL, NULL, 0 TSRMLS_CC); + return; } - hi = phar_file_action(phar, "text/html", PHAR_MIME_PHP, f404, f404_len, fname, fname_len, NULL, 0, NULL, 0 TSRMLS_CC); - } else { - sapi_header_line ctr = {0}; -nofile: - ctr.response_code = 404; - ctr.line_len = sizeof("HTTP/1.0 404 Not Found")+1; - ctr.line = "HTTP/1.0 404 Not Found"; - sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); - sapi_send_headers(TSRMLS_C); - PHPWRITE("\n \n File Not Found\n \n \n

404 - File ", sizeof("\n \n File Not Found\n \n \n

404 - File ") - 1); - PHPWRITE(entry, entry_len); - PHPWRITE(" Not Found

\n \n", sizeof(" Not Found\n \n") - 1); } + + ctr.response_code = 404; + ctr.line_len = sizeof("HTTP/1.0 404 Not Found")+1; + ctr.line = "HTTP/1.0 404 Not Found"; + sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); + sapi_send_headers(TSRMLS_C); + PHPWRITE("\n \n File Not Found\n \n \n

404 - File ", sizeof("\n \n File Not Found\n \n \n

404 - File ") - 1); + PHPWRITE(entry, entry_len); + PHPWRITE(" Not Found

\n \n", sizeof(" Not Found\n \n") - 1); } /* }}} */ @@ -356,11 +372,15 @@ static void phar_postprocess_ru_web(char *fname, int fname_len, char **entry, in { char *e = *entry + 1, *u = NULL, *u1 = NULL, *saveu = NULL; int e_len = *entry_len - 1, u_len = 0; - phar_archive_data **pphar; + phar_archive_data **pphar = NULL; /* we already know we can retrieve the phar if we reach here */ zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void **) &pphar); + if (!pphar && PHAR_G(manifest_cached)) { + zend_hash_find(&cached_phars, fname, fname_len, (void **) &pphar); + } + do { if (zend_hash_exists(&((*pphar)->manifest), e, e_len)) { if (u) { @@ -375,6 +395,7 @@ static void phar_postprocess_ru_web(char *fname, int fname_len, char **entry, in *entry_len = e_len + 1; return; } + if (u) { u1 = strrchr(e, '/'); u[0] = '/'; @@ -393,9 +414,11 @@ static void phar_postprocess_ru_web(char *fname, int fname_len, char **entry, in return; } } + u[0] = '\0'; u_len = strlen(u + 1); e_len -= u_len + 1; + if (e_len < 0) { if (saveu) { saveu[0] = '/'; @@ -433,6 +456,7 @@ PHP_METHOD(Phar, running) RETURN_STRINGL(arch, arch_len, 0); } } + RETURN_STRINGL("", 0, 1); } /* }}} */ @@ -456,9 +480,14 @@ PHP_METHOD(Phar, mount) fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); +#ifdef PHP_WIN32 + phar_unixify_path_separators(fname, fname_len); +#endif + if (fname_len > 7 && !memcmp(fname, "phar://", 7) && SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { efree(entry); entry = NULL; + if (path_len > 7 && !memcmp(path, "phar://", 7)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Can only mount internal paths within a phar archive, use a relative path instead of \"%s\"", path); efree(arch); @@ -466,7 +495,14 @@ PHP_METHOD(Phar, mount) } carry_on2: if (SUCCESS != zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **)&pphar)) { + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, arch, arch_len, (void **)&pphar)) { + if (SUCCESS == phar_copy_on_write(pphar TSRMLS_CC)) { + goto carry_on; + } + } + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s is not a phar archive, cannot mount", arch); + if (arch) { efree(arch); } @@ -478,25 +514,37 @@ carry_on: if (path && path == entry) { efree(entry); } + if (arch) { efree(arch); } + return; } + if (entry && path && path == entry) { efree(entry); } + if (arch) { efree(arch); } + return; } else if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void **)&pphar)) { + goto carry_on; + } else if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, fname, fname_len, (void **)&pphar)) { + if (SUCCESS == phar_copy_on_write(pphar TSRMLS_CC)) { + goto carry_on; + } + goto carry_on; } else if (SUCCESS == phar_split_fname(path, path_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { path = entry; path_len = entry_len; goto carry_on2; } + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Mounting of %s to %s failed", path, actual); } /* }}} */ @@ -510,14 +558,13 @@ carry_on: */ PHP_METHOD(Phar, webPhar) { - HashTable mimetypes; - phar_mime_type mime; zval *mimeoverride = NULL, *rewrite = NULL; char *alias = NULL, *error, *index_php = NULL, *f404 = NULL, *ru = NULL; int alias_len = 0, ret, f404_len = 0, free_pathinfo = 0, ru_len = 0; - char *fname, *basename, *path_info, *mime_type, *entry, *pt; + char *fname, *basename, *path_info, *mime_type = NULL, *entry, *pt; int fname_len, entry_len, code, index_php_len = 0, not_cgi; - phar_entry_data *phar; + phar_archive_data *phar = NULL; + phar_entry_info *info; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!saz", &alias, &alias_len, &index_php, &index_php_len, &f404, &f404_len, &mimeoverride, &rewrite) == FAILURE) { return; @@ -526,6 +573,7 @@ PHP_METHOD(Phar, webPhar) phar_request_initialize(TSRMLS_C); fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); + if (phar_open_executed_filename(alias, alias_len, &error TSRMLS_CC) != SUCCESS) { if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); @@ -538,11 +586,13 @@ PHP_METHOD(Phar, webPhar) if (!(SG(request_info).request_method && SG(request_info).request_uri && (!strcmp(SG(request_info).request_method, "GET") || !strcmp(SG(request_info).request_method, "POST")))) { return; } + #ifdef PHP_WIN32 fname = estrndup(fname, fname_len); phar_unixify_path_separators(fname, fname_len); #endif - basename = strrchr(fname, '/'); + basename = zend_memrchr(fname, '/', fname_len); + if (!basename) { basename = fname; } else { @@ -551,26 +601,58 @@ PHP_METHOD(Phar, webPhar) if ((strlen(sapi_module.name) == sizeof("cgi-fcgi")-1 && !strncmp(sapi_module.name, "cgi-fcgi", sizeof("cgi-fcgi")-1)) || (strlen(sapi_module.name) == sizeof("cgi")-1 && !strncmp(sapi_module.name, "cgi", sizeof("cgi")-1))) { - char *testit; - testit = sapi_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC); - if (!(pt = strstr(testit, basename))) { - efree(testit); - return; - } - path_info = sapi_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); - if (path_info) { - entry = path_info; - entry_len = strlen(entry); - spprintf(&path_info, 0, "%s%s", testit, path_info); - free_pathinfo = 1; + if (PG(http_globals)[TRACK_VARS_SERVER]) { + HashTable *ht = Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]); + zval **z_script_name, **z_path_info; + + if (SUCCESS != zend_hash_find(ht, "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void**)&z_script_name) || + IS_STRING != Z_TYPE_PP(z_script_name) || + !strstr(Z_STRVAL_PP(z_script_name), basename)) { + return; + } + + if (SUCCESS == zend_hash_find(ht, "PATH_INFO", sizeof("PATH_INFO"), (void**)&z_path_info) && + IS_STRING == Z_TYPE_PP(z_path_info)) { + entry_len = Z_STRLEN_PP(z_path_info); + entry = estrndup(Z_STRVAL_PP(z_path_info), entry_len); + path_info = emalloc(Z_STRLEN_PP(z_script_name) + entry_len + 1); + memcpy(path_info, Z_STRVAL_PP(z_script_name), Z_STRLEN_PP(z_script_name)); + memcpy(path_info + Z_STRLEN_PP(z_script_name), entry, entry_len + 1); + free_pathinfo = 1; + } else { + entry_len = 0; + entry = estrndup("", 0); + path_info = Z_STRVAL_PP(z_script_name); + } + + pt = estrndup(Z_STRVAL_PP(z_script_name), Z_STRLEN_PP(z_script_name)); + } else { - path_info = testit; - free_pathinfo = 1; - entry = estrndup("", 0); - entry_len = 0; + char *testit; + + testit = sapi_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC); + if (!(pt = strstr(testit, basename))) { + efree(testit); + return; + } + + path_info = sapi_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); + + if (path_info) { + entry = path_info; + entry_len = strlen(entry); + spprintf(&path_info, 0, "%s%s", testit, path_info); + free_pathinfo = 1; + } else { + path_info = testit; + free_pathinfo = 1; + entry = estrndup("", 0); + entry_len = 0; + } + + pt = estrndup(testit, (pt - testit) + (fname_len - (basename - fname))); } - pt = estrndup(testit, (pt - testit) + (fname_len - (basename - fname))); not_cgi = 0; } else { path_info = SG(request_info).request_uri; @@ -579,8 +661,8 @@ PHP_METHOD(Phar, webPhar) /* this can happen with rewrite rules - and we have no idea what to do then, so return */ return; } - entry_len = strlen(path_info); + entry_len = strlen(path_info); entry_len -= (pt - path_info) + (fname_len - (basename - fname)); entry = estrndup(pt + (fname_len - (basename - fname)), entry_len); @@ -603,9 +685,11 @@ PHP_METHOD(Phar, webPhar) if (FAILURE == zend_fcall_info_init(rewrite, 0, &fci, &fcc, NULL, NULL TSRMLS_CC)) { #endif zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: invalid rewrite callback"); + if (free_pathinfo) { efree(path_info); } + return; } @@ -622,11 +706,14 @@ PHP_METHOD(Phar, webPhar) if (!EG(exception)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: failed to call rewrite callback"); } + if (free_pathinfo) { efree(path_info); } + return; } + if (!fci.retval_ptr_ptr || !retval_ptr) { if (free_pathinfo) { efree(path_info); @@ -634,6 +721,7 @@ PHP_METHOD(Phar, webPhar) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: rewrite callback must return a string or false"); return; } + switch (Z_TYPE_P(retval_ptr)) { #if PHP_VERSION_ID >= 60000 case IS_UNICODE: @@ -642,6 +730,7 @@ PHP_METHOD(Phar, webPhar) #endif case IS_STRING: efree(entry); + if (fci.retval_ptr_ptr != &retval_ptr) { entry = estrndup(Z_STRVAL_PP(fci.retval_ptr_ptr), Z_STRLEN_PP(fci.retval_ptr_ptr)); entry_len = Z_STRLEN_PP(fci.retval_ptr_ptr); @@ -649,19 +738,24 @@ PHP_METHOD(Phar, webPhar) entry = Z_STRVAL_P(retval_ptr); entry_len = Z_STRLEN_P(retval_ptr); } + break; - case IS_BOOL : + case IS_BOOL: phar_do_403(entry, entry_len TSRMLS_CC); + if (free_pathinfo) { efree(path_info); } + zend_bailout(); return; default: efree(retval_ptr); + if (free_pathinfo) { efree(path_info); } + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: rewrite callback must return a string or false"); return; } @@ -670,6 +764,7 @@ PHP_METHOD(Phar, webPhar) if (entry_len) { phar_postprocess_ru_web(fname, fname_len, &entry, &entry_len, &ru, &ru_len TSRMLS_CC); } + if (!entry_len || (entry_len == 1 && entry[0] == '/')) { efree(entry); /* direct request */ @@ -685,11 +780,15 @@ PHP_METHOD(Phar, webPhar) entry = estrndup("/index.php", sizeof("/index.php")); entry_len = sizeof("/index.php")-1; } - if (FAILURE == phar_get_entry_data(&phar, fname, fname_len, entry, entry_len, "r", 0, NULL, 1 TSRMLS_CC)) { - phar_do_404(fname, fname_len, f404, f404_len, entry, entry_len TSRMLS_CC); + + if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL TSRMLS_CC) || + (info = phar_get_entry_info(phar, entry, entry_len, NULL, 0 TSRMLS_CC)) == NULL) { + phar_do_404(phar, fname, fname_len, f404, f404_len, entry, entry_len TSRMLS_CC); + if (free_pathinfo) { efree(path_info); } + zend_bailout(); } else { char *tmp, sa; @@ -704,161 +803,79 @@ PHP_METHOD(Phar, webPhar) sa = *tmp; *tmp = '\0'; } + ctr.response_code = 0; + if (path_info[strlen(path_info)-1] == '/') { ctr.line_len = spprintf(&(ctr.line), 4096, "Location: %s%s", path_info, entry + 1); } else { ctr.line_len = spprintf(&(ctr.line), 4096, "Location: %s%s", path_info, entry); } + if (not_cgi) { *tmp = sa; } + if (free_pathinfo) { efree(path_info); } + sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); sapi_send_headers(TSRMLS_C); - phar_entry_delref(phar TSRMLS_CC); efree(ctr.line); zend_bailout(); } } - if (FAILURE == phar_get_entry_data(&phar, fname, fname_len, entry, entry_len, "r", 0, &error, 1 TSRMLS_CC)) { - phar_do_404(fname, fname_len, f404, f404_len, entry, entry_len TSRMLS_CC); + if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL TSRMLS_CC) || + (info = phar_get_entry_info(phar, entry, entry_len, NULL, 0 TSRMLS_CC)) == NULL) { + phar_do_404(phar, fname, fname_len, f404, f404_len, entry, entry_len TSRMLS_CC); #ifdef PHP_WIN32 efree(fname); #endif zend_bailout(); } - /* set up mime types */ - zend_hash_init(&mimetypes, sizeof(phar_mime_type *), zend_get_hash_value, NULL, 0); -#define PHAR_SET_MIME(mimetype, ret, fileext) \ - mime.mime = mimetype; \ - mime.len = sizeof((mimetype))+1; \ - mime.type = ret; \ - zend_hash_add(&mimetypes, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type), NULL); \ - - PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt") - PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd") - PHAR_SET_MIME("", PHAR_MIME_PHP, "php") - PHAR_SET_MIME("", PHAR_MIME_PHP, "inc") - PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi") - PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp") - PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css") - PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif") - PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm") - PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html") - PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls") - PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico") - PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe") - PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg") - PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg") - PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js") - PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi") - PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid") - PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod") - PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov") - PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3") - PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg") - PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg") - PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf") - PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png") - PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf") - PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif") - PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff") - PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav") - PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm") - PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml") - - /* set up user overrides */ -#define PHAR_SET_USER_MIME(ret) \ - if (Z_TYPE_PP(val) == IS_LONG) { \ - mime.mime = ""; \ - mime.len = 0; \ - } else { \ - mime.mime = Z_STRVAL_PP(val); \ - mime.len = Z_STRLEN_PP(val); \ - } \ - mime.type = ret; \ - zend_hash_update(&mimetypes, str_key, keylen-1, (void *)&mime, sizeof(phar_mime_type), NULL); - - if (mimeoverride) { - if (!zend_hash_num_elements(Z_ARRVAL_P(mimeoverride))) { - goto no_mimes; - } - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(mimeoverride)); SUCCESS == zend_hash_has_more_elements(Z_ARRVAL_P(mimeoverride)); zend_hash_move_forward(Z_ARRVAL_P(mimeoverride))) { - zval **val; - phar_zstr key; - char *str_key; - uint keylen; - ulong intkey; - - if (HASH_KEY_IS_LONG == zend_hash_get_current_key_ex(Z_ARRVAL_P(mimeoverride), &key, &keylen, &intkey, 0, NULL)) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Key of MIME type overrides array must be a file extension, was \"%d\"", intkey); - phar_entry_delref(phar TSRMLS_CC); -#ifdef PHP_WIN32 - efree(fname); -#endif - RETURN_FALSE; - } + if (mimeoverride && zend_hash_num_elements(Z_ARRVAL_P(mimeoverride))) { + char *ext = zend_memrchr(entry, '.', entry_len); + zval **val; - PHAR_STR(key, str_key); + if (ext) { + ++ext; - if (FAILURE == zend_hash_get_current_data(Z_ARRVAL_P(mimeoverride), (void **) &val)) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Failed to retrieve Mime type for extension \"%s\"", str_key); - phar_entry_delref(phar TSRMLS_CC); + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(mimeoverride), ext, strlen(ext)+1, (void **) &val)) { + switch (Z_TYPE_PP(val)) { + case IS_LONG: + if (Z_LVAL_PP(val) == PHAR_MIME_PHP || Z_LVAL_PP(val) == PHAR_MIME_PHPS) { + mime_type = ""; + code = Z_LVAL_PP(val); + } else { + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unknown mime type specifier used, only Phar::PHP, Phar::PHPS and a mime type string are allowed"); #ifdef PHP_WIN32 - efree(fname); + efree(fname); #endif - RETURN_FALSE; - } - switch (Z_TYPE_PP(val)) { - case IS_LONG : - if (Z_LVAL_PP(val) == PHAR_MIME_PHP || Z_LVAL_PP(val) == PHAR_MIME_PHPS) { - PHAR_SET_USER_MIME((char) Z_LVAL_PP(val)) - } else { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unknown mime type specifier used, only Phar::PHP, Phar::PHPS and a mime type string are allowed"); - phar_entry_delref(phar TSRMLS_CC); + RETURN_FALSE; + } + break; + case IS_STRING: + mime_type = Z_STRVAL_PP(val); + code = PHAR_MIME_OTHER; + break; + default: + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unknown mime type specifier used (not a string or int), only Phar::PHP, Phar::PHPS and a mime type string are allowed"); #ifdef PHP_WIN32 efree(fname); #endif RETURN_FALSE; - } - break; -#if PHP_VERSION_ID >= 60000 - case IS_UNICODE: - zval_unicode_to_string(*(val) TSRMLS_CC); - /* break intentionally omitted */ -#endif - case IS_STRING: - PHAR_SET_USER_MIME(PHAR_MIME_OTHER) - break; - default : - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unknown mime type specifier used (not a string or int), only Phar::PHP, Phar::PHPS and a mime type string are allowed"); - phar_entry_delref(phar TSRMLS_CC); -#ifdef PHP_WIN32 - efree(fname); -#endif - RETURN_FALSE; + } } } } -no_mimes: - code = phar_file_type(&mimetypes, entry, &mime_type TSRMLS_CC); - zend_hash_destroy(&mimetypes); - ret = phar_file_action(phar, mime_type, code, entry, entry_len, fname, fname_len, pt, strlen(pt), ru, ru_len TSRMLS_CC); + if (!mime_type) { + code = phar_file_type(&PHAR_G(mime_types), entry, &mime_type TSRMLS_CC); + } + ret = phar_file_action(phar, info, mime_type, code, entry, entry_len, fname, fname_len, pt, ru, ru_len TSRMLS_CC); } /* }}} */ @@ -871,7 +888,7 @@ no_mimes: PHP_METHOD(Phar, mungServer) { zval *mungvalues; - int php_self = 0, request_uri = 0, script_name = 0, script_filename = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &mungvalues) == FAILURE) { return; } @@ -880,6 +897,7 @@ PHP_METHOD(Phar, mungServer) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "No values passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME"); return; } + if (zend_hash_num_elements(Z_ARRVAL_P(mungvalues)) > 4) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Too many values passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME"); return; @@ -895,46 +913,26 @@ PHP_METHOD(Phar, mungServer) return; } -#if PHP_VERSION_ID >= 60000 - if (Z_TYPE_PP(data) == IS_UNICODE) { - zval_unicode_to_string(*(data) TSRMLS_CC); - } -#endif - if (Z_TYPE_PP(data) != IS_STRING) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Non-string value passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME"); return; } - if (!php_self && Z_STRLEN_PP(data) == sizeof("PHP_SELF")-1 && !strncmp(Z_STRVAL_PP(data), "PHP_SELF", sizeof("PHP_SELF")-1)) { - if (SUCCESS != zend_hash_add_empty_element(&(PHAR_GLOBALS->phar_SERVER_mung_list), "PHP_SELF", sizeof("PHP_SELF")-1)) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unable to add PHP_SELF to Phar::mungServer() list of values to mung"); - return; - } - php_self = 1; + if (Z_STRLEN_PP(data) == sizeof("PHP_SELF")-1 && !strncmp(Z_STRVAL_PP(data), "PHP_SELF", sizeof("PHP_SELF")-1)) { + PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_PHP_SELF; } + if (Z_STRLEN_PP(data) == sizeof("REQUEST_URI")-1) { - if (!request_uri && !strncmp(Z_STRVAL_PP(data), "REQUEST_URI", sizeof("REQUEST_URI")-1)) { - if (SUCCESS != zend_hash_add_empty_element(&(PHAR_GLOBALS->phar_SERVER_mung_list), "REQUEST_URI", sizeof("REQUEST_URI")-1)) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unable to add REQUEST_URI to Phar::mungServer() list of values to mung"); - return; - } - request_uri = 1; + if (!strncmp(Z_STRVAL_PP(data), "REQUEST_URI", sizeof("REQUEST_URI")-1)) { + PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_REQUEST_URI; } - if (!script_name && !strncmp(Z_STRVAL_PP(data), "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1)) { - if (SUCCESS != zend_hash_add_empty_element(&(PHAR_GLOBALS->phar_SERVER_mung_list), "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1)) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unable to add SCRIPT_NAME to Phar::mungServer() list of values to mung"); - return; - } - script_name = 1; + if (!strncmp(Z_STRVAL_PP(data), "SCRIPT_NAME", sizeof("SCRIPT_NAME")-1)) { + PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_SCRIPT_NAME; } } - if (!script_filename && Z_STRLEN_PP(data) == sizeof("SCRIPT_FILENAME")-1 && !strncmp(Z_STRVAL_PP(data), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1)) { - if (SUCCESS != zend_hash_add_empty_element(&(PHAR_GLOBALS->phar_SERVER_mung_list), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1)) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unable to add SCRIPT_FILENAME to Phar::mungServer() list of values to mung"); - return; - } - script_filename = 1; + + if (Z_STRLEN_PP(data) == sizeof("SCRIPT_FILENAME")-1 && !strncmp(Z_STRVAL_PP(data), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1)) { + PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_SCRIPT_FILENAME; } } } @@ -987,6 +985,7 @@ PHP_METHOD(Phar, mapPhar) char *alias = NULL, *error; int alias_len = 0; long dataoffset = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!l", &alias, &alias_len, &dataoffset) == FAILURE) { return; } @@ -994,6 +993,7 @@ PHP_METHOD(Phar, mapPhar) phar_request_initialize(TSRMLS_C); RETVAL_BOOL(phar_open_executed_filename(alias, alias_len, &error TSRMLS_CC) == SUCCESS); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -1014,6 +1014,7 @@ PHP_METHOD(Phar, loadPhar) phar_request_initialize(TSRMLS_C); RETVAL_BOOL(phar_open_from_filename(fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, &error TSRMLS_CC) == SUCCESS); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -1046,14 +1047,12 @@ PHP_METHOD(Phar, canCompress) } else { RETURN_FALSE; } - case PHAR_ENT_COMPRESSED_BZ2: if (PHAR_G(has_bz2)) { RETURN_TRUE; } else { RETURN_FALSE; } - default: if (PHAR_G(has_zlib) || PHAR_G(has_bz2)) { RETURN_TRUE; @@ -1078,15 +1077,15 @@ PHP_METHOD(Phar, isValidPharFilename) { char *fname; const char *ext_str; - int fname_len, ext_len; + int fname_len, ext_len, is_executable; zend_bool executable = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &fname, &fname_len, &executable) == FAILURE) { return; } - fname_len = executable; - RETVAL_BOOL(phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len, fname_len, 2, 1 TSRMLS_CC) == SUCCESS); + is_executable = executable; + RETVAL_BOOL(phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, is_executable, 2, 1 TSRMLS_CC) == SUCCESS); } /* }}} */ @@ -1096,7 +1095,12 @@ PHP_METHOD(Phar, isValidPharFilename) */ static void phar_spl_foreign_dtor(spl_filesystem_object *object TSRMLS_DC) /* {{{ */ { - phar_archive_delref((phar_archive_data *) object->oth TSRMLS_CC); + phar_archive_data *phar = (phar_archive_data *) object->oth; + + if (!phar->is_persistent) { + phar_archive_delref(phar TSRMLS_CC); + } + object->oth = NULL; } /* }}} */ @@ -1108,13 +1112,15 @@ static void phar_spl_foreign_clone(spl_filesystem_object *src, spl_filesystem_ob { phar_archive_data *phar_data = (phar_archive_data *) dst->oth; - ++(phar_data->refcount); + if (!phar_data->is_persistent) { + ++(phar_data->refcount); + } } /* }}} */ static spl_other_handler phar_spl_foreign_handler = { - phar_spl_foreign_dtor, - phar_spl_foreign_clone + phar_spl_foreign_dtor, + phar_spl_foreign_clone }; #endif /* HAVE_SPL */ @@ -1128,22 +1134,21 @@ PHP_METHOD(Phar, __construct) #if !HAVE_SPL zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Cannot instantiate Phar object without SPL extension"); #else - char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname, *objname; + char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname; int fname_len, alias_len = 0, arch_len, entry_len, is_data; - long flags = 0, format = 0; +#if PHP_VERSION_ID < 50300 + long flags = 0; +#else + long flags = SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS; +#endif + long format = 0; phar_archive_object *phar_obj; phar_archive_data *phar_data; zval *zobj = getThis(), arg1, arg2; phar_obj = (phar_archive_object*)zend_object_store_get_object(getThis() TSRMLS_CC); - PHAR_STR(phar_obj->std.ce->name, objname); - - if (!strncmp(objname, "PharData", 8)) { - is_data = 1; - } else { - is_data = 0; - } + is_data = instanceof_function(Z_OBJCE_P(zobj), phar_ce_data TSRMLS_CC); if (is_data) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls!l", &fname, &fname_len, &flags, &alias, &alias_len, &format) == FAILURE) { @@ -1185,9 +1190,11 @@ PHP_METHOD(Phar, __construct) efree(arch); fname = save_fname; } + if (entry) { efree(entry); } + if (error) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "%s", error); @@ -1196,6 +1203,7 @@ PHP_METHOD(Phar, __construct) zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Phar creation or opening failed"); } + return; } @@ -1222,7 +1230,11 @@ PHP_METHOD(Phar, __construct) } is_data = phar_data->is_data; - ++(phar_data->refcount); + + if (!phar_data->is_persistent) { + ++(phar_data->refcount); + } + phar_obj->arc.archive = phar_data; phar_obj->spl.oth_handler = &phar_spl_foreign_handler; @@ -1235,20 +1247,17 @@ PHP_METHOD(Phar, __construct) INIT_PZVAL(&arg1); ZVAL_STRINGL(&arg1, fname, fname_len, 0); + INIT_PZVAL(&arg2); + ZVAL_LONG(&arg2, flags); - if (ZEND_NUM_ARGS() > 1) { - INIT_PZVAL(&arg2); - ZVAL_LONG(&arg2, flags); - zend_call_method_with_2_params(&zobj, Z_OBJCE_P(zobj), - &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1, &arg2); - } else { - zend_call_method_with_1_params(&zobj, Z_OBJCE_P(zobj), - &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1); + zend_call_method_with_2_params(&zobj, Z_OBJCE_P(zobj), + &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1, &arg2); + + if (!phar_data->is_persistent) { + phar_obj->arc.archive->is_data = is_data; } - phar_obj->arc.archive->is_data = is_data; phar_obj->spl.info_class = phar_ce_entry; - efree(fname); #endif /* HAVE_SPL */ } @@ -1267,6 +1276,13 @@ PHP_METHOD(Phar, getSupportedSignatures) add_next_index_stringl(return_value, "SHA-256", 7, 1); add_next_index_stringl(return_value, "SHA-512", 7, 1); #endif +#if PHAR_HAVE_OPENSSL + add_next_index_stringl(return_value, "OpenSSL", 7, 1); +#else + if (zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) { + add_next_index_stringl(return_value, "OpenSSL", 7, 1); + } +#endif } /* }}} */ @@ -1276,11 +1292,12 @@ PHP_METHOD(Phar, getSupportedSignatures) PHP_METHOD(Phar, getSupportedCompression) { array_init(return_value); - phar_request_initialize(TSRMLS_C); + if (PHAR_G(has_zlib)) { add_next_index_stringl(return_value, "GZ", 2, 1); } + if (PHAR_G(has_bz2)) { add_next_index_stringl(return_value, "BZIP2", 5, 1); } @@ -1329,11 +1346,22 @@ PHP_METHOD(Phar, unlinkArchive) efree(entry); } + if (phar->is_persistent) { + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar archive \"%s\" is in phar.cache_list, cannot unlinkArchive()", fname); + return; + } + if (phar->refcount) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar archive \"%s\" has open file handles or objects. fclose() all file handles, and unset() all objects prior to calling unlinkArchive()", fname); return; } + fname = estrndup(phar->fname, phar->fname_len); + + /* invalidate phar cache */ + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + phar_archive_delref(phar TSRMLS_CC); unlink(fname); efree(fname); @@ -1351,24 +1379,28 @@ PHP_METHOD(Phar, unlinkArchive) return; \ } +struct _phar_t { + phar_archive_object *p; + zend_class_entry *c; + char *b; + uint l; + zval *ret; + int count; + php_stream *fp; +}; + static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */ { zval **value; zend_uchar key_type; zend_bool is_splfileinfo = 0, close_fp = 1; ulong int_key; - struct _t { - phar_archive_object *p; - zend_class_entry *c; - char *b; - uint l; - zval *ret; - } *p_obj = (struct _t*) puser; + struct _phar_t *p_obj = (struct _phar_t*) puser; uint str_key_len, base_len = p_obj->l, fname_len; phar_entry_data *data; php_stream *fp; long contents_len; - char *fname, *error, *base = p_obj->b, *opened, *save = NULL, *temp = NULL; + char *fname, *error = NULL, *base = p_obj->b, *opened, *save = NULL, *temp = NULL; phar_zstr key; char *str_key; zend_class_entry *ce = p_obj->c; @@ -1376,14 +1408,17 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ char *str = "[stream]"; iter->funcs->get_current_data(iter, &value TSRMLS_CC); + if (EG(exception)) { return ZEND_HASH_APPLY_STOP; } + if (!value) { /* failure in get_current_data */ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned no value", ce->name); return ZEND_HASH_APPLY_STOP; } + switch (Z_TYPE_PP(value)) { #if PHP_VERSION_ID >= 60000 case IS_UNICODE: @@ -1394,10 +1429,12 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ break; case IS_RESOURCE: php_stream_from_zval_no_verify(fp, value); + if (!fp) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returned an invalid stream handle", ce->name); return ZEND_HASH_APPLY_STOP; } + if (iter->funcs->get_current_key) { key_type = iter->funcs->get_current_key(iter, &key, &str_key_len, &int_key TSRMLS_CC); @@ -1409,13 +1446,24 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } - PHAR_STR(key, str_key); + + if (key_type > 9) { /* IS_UNICODE == 10 */ + spprintf(&str_key, 0, "%v", key); + } else { + PHAR_STR(key, str_key); + } + save = str_key; - if (str_key[str_key_len - 1] == '\0') str_key_len--; + + if (str_key[str_key_len - 1] == '\0') { + str_key_len--; + } + } else { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } + close_fp = 0; opened = (char *) estrndup(str, sizeof("[stream]") + 1); goto after_open_fp; @@ -1429,6 +1477,7 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returns an SplFileInfo object, so base directory must be specified", ce->name); return ZEND_HASH_APPLY_STOP; } + switch (intern->type) { case SPL_FS_DIR: #if PHP_VERSION_ID >= 60000 @@ -1440,17 +1489,21 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ #endif fname_len = spprintf(&fname, 0, "%s%c%s", test, DEFAULT_SLASH, intern->u.dir.entry.d_name); php_stat(fname, fname_len, FS_IS_DIR, &dummy TSRMLS_CC); + if (Z_BVAL(dummy)) { /* ignore directories */ efree(fname); return ZEND_HASH_APPLY_KEEP; } + test = expand_filepath(fname, NULL TSRMLS_CC); + if (test) { efree(fname); fname = test; fname_len = strlen(fname); } + save = fname; is_splfileinfo = 1; goto phar_spl_fileinfo; @@ -1468,7 +1521,7 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ } } /* fall-through */ - default : + default: zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid value (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } @@ -1481,8 +1534,10 @@ phar_spl_fileinfo: temp = expand_filepath(base, NULL TSRMLS_CC); base = temp; base_len = strlen(base); + if (strstr(fname, base)) { str_key_len = fname_len - base_len; + if (str_key_len <= 0) { if (save) { efree(save); @@ -1490,17 +1545,22 @@ phar_spl_fileinfo: } return ZEND_HASH_APPLY_KEEP; } + str_key = fname + base_len; + if (*str_key == '/' || *str_key == '\\') { str_key++; str_key_len--; } + } else { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that is not in the base directory \"%s\"", ce->name, fname, base); + if (save) { efree(save); efree(temp); } + return ZEND_HASH_APPLY_STOP; } } else { @@ -1515,8 +1575,15 @@ phar_spl_fileinfo: zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } - PHAR_STR(key, str_key); + + if (key_type > 9) { /* IS_UNICODE == 10 */ + spprintf(&str_key, 0, "%v", key); + } else { + PHAR_STR(key, str_key); + } + save = str_key; + if (str_key[str_key_len - 1] == '\0') str_key_len--; } else { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); @@ -1526,79 +1593,111 @@ phar_spl_fileinfo: #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that safe mode prevents opening", ce->name, fname); + if (save) { efree(save); } + if (temp) { efree(temp); } + return ZEND_HASH_APPLY_STOP; } #endif if (php_check_open_basedir(fname TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that open_basedir prevents opening", ce->name, fname); + if (save) { efree(save); } + if (temp) { efree(temp); } + return ZEND_HASH_APPLY_STOP; } /* try to open source file, then create internal phar file and copy contents */ fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened); + if (!fp) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a file that could not be opened \"%s\"", ce->name, fname); + if (save) { efree(save); } + if (temp) { efree(temp); } + return ZEND_HASH_APPLY_STOP; } - after_open_fp: if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) { /* silently skip any files that would be added to the magic .phar directory */ if (save) { efree(save); } + if (temp) { efree(temp); } + if (opened) { efree(opened); } + if (close_fp) { php_stream_close(fp); } + return ZEND_HASH_APPLY_KEEP; } + if (!(data = phar_get_or_create_entry_data(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, str_key, str_key_len, "w+b", 0, &error, 1 TSRMLS_CC))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s cannot be created: %s", str_key, error); efree(error); + if (save) { efree(save); } + if (opened) { efree(opened); } + if (temp) { efree(temp); } + if (close_fp) { php_stream_close(fp); } + return ZEND_HASH_APPLY_STOP; + } else { if (error) { efree(error); } - contents_len = php_stream_copy_to_stream(fp, data->fp, PHP_STREAM_COPY_ALL); + /* convert to PHAR_UFP */ + if (data->internal_file->fp_type == PHAR_MOD) { + php_stream_close(data->internal_file->fp); + } + + data->internal_file->fp = NULL; + data->internal_file->fp_type = PHAR_UFP; + data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp); + data->fp = NULL; + contents_len = php_stream_copy_to_stream(fp, p_obj->fp, PHP_STREAM_COPY_ALL); + data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize = + php_stream_tell(p_obj->fp) - data->internal_file->offset; } + if (close_fp) { php_stream_close(fp); } @@ -1608,12 +1707,14 @@ after_open_fp: if (save) { efree(save); } + if (temp) { efree(temp); } data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len; phar_entry_delref(data TSRMLS_CC); + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -1630,13 +1731,7 @@ PHP_METHOD(Phar, buildFromDirectory) int dir_len, regex_len = 0; zend_bool apply_reg = 0; zval arg, arg2, *iter, *iteriter, *regexiter = NULL; - struct { - phar_archive_object *p; - zend_class_entry *c; - char *b; - uint l; - zval *ret; - } pass; + struct _phar_t pass; PHAR_ARCHIVE_OBJECT(); @@ -1660,9 +1755,15 @@ PHP_METHOD(Phar, buildFromDirectory) INIT_PZVAL(&arg); ZVAL_STRINGL(&arg, dir, dir_len, 0); + INIT_PZVAL(&arg2); +#if PHP_VERSION_ID < 50300 + ZVAL_LONG(&arg2, 0); +#else + ZVAL_LONG(&arg2, SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS); +#endif - zend_call_method_with_1_params(&iter, spl_ce_RecursiveDirectoryIterator, - &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg); + zend_call_method_with_2_params(&iter, spl_ce_RecursiveDirectoryIterator, + &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg, &arg2); if (EG(exception)) { zval_ptr_dtor(&iter); @@ -1673,6 +1774,7 @@ PHP_METHOD(Phar, buildFromDirectory) if (SUCCESS != object_init_ex(iteriter, spl_ce_RecursiveIteratorIterator)) { zval_ptr_dtor(&iter); + zval_ptr_dtor(&iteriter); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate directory iterator for %s", phar_obj->arc.archive->fname); RETURN_FALSE; } @@ -1693,6 +1795,7 @@ PHP_METHOD(Phar, buildFromDirectory) MAKE_STD_ZVAL(regexiter); if (SUCCESS != object_init_ex(regexiter, spl_ce_RegexIterator)) { + zval_ptr_dtor(&iteriter); zval_dtor(regexiter); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate regex iterator for %s", phar_obj->arc.archive->fname); RETURN_FALSE; @@ -1711,20 +1814,31 @@ PHP_METHOD(Phar, buildFromDirectory) pass.p = phar_obj; pass.b = dir; pass.l = dir_len; + pass.count = 0; pass.ret = return_value; + pass.fp = php_stream_fopen_tmpfile(); if (SUCCESS == spl_iterator_apply((apply_reg ? regexiter : iteriter), (spl_iterator_apply_func_t) phar_build, (void *) &pass TSRMLS_CC)) { zval_ptr_dtor(&iteriter); + if (apply_reg) { zval_ptr_dtor(®exiter); } + + phar_obj->arc.archive->ufp = pass.fp; phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + + } else { + zval_ptr_dtor(&iteriter); + php_stream_close(pass.fp); } } +/* }}} */ /* {{{ proto array Phar::buildFromIterator(Iterator iter[, string base_directory]) * Construct a phar archive from an iterator. The iterator must return a series of strings @@ -1742,13 +1856,8 @@ PHP_METHOD(Phar, buildFromIterator) char *error; uint base_len = 0; char *base = NULL; - struct { - phar_archive_object *p; - zend_class_entry *c; - char *b; - uint l; - zval *ret; - } pass; + struct _phar_t pass; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -1768,15 +1877,19 @@ PHP_METHOD(Phar, buildFromIterator) pass.b = base; pass.l = base_len; pass.ret = return_value; + pass.count = 0; + pass.fp = php_stream_fopen_tmpfile(); if (SUCCESS == spl_iterator_apply(obj, (spl_iterator_apply_func_t) phar_build, (void *) &pass TSRMLS_CC)) { + phar_obj->arc.archive->ufp = pass.fp; phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + } else { + php_stream_close(pass.fp); } - } /* }}} */ @@ -1786,7 +1899,7 @@ PHP_METHOD(Phar, buildFromIterator) PHP_METHOD(Phar, count) { PHAR_ARCHIVE_OBJECT(); - + RETURN_LONG(zend_hash_num_elements(&phar_obj->arc.archive->manifest)); } /* }}} */ @@ -1803,6 +1916,7 @@ PHP_METHOD(Phar, isFileFormat) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) { RETURN_FALSE; } + switch (type) { case PHAR_FORMAT_TAR: RETURN_BOOL(phar_obj->arc.archive->is_tar); @@ -1833,23 +1947,28 @@ static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS } return FAILURE; } + /* copy old contents in entirety */ phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC); offset = php_stream_tell(fp); link = phar_get_link_source(entry TSRMLS_CC); + if (!link) { link = entry; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename); return FAILURE; } + if (entry->fp_type == PHAR_MOD) { /* save for potential restore on error */ entry->cfp = entry->fp; entry->fp = NULL; } + /* set new location of file contents */ entry->fp_type = PHAR_FP; entry->offset = offset; @@ -1857,7 +1976,7 @@ static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS } /* }}} */ -static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool compress TSRMLS_DC) +static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool compress TSRMLS_DC) /* {{{ */ { char *oldname = NULL, *oldpath = NULL; char *basename = NULL, *basepath = NULL; @@ -1871,12 +1990,15 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c if (!ext) { if (phar->is_zip) { + if (phar->is_data) { ext = "zip"; } else { ext = "phar.zip"; } + } else if (phar->is_tar) { + switch (phar->flags) { case PHAR_FILE_COMPRESSED_GZ: if (phar->is_data) { @@ -1900,6 +2022,7 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c } } } else { + switch (phar->flags) { case PHAR_FILE_COMPRESSED_GZ: ext = "phar.gz"; @@ -1912,6 +2035,7 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c } } } else if (phar_path_check(&ext, &ext_len, &pcr_error) > pcr_is_ok) { + if (phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar converted from \"%s\" has invalid extension %s", phar->fname, ext); } else { @@ -1925,7 +2049,7 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c } oldpath = estrndup(phar->fname, phar->fname_len); - oldname = strrchr(phar->fname, '/'); + oldname = zend_memrchr(phar->fname, '/', phar->fname_len); ++oldname; basename = estrndup(oldname, strlen(oldname)); @@ -1939,6 +2063,12 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c efree(basepath); efree(newname); + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, newpath, phar->fname_len, (void **) &pphar)) { + efree(oldpath); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, new phar name is in phar.cache_list", phar->fname); + return NULL; + } + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), newpath, phar->fname_len, (void **) &pphar)) { if ((*pphar)->fname_len == phar->fname_len && !memcmp((*pphar)->fname, phar->fname, phar->fname_len)) { if (!zend_hash_num_elements(&phar->manifest)) { @@ -1955,11 +2085,11 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c goto its_ok; } } + efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, a phar with that name already exists", phar->fname); return NULL; } - its_ok: if (!phar->is_data) { if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 1, 1, 1 TSRMLS_CC)) { @@ -1967,6 +2097,7 @@ its_ok: zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" has invalid extension %s", phar->fname, ext); return NULL; } + if (phar->alias) { if (phar->is_temporary_alias) { phar->alias = NULL; @@ -1978,12 +2109,15 @@ its_ok: zend_hash_update(&(PHAR_GLOBALS->phar_alias_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL); } } + } else { + if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 0, 1, 1 TSRMLS_CC)) { efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar \"%s\" has invalid extension %s", phar->fname, ext); return NULL; } + phar->alias = NULL; phar->alias_len = 0; } @@ -1995,6 +2129,7 @@ its_ok: } phar_flush(phar, 0, 0, 1, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, error); efree(error); @@ -2011,18 +2146,20 @@ its_ok: } MAKE_STD_ZVAL(ret); + if (SUCCESS != object_init_ex(ret, ce)) { zval_dtor(ret); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname); return NULL; } + INIT_PZVAL(&arg1); ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len, 0); zend_call_method_with_1_params(&ret, ce, &ce->constructor, "__construct", NULL, &arg1); - return ret; } +/* }}} */ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, php_uint32 flags TSRMLS_DC) /* {{{ */ { @@ -2030,31 +2167,40 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char phar_entry_info *entry, newentry; zval *ret; + /* invalidate phar cache */ + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + phar = (phar_archive_data *) ecalloc(1, sizeof(phar_archive_data)); /* set whole-archive compression and type from parameter */ phar->flags = flags; - phar->is_data = source->is_data; + switch (convert) { - case PHAR_FORMAT_TAR : + case PHAR_FORMAT_TAR: phar->is_tar = 1; break; - case PHAR_FORMAT_ZIP : + case PHAR_FORMAT_ZIP: phar->is_zip = 1; break; - default : + default: phar->is_data = 0; break; } zend_hash_init(&(phar->manifest), sizeof(phar_entry_info), zend_get_hash_value, destroy_phar_manifest_entry, 0); + zend_hash_init(&phar->mounted_dirs, sizeof(char *), + zend_get_hash_value, NULL, 0); + zend_hash_init(&phar->virtual_dirs, sizeof(char *), + zend_get_hash_value, NULL, 0); phar->fp = php_stream_fopen_tmpfile(); phar->fname = source->fname; phar->fname_len = source->fname_len; phar->is_temporary_alias = source->is_temporary_alias; phar->alias = source->alias; + /* first copy each file's uncompressed contents to a temporary file and set per-file flags */ for (zend_hash_internal_pointer_reset(&source->manifest); SUCCESS == zend_hash_has_more_elements(&source->manifest); zend_hash_move_forward(&source->manifest)) { @@ -2066,16 +2212,21 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char "Cannot convert phar archive \"%s\"", source->fname); return NULL; } + newentry = *entry; + if (newentry.link) { newentry.link = estrdup(newentry.link); goto no_copy; } + if (newentry.tmp) { newentry.tmp = estrdup(newentry.tmp); goto no_copy; } + newentry.metadata_str.c = 0; + if (FAILURE == phar_copy_file_contents(&newentry, phar->fp TSRMLS_CC)) { zend_hash_destroy(&(phar->manifest)); php_stream_close(phar->fp); @@ -2085,6 +2236,7 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char } no_copy: newentry.filename = estrndup(newentry.filename, newentry.filename_len); + if (newentry.metadata) { zval *t; @@ -2101,21 +2253,28 @@ no_copy: newentry.metadata_str.c = NULL; newentry.metadata_str.len = 0; } + newentry.is_zip = phar->is_zip; newentry.is_tar = phar->is_tar; + if (newentry.is_tar) { newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE); } + newentry.is_modified = 1; newentry.phar = phar; - newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */ + newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */ + phar_set_inode(&newentry TSRMLS_CC); zend_hash_add(&(phar->manifest), newentry.filename, newentry.filename_len, (void*)&newentry, sizeof(phar_entry_info), NULL); + phar_add_virtual_dirs(phar, newentry.filename, newentry.filename_len TSRMLS_CC); } if ((ret = phar_rename_archive(phar, ext, 0 TSRMLS_CC))) { return ret; } else { zend_hash_destroy(&(phar->manifest)); + zend_hash_destroy(&(phar->mounted_dirs)); + zend_hash_destroy(&(phar->virtual_dirs)); php_stream_close(phar->fp); efree(phar->fname); efree(phar); @@ -2125,7 +2284,7 @@ no_copy: /* }}} */ /* {{{ proto object Phar::convertToExecutable([int format[, int compression [, string file_ext]]]) - * Convert a phar.tar or phar.zip archive to the phar file format. The + * Convert a phar.tar or phar.zip archive to the phar file format. The * optional parameter allows the user to determine the new * filename extension (default is phar). */ @@ -2184,25 +2343,28 @@ PHP_METHOD(Phar, convertToExecutable) "Cannot compress entire archive with gzip, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with gzip, enable ext/zlib in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_GZ; break; - case PHAR_ENT_COMPRESSED_BZ2: if (format == PHAR_FORMAT_ZIP) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_BZ2; break; default: @@ -2215,6 +2377,7 @@ PHP_METHOD(Phar, convertToExecutable) phar_obj->arc.archive->is_data = 0; ret = phar_convert_to_other(phar_obj->arc.archive, format, ext, flags TSRMLS_CC); phar_obj->arc.archive->is_data = is_data; + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2282,25 +2445,28 @@ PHP_METHOD(Phar, convertToData) "Cannot compress entire archive with gzip, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with gzip, enable ext/zlib in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_GZ; break; - case PHAR_ENT_COMPRESSED_BZ2: if (format == PHAR_FORMAT_ZIP) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_BZ2; break; default: @@ -2313,6 +2479,7 @@ PHP_METHOD(Phar, convertToData) phar_obj->arc.archive->is_data = 1; ret = phar_convert_to_other(phar_obj->arc.archive, format, ext, flags TSRMLS_CC); phar_obj->arc.archive->is_data = is_data; + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2328,13 +2495,15 @@ PHP_METHOD(Phar, convertToData) PHP_METHOD(Phar, isCompressed) { PHAR_ARCHIVE_OBJECT(); - + if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_GZ) { RETURN_LONG(PHAR_ENT_COMPRESSED_GZ); } + if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_BZ2) { RETURN_LONG(PHAR_ENT_COMPRESSED_BZ2); } + RETURN_FALSE; } /* }}} */ @@ -2346,10 +2515,11 @@ PHP_METHOD(Phar, isWritable) { php_stream_statbuf ssb; PHAR_ARCHIVE_OBJECT(); - + if (!phar_obj->arc.archive->is_writeable) { RETURN_FALSE; } + if (SUCCESS != php_stream_stat_path(phar_obj->arc.archive->fname, &ssb)) { if (phar_obj->arc.archive->is_brandnew) { /* assume it works if the file doesn't exist yet */ @@ -2357,6 +2527,7 @@ PHP_METHOD(Phar, isWritable) } RETURN_FALSE; } + RETURN_BOOL((ssb.sb.st_mode & (S_IWOTH | S_IWGRP | S_IWUSR)) != 0); } /* }}} */ @@ -2403,7 +2574,7 @@ PHP_METHOD(Phar, delete) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } - + RETURN_TRUE; } /* }}} */ @@ -2441,6 +2612,7 @@ PHP_METHOD(Phar, setAlias) char *alias, *error, *oldalias; phar_archive_data **fd_ptr; int alias_len, oldalias_len, old_temp, readd = 0; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -2449,9 +2621,18 @@ PHP_METHOD(Phar, setAlias) RETURN_FALSE; } + /* invalidate phar cache */ + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + if (phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "A Phar alias cannot be set in a plain %s archive", phar_obj->arc.archive->is_tar ? "tar" : "zip"); + if (phar_obj->arc.archive->is_tar) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar alias cannot be set in a plain tar archive"); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar alias cannot be set in a plain zip archive"); + } RETURN_FALSE; } @@ -2483,15 +2664,17 @@ valid_alias: oldalias = phar_obj->arc.archive->alias; oldalias_len = phar_obj->arc.archive->alias_len; old_temp = phar_obj->arc.archive->is_temporary_alias; + if (alias_len) { phar_obj->arc.archive->alias = estrndup(alias, alias_len); } else { phar_obj->arc.archive->alias = NULL; } + phar_obj->arc.archive->alias_len = alias_len; phar_obj->arc.archive->is_temporary_alias = 0; - phar_flush(phar_obj->arc.archive, NULL, 0, 0, &error TSRMLS_CC); + if (error) { phar_obj->arc.archive->alias = oldalias; phar_obj->arc.archive->alias_len = oldalias_len; @@ -2503,10 +2686,13 @@ valid_alias: efree(error); RETURN_FALSE; } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(phar_obj->arc.archive), sizeof(phar_archive_data*), NULL); + if (oldalias) { efree(oldalias); } + RETURN_TRUE; } @@ -2520,7 +2706,7 @@ valid_alias: PHP_METHOD(Phar, getVersion) { PHAR_ARCHIVE_OBJECT(); - + RETURN_STRING(phar_obj->arc.archive->version, 1); } /* }}} */ @@ -2531,7 +2717,7 @@ PHP_METHOD(Phar, getVersion) PHP_METHOD(Phar, startBuffering) { PHAR_ARCHIVE_OBJECT(); - + phar_obj->arc.archive->donotflush = 1; } /* }}} */ @@ -2542,7 +2728,7 @@ PHP_METHOD(Phar, startBuffering) PHP_METHOD(Phar, isBuffering) { PHAR_ARCHIVE_OBJECT(); - + RETURN_BOOL(!phar_obj->arc.archive->donotflush); } /* }}} */ @@ -2553,6 +2739,7 @@ PHP_METHOD(Phar, isBuffering) PHP_METHOD(Phar, stopBuffering) { char *error; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -2562,8 +2749,8 @@ PHP_METHOD(Phar, stopBuffering) } phar_obj->arc.archive->donotflush = 0; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -2591,8 +2778,13 @@ PHP_METHOD(Phar, setStub) } if (phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "A Phar stub cannot be set in a plain %s archive", phar_obj->arc.archive->is_tar ? "tar" : "zip"); + if (phar_obj->arc.archive->is_tar) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain tar archive"); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain zip archive"); + } return; } @@ -2615,10 +2807,12 @@ PHP_METHOD(Phar, setStub) } } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &stub, &stub_len) == SUCCESS) { phar_flush(phar_obj->arc.archive, stub, stub_len, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } @@ -2639,7 +2833,7 @@ PHP_METHOD(Phar, setStub) * files cannot be loaded via this kind of stub, no parameters are accepted * when the Phar object is zip- or tar-based. */ - PHP_METHOD(Phar, setDefaultStub) +PHP_METHOD(Phar, setDefaultStub) { char *index = NULL, *webindex = NULL, *error = NULL, *stub = NULL; int index_len = 0, webindex_len = 0, created_stub = 0; @@ -2647,8 +2841,13 @@ PHP_METHOD(Phar, setStub) PHAR_ARCHIVE_OBJECT(); if (phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "A Phar stub cannot be set in a plain %s archive", phar_obj->arc.archive->is_tar ? "tar" : "zip"); + if (phar_obj->arc.archive->is_tar) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain tar archive"); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain zip archive"); + } return; } @@ -2668,7 +2867,6 @@ PHP_METHOD(Phar, setStub) } if (!phar_obj->arc.archive->is_tar && !phar_obj->arc.archive->is_zip) { - stub = phar_create_default_stub(index, webindex, &stub_len, &error TSRMLS_CC); if (error) { @@ -2679,6 +2877,7 @@ PHP_METHOD(Phar, setStub) } RETURN_FALSE; } + created_stub = 1; } @@ -2698,52 +2897,51 @@ PHP_METHOD(Phar, setStub) } /* }}} */ -/* {{{ proto array Phar::setSignatureAlgorithm(int sigtype) +/* {{{ proto array Phar::setSignatureAlgorithm(int sigtype[, string privatekey]) * Sets the signature algorithm for a phar and applies it. The signature * algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, - * Phar::SHA512, or Phar::PGP (PGP is not yet supported and falls back to - * SHA-1). Note that zip- and tar- based phar archives cannot support - * signatures. + * Phar::SHA512, or Phar::OPENSSL. Note that zip- based phar archives + * cannot support signatures. */ PHP_METHOD(Phar, setSignatureAlgorithm) { long algo; - char *error; + char *error, *key = NULL; + int key_len = 0; + PHAR_ARCHIVE_OBJECT(); - + if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot set signature algorithm, phar is read-only"); return; } - if (phar_obj->arc.archive->is_tar) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, - "Cannot set signature algorithm, not possible with tar-based phar archives"); - return; - } + if (phar_obj->arc.archive->is_zip) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set signature algorithm, not possible with zip-based phar archives"); return; } - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "l", &algo) != SUCCESS) { + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &algo, &key, &key_len) != SUCCESS) { return; } switch (algo) { - case PHAR_SIG_SHA256 : - case PHAR_SIG_SHA512 : + case PHAR_SIG_SHA256: + case PHAR_SIG_SHA512: #if !HAVE_HASH_EXT zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "SHA-256 and SHA-512 signatures are only supported if the hash extension is enabled"); return; #endif - case PHAR_SIG_MD5 : - case PHAR_SIG_SHA1 : - case PHAR_SIG_PGP : + case PHAR_SIG_MD5: + case PHAR_SIG_SHA1: + case PHAR_SIG_OPENSSL: phar_obj->arc.archive->sig_flags = algo; phar_obj->arc.archive->is_modified = 1; + PHAR_G(openssl_privatekey) = key; + PHAR_G(openssl_privatekey_len) = key_len; phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); if (error) { @@ -2751,7 +2949,7 @@ PHP_METHOD(Phar, setSignatureAlgorithm) efree(error); } break; - default : + default: zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Unknown signature algorithm specified"); } @@ -2766,21 +2964,31 @@ PHP_METHOD(Phar, getSignature) PHAR_ARCHIVE_OBJECT(); if (phar_obj->arc.archive->signature) { + char *unknown; + int unknown_len; + array_init(return_value); add_assoc_stringl(return_value, "hash", phar_obj->arc.archive->signature, phar_obj->arc.archive->sig_len, 1); switch(phar_obj->arc.archive->sig_flags) { - case PHAR_SIG_MD5: - add_assoc_stringl(return_value, "hash_type", "MD5", 3, 1); - break; - case PHAR_SIG_SHA1: - add_assoc_stringl(return_value, "hash_type", "SHA-1", 5, 1); - break; - case PHAR_SIG_SHA256: - add_assoc_stringl(return_value, "hash_type", "SHA-256", 7, 1); - break; - case PHAR_SIG_SHA512: - add_assoc_stringl(return_value, "hash_type", "SHA-512", 7, 1); - break; + case PHAR_SIG_MD5: + add_assoc_stringl(return_value, "hash_type", "MD5", 3, 1); + break; + case PHAR_SIG_SHA1: + add_assoc_stringl(return_value, "hash_type", "SHA-1", 5, 1); + break; + case PHAR_SIG_SHA256: + add_assoc_stringl(return_value, "hash_type", "SHA-256", 7, 1); + break; + case PHAR_SIG_SHA512: + add_assoc_stringl(return_value, "hash_type", "SHA-512", 7, 1); + break; + case PHAR_SIG_OPENSSL: + add_assoc_stringl(return_value, "hash_type", "OpenSSL", 7, 1); + break; + default: + unknown_len = spprintf(&unknown, 0, "Unknown (%d)", phar_obj->arc.archive->sig_flags); + add_assoc_stringl(return_value, "hash_type", unknown, unknown_len, 0); + break; } } else { RETURN_FALSE; @@ -2807,6 +3015,7 @@ static int phar_set_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ */ if (entry->is_deleted) { return ZEND_HASH_APPLY_KEEP; } + entry->old_flags = entry->flags; entry->flags &= ~PHAR_ENT_COMPRESSION_MASK; entry->flags |= compress; @@ -2822,16 +3031,19 @@ static int phar_test_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ * if (entry->is_deleted) { return ZEND_HASH_APPLY_KEEP; } + if (!PHAR_G(has_bz2)) { if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) { *(int *) argument = 0; } } + if (!PHAR_G(has_zlib)) { if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { *(int *) argument = 0; } } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -2845,6 +3057,7 @@ static void pharobj_set_compression(HashTable *manifest, php_uint32 compress TSR static int pharobj_cancompress(HashTable *manifest TSRMLS_DC) /* {{{ */ { int test; + test = 1; zend_hash_apply_with_argument(manifest, phar_test_compression, &test TSRMLS_CC); return test; @@ -2868,7 +3081,7 @@ PHP_METHOD(Phar, compress) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &method, &ext, &ext_len) == FAILURE) { return; } - + if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot compress phar archive, phar is read-only"); @@ -2893,7 +3106,7 @@ PHP_METHOD(Phar, compress) } flags = PHAR_FILE_COMPRESSED_GZ; break; - + case PHAR_ENT_COMPRESSED_BZ2: if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -2913,6 +3126,7 @@ PHP_METHOD(Phar, compress) } else { ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_PHAR, ext, flags TSRMLS_CC); } + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2952,6 +3166,7 @@ PHP_METHOD(Phar, decompress) } else { ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_PHAR, ext, PHAR_FILE_COMPRESSED_NONE TSRMLS_CC); } + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -3005,11 +3220,13 @@ PHP_METHOD(Phar, compressFiles) "Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2"); return; } + if (phar_obj->arc.archive->is_tar) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with Gzip compression, tar archives cannot compress individual files, use compress() to compress the whole archive"); return; } + if (!pharobj_cancompress(&phar_obj->arc.archive->manifest TSRMLS_CC)) { if (flags == PHAR_FILE_COMPRESSED_GZ) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -3020,11 +3237,11 @@ PHP_METHOD(Phar, compressFiles) } return; } - pharobj_set_compression(&phar_obj->arc.archive->manifest, flags TSRMLS_CC); + pharobj_set_compression(&phar_obj->arc.archive->manifest, flags TSRMLS_CC); phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, error); efree(error); @@ -3045,11 +3262,13 @@ PHP_METHOD(Phar, decompressFiles) "Phar is readonly, cannot change compression"); return; } + if (!pharobj_cancompress(&phar_obj->arc.archive->manifest TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress all files, some are compressed as bzip2 or gzip and cannot be decompressed"); return; } + if (phar_obj->arc.archive->is_tar) { RETURN_TRUE; } else { @@ -3057,12 +3276,13 @@ PHP_METHOD(Phar, decompressFiles) } phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } /* }}} */ @@ -3076,6 +3296,7 @@ PHP_METHOD(Phar, copy) const char *pcr_error; int oldfile_len, newfile_len; phar_entry_info *oldentry, newentry = {0}, *temp; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) { @@ -3123,6 +3344,7 @@ PHP_METHOD(Phar, copy) } memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info)); + if (newentry.metadata) { zval *t; @@ -3139,6 +3361,7 @@ PHP_METHOD(Phar, copy) newentry.metadata_str.c = NULL; newentry.metadata_str.len = 0; } + newentry.filename = estrndup(newfile, newfile_len); newentry.filename_len = newfile_len; newentry.fp_refcount = 0; @@ -3155,8 +3378,8 @@ PHP_METHOD(Phar, copy) zend_hash_add(&oldentry->phar->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL); phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3174,6 +3397,7 @@ PHP_METHOD(Phar, offsetExists) char *fname; int fname_len; phar_entry_info *entry; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { @@ -3222,12 +3446,12 @@ PHP_METHOD(Phar, offsetGet) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot get stub \".phar/stub.php\" directly in phar \"%s\", use getStub", phar_obj->arc.archive->fname); return; } - + if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot get alias \".phar/alias.txt\" directly in phar \"%s\", use getAlias", phar_obj->arc.archive->fname); return; } - + if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot directly get any files or directories in magic \".phar\" directory", phar_obj->arc.archive->fname); return; @@ -3237,13 +3461,13 @@ PHP_METHOD(Phar, offsetGet) efree(entry->filename); efree(entry); } + fname_len = spprintf(&fname, 0, "phar://%s/%s", phar_obj->arc.archive->fname, fname); MAKE_STD_ZVAL(zfname); ZVAL_STRINGL(zfname, fname, fname_len, 0); spl_instantiate_arg_ex1(phar_obj->spl.info_class, &return_value, 0, zfname TSRMLS_CC); zval_ptr_dtor(&zfname); } - } /* }}} */ @@ -3273,6 +3497,7 @@ static void phar_add_file(phar_archive_data *phar, char *filename, int filename_ if (error) { efree(error); } + if (!data->internal_file->is_dir) { if (cont_str) { contents_len = php_stream_write(data->fp, cont_str, cont_len); @@ -3287,10 +3512,13 @@ static void phar_add_file(phar_archive_data *phar, char *filename, int filename_ } contents_len = php_stream_copy_to_stream(contents_file, data->fp, PHP_STREAM_COPY_ALL); } + data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len; } + phar_entry_delref(data TSRMLS_CC); phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3313,13 +3541,16 @@ static void phar_mkdir(phar_archive_data *phar, char *dirname, int dirname_len T } else { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Directory %s does not exist and cannot be created", dirname); } + return; } else { if (error) { efree(error); } + phar_entry_delref(data TSRMLS_CC); phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3328,7 +3559,6 @@ static void phar_mkdir(phar_archive_data *phar, char *dirname, int dirname_len T } /* }}} */ - /* {{{ proto int Phar::offsetSet(string entry, string value) * set the contents of an internal file to those of an external file */ @@ -3343,7 +3573,7 @@ PHP_METHOD(Phar, offsetSet) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by INI setting"); return; } - + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sr", &fname, &fname_len, &zresource) == FAILURE && zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) { return; @@ -3363,6 +3593,7 @@ PHP_METHOD(Phar, offsetSet) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set any files or directories in magic \".phar\" directory", phar_obj->arc.archive->fname); return; } + phar_add_file(phar_obj->arc.archive, fname, fname_len, cont_str, cont_len, zresource TSRMLS_CC); } /* }}} */ @@ -3392,14 +3623,17 @@ PHP_METHOD(Phar, offsetUnset) /* entry is deleted, but has not been flushed to disk yet */ return; } + entry->is_modified = 0; entry->is_deleted = 1; /* we need to "flush" the stream to save the newly deleted file on disk */ phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } } else { @@ -3415,6 +3649,7 @@ PHP_METHOD(Phar, addEmptyDir) { char *dirname; int dirname_len; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &dirname, &dirname_len) == FAILURE) { @@ -3425,6 +3660,7 @@ PHP_METHOD(Phar, addEmptyDir) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot create a directory in magic \".phar\" directory"); return; } + phar_mkdir(phar_obj->arc.archive, dirname, dirname_len TSRMLS_CC); } /* }}} */ @@ -3438,6 +3674,7 @@ PHP_METHOD(Phar, addFile) int fname_len, localname_len = 0; php_stream *resource; zval *zresource; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &fname, &fname_len, &localname, &localname_len) == FAILURE) { @@ -3460,6 +3697,7 @@ PHP_METHOD(Phar, addFile) zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "phar error: unable to open file \"%s\" to add to phar archive", fname); return; } + if (localname) { fname = localname; fname_len = localname_len; @@ -3480,6 +3718,7 @@ PHP_METHOD(Phar, addFromString) { char *localname, *cont_str; int localname_len, cont_len; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &localname, &localname_len, &cont_str, &cont_len) == FAILURE) { @@ -3500,6 +3739,7 @@ PHP_METHOD(Phar, getStub) php_stream *fp; php_stream_filter *filter = NULL; phar_entry_info *stub; + PHAR_ARCHIVE_OBJECT(); if (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) { @@ -3555,6 +3795,7 @@ PHP_METHOD(Phar, getStub) php_stream_rewind(fp); carry_on: buf = safe_emalloc(len, 1, 1); + if (len != php_stream_read(fp, buf, len)) { if (fp != phar_obj->arc.archive->fp) { php_stream_close(fp); @@ -3564,15 +3805,17 @@ carry_on: efree(buf); return; } + if (filter) { php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); } + if (fp != phar_obj->arc.archive->fp) { php_stream_close(fp); } - buf[len] = '\0'; + buf[len] = '\0'; RETURN_STRINGL(buf, len, 0); } /* }}}*/ @@ -3608,6 +3851,7 @@ PHP_METHOD(Phar, setMetadata) { char *error; zval *metadata; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -3627,8 +3871,8 @@ PHP_METHOD(Phar, setMetadata) MAKE_STD_ZVAL(phar_obj->arc.archive->metadata); ZVAL_ZVAL(phar_obj->arc.archive->metadata, metadata, 1, 0); phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3642,6 +3886,7 @@ PHP_METHOD(Phar, setMetadata) PHP_METHOD(Phar, delMetadata) { char *error; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -3653,8 +3898,8 @@ PHP_METHOD(Phar, delMetadata) zval_ptr_dtor(&phar_obj->arc.archive->metadata); phar_obj->arc.archive->metadata = NULL; phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3662,6 +3907,7 @@ PHP_METHOD(Phar, delMetadata) } else { RETURN_TRUE; } + } else { RETURN_TRUE; } @@ -3670,12 +3916,12 @@ PHP_METHOD(Phar, delMetadata) #if (PHP_MAJOR_VERSION < 6) #define OPENBASEDIR_CHECKPATH(filename) \ (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC) -#else +#else #define OPENBASEDIR_CHECKPATH(filename) \ php_check_open_basedir(filename TSRMLS_CC) #endif -static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *dest, int dest_len, char **error TSRMLS_DC) +static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *dest, int dest_len, char **error TSRMLS_DC) /* {{{ */ { php_stream_statbuf ssb; int len; @@ -3687,10 +3933,13 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * /* silently ignore mounted entries */ return SUCCESS; } + if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) { return SUCCESS; } + len = spprintf(&fullpath, 0, "%s/%s", dest, entry->filename); + if (len >= MAXPATHLEN) { char *tmp; /* truncate for error message */ @@ -3705,6 +3954,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * efree(fullpath); return FAILURE; } + if (!len) { spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename); efree(fullpath); @@ -3723,13 +3973,16 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * efree(fullpath); return FAILURE; } + /* perform dirname */ - slash = strrchr(entry->filename, '/'); + slash = zend_memrchr(entry->filename, '/', entry->filename_len); + if (slash) { fullpath[dest_len + (slash - entry->filename) + 1] = '\0'; } else { fullpath[dest_len] = '\0'; } + if (FAILURE == php_stream_stat_path(fullpath, &ssb)) { if (entry->is_dir) { if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK, PHP_STREAM_MKDIR_RECURSIVE, NULL)) { @@ -3745,6 +3998,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * } } } + if (slash) { fullpath[dest_len + (slash - entry->filename) + 1] = '/'; } else { @@ -3804,6 +4058,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * efree(fullpath); return SUCCESS; } +/* }}} */ /* {{{ proto bool Phar::extractTo(string pathto[[, mixed files], bool overwrite]) * Extract one or more file from a phar archive, optionally overwriting existing files @@ -3811,25 +4066,39 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * PHP_METHOD(Phar, extractTo) { char *error = NULL; + php_stream *fp; php_stream_statbuf ssb; phar_entry_info *entry; - char *pathto, *filename; + char *pathto, *filename, *actual; int pathto_len, filename_len; int ret, i; int nelems; zval *zval_files = NULL; zend_bool overwrite = 0; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!b", &pathto, &pathto_len, &zval_files, &overwrite) == FAILURE) { return; } + fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual); + + if (!fp) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, + "Invalid argument, %s cannot be found", phar_obj->arc.archive->fname); + return; + } + + efree(actual); + php_stream_close(fp); + if (pathto_len < 1) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Invalid argument, extraction path must be non-zero length"); return; } + if (pathto_len >= MAXPATHLEN) { char *tmp = estrndup(pathto, 50); /* truncate for error message */ @@ -3903,11 +4172,13 @@ PHP_METHOD(Phar, extractTo) "Invalid argument, expected a filename (string) or array of filenames"); return; } + if (FAILURE == zend_hash_find(&phar_obj->arc.archive->manifest, filename, filename_len, (void **)&entry)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Phar Error: attempted to extract non-existent file \"%s\" from phar \"%s\"", filename, phar_obj->arc.archive->fname); return; } + if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Extraction from phar \"%s\" failed: %s", phar_obj->arc.archive->fname, error); @@ -3916,7 +4187,6 @@ PHP_METHOD(Phar, extractTo) } } else { phar_archive_data *phar = phar_obj->arc.archive; - all_files: /* Extract all files */ if (!zend_hash_num_elements(&(phar->manifest))) { @@ -3927,9 +4197,11 @@ all_files: zend_hash_has_more_elements(&phar->manifest) == SUCCESS; zend_hash_move_forward(&phar->manifest)) { phar_entry_info *entry; + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } + if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Extraction from phar \"%s\" failed: %s", phar->fname, error); @@ -3950,15 +4222,15 @@ PHP_METHOD(PharFileInfo, __construct) { char *fname, *arch, *entry, *error; int fname_len, arch_len, entry_len; - phar_entry_object *entry_obj; - phar_entry_info *entry_info; + phar_entry_object *entry_obj; + phar_entry_info *entry_info; phar_archive_data *phar_data; zval *zobj = getThis(), arg1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { return; } - + entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC); if (entry_obj->ent.entry) { @@ -3988,7 +4260,7 @@ PHP_METHOD(PharFileInfo, __construct) if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, 1 TSRMLS_CC)) == NULL) { zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, - "Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error?", ":"", error?error:""); + "Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : ""); efree(arch); efree(entry); return; @@ -4027,6 +4299,7 @@ PHP_METHOD(PharFileInfo, __destruct) efree(entry_obj->ent.entry->filename); entry_obj->ent.entry->filename = NULL; } + efree(entry_obj->ent.entry); entry_obj->ent.entry = NULL; } @@ -4083,6 +4356,7 @@ PHP_METHOD(PharFileInfo, getCRC32) "Phar entry is a directory, does not have a CRC"); \ return; } + if (entry_obj->ent.entry->is_crc_checked) { RETURN_LONG(entry_obj->ent.entry->crc32); } else { @@ -4098,7 +4372,7 @@ PHP_METHOD(PharFileInfo, getCRC32) PHP_METHOD(PharFileInfo, isCRCChecked) { PHAR_ENTRY_OBJECT(); - + RETURN_BOOL(entry_obj->ent.entry->is_crc_checked); } /* }}} */ @@ -4109,7 +4383,7 @@ PHP_METHOD(PharFileInfo, isCRCChecked) PHP_METHOD(PharFileInfo, getPharFlags) { PHAR_ENTRY_OBJECT(); - + RETURN_LONG(entry_obj->ent.entry->flags & ~(PHAR_ENT_PERM_MASK|PHAR_ENT_COMPRESSION_MASK)); } /* }}} */ @@ -4128,32 +4402,38 @@ PHP_METHOD(PharFileInfo, chmod) "Phar entry \"%s\" is a temporary directory (not an actual entry in the archive), cannot chmod", entry_obj->ent.entry->filename); \ return; } + if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Cannot modify permissions for file \"%s\" in phar \"%s\", write operations are prohibited", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); return; } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perms) == FAILURE) { return; - } - /* clear permissions */ + } + + /* clear permissions */ entry_obj->ent.entry->flags &= ~PHAR_ENT_PERM_MASK; perms &= 0777; entry_obj->ent.entry->flags |= perms; entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags; entry_obj->ent.entry->phar->is_modified = 1; entry_obj->ent.entry->is_modified = 1; + /* hackish cache in php_stat needs to be cleared */ /* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */ if (BG(CurrentLStatFile)) { efree(BG(CurrentLStatFile)); } + if (BG(CurrentStatFile)) { efree(BG(CurrentStatFile)); } + BG(CurrentLStatFile) = NULL; BG(CurrentStatFile) = NULL; - phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4167,7 +4447,7 @@ PHP_METHOD(PharFileInfo, chmod) PHP_METHOD(PharFileInfo, hasMetadata) { PHAR_ENTRY_OBJECT(); - + RETURN_BOOL(entry_obj->ent.entry->metadata != NULL); } /* }}} */ @@ -4178,7 +4458,7 @@ PHP_METHOD(PharFileInfo, hasMetadata) PHP_METHOD(PharFileInfo, getMetadata) { PHAR_ENTRY_OBJECT(); - + if (entry_obj->ent.entry->metadata) { RETURN_ZVAL(entry_obj->ent.entry->metadata, 1, 0); } @@ -4192,17 +4472,20 @@ PHP_METHOD(PharFileInfo, setMetadata) { char *error; zval *metadata; + PHAR_ENTRY_OBJECT(); if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by phar.readonly INI setting"); return; } + if (entry_obj->ent.entry->is_temp_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ "Phar entry is a temporary directory (not an actual entry in the archive), cannot set metadata"); \ return; } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &metadata) == FAILURE) { return; } @@ -4218,6 +4501,7 @@ PHP_METHOD(PharFileInfo, setMetadata) entry_obj->ent.entry->is_modified = 1; entry_obj->ent.entry->phar->is_modified = 1; phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4231,17 +4515,20 @@ PHP_METHOD(PharFileInfo, setMetadata) PHP_METHOD(PharFileInfo, delMetadata) { char *error; + PHAR_ENTRY_OBJECT(); if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by phar.readonly INI setting"); return; } + if (entry_obj->ent.entry->is_temp_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ "Phar entry is a temporary directory (not an actual entry in the archive), cannot delete metadata"); \ return; } + if (entry_obj->ent.entry->metadata) { zval_ptr_dtor(&entry_obj->ent.entry->metadata); entry_obj->ent.entry->metadata = NULL; @@ -4249,6 +4536,7 @@ PHP_METHOD(PharFileInfo, delMetadata) entry_obj->ent.entry->phar->is_modified = 1; phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4256,6 +4544,7 @@ PHP_METHOD(PharFileInfo, delMetadata) } else { RETURN_TRUE; } + } else { RETURN_TRUE; } @@ -4270,6 +4559,7 @@ PHP_METHOD(PharFileInfo, getContent) char *error; php_stream *fp; phar_entry_info *link; + PHAR_ENTRY_OBJECT(); if (entry_obj->ent.entry->is_dir) { @@ -4277,24 +4567,30 @@ PHP_METHOD(PharFileInfo, getContent) "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\" is a directory", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); return; } + link = phar_get_link_source(entry_obj->ent.entry TSRMLS_CC); + if (!link) { link = entry_obj->ent.entry; } + if (SUCCESS != phar_open_entry_fp(link, &error, 0 TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\": %s", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname, error); efree(error); return; } + if (!(fp = phar_get_efp(link, 0 TSRMLS_CC))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar error: Cannot retrieve contents of \"%s\" in phar \"%s\"", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); return; } + phar_seek_efp(link, 0, SEEK_SET, 0, 0 TSRMLS_CC); Z_TYPE_P(return_value) = IS_STRING; Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0); + if (!Z_STRVAL_P(return_value)) { Z_STRVAL_P(return_value) = estrndup("", 0); } @@ -4319,16 +4615,19 @@ PHP_METHOD(PharFileInfo, compress) "Cannot compress with Gzip compression, not possible with tar-based phar archives"); return; } + if (entry_obj->ent.entry->is_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ "Phar entry is a directory, cannot set compression"); \ return; } + if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar is readonly, cannot change compression"); return; } + if (entry_obj->ent.entry->is_deleted) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress deleted file"); @@ -4341,12 +4640,14 @@ PHP_METHOD(PharFileInfo, compress) RETURN_TRUE; return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0) { if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with gzip compression, file is already compressed with bzip2 compression and bz2 extension is not enabled, cannot decompress"); return; } + /* decompress this file indirectly */ if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -4355,11 +4656,13 @@ PHP_METHOD(PharFileInfo, compress) return; } } + if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with gzip compression, zlib extension is not enabled"); return; } + entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags; entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK; entry_obj->ent.entry->flags |= PHAR_ENT_COMPRESSED_GZ; @@ -4369,12 +4672,14 @@ PHP_METHOD(PharFileInfo, compress) RETURN_TRUE; return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0) { if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with bzip2 compression, file is already compressed with gzip compression and zlib extension is not enabled, cannot decompress"); return; } + /* decompress this file indirectly */ if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -4383,6 +4688,7 @@ PHP_METHOD(PharFileInfo, compress) return; } } + if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with bzip2 compression, bz2 extension is not enabled"); @@ -4399,12 +4705,13 @@ PHP_METHOD(PharFileInfo, compress) entry_obj->ent.entry->phar->is_modified = 1; entry_obj->ent.entry->is_modified = 1; - phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } /* }}} */ @@ -4422,30 +4729,36 @@ PHP_METHOD(PharFileInfo, decompress) "Phar entry is a directory, cannot set compression"); \ return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSION_MASK) == 0) { RETURN_TRUE; return; } + if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar is readonly, cannot decompress"); return; } + if (entry_obj->ent.entry->is_deleted) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress deleted file"); return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0 && !PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress Gzip-compressed file, zlib extension is not enabled"); return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0 && !PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress Bzip2-compressed file, bz2 extension is not enabled"); return; } + if (!entry_obj->ent.entry->fp) { if (FAILURE == phar_open_archive_fp(entry_obj->ent.entry->phar TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress entry \"%s\", phar error: Cannot open phar archive \"%s\" for reading", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); @@ -4453,12 +4766,13 @@ PHP_METHOD(PharFileInfo, decompress) } entry_obj->ent.entry->fp_type = PHAR_FP; } + entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags; entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK; entry_obj->ent.entry->phar->is_modified = 1; entry_obj->ent.entry->is_modified = 1; - phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4604,6 +4918,7 @@ ZEND_END_ARG_INFO(); static ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setSigAlgo, 0, 0, 1) ZEND_ARG_INFO(0, algorithm) + ZEND_ARG_INFO(0, privatekey) ZEND_END_ARG_INFO(); static @@ -4792,7 +5107,7 @@ void phar_object_init(TSRMLS_D) /* {{{ */ REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "PHP", PHAR_MIME_PHP) REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "PHPS", PHAR_MIME_PHPS) REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "MD5", PHAR_SIG_MD5) - REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "PGP", PHAR_SIG_PGP) + REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "OPENSSL", PHAR_SIG_OPENSSL) REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "SHA1", PHAR_SIG_SHA1) REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "SHA256", PHAR_SIG_SHA256) REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "SHA512", PHAR_SIG_SHA512) diff --git a/ext/phar/phar_path_check.c b/ext/phar/phar_path_check.c index c1a273281b..480eed717b 100755 --- a/ext/phar/phar_path_check.c +++ b/ext/phar/phar_path_check.c @@ -4,7 +4,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2007 The PHP Group | + | 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 | diff --git a/ext/phar/phar_path_check.re b/ext/phar/phar_path_check.re index 97a6b5d565..9e89151fff 100755 --- a/ext/phar/phar_path_check.re +++ b/ext/phar/phar_path_check.re @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2007 The PHP Group | + | 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 | diff --git a/ext/phar/php_phar.h b/ext/phar/php_phar.h index 89023972f6..f0a34a2e01 100644 --- a/ext/phar/php_phar.h +++ b/ext/phar/php_phar.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2005 The PHP Group | + | Copyright (c) 2005-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -22,7 +22,7 @@ #ifndef PHP_PHAR_H #define PHP_PHAR_H -#define PHP_PHAR_VERSION "2.0.0b2-dev" +#define PHP_PHAR_VERSION "2.0.0b2-dev" #include "ext/standard/basic_functions.h" extern zend_module_entry phar_module_entry; @@ -34,7 +34,7 @@ extern zend_module_entry phar_module_entry; #define PHP_PHAR_API #endif -#endif /* PHP_PHAR_H */ +#endif /* PHP_PHAR_H */ /* diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 32add212d1..7aecd8fb37 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -35,22 +35,22 @@ php_stream_ops phar_ops = { }; php_stream_wrapper_ops phar_stream_wops = { - phar_wrapper_open_url, - NULL, /* phar_wrapper_close */ - NULL, /* phar_wrapper_stat, */ - phar_wrapper_stat, /* stat_url */ - phar_wrapper_open_dir, /* opendir */ - "phar", - phar_wrapper_unlink, /* unlink */ - phar_wrapper_rename, /* rename */ - phar_wrapper_mkdir, /* create directory */ - phar_wrapper_rmdir, /* remove directory */ + phar_wrapper_open_url, + NULL, /* phar_wrapper_close */ + NULL, /* phar_wrapper_stat, */ + phar_wrapper_stat, /* stat_url */ + phar_wrapper_open_dir, /* opendir */ + "phar", + phar_wrapper_unlink, /* unlink */ + phar_wrapper_rename, /* rename */ + phar_wrapper_mkdir, /* create directory */ + phar_wrapper_rmdir, /* remove directory */ }; -php_stream_wrapper php_stream_phar_wrapper = { - &phar_stream_wops, - NULL, - 0 /* is_url */ +php_stream_wrapper php_stream_phar_wrapper = { + &phar_stream_wops, + NULL, + 0 /* is_url */ }; /** @@ -69,8 +69,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, if (!(options & PHP_STREAM_URL_STAT_QUIET)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported"); } - return NULL; - } + return NULL; + } if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0) TSRMLS_CC) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { @@ -101,7 +101,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, } #endif if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { - phar_archive_data **pphar = NULL; + phar_archive_data **pphar = NULL, *phar; if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **)&pphar)) { pphar = NULL; @@ -113,7 +113,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, php_url_free(resource); return NULL; } - if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, 0, options, NULL, &error TSRMLS_CC) == FAILURE) + if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, 0, options, &phar, &error TSRMLS_CC) == FAILURE) { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { @@ -124,6 +124,17 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, php_url_free(resource); return NULL; } + if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { + if (error) { + spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", resource->host); + if (!(options & PHP_STREAM_URL_STAT_QUIET)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + } + efree(error); + } + php_url_free(resource); + return NULL; + } } else { if (phar_open_from_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE) { @@ -136,7 +147,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, php_url_free(resource); return NULL; } - } + } return resource; } /* }}} */ @@ -146,6 +157,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, */ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ { + phar_archive_data *phar; phar_entry_data *idata; char *internal_file; char *error; @@ -223,8 +235,55 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat } return fpf; } else { + if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) { + /* retrieve the stub */ + if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, NULL TSRMLS_CC)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "file %s is not a valid phar archive"); + efree(internal_file); + php_url_free(resource); + return NULL; + } + if (phar->is_tar || phar->is_zip) { + if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, ".phar/stub.php", sizeof(".phar/stub.php")-1, "r", 0, &error, 0 TSRMLS_CC)) || !idata) { + goto idata_error; + } + efree(internal_file); + if (opened_path) { + spprintf(opened_path, MAXPATHLEN, "%s", phar->fname); + } + php_url_free(resource); + goto phar_stub; + } else { + phar_entry_info *entry; + + entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info)); + entry->is_temp_dir = 1; + entry->filename = ""; + entry->filename_len = 0; + entry->phar = phar; + entry->offset = entry->offset_abs = 0; + entry->compressed_filesize = entry->uncompressed_filesize = phar->halt_offset; + entry->is_crc_checked = 1; + + idata = (phar_entry_data *) ecalloc(1, sizeof(phar_entry_data)); + idata->fp = phar_get_pharfp(phar TSRMLS_CC); + idata->phar = phar; + idata->internal_file = entry; + if (!phar->is_persistent) { + ++(entry->phar->refcount); + } + ++(entry->fp_refcount); + php_url_free(resource); + if (opened_path) { + spprintf(opened_path, MAXPATHLEN, "%s", phar->fname); + } + efree(internal_file); + goto phar_stub; + } + } /* read-only access is allowed to magic files in .phar directory */ if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", 0, &error, 0 TSRMLS_CC)) || !idata) { +idata_error: if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); efree(error); @@ -237,7 +296,6 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat } } php_url_free(resource); - #if MBO_0 fprintf(stderr, "Pharname: %s\n", idata->phar->filename); fprintf(stderr, "Filename: %s\n", internal_file); @@ -272,11 +330,12 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat PHAR_G(cwd) = NULL; } } - fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); if (opened_path) { spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename); } efree(internal_file); +phar_stub: + fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); return fpf; } /* }}} */ @@ -286,8 +345,15 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *pat */ static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ { + phar_entry_info *entry = ((phar_entry_data *)stream->abstract)->internal_file; + int is_temp_dir = entry->is_temp_dir; + phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC); + if (is_temp_dir) { + /* phar archive stub, free it */ + efree(entry); + } return 0; } /* }}} */ @@ -299,7 +365,7 @@ static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRML { phar_entry_data *data = (phar_entry_data *)stream->abstract; size_t got; - + if (data->internal_file->is_deleted) { stream->eof = 1; return 0; @@ -312,7 +378,6 @@ static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRML data->position = php_stream_tell(data->fp) - data->zero; stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize); - return got; } /* }}} */ @@ -402,8 +467,6 @@ static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */ void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_temp_dir, char *alias, int alias_len TSRMLS_DC) { - char *tmp; - int tmp_len; memset(ssb, 0, sizeof(php_stream_statbuf)); if (!is_temp_dir && !data->is_dir) { @@ -454,23 +517,12 @@ void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_stat ssb->sb.st_nlink = 1; ssb->sb.st_rdev = -1; - if (data) { - tmp_len = data->filename_len + alias_len; - } else { - tmp_len = alias_len + 1; - } - tmp = (char *) emalloc(tmp_len); - memcpy(tmp, alias, alias_len); - if (data) { - memcpy(tmp + alias_len, data->filename, data->filename_len); - } else { - *(tmp+alias_len) = '/'; - } /* this is only for APC, so use /dev/null device - no chance of conflict there! */ ssb->sb.st_dev = 0xc; /* generate unique inode number for alias/filename, so no phars will conflict */ - ssb->sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len); - efree(tmp); + if (!is_temp_dir) { + ssb->sb.st_ino = data->inode; + } #ifndef PHP_WIN32 ssb->sb.st_blksize = -1; ssb->sb.st_blocks = -1; @@ -502,10 +554,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */ { php_url *resource = NULL; - phar_zstr key; - char *internal_file, *error, *str_key; - uint keylen; - ulong unused; + char *internal_file, *error; phar_archive_data *phar; phar_entry_info *entry; uint host_len; @@ -557,76 +606,62 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC); php_url_free(resource); return SUCCESS; - } else { - /* search for directory (partial match of a file) */ - zend_hash_internal_pointer_reset(&phar->manifest); - while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) { - if (HASH_KEY_NON_EXISTANT != - zend_hash_get_current_key_ex( - &phar->manifest, &key, &keylen, &unused, 0, NULL)) { - PHAR_STR(key, str_key); - if (keylen >= (uint)internal_file_len && 0 == memcmp(internal_file, str_key, internal_file_len)) { - /* directory found, all dirs have the same stat */ - if (str_key[internal_file_len] == '/') { - phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); - php_url_free(resource); - return SUCCESS; - } - } - } - if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { + } + if (zend_hash_exists(&(phar->virtual_dirs), internal_file, internal_file_len)) { + phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); + php_url_free(resource); + return SUCCESS; + } + /* check for mounted directories */ + if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) { + phar_zstr key; + char *str_key; + ulong unused; + uint keylen; + HashPosition pos; + + zend_hash_internal_pointer_reset_ex(&phar->mounted_dirs, &pos); + while (FAILURE != zend_hash_has_more_elements_ex(&phar->mounted_dirs, &pos)) { + if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &keylen, &unused, 0, &pos)) { break; } - } - /* check for mounted directories */ - if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) { - phar_zstr key; - char *str_key; - ulong unused; - uint keylen; - - zend_hash_internal_pointer_reset(&phar->mounted_dirs); - while (FAILURE != zend_hash_has_more_elements(&phar->mounted_dirs)) { - if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &keylen, &unused, 0, NULL)) { - break; + PHAR_STR(key, str_key); + if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) { + zend_hash_move_forward_ex(&phar->mounted_dirs, &pos); + continue; + } else { + char *test; + int test_len; + phar_entry_info *entry; + php_stream_statbuf ssbi; + + if (SUCCESS != zend_hash_find(&phar->manifest, str_key, keylen, (void **) &entry)) { + goto free_resource; } - PHAR_STR(key, str_key); - if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) { + if (!entry->tmp || !entry->is_mounted) { + goto free_resource; + } + test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen); + if (SUCCESS != php_stream_stat_path(test, &ssbi)) { + efree(test); + zend_hash_move_forward_ex(&phar->mounted_dirs, &pos); continue; - } else { - char *test; - int test_len; - phar_entry_info *entry; - php_stream_statbuf ssbi; - - if (SUCCESS != zend_hash_find(&phar->manifest, str_key, keylen, (void **) &entry)) { - goto free_resource; - } - if (!entry->tmp || !entry->is_mounted) { - goto free_resource; - } - test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen); - if (SUCCESS != php_stream_stat_path(test, &ssbi)) { - efree(test); - continue; - } - /* mount the file/directory just in time */ - if (SUCCESS != phar_mount_entry(phar, test, test_len, internal_file, internal_file_len TSRMLS_CC)) { - efree(test); - goto free_resource; - } + } + /* mount the file/directory just in time */ + if (SUCCESS != phar_mount_entry(phar, test, test_len, internal_file, internal_file_len TSRMLS_CC)) { efree(test); - if (SUCCESS != zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) { - goto free_resource; - } - phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC); - php_url_free(resource); - return SUCCESS; + goto free_resource; + } + efree(test); + if (SUCCESS != zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) { + goto free_resource; } + phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC); + php_url_free(resource); + return SUCCESS; } } } - free_resource: php_url_free(resource); return FAILURE; @@ -692,7 +727,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio efree(error); } if (idata->internal_file->fp_refcount > 1) { - /* more than just our fp resource is open for this file */ + /* more than just our fp resource is open for this file */ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host); efree(internal_file); php_url_free(resource); @@ -717,6 +752,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char phar_archive_data *phar, *pfrom, *pto; phar_entry_info *entry; uint host_len; + int is_dir = 0; + int is_modified = 0; error = NULL; @@ -767,7 +804,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); return 0; } - + if (!resource_to->scheme || !resource_to->host || !resource_to->path) { php_url_free(resource_from); php_url_free(resource_to); @@ -790,7 +827,6 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char } host_len = strlen(resource_from->host); - phar_request_initialize(TSRMLS_C); if (SUCCESS != phar_get_archive(&phar, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) { php_url_free(resource_from); @@ -800,6 +836,13 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char return 0; } + if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { + php_url_free(resource_from); + php_url_free(resource_to); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to); + return 0; + } + if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) { phar_entry_info new, *source; @@ -831,20 +874,122 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename)); return 0; } + is_modified = 1; entry->is_modified = 1; entry->filename_len = strlen(entry->filename); + is_dir = entry->is_dir; + } else { + is_dir = zend_hash_exists(&(phar->virtual_dirs), resource_from->path+1, strlen(resource_from->path)-1); + } + + /* Rename directory. Update all nested paths */ + if (is_dir) { + int key_type; + phar_zstr key, new_key; + char *str_key, *new_str_key; + uint key_len, new_key_len; + ulong unused; + uint from_len = strlen(resource_from->path+1); + uint to_len = strlen(resource_to->path+1); + + for (zend_hash_internal_pointer_reset(&phar->manifest); + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)) && + SUCCESS == zend_hash_get_current_data(&phar->manifest, (void **) &entry); + zend_hash_move_forward(&phar->manifest)) { + + PHAR_STR(key, str_key); + + if (!entry->is_deleted && + key_len > from_len && + memcmp(str_key, resource_from->path+1, from_len) == 0 && + IS_SLASH(str_key[from_len])) { + + new_key_len = key_len + to_len - from_len; + new_str_key = emalloc(new_key_len+1); + memcpy(new_str_key, resource_to->path + 1, to_len); + memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len); + new_str_key[new_key_len] = 0; + is_modified = 1; + entry->is_modified = 1; + efree(entry->filename); + entry->filename = new_str_key; + entry->filename_len = new_key_len; + + PHAR_ZSTR(new_str_key, new_key); + zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, NULL); + efree(new_str_key); + } + } + + for (zend_hash_internal_pointer_reset(&phar->virtual_dirs); + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL)); + zend_hash_move_forward(&phar->virtual_dirs)) { + + PHAR_STR(key, str_key); + + if (key_len >= from_len && + memcmp(str_key, resource_from->path+1, from_len) == 0 && + (key_len == from_len || IS_SLASH(str_key[from_len]))) { + + new_key_len = key_len + to_len - from_len; + new_str_key = emalloc(new_key_len+1); + memcpy(new_str_key, resource_to->path + 1, to_len); + memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len); + new_str_key[new_key_len] = 0; + + PHAR_ZSTR(new_str_key, new_key); + zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, NULL); + efree(new_str_key); + } + } + + for (zend_hash_internal_pointer_reset(&phar->mounted_dirs); + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &key_len, &unused, 0, NULL)) && + SUCCESS == zend_hash_get_current_data(&phar->mounted_dirs, (void **) &entry); + zend_hash_move_forward(&phar->mounted_dirs)) { + + PHAR_STR(key, str_key); + + if (key_len >= from_len && + memcmp(str_key, resource_from->path+1, from_len) == 0 && + (key_len == from_len || IS_SLASH(str_key[from_len]))) { + + new_key_len = key_len + to_len - from_len; + new_str_key = emalloc(new_key_len+1); + memcpy(new_str_key, resource_to->path + 1, to_len); + memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len); + new_str_key[new_key_len] = 0; + + PHAR_ZSTR(new_str_key, new_key); + zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, NULL); + efree(new_str_key); + } + } + } + + if (is_modified) { phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); if (error) { php_url_free(resource_from); php_url_free(resource_to); php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); - zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename)); return 0; } } + php_url_free(resource_from); php_url_free(resource_to); + return 1; } /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/stream.h b/ext/phar/stream.h index 49f6b612f1..c69202bfcf 100644 --- a/ext/phar/stream.h +++ b/ext/phar/stream.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-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 | diff --git a/ext/phar/tar.c b/ext/phar/tar.c index d53b077629..e5159dab92 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -27,19 +27,19 @@ static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */ while (i < len && buf[i] == ' ') { ++i; } - while (i < len && - buf[i] >= '0' && - buf[i] <= '7') { + + while (i < len && buf[i] >= '0' && buf[i] <= '7') { num = num * 8 + (buf[i] - '0'); ++i; } + return num; } /* }}} */ /* adapted from format_octal() in libarchive * - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -97,7 +97,7 @@ static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */ } /* }}} */ -int phar_is_tar(char *buf, char *fname) +int phar_is_tar(char *buf, char *fname) /* {{{ */ { tar_header *header = (tar_header *) buf; php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum)); @@ -119,6 +119,7 @@ int phar_is_tar(char *buf, char *fname) } return ret; } +/* }}} */ int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ { @@ -152,8 +153,9 @@ int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_l } return FAILURE; } +/* }}} */ -int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) +int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */ { char *metadata; size_t save = php_stream_tell(fp), read; @@ -174,6 +176,7 @@ int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) php_stream_seek(fp, save, SEEK_SET); return FAILURE; } + if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) { entry->phar->metadata = entry->metadata; entry->metadata = NULL; @@ -182,10 +185,12 @@ int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) mentry->metadata = entry->metadata; entry->metadata = NULL; } + efree(metadata); php_stream_seek(fp, save, SEEK_SET); return SUCCESS; } +/* }}} */ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ { @@ -204,6 +209,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, totalsize = php_stream_tell(fp); php_stream_seek(fp, 0, SEEK_SET); read = php_stream_read(fp, buf, sizeof(buf)); + if (read != sizeof(buf)) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname); @@ -211,14 +217,19 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, php_stream_close(fp); return FAILURE; } + hdr = (tar_header*)buf; old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0); - myphar = (phar_archive_data *) ecalloc(1, sizeof(phar_archive_data)); - zend_hash_init(&myphar->manifest, sizeof(phar_entry_info), - zend_get_hash_value, destroy_phar_manifest_entry, 0); - zend_hash_init(&myphar->mounted_dirs, sizeof(char *), - zend_get_hash_value, NULL, 0); + myphar = (phar_archive_data *) pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist)); + myphar->is_persistent = PHAR_G(persist); + /* estimate number of entries, can't be certain with tar files */ + zend_hash_init(&myphar->manifest, 2 + (totalsize >> 12), + zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)myphar->is_persistent); + zend_hash_init(&myphar->mounted_dirs, 5, + zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent); + zend_hash_init(&myphar->virtual_dirs, 4 + (totalsize >> 11), + zend_get_hash_value, NULL, (zend_bool)myphar->is_persistent); myphar->is_tar = 1; /* remember whether this entire phar was compressed with gz/bzip2 */ myphar->flags = compression; @@ -227,6 +238,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, entry.is_crc_checked = 1; entry.phar = myphar; pos += sizeof(buf); + do { phar_entry_info *newentry; @@ -242,6 +254,81 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, size = entry.uncompressed_filesize = entry.compressed_filesize = phar_tar_number(hdr->size, sizeof(hdr->size)); + if (((!old && hdr->prefix[0] == 0) || old) && strlen(hdr->name) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { + size_t read; + if (size > 511) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname); + } +bail: + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + read = php_stream_read(fp, buf, size); + if (read != size) { + if (error) { + spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname); + } + goto bail; + } +#ifdef WORDS_BIGENDIAN +# define PHAR_GET_32(buffer) \ + (((((unsigned char*)(buffer))[3]) << 24) \ + | ((((unsigned char*)(buffer))[2]) << 16) \ + | ((((unsigned char*)(buffer))[1]) << 8) \ + | (((unsigned char*)(buffer))[0])) +#else +# define PHAR_GET_32(buffer) (php_uint32) *(buffer) +#endif + if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, PHAR_GET_32(buf), buf + 8, PHAR_GET_32(buf + 4), fname, &myphar->signature, &myphar->sig_len, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save); + efree(save); + } + goto bail; + } + /* signature checked out, let's ensure this is the last file in the phar */ + size = ((size+511)&~511) + 512; + if (((hdr->typeflag == 0) || (hdr->typeflag == TAR_FILE)) && size > 0) { + /* this is not good enough - seek succeeds even on truncated tars */ + php_stream_seek(fp, size, SEEK_CUR); + if ((uint)php_stream_tell(fp) > totalsize) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + } + + read = php_stream_read(fp, buf, sizeof(buf)); + + if (read != sizeof(buf)) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); + } + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + return FAILURE; + } + + hdr = (tar_header*) buf; + sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum)); + + if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) { + break; + } + + if (error) { + spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname); + } + + goto bail; + } + if (!old && hdr->prefix[0] != 0) { char name[256]; @@ -252,32 +339,34 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, } else { strcat(name, hdr->name); } + entry.filename_len = strlen(hdr->prefix) + 100; + if (name[entry.filename_len - 1] == '/') { /* some tar programs store directories with trailing slash */ entry.filename_len--; } - entry.filename = estrndup(name, entry.filename_len); + entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent); } else { - entry.filename = estrdup(hdr->name); + entry.filename = pestrdup(hdr->name, myphar->is_persistent); entry.filename_len = strlen(entry.filename); + if (entry.filename[entry.filename_len - 1] == '/') { /* some tar programs store directories with trailing slash */ entry.filename[entry.filename_len - 1] = '\0'; entry.filename_len--; } } + + phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC); + if (sum1 != sum2) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename); } - efree(entry.filename); + pefree(entry.filename, myphar->is_persistent); php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } @@ -286,13 +375,14 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, entry.fp_type = PHAR_FP; entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK; entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime)); - + entry.is_persistent = myphar->is_persistent; #ifndef S_ISDIR #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) #endif if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) { entry.tar_type = TAR_DIR; } + if (entry.tar_type == TAR_DIR) { entry.is_dir = 1; } else { @@ -300,39 +390,39 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, } entry.link = NULL; + if (entry.tar_type == TAR_LINK) { if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname); } - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } entry.link = estrdup(hdr->linkname); } else if (entry.tar_type == TAR_SYMLINK) { entry.link = estrdup(hdr->linkname); } + phar_set_inode(&entry TSRMLS_CC); zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry); + + if (entry.is_persistent) { + ++entry.manifest_pos; + } + if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) { if (FAILURE == phar_tar_process_metadata(newentry, fp TSRMLS_CC)) { if (error) { spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename); } php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } } + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { size_t read; /* found explicit alias */ @@ -341,14 +431,12 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname); } php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } + read = php_stream_read(fp, buf, size); + if (read == size) { buf[size] = '\0'; if (!phar_validate_alias(buf, size)) { @@ -358,18 +446,17 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, buf[52] = '.'; buf[53] = '\0'; } + if (error) { spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname); } + php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } - actual_alias = estrndup(buf, size); + + actual_alias = pestrndup(buf, size, myphar->is_persistent); myphar->alias = actual_alias; myphar->alias_len = size; php_stream_seek(fp, pos, SEEK_SET); @@ -377,16 +464,15 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, if (error) { spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname); } + php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } } + size = (size+511)&~511; + if (((hdr->typeflag == 0) || (hdr->typeflag == TAR_FILE)) && size > 0) { /* this is not good enough - seek succeeds even on truncated tars */ php_stream_seek(fp, size, SEEK_CUR); @@ -395,33 +481,39 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); } php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } } + read = php_stream_read(fp, buf, sizeof(buf)); + if (read != sizeof(buf)) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); } php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } } while (read != 0); - myphar->fname = estrndup(fname, fname_len); + + /* ensure signature set */ + if (PHAR_G(require_hash) && !myphar->signature) { + php_stream_close(fp); + phar_destroy_phar_data(myphar TSRMLS_CC); + if (error) { + spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname); + } + return FAILURE; + } + + myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent); #ifdef PHP_WIN32 phar_unixify_path_separators(myphar->fname, fname_len); #endif myphar->fname_len = fname_len; + myphar->fp = fp; p = strrchr(myphar->fname, '/'); if (zend_hash_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) { @@ -439,25 +531,25 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, myphar->ext_len = (myphar->fname + fname_len) - myphar->ext; } } - myphar->fp = fp; + phar_request_initialize(TSRMLS_C); + if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) { if (error) { spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname); } php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); + phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } + myphar = *actual; + if (actual_alias) { phar_archive_data **fd_ptr; myphar->is_temporary_alias = 0; + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) { if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) { if (error) { @@ -467,6 +559,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, return FAILURE; } } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); } else { phar_archive_data **fd_ptr; @@ -481,18 +574,21 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, return FAILURE; } } - zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); - myphar->alias = estrndup(alias, alias_len); + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); + myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent); myphar->alias_len = alias_len; } else { - myphar->alias = estrndup(myphar->fname, fname_len); + myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent); myphar->alias_len = fname_len; } + myphar->is_temporary_alias = 1; } + if (pphar) { *pphar = myphar; } + return SUCCESS; } /* }}} */ @@ -505,7 +601,7 @@ struct _phar_pass_tar_info { char **error; }; -int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) +int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ { tar_header header; size_t pos; @@ -516,6 +612,7 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) if (entry->is_mounted) { return ZEND_HASH_APPLY_KEEP; } + if (entry->is_deleted) { if (entry->fp_refcount <= 0) { return ZEND_HASH_APPLY_REMOVE; @@ -526,6 +623,7 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) } memset((char *) &header, 0, sizeof(header)); + if (entry->filename_len > 100) { if (entry->filename_len > 255) { if (fp->error) { @@ -538,28 +636,35 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) } else { memcpy(header.name, entry->filename, entry->filename_len); } + phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1); + if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file modification time of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + /* calc checksum */ header.typeflag = entry->tar_type; + if (entry->link) { strncpy(header.linkname, entry->link, strlen(entry->link)); } + strncpy(header.magic, "ustar", sizeof("ustar")-1); strncpy(header.version, "00", sizeof("00")-1); strncpy(header.checksum, " ", sizeof(" ")-1); entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header)); + if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, checksum of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); @@ -569,12 +674,14 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* write header */ entry->header_offset = php_stream_tell(fp->new); + if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + pos = php_stream_tell(fp->new); /* save start of file within tar */ /* write contents */ @@ -582,22 +689,25 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) { return ZEND_HASH_APPLY_STOP; } + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } - + memset(padding, 0, 512); php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize); } + if (!entry->is_modified && entry->fp_refcount) { /* open file pointers refer to this fp, do not free the stream */ switch (entry->fp_type) { @@ -612,48 +722,57 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) } entry->is_modified = 0; + if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) { if (!entry->fp_refcount) { php_stream_close(entry->fp); } entry->fp = NULL; } + entry->fp_type = PHAR_FP; /* note new location within tar */ entry->offset = entry->offset_abs = pos; return ZEND_HASH_APPLY_KEEP; } +/* }}} */ -int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error, php_stream *fp TSRMLS_DC) +int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error, php_stream *fp TSRMLS_DC) /* {{{ */ { 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, &metadata, &metadata_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(metadata_hash); entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len; + if (entry->fp && entry->fp_type == PHAR_MOD) { php_stream_close(entry->fp); } + entry->fp_type = PHAR_MOD; entry->is_modified = 1; entry->fp = php_stream_fopen_tmpfile(); entry->offset = entry->offset_abs = 0; + if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) { spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename); zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len); return ZEND_HASH_APPLY_STOP; } + return ZEND_HASH_APPLY_KEEP; } +/* }}} */ -int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) +int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */ { int lookfor_len; struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument; @@ -677,13 +796,16 @@ int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) if (!entry->is_modified) { return ZEND_HASH_APPLY_KEEP; } + /* now we are dealing with regular files, so look for metadata */ lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename); + if (!entry->metadata) { zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len); efree(lookfor); return ZEND_HASH_APPLY_KEEP; } + if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) { int ret; ret = phar_tar_setmetadata(entry->metadata, metadata, error, fp TSRMLS_CC); @@ -705,15 +827,16 @@ int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) return phar_tar_setmetadata(entry->metadata, metadata, error, fp TSRMLS_CC); } +/* }}} */ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */ { phar_entry_info entry = {0}; static const char newstub[] = "is_persistent) { + if (error) { + spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname); + } + return EOF; + } + if (phar->is_data) { goto nostub; } @@ -733,14 +863,16 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); entry.filename_len = sizeof(".phar/alias.txt")-1; entry.fp = php_stream_fopen_tmpfile(); - entry.crc32 = phar_tar_checksum(phar->alias, phar->alias_len); + if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) { if (error) { spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); } return EOF; } + entry.uncompressed_filesize = phar->alias_len; + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { if (error) { spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); @@ -778,8 +910,8 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } else { free_user_stub = 0; } - if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) - { + + if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) { if (error) { spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname); } @@ -788,6 +920,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + len = pos - user_stub + 18; entry.fp = php_stream_fopen_tmpfile(); entry.uncompressed_filesize = len + 5; @@ -803,9 +936,11 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau 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; zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + if (free_user_stub) { efree(user_stub); } @@ -850,9 +985,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } } } - nostub: - if (phar->fp && !phar->is_brandnew) { oldfile = phar->fp; closeoldfile = 0; @@ -861,7 +994,9 @@ nostub: oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL); closeoldfile = oldfile != NULL; } + newfile = php_stream_fopen_tmpfile(); + if (!newfile) { if (error) { spprintf(error, 0, "unable to create temporary file"); @@ -903,6 +1038,7 @@ nostub: } return EOF; } + if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error, oldfile TSRMLS_CC)) { zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1); if (closeoldfile) { @@ -912,12 +1048,14 @@ nostub: } } } + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC); if (error && *error) { if (closeoldfile) { php_stream_close(oldfile); } + /* on error in the hash iterator above, error is set */ php_stream_close(newfile); return EOF; @@ -925,6 +1063,67 @@ nostub: zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC); + /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ + if (!phar->is_data || phar->sig_flags) { + if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error TSRMLS_CC)) { + if (error) { + char *save = *error; + spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save); + efree(save); + } + + if (closeoldfile) { + php_stream_close(oldfile); + } + + php_stream_close(newfile); + return EOF; + } + + entry.filename = ".phar/signature.bin"; + entry.filename_len = sizeof(".phar/signature.bin")-1; + entry.fp = php_stream_fopen_tmpfile(); + +#ifdef WORDS_BIGENDIAN +# define PHAR_SET_32(var, buffer) \ + *(php_uint32 *)(var) = (((((unsigned char*)(buffer))[3]) << 24) \ + | ((((unsigned char*)(buffer))[2]) << 16) \ + | ((((unsigned char*)(buffer))[1]) << 8) \ + | (((unsigned char*)(buffer))[0])) +#else +# define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer) +#endif + PHAR_SET_32(sigbuf, phar->sig_flags); + PHAR_SET_32(sigbuf + 4, signature_length); + + if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) { + efree(signature); + if (error) { + spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname); + } + + if (closeoldfile) { + php_stream_close(oldfile); + } + php_stream_close(newfile); + return EOF; + } + + efree(signature); + entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8; + /* throw out return value and write the signature */ + entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC); + + if (error && *error) { + if (closeoldfile) { + php_stream_close(oldfile); + } + /* error is set by writeheaders */ + php_stream_close(newfile); + return EOF; + } + } /* signature */ + /* add final zero blocks */ buf = (char *) ecalloc(1024, 1); php_stream_write(newfile, buf, 1024); @@ -933,14 +1132,17 @@ nostub: if (closeoldfile) { php_stream_close(oldfile); } + /* on error in the hash iterator above, error is set */ if (error && *error) { php_stream_close(newfile); return EOF; } + if (phar->fp && pass.free_fp) { php_stream_close(phar->fp); } + if (phar->ufp) { if (pass.free_ufp) { php_stream_close(phar->ufp); @@ -949,7 +1151,6 @@ nostub: } phar->is_brandnew = 0; - php_stream_rewind(newfile); if (phar->donotflush) { @@ -964,6 +1165,7 @@ nostub: } return EOF; } + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { php_stream_filter *filter; /* to properly compress, we have to tell zlib to add a zlib header */ @@ -977,6 +1179,7 @@ nostub: add_assoc_long(&filterparams, "window", MAX_WBITS + 16); filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); zval_dtor(&filterparams); + if (!filter) { /* copy contents uncompressed rather than lose them */ php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); @@ -986,6 +1189,7 @@ nostub: } return EOF; } + php_stream_filter_append(&phar->fp->writefilters, filter); php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); php_stream_filter_flush(filter, 1); @@ -1013,3 +1217,12 @@ nostub: return EOF; } /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/util.c b/ext/phar/util.c index 0099928f8b..008bd3f5ea 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -21,12 +21,29 @@ /* $Id$ */ #include "phar_internal.h" + +#ifdef PHAR_HAVE_OPENSSL +/* OpenSSL includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, char *key, int key_len, char **signature, int *signature_len TSRMLS_DC); +#endif + #if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 extern php_stream_wrapper php_stream_phar_wrapper; #endif /* for links to relative location, prepend cwd of the entry */ -static char *phar_get_link_location(phar_entry_info *entry TSRMLS_DC) +static char *phar_get_link_location(phar_entry_info *entry TSRMLS_DC) /* {{{ */ { char *p, *ret = NULL; if (!entry->link) { @@ -43,8 +60,9 @@ static char *phar_get_link_location(phar_entry_info *entry TSRMLS_DC) } return entry->link; } +/* }}} */ -phar_entry_info *phar_get_link_source(phar_entry_info *entry TSRMLS_DC) +phar_entry_info *phar_get_link_source(phar_entry_info *entry TSRMLS_DC) /* {{{ */ { phar_entry_info *link_entry; char *link = phar_get_link_location(entry TSRMLS_CC); @@ -66,9 +84,10 @@ phar_entry_info *phar_get_link_source(phar_entry_info *entry TSRMLS_DC) return NULL; } } +/* }}} */ /* retrieve a phar_entry_info's current file pointer for reading contents */ -php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC) +php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC) /* {{{ */ { if (follow_links && entry->link) { phar_entry_info *link_entry = phar_get_link_source(entry TSRMLS_CC); @@ -77,14 +96,15 @@ php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC) return phar_get_efp(link_entry, 1 TSRMLS_CC); } } - if (entry->fp_type == PHAR_FP) { - if (!entry->phar->fp) { + + if (phar_get_fp_type(entry TSRMLS_CC) == PHAR_FP) { + if (!phar_get_entrypfp(entry TSRMLS_CC)) { /* re-open just in time for cases where our refcount reached 0 on the phar archive */ phar_open_archive_fp(entry->phar TSRMLS_CC); } - return entry->phar->fp; - } else if (entry->fp_type == PHAR_UFP) { - return entry->phar->ufp; + return phar_get_entrypfp(entry TSRMLS_CC); + } else if (phar_get_fp_type(entry TSRMLS_CC) == PHAR_UFP) { + return phar_get_entrypufp(entry TSRMLS_CC); } else if (entry->fp_type == PHAR_MOD) { return entry->fp; } else { @@ -95,15 +115,17 @@ php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC) return entry->fp; } } +/* }}} */ -int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position, int follow_links TSRMLS_DC) +int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position, int follow_links TSRMLS_DC) /* {{{ */ { php_stream *fp = phar_get_efp(entry, follow_links TSRMLS_CC); - off_t temp; + off_t temp, eoffset; if (!fp) { return -1; } + if (follow_links) { phar_entry_info *t; t = phar_get_link_source(entry TSRMLS_CC); @@ -111,31 +133,39 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi entry = t; } } + if (entry->is_dir) { return 0; } + + eoffset = phar_get_fp_offset(entry TSRMLS_CC); + switch (whence) { - case SEEK_END : - temp = entry->offset + entry->uncompressed_filesize + offset; + case SEEK_END: + temp = eoffset + entry->uncompressed_filesize + offset; break; - case SEEK_CUR : - temp = entry->offset + position + offset; + case SEEK_CUR: + temp = eoffset + position + offset; break; - case SEEK_SET : - temp = entry->offset + offset; + case SEEK_SET: + temp = eoffset + offset; break; } - if (temp > entry->offset + (off_t) entry->uncompressed_filesize) { + + if (temp > eoffset + (off_t) entry->uncompressed_filesize) { return -1; } - if (temp < entry->offset) { + + if (temp < eoffset) { return -1; } + return php_stream_seek(fp, temp, SEEK_SET); } +/* }}} */ /* mount an absolute path or uri to a path internal to the phar archive */ -int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, char *path, int path_len TSRMLS_DC) +int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, char *path, int path_len TSRMLS_DC) /* {{{ */ { phar_entry_info entry = {0}; php_stream_statbuf ssb; @@ -177,12 +207,14 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, filename_len = strlen(entry.tmp); filename = entry.tmp; + /* only check openbasedir for files, not for phar streams */ if (!is_phar && php_check_open_basedir(filename TSRMLS_CC)) { efree(entry.tmp); efree(entry.filename); return FAILURE; } + entry.is_mounted = 1; entry.is_crc_checked = 1; entry.fp_type = PHAR_TMP; @@ -192,6 +224,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, efree(entry.filename); return FAILURE; } + if (ssb.sb.st_mode & S_IFDIR) { entry.is_dir = 1; if (SUCCESS != zend_hash_add(&phar->mounted_dirs, entry.filename, path_len, (void *)&(entry.filename), sizeof(char *), NULL)) { @@ -204,64 +237,90 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, entry.is_dir = 0; entry.uncompressed_filesize = entry.compressed_filesize = ssb.sb.st_size; } + entry.flags = ssb.sb.st_mode; + if (SUCCESS == zend_hash_add(&phar->manifest, entry.filename, path_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { return SUCCESS; } + efree(entry.tmp); efree(entry.filename); return FAILURE; } +/* }}} */ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_data **pphar TSRMLS_DC) /* {{{ */ { #if PHP_VERSION_ID >= 50300 char *path, *fname, *arch, *entry, *ret, *test; int arch_len, entry_len, fname_len; + phar_archive_data *phar; if (pphar) { *pphar = NULL; + } else { + pphar = &phar; } if (!zend_is_executing(TSRMLS_C) || !PHAR_G(cwd)) { return phar_save_resolve_path(filename, filename_len TSRMLS_CC); } + fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); + if (PHAR_G(last_phar) && !memcmp(fname, "phar://", 7) && fname_len - 7 >= PHAR_G(last_phar_name_len) && !memcmp(fname + 7, PHAR_G(last_phar_name), PHAR_G(last_phar_name_len))) { + arch = estrndup(PHAR_G(last_phar_name), PHAR_G(last_phar_name_len)); + arch_len = PHAR_G(last_phar_name_len); + phar = PHAR_G(last_phar); + goto splitted; + } + if (fname_len < 7 || memcmp(fname, "phar://", 7) || SUCCESS != phar_split_fname(fname, strlen(fname), &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return phar_save_resolve_path(filename, filename_len TSRMLS_CC); } + efree(entry); + if (*filename == '.') { int try_len; - if (SUCCESS != (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { efree(arch); return phar_save_resolve_path(filename, filename_len TSRMLS_CC); } +splitted: + if (pphar) { + *pphar = phar; + } + try_len = filename_len; test = phar_fix_filepath(estrndup(filename, filename_len), &try_len, 1 TSRMLS_CC); + if (*test == '/') { - if (zend_hash_exists(&((*pphar)->manifest), test + 1, try_len - 1)) { + if (zend_hash_exists(&(phar->manifest), test + 1, try_len - 1)) { spprintf(&ret, 0, "phar://%s%s", arch, test); efree(arch); efree(test); return ret; } } else { - if (zend_hash_exists(&((*pphar)->manifest), test, try_len)) { + if (zend_hash_exists(&(phar->manifest), test, try_len)) { spprintf(&ret, 0, "phar://%s/%s", arch, test); efree(arch); efree(test); return ret; } } + efree(test); } + spprintf(&path, MAXPATHLEN, "phar://%s/%s%c%s", arch, PHAR_G(cwd), DEFAULT_DIR_SEPARATOR, PG(include_path)); efree(arch); ret = php_resolve_path(filename, filename_len, path TSRMLS_CC); efree(path); + if (ret && strlen(ret) > 8 && !strncmp(ret, "phar://", 7)) { char *arch; int arch_len, ret_len; @@ -272,10 +331,17 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d if (SUCCESS != phar_split_fname(ret, ret_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return ret; } + zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + + if (!pphar && PHAR_G(manifest_cached)) { + zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); + } + efree(arch); efree(entry); } + return ret; #else /* PHP 5.2 */ char resolved_path[MAXPATHLEN]; @@ -286,6 +352,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d int n = 0; char *fname, *arch, *entry, *ret, *test; int arch_len, entry_len; + phar_archive_data *phar = NULL; if (!filename) { return NULL; @@ -294,54 +361,58 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d if (!zend_is_executing(TSRMLS_C) || !PHAR_G(cwd)) { goto doit; } + fname = zend_get_executed_filename(TSRMLS_C); + if (SUCCESS != phar_split_fname(fname, strlen(fname), &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { goto doit; } efree(entry); + if (*filename == '.') { - phar_archive_data **pphar; int try_len; - if (SUCCESS != (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { efree(arch); goto doit; } + try_len = filename_len; test = phar_fix_filepath(estrndup(filename, filename_len), &try_len, 1 TSRMLS_CC); + if (*test == '/') { - if (zend_hash_exists(&((*pphar)->manifest), test + 1, try_len - 1)) { + if (zend_hash_exists(&(phar->manifest), test + 1, try_len - 1)) { spprintf(&ret, 0, "phar://%s%s", arch, test); efree(arch); efree(test); return ret; } } else { - if (zend_hash_exists(&((*pphar)->manifest), test, try_len)) { + if (zend_hash_exists(&(phar->manifest), test, try_len)) { spprintf(&ret, 0, "phar://%s/%s", arch, test); efree(arch); efree(test); return ret; } } + efree(test); } - efree(arch); + efree(arch); doit: - if (*filename == '.' || - IS_ABSOLUTE_PATH(filename, filename_len) || - !path || - !*path) { + if (*filename == '.' || IS_ABSOLUTE_PATH(filename, filename_len) || !path || !*path) { if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) { return estrdup(resolved_path); } else { return NULL; } } + /* test for stream wrappers and return */ for (p = filename; p - filename < filename_len && (isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'); ++p, ++n); + if (n < filename_len - 3 && (*p == ':') && (!strncmp("//", p+1, 2) || ( filename_len > 4 && !memcmp("data", filename, 4)))) { /* found stream wrapper, this is an absolute path until stream wrappers implement realpath */ return estrndup(filename, filename_len); @@ -358,6 +429,7 @@ doit: maybe_stream = 0; goto not_stream; } + for (p = ptr, n = 0; p < end && (isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'); ++p, ++n); if (n == end - ptr && *p && !strncmp("//", p+1, 2)) { @@ -374,6 +446,7 @@ not_stream: ptr = end + 1; continue; } + memcpy(trypath, ptr, end-ptr); len = end-ptr; trypath[end-ptr] = '/'; @@ -385,6 +458,7 @@ not_stream: if (len + 1 + filename_len + 1 >= MAXPATHLEN) { break; } + memcpy(trypath, ptr, len); trypath[len] = '/'; memcpy(trypath+len+1, filename, filename_len+1); @@ -413,16 +487,23 @@ not_stream: if (wrapper == &php_stream_phar_wrapper) { char *arch, *entry; int arch_len, entry_len, ret_len; - + ret_len = strlen(trypath); /* found phar:// */ if (SUCCESS != phar_split_fname(trypath, ret_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return estrndup(trypath, ret_len); } + zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + + if (!pphar && PHAR_G(manifest_cached)) { + zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); + } + efree(arch); efree(entry); + return estrndup(trypath, ret_len); } return estrdup(trypath); @@ -437,8 +518,7 @@ not_stream: } } /* end provided path */ - /* check in calling scripts' current working directory as a fall back case - */ + /* check in calling scripts' current working directory as a fall back case */ if (zend_is_executing(TSRMLS_C)) { char *exec_fname = zend_get_executed_filename(TSRMLS_C); int exec_fname_length = strlen(exec_fname); @@ -446,29 +526,32 @@ not_stream: int n = 0; while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); - if (exec_fname && exec_fname[0] != '[' && - exec_fname_length > 0 && - exec_fname_length + 1 + filename_len + 1 < MAXPATHLEN) { + if (exec_fname && exec_fname[0] != '[' && + exec_fname_length > 0 && + exec_fname_length + 1 + filename_len + 1 < MAXPATHLEN) { memcpy(trypath, exec_fname, exec_fname_length + 1); memcpy(trypath+exec_fname_length + 1, filename, filename_len+1); /* search for stream wrapper */ for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; ++p, ++n); + if (n < exec_fname_length - 3 && (*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || !memcmp("data", trypath, 4))) { char *actual; - + wrapper = php_stream_locate_url_wrapper(trypath, &actual, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + if (wrapper == &php_plain_files_wrapper) { /* this should never technically happen, but we'll leave it here for completeness */ strncpy(trypath, actual, MAXPATHLEN); } else if (!wrapper) { - /* if wrapper is NULL, there was a mal-formed include_path stream wrapper + /* if wrapper is NULL, there was a malformed include_path stream wrapper this also should be impossible */ return NULL; } else { return estrdup(trypath); } } + if (tsrm_realpath(trypath, resolved_path TSRMLS_CC)) { return estrdup(resolved_path); } @@ -500,25 +583,31 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char if (!ret) { return FAILURE; } + *ret = NULL; + if (error) { *error = NULL; } + if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) { return FAILURE; } + if (for_write && PHAR_G(readonly) && !phar->is_data) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname); } return FAILURE; } + if (!path_len) { if (error) { spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname); } return FAILURE; } +really_get_entry: if (allow_dir) { if ((entry = phar_get_entry_info_dir(phar, path, path_len, allow_dir, for_create && !PHAR_G(readonly) && !phar->is_data ? NULL : error, security TSRMLS_CC)) == NULL) { if (for_create && (!PHAR_G(readonly) || phar->is_data)) { @@ -534,24 +623,39 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char return FAILURE; } } + + if (for_write && phar->is_persistent) { + if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, could not make cached phar writeable", path, fname); + } + return FAILURE; + } else { + goto really_get_entry; + } + } + if (entry->is_modified && !for_write) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname); } return FAILURE; } + if (entry->fp_refcount && for_write) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname); } return FAILURE; } + if (entry->is_deleted) { if (!for_create) { return FAILURE; } entry->is_deleted = 0; } + if (entry->is_dir) { *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); (*ret)->position = 0; @@ -561,10 +665,15 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char (*ret)->internal_file = entry; (*ret)->is_zip = entry->is_zip; (*ret)->is_tar = entry->is_tar; - ++(entry->phar->refcount); - ++(entry->fp_refcount); + + if (!phar->is_persistent) { + ++(entry->phar->refcount); + ++(entry->fp_refcount); + } + return SUCCESS; } + if (entry->fp_type == PHAR_MOD) { if (for_trunc) { if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) { @@ -579,6 +688,7 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char entry->link = NULL; entry->tar_type = (entry->tar_type ? TAR_FILE : 0); } + if (for_write) { if (for_trunc) { if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) { @@ -595,6 +705,7 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char } } } + *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); (*ret)->position = 0; (*ret)->phar = phar; @@ -603,9 +714,13 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char (*ret)->is_zip = entry->is_zip; (*ret)->is_tar = entry->is_tar; (*ret)->fp = phar_get_efp(entry, 1 TSRMLS_CC); - (*ret)->zero = entry->offset; - ++(entry->fp_refcount); - ++(entry->phar->refcount); + (*ret)->zero = phar_get_fp_offset(entry TSRMLS_CC); + + if (!phar->is_persistent) { + ++(entry->fp_refcount); + ++(entry->phar->refcount); + } + return SUCCESS; } /* }}} */ @@ -644,6 +759,13 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char return NULL; } + if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be created, could not make cached phar writeable", path, fname); + } + return NULL; + } + /* create a new phar data holder */ ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); @@ -652,12 +774,15 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char etemp.filename_len = path_len; etemp.fp_type = PHAR_MOD; etemp.fp = php_stream_fopen_tmpfile(); + if (!etemp.fp) { if (error) { spprintf(error, 0, "phar error: unable to create temporary file"); } + efree(ret); return NULL; } + etemp.fp_refcount = 1; if (allow_dir == 2) { @@ -670,26 +795,34 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char } else { etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE; } + + phar_add_virtual_dirs(phar, path, path_len TSRMLS_CC); etemp.is_modified = 1; etemp.timestamp = time(0); etemp.is_crc_checked = 1; etemp.phar = phar; etemp.filename = estrndup(path, path_len); etemp.is_zip = phar->is_zip; + if (phar->is_tar) { etemp.is_tar = phar->is_tar; etemp.tar_type = TAR_FILE; } + if (FAILURE == zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry)) { + php_stream_close(etemp.fp); if (error) { spprintf(error, 0, "phar error: unable to add new entry \"%s\" to phar \"%s\"", etemp.filename, phar->fname); } + efree(ret); + efree(etemp.filename); return NULL; } - + if (!entry) { php_stream_close(etemp.fp); efree(etemp.filename); + efree(ret); return NULL; } @@ -701,57 +834,63 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char ret->is_zip = entry->is_zip; ret->is_tar = entry->is_tar; ret->internal_file = entry; + return ret; } /* }}} */ /* initialize a phar_archive_data's read-only fp for existing phar data */ -int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) +int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ { - if (phar->fp) { + if (phar_get_pharfp(phar TSRMLS_CC)) { return SUCCESS; } - #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { return FAILURE; } #endif - + if (php_check_open_basedir(phar->fname TSRMLS_CC)) { return FAILURE; } - phar->fp = php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL); - if (!phar->fp) { + phar_set_pharfp(phar, php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL) TSRMLS_CC); + + if (!phar_get_pharfp(phar TSRMLS_CC)) { return FAILURE; } + return SUCCESS; } +/* }}} */ /* copy file data from an existing to a new phar_entry_info that is not in the manifest */ -int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC) +int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC) /* {{{ */ { phar_entry_info *link; if (FAILURE == phar_open_entry_fp(source, error, 1 TSRMLS_CC)) { return FAILURE; } + if (dest->link) { efree(dest->link); dest->link = NULL; dest->tar_type = (dest->tar_type ? TAR_FILE : 0); } + dest->fp_type = PHAR_MOD; dest->offset = 0; dest->is_modified = 1; dest->fp = php_stream_fopen_tmpfile(); - phar_seek_efp(source, 0, SEEK_SET, 0, 1 TSRMLS_CC); link = phar_get_link_source(source TSRMLS_CC); + if (!link) { link = source; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), dest->fp, link->uncompressed_filesize)) { php_stream_close(dest->fp); dest->fp_type = PHAR_FP; @@ -760,87 +899,101 @@ int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **er } return FAILURE; } + return SUCCESS; } +/* }}} */ /* open and decompress a compressed phar entry */ -int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) +int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) /* {{{ */ { php_stream_filter *filter; phar_archive_data *phar = entry->phar; char *filtername; off_t loc; + php_stream *ufp; if (follow_links && entry->link) { phar_entry_info *link_entry = phar_get_link_source(entry TSRMLS_CC); - if (link_entry && link_entry != entry) { return phar_open_entry_fp(link_entry, error, 1 TSRMLS_CC); } } + if (entry->fp_type == PHAR_TMP) { if (!entry->fp) { entry->fp = php_stream_open_wrapper(entry->tmp, "rb", STREAM_MUST_SEEK|0, NULL); } return SUCCESS; } + if (entry->fp_type != PHAR_FP) { /* either newly created or already modified */ return SUCCESS; } - if (!phar->fp) { + + if (!phar_get_pharfp(phar TSRMLS_CC)) { if (FAILURE == phar_open_archive_fp(phar TSRMLS_CC)) { spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname); return FAILURE; } } + if ((entry->old_flags && !(entry->old_flags & PHAR_ENT_COMPRESSION_MASK)) || !(entry->flags & PHAR_ENT_COMPRESSION_MASK)) { return SUCCESS; } - if (!phar->ufp) { - phar->ufp = php_stream_fopen_tmpfile(); - if (!phar->ufp) { + + if (!phar_get_entrypufp(entry TSRMLS_CC)) { + phar_set_entrypufp(entry, php_stream_fopen_tmpfile() TSRMLS_CC); + if (!phar_get_entrypufp(entry TSRMLS_CC)) { spprintf(error, 4096, "phar error: Cannot open temporary file for decompressing phar archive \"%s\" file \"%s\"", phar->fname, entry->filename); return FAILURE; } } + ufp = phar_get_entrypufp(entry TSRMLS_CC); + if ((filtername = phar_decompress_filter(entry, 0)) != NULL) { - filter = php_stream_filter_create(filtername, NULL, php_stream_is_persistent(phar->ufp) TSRMLS_CC); + filter = php_stream_filter_create(filtername, NULL, 0 TSRMLS_CC); } else { filter = NULL; } + if (!filter) { spprintf(error, 4096, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename); return FAILURE; } + /* now we can safely use proper decompression */ /* save the new offset location within ufp */ - php_stream_seek(phar->ufp, 0, SEEK_END); - loc = php_stream_tell(phar->ufp); - php_stream_filter_append(&phar->ufp->writefilters, filter); - php_stream_seek(phar->fp, entry->offset, SEEK_SET); - if (php_stream_copy_to_stream(phar->fp, phar->ufp, entry->compressed_filesize) != entry->compressed_filesize) { + php_stream_seek(ufp, 0, SEEK_END); + loc = php_stream_tell(ufp); + php_stream_filter_append(&ufp->writefilters, filter); + php_stream_seek(phar_get_entrypfp(entry TSRMLS_CC), phar_get_fp_offset(entry TSRMLS_CC), SEEK_SET); + + if (php_stream_copy_to_stream(phar_get_entrypfp(entry TSRMLS_CC), ufp, entry->compressed_filesize) != entry->compressed_filesize) { spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename); php_stream_filter_remove(filter, 1 TSRMLS_CC); return FAILURE; } + php_stream_filter_flush(filter, 1); - php_stream_flush(phar->ufp); + php_stream_flush(ufp); php_stream_filter_remove(filter, 1 TSRMLS_CC); - if (php_stream_tell(phar->ufp) - loc != (off_t) entry->uncompressed_filesize) { + + if (php_stream_tell(ufp) - loc != (off_t) entry->uncompressed_filesize) { spprintf(error, 4096, "phar error: internal corruption of phar \"%s\" (actual filesize mismatch on file \"%s\")", phar->fname, entry->filename); return FAILURE; } entry->old_flags = entry->flags; - entry->fp_type = PHAR_UFP; - /* this is now the new location of the file contents within this fp */ - entry->offset = loc; + /* this is now the new location of the file contents within this fp */ + phar_set_fp_type(entry, PHAR_UFP, loc TSRMLS_CC); return SUCCESS; } +/* }}} */ #if defined(PHP_VERSION_ID) && PHP_VERSION_ID < 50202 typedef struct { @@ -895,22 +1048,27 @@ int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, entry->offset = 0; return SUCCESS; } + if (error) { *error = NULL; } + /* open a new temp file for writing */ if (entry->link) { efree(entry->link); entry->link = NULL; entry->tar_type = (entry->tar_type ? TAR_FILE : 0); } + entry->fp = php_stream_fopen_tmpfile(); + if (!entry->fp) { if (error) { spprintf(error, 0, "phar error: unable to create temporary file"); } return FAILURE; } + entry->old_flags = entry->flags; entry->is_modified = 1; phar->is_modified = 1; @@ -925,7 +1083,7 @@ int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, } /* }}} */ -int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) +int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) /* {{{ */ { php_stream *fp; phar_entry_info *link; @@ -941,9 +1099,11 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) fp = php_stream_fopen_tmpfile(); phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC); link = phar_get_link_source(entry TSRMLS_CC); + if (!link) { link = entry; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) { if (error) { spprintf(error, 4096, "phar error: cannot separate entry file \"%s\" contents in phar archive \"%s\" for write access", entry->filename, entry->phar->fname); @@ -963,12 +1123,12 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) entry->is_modified = 1; return SUCCESS; } +/* }}} */ /** * helper function to open an internal file's fp just-in-time */ -phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp, - char **error, int for_write TSRMLS_DC) +phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp, char **error, int for_write TSRMLS_DC) /* {{{ */ { if (error) { *error = NULL; @@ -983,16 +1143,23 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, } return entry; } +/* }}} */ int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len TSRMLS_DC) /* {{{ */ { - if (phar->refcount) { + if (phar->refcount || phar->is_persistent) { return FAILURE; } + /* this archive has no open references, so emit an E_STRICT and remove it */ if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) { return FAILURE; } + + /* invalidate phar cache */ + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + return SUCCESS; } /* }}} */ @@ -1006,15 +1173,50 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch phar_archive_data *fd, **fd_ptr; char *my_realpath, *save; int save_len; + ulong fhash, ahash; phar_request_initialize(TSRMLS_C); if (error) { *error = NULL; } + *archive = NULL; + + if (PHAR_G(last_phar) && fname_len == PHAR_G(last_phar_name_len) && !memcmp(fname, PHAR_G(last_phar_name), fname_len)) { + *archive = PHAR_G(last_phar); + if (alias && alias_len) { + + if (!PHAR_G(last_phar)->is_temporary_alias && (alias_len != PHAR_G(last_phar)->alias_len || memcmp(PHAR_G(last_phar)->alias, alias, alias_len))) { + if (error) { + spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, PHAR_G(last_phar)->fname, fname); + } + *archive = NULL; + return FAILURE; + } + + if (PHAR_G(last_phar)->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len, (void**)&fd_ptr)) { + zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len); + } + + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(*archive), sizeof(phar_archive_data*), NULL); + PHAR_G(last_alias) = alias; + PHAR_G(last_alias_len) = alias_len; + } + + return SUCCESS; + } + + if (alias && alias_len && PHAR_G(last_phar) && alias_len == PHAR_G(last_alias_len) && !memcmp(alias, PHAR_G(last_alias), alias_len)) { + fd = PHAR_G(last_phar); + fd_ptr = &fd; + goto alias_success; + } + if (alias && alias_len) { - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void**)&fd_ptr)) { + ahash = zend_inline_hash_func(alias, alias_len); + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void**)&fd_ptr)) { +alias_success: if (fname && (fname_len != (*fd_ptr)->fname_len || strncmp(fname, (*fd_ptr)->fname, fname_len))) { if (error) { spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname); @@ -1025,17 +1227,33 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch } return FAILURE; } + *archive = *fd_ptr; + fd = *fd_ptr; + PHAR_G(last_phar) = fd; + PHAR_G(last_phar_name) = fd->fname; + PHAR_G(last_phar_name_len) = fd->fname_len; + PHAR_G(last_alias) = alias; + PHAR_G(last_alias_len) = alias_len; + return SUCCESS; } + + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, alias, alias_len, ahash, (void **)&fd_ptr)) { + goto alias_success; + } } + + fhash = zend_inline_hash_func(fname, fname_len); my_realpath = NULL; save = fname; save_len = fname_len; + if (fname && fname_len) { - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) { + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, fhash, (void**)&fd_ptr)) { *archive = *fd_ptr; fd = *fd_ptr; + if (alias && alias_len) { if (!fd->is_temporary_alias && (alias_len != fd->alias_len || memcmp(fd->alias, alias, alias_len))) { if (error) { @@ -1043,20 +1261,74 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch } return FAILURE; } + if (fd->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len); } - zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL); + + zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); } + + PHAR_G(last_phar) = fd; + PHAR_G(last_phar_name) = fd->fname; + PHAR_G(last_phar_name_len) = fd->fname_len; + PHAR_G(last_alias) = fd->alias; + PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, (void**)&fd_ptr)) { + + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_phars, fname, fname_len, fhash, (void**)&fd_ptr)) { *archive = *fd_ptr; + fd = *fd_ptr; + + /* this could be problematic - alias should never be different from manifest alias + for cached phars */ + if (!fd->is_temporary_alias && alias && alias_len) { + if (alias_len != fd->alias_len || memcmp(fd->alias, alias, alias_len)) { + if (error) { + spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname); + } + return FAILURE; + } + } + + PHAR_G(last_phar) = fd; + PHAR_G(last_phar_name) = fd->fname; + PHAR_G(last_phar_name_len) = fd->fname_len; + PHAR_G(last_alias) = fd->alias; + PHAR_G(last_alias_len) = fd->alias_len; + + return SUCCESS; + } + + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, fhash, (void**)&fd_ptr)) { + fd = *archive = *fd_ptr; + + PHAR_G(last_phar) = fd; + PHAR_G(last_phar_name) = fd->fname; + PHAR_G(last_phar_name_len) = fd->fname_len; + PHAR_G(last_alias) = fd->alias; + PHAR_G(last_alias_len) = fd->alias_len; + + return SUCCESS; + } + + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, save, save_len, fhash, (void**)&fd_ptr)) { + fd = *archive = *fd_ptr; + + PHAR_G(last_phar) = fd; + PHAR_G(last_phar_name) = fd->fname; + PHAR_G(last_phar_name_len) = fd->fname_len; + PHAR_G(last_alias) = fd->alias; + PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } /* not found, try converting \ to / */ my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC); + if (my_realpath) { fname_len = strlen(my_realpath); fname = my_realpath; @@ -1066,17 +1338,35 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch #ifdef PHP_WIN32 phar_unixify_path_separators(fname, fname_len); #endif - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void**)&fd_ptr)) { + fhash = zend_inline_hash_func(fname, fname_len); + + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, fhash, (void**)&fd_ptr)) { +realpath_success: *archive = *fd_ptr; fd = *fd_ptr; + if (alias && alias_len) { - zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&fd, sizeof(phar_archive_data*), NULL); + zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); } + efree(my_realpath); + + PHAR_G(last_phar) = fd; + PHAR_G(last_phar_name) = fd->fname; + PHAR_G(last_phar_name_len) = fd->fname_len; + PHAR_G(last_alias) = fd->alias; + PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_phars, fname, fname_len, fhash, (void**)&fd_ptr)) { + goto realpath_success; + } + efree(my_realpath); } + return FAILURE; } /* }}} */ @@ -1109,13 +1399,14 @@ char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{ } else { flags = entry->flags; } + switch (flags & PHAR_ENT_COMPRESSION_MASK) { - case PHAR_ENT_COMPRESSED_GZ: - return "zlib.inflate"; - case PHAR_ENT_COMPRESSED_BZ2: - return "bzip2.decompress"; - default: - return return_unknown ? "unknown" : NULL; + case PHAR_ENT_COMPRESSED_GZ: + return "zlib.inflate"; + case PHAR_ENT_COMPRESSED_BZ2: + return "bzip2.decompress"; + default: + return return_unknown ? "unknown" : NULL; } } /* }}} */ @@ -1155,12 +1446,14 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if (!path_len && !dir) { if (error) { spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path); } return NULL; } + if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) { if (error) { spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error); @@ -1198,7 +1491,22 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in return NULL; } return entry; - } else if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) { + } + + if (dir) { + if (zend_hash_exists(&phar->virtual_dirs, path, path_len)) { + /* a file or directory exists in a sub-directory of this path */ + entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info)); + /* this next line tells PharFileInfo->__destruct() to efree the filename */ + entry->is_temp_dir = entry->is_dir = 1; + entry->filename = (char *) estrndup(path, path_len + 1); + entry->filename_len = path_len; + entry->phar = phar; + return entry; + } + } + + if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) { phar_zstr key; char *str_key; ulong unused; @@ -1226,17 +1534,21 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if (!entry->tmp || !entry->is_mounted) { if (error) { spprintf(error, 4096, "phar internal error: mounted path \"%s\" is not properly initialized as a mounted path", str_key); } return NULL; } + test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, path + keylen); + if (SUCCESS != php_stream_stat_path(test, &ssb)) { efree(test); return NULL; } + if (ssb.sb.st_mode & S_IFDIR && !dir) { efree(test); if (error) { @@ -1244,6 +1556,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if ((ssb.sb.st_mode & S_IFDIR) == 0 && dir) { efree(test); /* user requested a directory, we must return one */ @@ -1252,6 +1565,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + /* mount the file just in time */ if (SUCCESS != phar_mount_entry(phar, test, test_len, path, path_len TSRMLS_CC)) { efree(test); @@ -1260,7 +1574,9 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + efree(test); + if (SUCCESS != zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) { if (error) { spprintf(error, 4096, "phar error: path \"%s\" exists as file \"%s\" and could not be retrieved after being mounted", path, test); @@ -1271,50 +1587,711 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } } } - if (dir) { - /* try to find a directory */ - HashTable *manifest; - phar_zstr key; - char *str_key; - uint keylen; - ulong unused; - if (!path_len) { - path = "/"; - } - manifest = &phar->manifest; - zend_hash_internal_pointer_reset(manifest); - while (FAILURE != zend_hash_has_more_elements(manifest)) { - if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) { - break; - } + return NULL; +} +/* }}} */ - PHAR_STR(key, str_key); +static const char hexChars[] = "0123456789ABCDEF"; - if (0 != memcmp(str_key, path, path_len)) { - /* entry in directory not found */ - if (SUCCESS != zend_hash_move_forward(manifest)) { - break; - } - continue; - } else { - if (str_key[path_len] != '/') { - if (SUCCESS != zend_hash_move_forward(manifest)) { - break; - } - continue; - } - /* found a file in this path */ - entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info)); - /* this next line tells PharFileInfo->__destruct() to efree the filename */ - entry->is_temp_dir = entry->is_dir = 1; - entry->filename = (char *) estrndup(path, path_len + 1); - entry->filename_len = path_len; - entry->phar = phar; - return entry; - } - } +static int phar_hex_str(const char *digest, size_t digest_len, char **signature TSRMLS_DC) /* {{{ */ +{ + int pos = -1; + size_t len = 0; + + *signature = (char*)safe_pemalloc(digest_len, 2, 1, PHAR_G(persist)); + + for (; len < digest_len; ++len) { + (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] >> 4]; + (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] & 0x0F]; } - return NULL; + (*signature)[++pos] = '\0'; + return pos; } /* }}} */ + +#ifndef PHAR_HAVE_OPENSSL +static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, char *key, int key_len, char **signature, int *signature_len TSRMLS_DC) /* {{{ */ +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + zval *zdata, *zsig, *zkey, *retval_ptr, **zp[3], *openssl; + + MAKE_STD_ZVAL(zdata); + MAKE_STD_ZVAL(openssl); + ZVAL_STRINGL(openssl, is_sign ? "openssl_sign" : "openssl_verify", is_sign ? sizeof("openssl_sign")-1 : sizeof("openssl_verify")-1, 1); + MAKE_STD_ZVAL(zsig); + ZVAL_STRINGL(zsig, *signature, *signature_len, 1); + MAKE_STD_ZVAL(zkey); + ZVAL_STRINGL(zkey, key, key_len, 1); + zp[0] = &zdata; + zp[1] = &zsig; + zp[2] = &zkey; + + php_stream_rewind(fp); + Z_TYPE_P(zdata) = IS_STRING; + Z_STRLEN_P(zdata) = end; + + if (end != (off_t) php_stream_copy_to_mem(fp, &(Z_STRVAL_P(zdata)), (size_t) end, 0)) { + zval_dtor(zdata); + zval_dtor(zsig); + zval_dtor(zkey); + return FAILURE; + } +#if PHP_VERSION_ID < 50300 + if (FAILURE == zend_fcall_info_init(openssl, &fci, &fcc TSRMLS_CC)) { +#else + if (FAILURE == zend_fcall_info_init(openssl, 0, &fci, &fcc, NULL, NULL TSRMLS_CC)) { +#endif + zval_dtor(zdata); + zval_dtor(zsig); + zval_dtor(zkey); + zval_dtor(openssl); + efree(openssl); + return FAILURE; + } + + fci.param_count = 3; + fci.params = zp; +#if PHP_VERSION_ID < 50300 + ++(zdata->refcount); + ++(zsig->refcount); + ++(zkey->refcount); +#else + Z_ADDREF_P(zdata); + if (is_sign) { + Z_SET_ISREF_P(zsig); + } else { + Z_ADDREF_P(zsig); + } + Z_ADDREF_P(zkey); +#endif + fci.retval_ptr_ptr = &retval_ptr; + + if (FAILURE == zend_call_function(&fci, &fcc TSRMLS_CC)) { + zval_dtor(zdata); + zval_dtor(zsig); + zval_dtor(zkey); + zval_dtor(openssl); + efree(openssl); + efree(zdata); + efree(zkey); + efree(zsig); + return FAILURE; + } + + zval_dtor(openssl); + efree(openssl); +#if PHP_VERSION_ID < 50300 + --(zdata->refcount); + --(zsig->refcount); + --(zkey->refcount); +#else + Z_DELREF_P(zdata); + if (is_sign) { + Z_UNSET_ISREF_P(zsig); + } else { + Z_DELREF_P(zsig); + } + Z_DELREF_P(zkey); +#endif + zval_dtor(zdata); + efree(zdata); + zval_dtor(zkey); + efree(zkey); + + switch (Z_TYPE_P(retval_ptr)) { + default: + case IS_LONG: + zval_dtor(zsig); + efree(zsig); + if (1 == Z_LVAL_P(retval_ptr)) { + efree(retval_ptr); + return SUCCESS; + } + efree(retval_ptr); + return FAILURE; + case IS_BOOL: + efree(retval_ptr); + if (Z_BVAL_P(retval_ptr)) { + *signature = estrndup(Z_STRVAL_P(zsig), Z_STRLEN_P(zsig)); + *signature_len = Z_STRLEN_P(zsig); + zval_dtor(zsig); + efree(zsig); + return SUCCESS; + } + zval_dtor(zsig); + efree(zsig); + return FAILURE; + } +} +/* }}} */ +#endif /* #ifndef PHAR_HAVE_OPENSSL */ + +int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error TSRMLS_DC) /* {{{ */ +{ + int read_size, len; + off_t read_len; + unsigned char buf[1024]; + + php_stream_rewind(fp); + + switch (sig_type) { + case PHAR_SIG_OPENSSL: { +#ifdef PHAR_HAVE_OPENSSL + BIO *in; + EVP_PKEY *key; + EVP_MD *mdtype = (EVP_MD *) EVP_sha1(); + EVP_MD_CTX md_ctx; +#else + int tempsig; +#endif + php_uint32 pubkey_len; + char *pubkey = NULL, *pfile; + php_stream *pfp; +#ifndef PHAR_HAVE_OPENSSL + if (!zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) { + if (error) { + spprintf(error, 0, "openssl not loaded"); + } + return FAILURE; + } +#endif + /* use __FILE__ . '.pubkey' for public key file */ + spprintf(&pfile, 0, "%s.pubkey", fname); + pfp = php_stream_open_wrapper(pfile, "rb", 0, NULL); + efree(pfile); + + if (!pfp || !(pubkey_len = php_stream_copy_to_mem(pfp, &pubkey, PHP_STREAM_COPY_ALL, 0)) || !pubkey) { + if (error) { + spprintf(error, 0, "openssl public key could not be read"); + } + return FAILURE; + } + + php_stream_close(pfp); +#ifndef PHAR_HAVE_OPENSSL + tempsig = sig_len; + + if (FAILURE == phar_call_openssl_signverify(0, fp, end_of_phar, pubkey, pubkey_len, &sig, &tempsig TSRMLS_CC)) { + if (pubkey) { + efree(pubkey); + } + + if (error) { + spprintf(error, 0, "openssl signature could not be verified"); + } + + return FAILURE; + } + + if (pubkey) { + efree(pubkey); + } + + sig_len = tempsig; +#else + in = BIO_new_mem_buf(pubkey, pubkey_len); + + if (NULL == in) { + efree(pubkey); + if (error) { + spprintf(error, 0, "openssl signature could not be processed"); + } + return FAILURE; + } + + key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL); + BIO_free(in); + efree(pubkey); + + if (NULL == key) { + if (error) { + spprintf(error, 0, "openssl signature could not be processed"); + } + return FAILURE; + } + + EVP_VerifyInit(&md_ctx, mdtype); + read_len = end_of_phar; + + if (read_len > sizeof(buf)) { + read_size = sizeof(buf); + } else { + read_size = (int)read_len; + } + + php_stream_seek(fp, 0, SEEK_SET); + + while (read_size && (len = php_stream_read(fp, (char*)buf, read_size)) > 0) { + EVP_VerifyUpdate (&md_ctx, buf, len); + read_len -= (off_t)len; + + if (read_len < read_size) { + read_size = (int)read_len; + } + } + + if (EVP_VerifyFinal(&md_ctx, (unsigned char *)sig, sig_len, key) != 1) { + /* 1: signature verified, 0: signature does not match, -1: failed signature operation */ + EVP_MD_CTX_cleanup(&md_ctx); + + if (error) { + spprintf(error, 0, "broken openssl signature"); + } + + return FAILURE; + } + + EVP_MD_CTX_cleanup(&md_ctx); +#endif + + *signature_len = phar_hex_str((const char*)sig, sig_len, signature TSRMLS_CC); + } + break; +#if HAVE_HASH_EXT + case PHAR_SIG_SHA512: { + unsigned char digest[64]; + PHP_SHA512_CTX context; + + PHP_SHA512Init(&context); + read_len = end_of_phar; + + if (read_len > sizeof(buf)) { + read_size = sizeof(buf); + } else { + read_size = (int)read_len; + } + + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { + PHP_SHA512Update(&context, buf, len); + read_len -= (off_t)len; + if (read_len < read_size) { + read_size = (int)read_len; + } + } + + PHP_SHA512Final(digest, &context); + + if (memcmp(digest, sig, sizeof(digest))) { + if (error) { + spprintf(error, 0, "broken signature"); + } + return FAILURE; + } + + *signature_len = phar_hex_str((const char*)digest, sizeof(digest), signature TSRMLS_CC); + break; + } + case PHAR_SIG_SHA256: { + unsigned char digest[32]; + PHP_SHA256_CTX context; + + PHP_SHA256Init(&context); + read_len = end_of_phar; + + if (read_len > sizeof(buf)) { + read_size = sizeof(buf); + } else { + read_size = (int)read_len; + } + + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { + PHP_SHA256Update(&context, buf, len); + read_len -= (off_t)len; + if (read_len < read_size) { + read_size = (int)read_len; + } + } + + PHP_SHA256Final(digest, &context); + + if (memcmp(digest, sig, sizeof(digest))) { + if (error) { + spprintf(error, 0, "broken signature"); + } + return FAILURE; + } + + *signature_len = phar_hex_str((const char*)digest, sizeof(digest), signature TSRMLS_CC); + break; + } +#else + case PHAR_SIG_SHA512: + case PHAR_SIG_SHA256: + if (error) { + spprintf(error, 0, "unsupported signature"); + } + return FAILURE; +#endif + case PHAR_SIG_SHA1: { + unsigned char digest[20]; + PHP_SHA1_CTX context; + + PHP_SHA1Init(&context); + read_len = end_of_phar; + + if (read_len > sizeof(buf)) { + read_size = sizeof(buf); + } else { + read_size = (int)read_len; + } + + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { + PHP_SHA1Update(&context, buf, len); + read_len -= (off_t)len; + if (read_len < read_size) { + read_size = (int)read_len; + } + } + + PHP_SHA1Final(digest, &context); + + if (memcmp(digest, sig, sizeof(digest))) { + if (error) { + spprintf(error, 0, "broken signature"); + } + return FAILURE; + } + + *signature_len = phar_hex_str((const char*)digest, sizeof(digest), signature TSRMLS_CC); + break; + } + case PHAR_SIG_MD5: { + unsigned char digest[16]; + PHP_MD5_CTX context; + + PHP_MD5Init(&context); + read_len = end_of_phar; + + if (read_len > sizeof(buf)) { + read_size = sizeof(buf); + } else { + read_size = (int)read_len; + } + + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { + PHP_MD5Update(&context, buf, len); + read_len -= (off_t)len; + if (read_len < read_size) { + read_size = (int)read_len; + } + } + + PHP_MD5Final(digest, &context); + + if (memcmp(digest, sig, sizeof(digest))) { + if (error) { + spprintf(error, 0, "broken signature"); + } + return FAILURE; + } + + *signature_len = phar_hex_str((const char*)digest, sizeof(digest), signature TSRMLS_CC); + break; + } + default: + if (error) { + spprintf(error, 0, "broken or unsupported signature"); + } + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, int *signature_length, char **error TSRMLS_DC) /* {{{ */ +{ + unsigned char buf[1024]; + int sig_len; + + php_stream_rewind(fp); + + if (phar->signature) { + efree(phar->signature); + phar->signature = NULL; + } + + switch(phar->sig_flags) { +#if HAVE_HASH_EXT + case PHAR_SIG_SHA512: { + unsigned char digest[64]; + PHP_SHA512_CTX context; + + PHP_SHA512Init(&context); + + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { + PHP_SHA512Update(&context, buf, sig_len); + } + + PHP_SHA512Final(digest, &context); + *signature = estrndup((char *) digest, 64); + *signature_length = 64; + break; + } + case PHAR_SIG_SHA256: { + unsigned char digest[32]; + PHP_SHA256_CTX context; + + PHP_SHA256Init(&context); + + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { + PHP_SHA256Update(&context, buf, sig_len); + } + + PHP_SHA256Final(digest, &context); + *signature = estrndup((char *) digest, 32); + *signature_length = 32; + break; + } +#else + case PHAR_SIG_SHA512: + case PHAR_SIG_SHA256: + if (error) { + spprintf(error, 0, "unable to write to phar \"%s\" with requested hash type", phar->fname); + } + + return FAILURE; +#endif + case PHAR_SIG_OPENSSL: { + int siglen; + unsigned char *sigbuf; +#ifdef PHAR_HAVE_OPENSSL + BIO *in; + EVP_PKEY *key; + EVP_MD *mdtype = (EVP_MD *) EVP_sha1(); + EVP_MD_CTX md_ctx; + + in = BIO_new_mem_buf(PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len)); + + if (in == NULL) { + if (error) { + spprintf(error, 0, "unable to write to phar \"%s\" with requested openssl signature", phar->fname); + } + return FAILURE; + } + + key = PEM_read_bio_PrivateKey(in, NULL,NULL, ""); + BIO_free(in); + + if (!key) { + if (error) { + spprintf(error, 0, "unable to process private key"); + } + return FAILURE; + } + + siglen = EVP_PKEY_size(key); + sigbuf = emalloc(siglen + 1); + EVP_SignInit(&md_ctx, mdtype); + + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { + EVP_SignUpdate(&md_ctx, buf, sig_len); + } + + if (!EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, key)) { + efree(sigbuf); + if (error) { + spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", phar->fname); + } + return FAILURE; + } + + sigbuf[siglen] = '\0'; + EVP_MD_CTX_cleanup(&md_ctx); +#else + sigbuf = NULL; + siglen = 0; + php_stream_seek(fp, 0, SEEK_END); + + if (FAILURE == phar_call_openssl_signverify(1, fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen TSRMLS_CC)) { + if (error) { + spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", phar->fname); + } + return FAILURE; + } +#endif + *signature = (char *) sigbuf; + *signature_length = siglen; + } + break; + default: + phar->sig_flags = PHAR_SIG_SHA1; + case PHAR_SIG_SHA1: { + unsigned char digest[20]; + PHP_SHA1_CTX context; + + PHP_SHA1Init(&context); + + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { + PHP_SHA1Update(&context, buf, sig_len); + } + + PHP_SHA1Final(digest, &context); + *signature = estrndup((char *) digest, 20); + *signature_length = 20; + break; + } + case PHAR_SIG_MD5: { + unsigned char digest[16]; + PHP_MD5_CTX context; + + PHP_MD5Init(&context); + + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { + PHP_MD5Update(&context, buf, sig_len); + } + + PHP_MD5Final(digest, &context); + *signature = estrndup((char *) digest, 16); + *signature_length = 16; + break; + } + } + + phar->sig_len = phar_hex_str((const char *)*signature, *signature_length, &phar->signature TSRMLS_CC); + return SUCCESS; +} +/* }}} */ + +void phar_add_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC) /* {{{ */ +{ + char *s; + + while ((s = zend_memrchr(filename, '/', filename_len))) { + filename_len = s - filename; + if (FAILURE == zend_hash_add_empty_element(&phar->virtual_dirs, filename, filename_len)) { + break; + } + } +} +/* }}} */ + +static void phar_update_cached_entry(void *data, void *argument) /* {{{ */ +{ + phar_entry_info *entry = (phar_entry_info *)data; + TSRMLS_FETCH(); + + entry->phar = (phar_archive_data *)argument; + + if (entry->link) { + entry->link = estrdup(entry->link); + } + + if (entry->tmp) { + entry->tmp = estrdup(entry->tmp); + } + + entry->metadata_str.c = 0; + entry->filename = estrndup(entry->filename, entry->filename_len); + entry->is_persistent = 0; + + if (entry->metadata) { + if (entry->metadata_len) { + /* assume success, we would have failed before */ + phar_parse_metadata((char **) &entry->metadata, &entry->metadata, entry->metadata_len TSRMLS_CC); + } else { + zval *t; + + t = entry->metadata; + ALLOC_ZVAL(entry->metadata); + *entry->metadata = *t; + zval_copy_ctor(entry->metadata); +#if PHP_VERSION_ID < 50300 + entry->metadata->refcount = 1; +#else + Z_SET_REFCOUNT_P(entry->metadata, 1); +#endif + entry->metadata_str.c = NULL; + entry->metadata_str.len = 0; + } + } +} +/* }}} */ + +static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ +{ + phar_archive_data *phar; + HashTable newmanifest; + char *fname; + + phar = (phar_archive_data *) emalloc(sizeof(phar_archive_data)); + *phar = **pphar; + phar->is_persistent = 0; + fname = phar->fname; + phar->fname = estrndup(phar->fname, phar->fname_len); + phar->ext = phar->fname + (phar->ext - fname); + + if (phar->alias) { + phar->alias = estrndup(phar->alias, phar->alias_len); + } + + if (phar->signature) { + phar->signature = estrdup(phar->signature); + } + + if (phar->metadata) { + /* assume success, we would have failed before */ + if (phar->metadata_len) { + phar_parse_metadata((char **) &phar->metadata, &phar->metadata, phar->metadata_len TSRMLS_CC); + } else { + zval *t; + + t = phar->metadata; + ALLOC_ZVAL(phar->metadata); + *phar->metadata = *t; + zval_copy_ctor(phar->metadata); +#if PHP_VERSION_ID < 50300 + phar->metadata->refcount = 1; +#else + Z_SET_REFCOUNT_P(phar->metadata, 1); +#endif + } + } + + zend_hash_init(&newmanifest, sizeof(phar_entry_info), + zend_get_hash_value, destroy_phar_manifest_entry, 0); + zend_hash_copy(&newmanifest, &(*pphar)->manifest, NULL, NULL, sizeof(phar_entry_info)); + zend_hash_apply_with_argument(&newmanifest, (apply_func_arg_t) phar_update_cached_entry, (void *)phar TSRMLS_CC); + phar->manifest = newmanifest; + zend_hash_init(&phar->mounted_dirs, sizeof(char *), + zend_get_hash_value, NULL, 0); + zend_hash_init(&phar->virtual_dirs, sizeof(char *), + zend_get_hash_value, NULL, 0); + zend_hash_copy(&phar->virtual_dirs, &(*pphar)->virtual_dirs, NULL, NULL, sizeof(void *)); + *pphar = phar; +} +/* }}} */ + +int phar_copy_on_write(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ +{ + phar_archive_data **newpphar, *newphar = NULL; + + if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), (*pphar)->fname, (*pphar)->fname_len, (void *)&newphar, sizeof(phar_archive_data *), (void **)&newpphar)) { + return FAILURE; + } + + *newpphar = *pphar; + phar_copy_cached_phar(newpphar TSRMLS_CC); + /* invalidate phar cache */ + PHAR_G(last_phar) = NULL; + PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + + if (newpphar[0]->alias_len && FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), newpphar[0]->alias, newpphar[0]->alias_len, (void*)newpphar, sizeof(phar_archive_data*), NULL)) { + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), (*pphar)->fname, (*pphar)->fname_len); + return FAILURE; + } + + *pphar = *newpphar; + return SUCCESS; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 256730fbec..07725875f4 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -34,7 +34,7 @@ # define PHAR_SET_16(buffer) (buffer) #endif -static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC) +static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC) /* {{{ */ { union { phar_zip_extra_field_header header; @@ -46,32 +46,41 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_ui if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) { return FAILURE; } + if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') { /* skip to next header */ php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR); len -= PHAR_GET_16(h.header.size) + 4; continue; } + /* unix3 header found */ read = php_stream_read(fp, (char *) &(h.unix3.crc32), sizeof(h.unix3) - sizeof(h.header)); len -= read + 4; + if (sizeof(h.unix3) - sizeof(h.header) != read) { return FAILURE; } + if (PHAR_GET_16(h.unix3.size) > sizeof(h.unix3) - 4) { /* skip symlink filename - we may add this support in later */ php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR); } + /* set permissions */ entry->flags &= PHAR_ENT_COMPRESSION_MASK; + if (entry->is_dir) { entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK; } else { entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK; } + } while (len); + return SUCCESS; } +/* }}} */ /* extracted from libzip @@ -93,7 +102,7 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_ui 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -106,38 +115,40 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_ui OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -static time_t phar_zip_d2u_time(int dtime, int ddate) +static time_t phar_zip_d2u_time(int dtime, int ddate) /* {{{ */ { - struct tm *tm, tmbuf; - time_t now; + struct tm *tm, tmbuf; + time_t now; + + now = time(NULL); + tm = php_localtime_r(&now, &tmbuf); - now = time(NULL); - tm = php_localtime_r(&now, &tmbuf); - - tm->tm_year = ((ddate>>9)&127) + 1980 - 1900; - tm->tm_mon = ((ddate>>5)&15) - 1; - tm->tm_mday = ddate&31; + tm->tm_year = ((ddate>>9)&127) + 1980 - 1900; + tm->tm_mon = ((ddate>>5)&15) - 1; + tm->tm_mday = ddate&31; - tm->tm_hour = (dtime>>11)&31; - tm->tm_min = (dtime>>5)&63; - tm->tm_sec = (dtime<<1)&62; + tm->tm_hour = (dtime>>11)&31; + tm->tm_min = (dtime>>5)&63; + tm->tm_sec = (dtime<<1)&62; - return mktime(tm); + return mktime(tm); } +/* }}} */ -static void phar_zip_u2d_time(time_t time, php_uint16 *dtime, php_uint16 *ddate) +static void phar_zip_u2d_time(time_t time, php_uint16 *dtime, php_uint16 *ddate) /* {{{ */ { - struct tm *tm, tmbuf; + struct tm *tm, tmbuf; - tm = php_localtime_r(&time, &tmbuf); - *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday; - *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1); + tm = php_localtime_r(&time, &tmbuf); + *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday; + *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1); } +/* }}} */ /** * Does not check for a previously opened phar in the cache. * - * Parse a new one and add it to the cache, returning either SUCCESS or + * 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_from_fp to process a zip-based phar, but can be called @@ -155,6 +166,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, char *p = buf, *ext, *actual_alias = NULL; size = php_stream_tell(fp); + if (size > sizeof(locator) + 65536) { /* seek to max comment length + end of central directory record */ size = sizeof(locator) + 65536; @@ -168,6 +180,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } else { php_stream_seek(fp, 0, SEEK_SET); } + if (!(read = php_stream_read(fp, buf, size))) { php_stream_close(fp); if (error) { @@ -175,6 +188,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } return FAILURE; } + while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) { if (!memcmp(p + 1, "K\5\6", 3)) { memcpy((void *)&locator, (void *) p, sizeof(locator)); @@ -186,6 +200,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } return FAILURE; } + if (locator.counthere != locator.count) { if (error) { spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname); @@ -193,45 +208,65 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, php_stream_close(fp); return FAILURE; } - mydata = ecalloc(sizeof(phar_archive_data), 1); + + mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist)); + mydata->is_persistent = PHAR_G(persist); /* read in archive comment, if any */ if (locator.comment_len) { char *metadata; metadata = p + sizeof(locator); + if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) { if (error) { spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname); } php_stream_close(fp); - efree(mydata); + pefree(mydata, mydata->is_persistent); return FAILURE; } + + mydata->metadata_len = PHAR_GET_16(locator.comment_len); + if (phar_parse_metadata(&metadata, &mydata->metadata, PHAR_GET_16(locator.comment_len) TSRMLS_CC) == FAILURE) { + mydata->metadata_len = 0; /* if not valid serialized data, it is a regular string */ - ALLOC_INIT_ZVAL(mydata->metadata); - ZVAL_STRINGL(mydata->metadata, metadata, PHAR_GET_16(locator.comment_len), 1); + + if (entry.is_persistent) { + ALLOC_PERMANENT_ZVAL(mydata->metadata); + } else { + ALLOC_ZVAL(mydata->metadata); + } + + INIT_ZVAL(*mydata->metadata); + metadata = pestrndup(metadata, PHAR_GET_16(locator.comment_len), mydata->is_persistent); + ZVAL_STRINGL(mydata->metadata, metadata, PHAR_GET_16(locator.comment_len), 0); } } else { mydata->metadata = NULL; } + goto foundit; } } + php_stream_close(fp); + if (error) { spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname); } + return FAILURE; foundit: - mydata->fname = estrndup(fname, fname_len); + mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent); #ifdef PHP_WIN32 phar_unixify_path_separators(mydata->fname, fname_len); #endif mydata->is_zip = 1; mydata->fname_len = fname_len; ext = strrchr(mydata->fname, '/'); + if (ext) { mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext); if (mydata->ext == ext) { @@ -241,22 +276,28 @@ foundit: mydata->ext_len = (mydata->fname + fname_len) - mydata->ext; } } + /* clean up on big-endian systems */ /* seek to central directory */ php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET); /* read in central directory */ - zend_hash_init(&mydata->manifest, sizeof(phar_entry_info), - zend_get_hash_value, destroy_phar_manifest_entry, 0); - zend_hash_init(&mydata->mounted_dirs, sizeof(char *), - zend_get_hash_value, NULL, 0); + zend_hash_init(&mydata->manifest, PHAR_GET_16(locator.count), + zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent); + zend_hash_init(&mydata->mounted_dirs, 5, + zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent); + zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2, + zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent); entry.phar = mydata; entry.is_zip = 1; entry.fp_type = PHAR_FP; + entry.is_persistent = mydata->is_persistent; #define PHAR_ZIP_FAIL(errmsg) \ zend_hash_destroy(&mydata->manifest); \ mydata->manifest.arBuckets = 0; \ zend_hash_destroy(&mydata->mounted_dirs); \ mydata->mounted_dirs.arBuckets = 0; \ + zend_hash_destroy(&mydata->virtual_dirs); \ + mydata->virtual_dirs.arBuckets = 0; \ php_stream_close(fp); \ if (mydata->metadata) { \ zval_dtor(mydata->metadata); \ @@ -264,25 +305,31 @@ foundit: if (error) { \ spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \ } \ - efree(mydata->fname); \ + pefree(mydata->fname, mydata->is_persistent); \ if (mydata->alias) { \ - efree(mydata->alias); \ + pefree(mydata->alias, mydata->is_persistent); \ } \ - efree(mydata); \ + pefree(mydata, mydata->is_persistent); \ return FAILURE; /* add each central directory item to the manifest */ - for (i = 0; i < locator.count; ++i) { + for (i = 0; i < PHAR_GET_16(locator.count); ++i) { phar_zip_central_dir_file zipentry; if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) { PHAR_ZIP_FAIL("unable to read central directory entry, truncated"); } + /* clean up for bigendian systems */ if (memcmp("PK\1\2", zipentry.signature, 4)) { /* corrupted entry */ PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature"); } + + if (entry.is_persistent) { + entry.manifest_pos = i; + } + entry.compressed_filesize = PHAR_GET_32(zipentry.compsize); entry.uncompressed_filesize = PHAR_GET_32(zipentry.uncompsize); entry.crc32 = PHAR_GET_32(zipentry.crc32); @@ -292,19 +339,25 @@ foundit: entry.header_offset = PHAR_GET_32(zipentry.offset); entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) + PHAR_GET_16(zipentry.extra_len); + if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) { PHAR_ZIP_FAIL("Cannot process encrypted zip files"); } + if (!PHAR_GET_16(zipentry.filename_len)) { PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)"); } + entry.filename_len = PHAR_GET_16(zipentry.filename_len); - entry.filename = (char *) emalloc(entry.filename_len + 1); + entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent); + if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated"); } + entry.filename[entry.filename_len] = '\0'; + if (entry.filename[entry.filename_len - 1] == '/') { entry.is_dir = 1; entry.filename_len--; @@ -312,14 +365,18 @@ foundit: } else { entry.is_dir = 0; } + + phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC); + if (PHAR_GET_16(zipentry.extra_len)) { off_t loc = php_stream_tell(fp); if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len) TSRMLS_CC)) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory"); } php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET); } + switch (zipentry.compressed) { case PHAR_ZIP_COMP_NONE : /* compression flag already set */ @@ -327,112 +384,134 @@ foundit: case PHAR_ZIP_COMP_DEFLATE : entry.flags |= PHAR_ENT_COMPRESSED_GZ; if (!PHAR_G(has_zlib)) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("zlib extension is required"); } break; case PHAR_ZIP_COMP_BZIP2 : entry.flags |= PHAR_ENT_COMPRESSED_BZ2; if (!PHAR_G(has_bz2)) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("bzip2 extension is required"); } break; case 1 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip"); case 2 : case 3 : case 4 : case 5 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip"); case 6 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip"); case 7 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip"); case 9 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip"); case 10 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip"); case 14 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip"); case 18 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip"); case 19 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip"); case 97 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip"); case 98 : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip"); default : - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip"); } + /* get file metadata */ if (zipentry.comment_len) { if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in file comment, truncated"); } + p = buf; + entry.metadata_len = zipentry.comment_len; + if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len) TSRMLS_CC) == FAILURE) { + entry.metadata_len = 0; /* if not valid serialized data, it is a regular string */ - ALLOC_INIT_ZVAL(entry.metadata); - ZVAL_STRINGL(entry.metadata, buf, PHAR_GET_16(zipentry.comment_len), 1); + + if (entry.is_persistent) { + ALLOC_PERMANENT_ZVAL(entry.metadata); + } else { + ALLOC_ZVAL(entry.metadata); + } + + INIT_ZVAL(*entry.metadata); + ZVAL_STRINGL(entry.metadata, pestrndup(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent), PHAR_GET_16(zipentry.comment_len), 0); } } else { entry.metadata = NULL; } + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { php_stream_filter *filter; off_t saveloc; - /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but - not very. */ + /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but not very. */ saveloc = php_stream_tell(fp); php_stream_seek(fp, PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET); mydata->alias_len = entry.uncompressed_filesize; + if (entry.flags & PHAR_ENT_COMPRESSED_GZ) { filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed"); } + php_stream_filter_append(&fp->readfilters, filter); + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); + } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) { php_stream_filter *filter; filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed"); } + php_stream_filter_append(&fp->readfilters, filter); php_stream_filter_append(&fp->readfilters, filter); + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); } else { if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { - efree(entry.filename); + pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } } @@ -440,8 +519,11 @@ foundit: /* return to central directory parsing */ php_stream_seek(fp, saveloc, SEEK_SET); } + + phar_set_inode(&entry TSRMLS_CC); zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL); } + mydata->fp = fp; if (zend_hash_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) { @@ -451,6 +533,7 @@ foundit: } zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (actual_alias) { phar_archive_data **fd_ptr; @@ -462,7 +545,9 @@ foundit: zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); return FAILURE; } + mydata->is_temporary_alias = 0; + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void **)&fd_ptr)) { if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, mydata->alias_len TSRMLS_CC)) { if (error) { @@ -473,7 +558,13 @@ foundit: return FAILURE; } } - mydata->alias = actual_alias; + + mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias; + + if (entry.is_persistent) { + efree(actual_alias); + } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); } else { phar_archive_data **fd_ptr; @@ -488,18 +579,22 @@ foundit: return FAILURE; } } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); - mydata->alias = estrndup(alias, alias_len); + mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent); mydata->alias_len = alias_len; } else { - mydata->alias = estrndup(mydata->fname, fname_len); + mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent); mydata->alias_len = fname_len; } + mydata->is_temporary_alias = 1; } + if (pphar) { *pphar = mydata; } + return SUCCESS; } /* }}} */ @@ -537,6 +632,7 @@ int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_l 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; } /* }}} */ @@ -562,9 +658,11 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ entry = (phar_entry_info *)data; p = (struct _phar_zip_pass*) arg; + if (entry->is_mounted) { return ZEND_HASH_APPLY_KEEP; } + if (entry->is_deleted) { if (entry->fp_refcount <= 0) { return ZEND_HASH_APPLY_REMOVE; @@ -573,6 +671,7 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ return ZEND_HASH_APPLY_KEEP; } } + memset(&local, 0, sizeof(local)); memset(¢ral, 0, sizeof(central)); memset(&perms, 0, sizeof(perms)); @@ -587,18 +686,22 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ CRC32(perms.crc32, (char)perms.perms & 0xFF); CRC32(perms.crc32, (char)perms.perms & 0xFF00 >> 8); perms.crc32 = PHAR_SET_32(~(perms.crc32)); + if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { local.compressed = central.compressed = PHAR_SET_16(PHAR_ZIP_COMP_DEFLATE); } + if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) { local.compressed = central.compressed = PHAR_SET_16(PHAR_ZIP_COMP_BZIP2); } + /* do not use PHAR_SET_16 on either field of the next line */ phar_zip_u2d_time(entry->timestamp, &local.timestamp, &local.datestamp); central.timestamp = local.timestamp; central.datestamp = local.datestamp; central.filename_len = local.filename_len = PHAR_SET_16(entry->filename_len + (entry->is_dir ? 1 : 0)); central.offset = PHAR_SET_32(php_stream_tell(p->filefp)); + /* do extra field for perms later */ if (entry->is_modified) { php_uint32 loc; @@ -614,29 +717,36 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ } goto continue_dir; } + if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) { spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } - efp = phar_get_efp(entry, 0 TSRMLS_CC); + efp = phar_get_efp(entry, 0 TSRMLS_CC); newcrc32 = ~0; + for (loc = 0;loc < entry->uncompressed_filesize; ++loc) { CRC32(newcrc32, php_stream_getc(efp)); } + entry->crc32 = ~newcrc32; central.uncompsize = local.uncompsize = PHAR_SET_32(entry->uncompressed_filesize); + if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) { /* not compressed */ entry->compressed_filesize = entry->uncompressed_filesize; central.compsize = local.compsize = PHAR_SET_32(entry->compressed_filesize); goto not_compressed; } + filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC); + if (!filter) { if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); @@ -650,20 +760,26 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ /* work around inability to specify freedom in write and strictness in read count */ entry->cfp = php_stream_fopen_tmpfile(); + if (!entry->cfp) { spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_flush(efp); + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_filter_append((&entry->cfp->writefilters), filter); + if (entry->uncompressed_filesize != php_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize)) { spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_filter_flush(filter, 1); php_stream_flush(entry->cfp); php_stream_filter_remove(filter, 1 TSRMLS_CC); @@ -677,6 +793,7 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ } else { central.uncompsize = local.uncompsize = PHAR_SET_32(entry->uncompressed_filesize); central.compsize = local.compsize = PHAR_SET_32(entry->compressed_filesize); + if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) { spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; @@ -699,29 +816,36 @@ continue_dir: PHP_VAR_SERIALIZE_DESTROY(metadata_hash); central.comment_len = PHAR_SET_16(entry->metadata_str.len); } + entry->header_offset = php_stream_tell(p->filefp); offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms); + if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) { spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) { spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->is_dir) { if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) { spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (1 != php_stream_write(p->filefp, "/", 1)) { spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) { spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (1 != php_stream_write(p->centralfp, "/", 1)) { spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; @@ -731,40 +855,49 @@ continue_dir: spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) { spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } } + if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) { spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) { spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->is_modified) { if (entry->cfp) { if (entry->compressed_filesize != php_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize)) { spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_close(entry->cfp); entry->cfp = NULL; } else { if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) { return ZEND_HASH_APPLY_STOP; } + phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC); + if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), p->filefp, entry->uncompressed_filesize)) { spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } } + if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) { php_stream_close(entry->fp); } + entry->is_modified = 0; } else { if (entry->fp_refcount) { @@ -779,22 +912,27 @@ continue_dir: break; } } + if (!entry->is_dir && entry->compressed_filesize && entry->compressed_filesize != php_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize)) { spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } } + entry->fp = NULL; entry->offset = entry->offset_abs = offset; entry->fp_type = PHAR_FP; + if (entry->metadata_str.c) { if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) { spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); smart_str_free(&entry->metadata_str); return ZEND_HASH_APPLY_STOP; } + smart_str_free(&entry->metadata_str); } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -820,6 +958,13 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau entry.phar = phar; entry.fp_type = PHAR_MOD; + if (phar->is_persistent) { + if (error) { + spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname); + } + return EOF; + } + if (phar->is_data) { goto nostub; } @@ -827,15 +972,18 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau /* set alias */ if (!phar->is_temporary_alias && phar->alias_len) { entry.fp = php_stream_fopen_tmpfile(); + if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) { if (error) { spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname); } return EOF; } + entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len; entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); entry.filename_len = sizeof(".phar/alias.txt")-1; + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { if (error) { spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname); @@ -845,6 +993,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } else { zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1); } + /* register alias */ if (phar->alias_len) { if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error TSRMLS_CC)) { @@ -862,12 +1011,15 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } 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\"", phar->fname); @@ -878,6 +1030,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } else { free_user_stub = 0; } + if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) { if (error) { @@ -888,6 +1041,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + len = pos - user_stub + 18; entry.fp = php_stream_fopen_tmpfile(); entry.uncompressed_filesize = len + 5; @@ -903,8 +1057,10 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau 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; + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { if (free_user_stub) { efree(user_stub); @@ -914,6 +1070,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + if (free_user_stub) { efree(user_stub); } @@ -958,9 +1115,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } } } - nostub: - if (phar->fp && !phar->is_brandnew) { oldfile = phar->fp; closeoldfile = 0; @@ -973,6 +1128,7 @@ nostub: /* save modified files to the zip */ pass.old = oldfile; pass.filefp = php_stream_fopen_tmpfile(); + if (!pass.filefp) { if (closeoldfile) { php_stream_close(oldfile); @@ -982,7 +1138,9 @@ nostub: } return EOF; } + pass.centralfp = php_stream_fopen_tmpfile(); + if (!pass.centralfp) { if (closeoldfile) { php_stream_close(oldfile); @@ -992,12 +1150,14 @@ nostub: } return EOF; } + pass.free_fp = pass.free_ufp = 1; memset(&eocd, 0, sizeof(eocd)); strncpy(eocd.signature, "PK\5\6", 4); eocd.counthere = eocd.count = zend_hash_num_elements(&phar->manifest); zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC); + if (temperr) { php_stream_close(pass.filefp); php_stream_close(pass.centralfp); @@ -1015,6 +1175,7 @@ nostub: eocd.cdir_size = php_stream_tell(pass.centralfp); eocd.cdir_offset = php_stream_tell(pass.filefp); php_stream_seek(pass.centralfp, 0, SEEK_SET); + if (eocd.cdir_size != php_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL)) { php_stream_close(pass.filefp); php_stream_close(pass.centralfp); @@ -1026,13 +1187,16 @@ nostub: } return EOF; } + php_stream_close(pass.centralfp); + if (phar->metadata) { /* set phar metadata */ PHP_VAR_SERIALIZE_INIT(metadata_hash); php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(metadata_hash); eocd.comment_len = PHAR_SET_16(main_metadata_str.len); + if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) { php_stream_close(pass.filefp); if (error) { @@ -1044,6 +1208,7 @@ nostub: smart_str_free(&main_metadata_str); return EOF; } + if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) { php_stream_close(pass.filefp); if (error) { @@ -1055,7 +1220,9 @@ nostub: smart_str_free(&main_metadata_str); return EOF; } + smart_str_free(&main_metadata_str); + } else { if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) { php_stream_close(pass.filefp); @@ -1068,17 +1235,21 @@ nostub: return EOF; } } + if (phar->fp && pass.free_fp) { php_stream_close(phar->fp); } + if (phar->ufp) { if (pass.free_ufp) { php_stream_close(phar->ufp); } phar->ufp = NULL; } + /* re-open */ phar->is_brandnew = 0; + if (phar->donotflush) { /* deferred flush */ phar->fp = pass.filefp; @@ -1106,3 +1277,12 @@ nostub: return EOF; } /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */