]> granicus.if.org Git - php/commitdiff
- This is as good as it gets in HEAD at present (~30% tests fail here)
authorSteph Fox <sfox@php.net>
Fri, 1 Aug 2008 13:45:06 +0000 (13:45 +0000)
committerSteph Fox <sfox@php.net>
Fri, 1 Aug 2008 13:45:06 +0000 (13:45 +0000)
23 files changed:
ext/phar/Makefile.frag
ext/phar/build_precommand.php
ext/phar/cgidebug
ext/phar/config.m4
ext/phar/config.w32
ext/phar/dirstream.c
ext/phar/dirstream.h
ext/phar/func_interceptors.c
ext/phar/func_interceptors.h
ext/phar/makestub.php
ext/phar/package.php
ext/phar/phar.c
ext/phar/phar.phar
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/phar_path_check.c
ext/phar/phar_path_check.re
ext/phar/php_phar.h
ext/phar/stream.c
ext/phar/stream.h
ext/phar/tar.c
ext/phar/util.c
ext/phar/zip.c

index d693e8dd86ae966ccf996dcca33a3e17c7d87074..32b5d04104fa5180aed14ceb7e59e5382a3710c8 100755 (executable)
@@ -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)
index 8904afdf054c61d5717954fb290a434decd8e79a..c2fe9c8e5236adeb0b33409edf2fb175e933e8f1 100755 (executable)
@@ -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);
index c0d89d7d25c96227d95422f4015a8e1d1935c190..1907581900e270aec7d327af92cb1896d7aca04e 100755 (executable)
@@ -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
index 78b4fa30c754ef982bd0cfd60dcbc89e8713820e..32337ec0072160228c5da57f442dea39c522ef06 100644 (file)
@@ -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
index cbc68ca2e540986a9fe57e5dc185f732c82a81d0..6632180649db80baa04ce10c212d0675900f7b2a 100644 (file)
@@ -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);
 }
index 90f02fc2a1bf1ab636fd158d2ead278e996442f1..e94a11a59666d9ab95a7265bb52b79dacc8b056e 100644 (file)
@@ -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
+ */
index 6467b55bcee2bcf690201914658e42b6638fe609..b6b49699b9716a1dc8f3e3182f1d74a694212f7f 100644 (file)
@@ -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        |
index 853cf1477d01b482b27875782eb9d5bb1d56a9b5..d3308b571a808a11f4593daf6dbca84ccc2fe261 100644 (file)
@@ -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://<archive>/ */
                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://<archive>/ */
                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://<archive>/ */
+                       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://<archive>/ */
                        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://<archive>/ */
                        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://<archive>/ */
                        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;
 }
 /* }}} */
 
index b48be591ed7758894f7f143ec5c7665fc626e057..d67d5f63803f0b9a7b35195868ac7205ce942e71 100644 (file)
@@ -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()
 
 /*
index 44e0b963378f50f2b290e8966ac3965f1458d944..2bb30e96d8375ad6df1cb1284f01098f74c6e2d1 100644 (file)
@@ -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 .= ");
 }";
index f4d63ae21da2601ced3ce538e117985873855684..d7d81e2e6af5308730571a0b772029915c9c4fe0 100644 (file)
@@ -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();
 }
 
 ?>
index 27afc67dc74f35d032380740fd00abe96d7352d3..9cb0b749162e1da08fe452adfcb2a723232d9edd 100644 (file)
@@ -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?"<br />":"\n");       
+       PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
        PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
-       PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");       
-       PUTS("Portions of tar implementation Copyright (c) 2003-2007 Tim Kientzle.");
+       PUTS(!sapi_module.phpinfo_as_text?"<br />":"\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
index f26ce2b756fe83dcb0cf7fcd5d06151c0a6aa102..30ad018170c63730cbcb642cdf65bf81ba7fd61b 100755 (executable)
Binary files a/ext/phar/phar.phar and b/ext/phar/phar.phar differ
index 9005ca84eb2958087ab8b7624492b8116f6afa93..d97dee0adbf98d42d06bf670d8f2210700b7858f 100755 (executable)
@@ -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        |
 #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 */
 #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 */
 #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 {
index eab95dd1aaab273cf399165affd321902b72d636..fe8eff26614b99c1d302f6ad366be66877574cff 100755 (executable)
@@ -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("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ", sizeof("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ") - 1);
-               PHPWRITE(entry, entry_len);
-               PHPWRITE(" Not Found</h1>\n </body>\n</html>",  sizeof(" Not Found</h1>\n </body>\n</html>") - 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("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ", sizeof("<html>\n <head>\n  <title>File Not Found</title>\n </head>\n <body>\n  <h1>404 - File ") - 1);
+       PHPWRITE(entry, entry_len);
+       PHPWRITE(" Not Found</h1>\n </body>\n</html>",  sizeof(" Not Found</h1>\n </body>\n</html>") - 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(&regexiter);
                }
+
+               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)
index c1a273281b58f9a0974a13050301b80f84cd3661..480eed717b27a13284f421aeae2e11d35906dd35 100755 (executable)
@@ -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        |
index 97a6b5d565f1f6f619b92f297f0676ed3124c46e..9e89151fff19bc0303ceffa4ec4fbe3182960666 100755 (executable)
@@ -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        |
index 89023972f6109d03cbcf1eb40a9db0e58f971949..f0a34a2e017cf51ce497ca10a58bc4196b3ee328 100644 (file)
@@ -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 */
 
 
 /*
index 32add212d10f09de3817b622fd87341f6a7bb048..7aecd8fb37c2b05ed00887cbbd13b651501686d7 100644 (file)
@@ -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
+ */
index 49f6b612f1288a3d27bf9173769eb05b3f77f505..c69202bfcf0e75d599890e6f549d4372b1c8a689 100644 (file)
@@ -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        |
index d53b0776290ae51a81291b3efa60e45b95021c0c..e5159dab92ed0f25b3dac00abbf40903ba86d932 100644 (file)
@@ -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[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();";
        php_stream *oldfile, *newfile, *stubfile;
-       int closeoldfile, free_user_stub;
+       int closeoldfile, free_user_stub, signature_length;
        struct _phar_pass_tar_info pass;
-       char *buf;
+       char *buf, *signature, sigbuf[8];
 
        entry.flags = PHAR_ENT_PERM_DEF_FILE;
        entry.timestamp = time(NULL);
@@ -724,6 +847,13 @@ int phar_tar_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 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
+ */
index 0099928f8bea92bcd01f471e38fdb80f63c6aec8..008bd3f5ead835c4e584ef91c2d1115a2e73607c 100644 (file)
 /* $Id$ */
 
 #include "phar_internal.h"
+
+#ifdef PHAR_HAVE_OPENSSL
+/* OpenSSL includes */
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/crypto.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/conf.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/pkcs12.h>
+#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
+ */
index 256730fbecdbfbd225e1543150bd3d1fb4f95390..07725875f41563623d11386c723e45122b538bd4 100644 (file)
@@ -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(&central, 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 *)&central, sizeof(central))) {
                spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                return ZEND_HASH_APPLY_STOP;
        }
+
        if (entry->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
+ */