]> granicus.if.org Git - php/commitdiff
fix windows build and more performance jumps (these are minor)
authorGreg Beaver <cellog@php.net>
Wed, 18 Jun 2008 06:38:47 +0000 (06:38 +0000)
committerGreg Beaver <cellog@php.net>
Wed, 18 Jun 2008 06:38:47 +0000 (06:38 +0000)
implement real copy-on-write
use virtual_dirs for wrapper stat

ext/phar/dirstream.c
ext/phar/func_interceptors.c
ext/phar/phar.c
ext/phar/phar.phar
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/stream.c
ext/phar/tar.c
ext/phar/tests/cached_manifest_1.phpt [new file with mode: 0644]
ext/phar/util.c
ext/phar/zip.c

index 19ac6f9cef4c72d1fac7942e100735338b2cb2c7..6c2bd7231cc42c0b479a7aaa0b43752e261c9d10 100644 (file)
@@ -145,9 +145,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);
 }
 /* }}} */
 
@@ -445,7 +445,6 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in
        }
 
        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);
@@ -483,6 +482,12 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in
                return FAILURE;
        }
 
+       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 FAILURE;
+       }
+
        memset((void *) &entry, 0, sizeof(phar_entry_info));
 
        /* strip leading "/" */
@@ -565,7 +570,6 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_
        }
 
        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);
@@ -586,6 +590,12 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_
        }
 
        /* now for the easy part */
+       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);
+               return FAILURE;
+       }
+
        entry->is_deleted = 1;
        entry->is_modified = 1;
        phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
index 621d87392a5d5359921eb4d100c859625f48bf2e..0abc62396359209641afdbb93ff7f8cfad059152 100644 (file)
@@ -29,7 +29,8 @@ PHAR_FUNC(phar_opendir) /* {{{ */
        int filename_len;
        zval *zcontext = NULL;
 
-       if (!PHAR_GLOBALS->phar_fname_map.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) {
+       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) {
@@ -97,7 +98,8 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */
        long maxlen = PHP_STREAM_COPY_ALL;
        zval *zcontext = NULL;
 
-       if (!PHAR_GLOBALS->phar_fname_map.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) {
+       if ((PHAR_GLOBALS->phar_fname_map.arBuckets && !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map)))
+               && !cached_phars.arBuckets) {
                goto skip_phar;
        }
        /* Parse arguments */
@@ -131,7 +133,8 @@ 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 ((PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar)))
+                               && (PHAR_G(manifest_cached) && FAILURE == (zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)))) {
                                efree(arch);
                                goto skip_phar;
                        }
@@ -222,7 +225,8 @@ PHAR_FUNC(phar_readfile) /* {{{ */
        zval *zcontext = NULL;
        php_stream *stream;
 
-       if (!PHAR_GLOBALS->phar_fname_map.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) {
+       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) {
@@ -249,7 +253,8 @@ 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 ((PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar)))
+                       && (PHAR_G(manifest_cached) && FAILURE == (zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)))) {
                        efree(arch);
                        goto skip_phar;
                }
@@ -312,7 +317,8 @@ PHAR_FUNC(phar_fopen) /* {{{ */
        zval *zcontext = NULL;
        php_stream *stream;
 
-       if (!PHAR_GLOBALS->phar_fname_map.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) {
+       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;
        }
@@ -340,7 +346,8 @@ 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 ((PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar)))
+                       && (PHAR_G(manifest_cached) && FAILURE == (zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)))) {
                        efree(arch);
                        goto skip_phar;
                }
@@ -614,7 +621,8 @@ void phar_file_stat(const char *filename, php_stat_len filename_length, int type
                        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 ((PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar)))
+                               && (PHAR_G(manifest_cached) && FAILURE == (zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)))) {
                                efree(arch);
                                goto skip_phar;
                        }
@@ -876,7 +884,8 @@ PHAR_FUNC(phar_is_file) /* {{{ */
        char *filename;
        int filename_len;
 
-       if (!PHAR_GLOBALS->phar_fname_map.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) {
+       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) {
@@ -902,7 +911,8 @@ PHAR_FUNC(phar_is_file) /* {{{ */
                        /* 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 ((PHAR_GLOBALS->phar_fname_map.arBuckets && SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar)))
+                               || (PHAR_G(manifest_cached) && SUCCESS == (zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)))) {
                                phar_entry_info *etemp;
 
                                entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC);
@@ -936,7 +946,8 @@ PHAR_FUNC(phar_is_link) /* {{{ */
        char *filename;
        int filename_len;
 
-       if (!PHAR_GLOBALS->phar_fname_map.arBuckets || !zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) {
+       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) {
@@ -962,7 +973,8 @@ PHAR_FUNC(phar_is_link) /* {{{ */
                        /* 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 ((PHAR_GLOBALS->phar_fname_map.arBuckets && SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar)))
+                               || (PHAR_G(manifest_cached) && SUCCESS == (zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)))) {
                                phar_entry_info *etemp;
 
                                entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1 TSRMLS_CC);
index 3e19e3d4fc11ae2062628a787095408a5df65600..e4f917cce974262e36eee60cb1c3265c18ad204a 100644 (file)
@@ -95,8 +95,8 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
 /* }}}*/
 
 /* this global stores the global cached pre-parsed manifests */
-static HashTable cached_phars;
-static HashTable cached_alias;
+HashTable cached_phars;
+HashTable cached_alias;
 
 static void phar_split_cache_list(TSRMLS_D)
 {
@@ -104,19 +104,24 @@ static void phar_split_cache_list(TSRMLS_D)
        char *key, *lasts, *end;
        char ds[1];
        phar_archive_data *phar;
+       uint i = 0;
 
        if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) {
                return;
        }
 
        ds[0] = DEFAULT_DIR_SEPARATOR;
-       zend_init_rsrc_list(TSRMLS_C);
        tmp = estrdup(PHAR_GLOBALS->cache_list);
 
        /* fake request startup */
        PHAR_GLOBALS->request_init = 1;
+       zend_init_rsrc_list(TSRMLS_C);
        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;
@@ -130,19 +135,22 @@ static void phar_split_cache_list(TSRMLS_D)
                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;
-                               zend_destroy_rsrc_list(&EG(regular_list) TSRMLS_CC);
-                               memset(&EG(regular_list), 0, sizeof(HashTable));
                                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_destroy_rsrc_list(&EG(regular_list) TSRMLS_CC);
+                               memset(&EG(regular_list), 0, sizeof(HashTable));
                                /* free cached manifests */
                                PHAR_GLOBALS->request_init = 0;
                                return;
@@ -157,11 +165,13 @@ 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_destroy_rsrc_list(&EG(regular_list) TSRMLS_CC);
        memset(&EG(regular_list), 0, sizeof(HashTable));
        efree(tmp);
@@ -236,7 +246,7 @@ void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
        }
        if (phar->ufp) {
                php_stream_close(phar->ufp);
-               phar->fp = 0;
+               phar->ufp = 0;
        }
        pefree(phar, phar->is_persistent);
 }
@@ -247,6 +257,7 @@ 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) {
@@ -388,7 +399,7 @@ 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;
                }
@@ -984,6 +995,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
                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)");
                }
@@ -1079,7 +1091,6 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
        mydata->internal_file_start = halt_offset + manifest_len + 4;
        mydata->halt_offset = halt_offset;
        mydata->flags = manifest_flags;
-       mydata->fp = fp;
        mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
 #ifdef PHP_WIN32
        phar_unixify_path_separators(mydata->fname, fname_len);
@@ -1100,6 +1111,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
                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);
@@ -1125,7 +1137,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
        }
        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;
        }
@@ -2145,9 +2157,9 @@ 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;
@@ -2387,14 +2399,16 @@ 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 (phar->is_persistent) {
+               if (error) {
+                       spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
+               }
+               return EOF;
+       }
        if (error) {
                *error = NULL;
        }
 
-       if (PHAR_G(readonly) && !phar->is_data) {
-               return EOF;
-       }
-
        if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
                return EOF;
        }
@@ -2406,6 +2420,10 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
                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;
@@ -3065,7 +3083,7 @@ 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);
 }
 /* }}} */
 
@@ -3078,7 +3096,7 @@ static size_t phar_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */
 
 static long 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
@@ -3125,22 +3143,28 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type
 #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;
+                               /* 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 = (zend_stream_reader_t)_php_stream_read;
                                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.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
                        }
                }
@@ -3251,108 +3275,6 @@ PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
 }
 /* }}} */
 
-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(void *data) /* {{{ */
-{
-       phar_archive_data *phar, **pphar = (phar_archive_data **)data;
-       HashTable newmanifest;
-       char *fname;
-       TSRMLS_FETCH();
-
-       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;
-}
-/* }}} */
-
-static int phar_update_alias_map(void *data) /* {{{ */
-{
-       phar_archive_data **pphar, **old = (phar_archive_data **)data;
-       TSRMLS_FETCH();
-
-       zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), (*old)->fname, (*old)->fname_len, (void **) &pphar);
-       if (pphar) {
-               *old = *pphar;
-       }
-       return ZEND_HASH_APPLY_KEEP;
-}
-/* }}} */
-
 void phar_request_initialize(TSRMLS_D) /* {{{ */
 {
        if (!PHAR_GLOBALS->request_init)
@@ -3362,14 +3284,23 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */
                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_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)) {
-                       zend_hash_copy(&(PHAR_GLOBALS->phar_fname_map), &cached_phars, phar_copy_cached_phar, NULL, sizeof(phar_archive_data *));
-                       zend_hash_copy(&(PHAR_GLOBALS->phar_alias_map), &cached_alias, NULL, NULL, sizeof(phar_archive_data *));
-                       zend_hash_apply(&(PHAR_GLOBALS->phar_alias_map), (apply_func_t) phar_update_alias_map TSRMLS_CC);
+                       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_has_more_elements(&cached_phars) == SUCCESS;
+                       zend_hash_move_forward(&cached_phars)) {
+                               if (zend_hash_get_current_data(&cached_phars, (void **)&pphar) == FAILURE) {
+                                       continue;
+                               }
+                               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;
                }
-               zend_hash_init(&(PHAR_GLOBALS->phar_SERVER_mung_list), sizeof(const char *),       zend_get_hash_value, NULL, 0);
+               zend_hash_init(&(PHAR_GLOBALS->phar_SERVER_mung_list), 5,       zend_get_hash_value, NULL, 0);
                PHAR_G(cwd) = NULL;
                PHAR_G(cwd_len) = 0;
                PHAR_G(cwd_init) = 0;
@@ -3382,6 +3313,8 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */
 
 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
 {
+       int i;
+
        PHAR_GLOBALS->request_ends = 1;
        if (PHAR_GLOBALS->request_init)
        {
@@ -3391,6 +3324,19 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
                zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map));
                PHAR_GLOBALS->phar_fname_map.arBuckets = NULL;
                zend_hash_destroy(&(PHAR_GLOBALS->phar_SERVER_mung_list));
+               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->phar_SERVER_mung_list.arBuckets = NULL;
                PHAR_GLOBALS->request_init = 0;
                if (PHAR_G(cwd)) {
index 730a2d311cadf5258dddf609b795ab4d46cda0d6..336c929e64f2ff173a26ed660c47cdbdcd8f17da 100755 (executable)
Binary files a/ext/phar/phar.phar and b/ext/phar/phar.phar differ
index 85a2b8551c96eaa732cc0114fa78da4056e4e30f..a14c707a9e7603830b4b817323996cd9405d55a5 100755 (executable)
 #define TAR_DIR     '5'
 #define TAR_NEW     '8'
 
+typedef struct _phar_entry_fp phar_entry_fp;
+
 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         readonly;
@@ -223,6 +227,7 @@ enum phar_fp_type {
 };
 
 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 */
@@ -268,6 +273,8 @@ typedef struct _phar_entry_info {
        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;
@@ -316,8 +323,128 @@ struct _phar_archive_data {
        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_CC)
+{
+       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_CC)
+{
+       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'
@@ -460,6 +587,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);
@@ -475,6 +603,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);
index 0c76b876d976d8914ceaf97d17e24da810bdad6a..2f5b1635dbc2c3a4f344b035588873ca34e0156b 100755 (executable)
@@ -161,7 +161,7 @@ static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char
 }
 /* }}} */
 
-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_entry_data *idata, 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) /* {{{ */
 {
        char *name = NULL, buf[8192], *cwd;
        zend_syntax_highlighter_ini syntax_highlighter_ini;
@@ -185,7 +185,7 @@ 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);
+                       phar_entry_delref(idata TSRMLS_CC);
                        efree(name);
 #ifdef PHP_WIN32
                        efree(arch);
@@ -197,45 +197,45 @@ 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", idata->internal_file->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);
+                               phar_entry_delref(idata TSRMLS_CC);
                                zend_bailout();
                        }
 
                        /* prepare to output  */
-                       if (!phar_get_efp(phar->internal_file, 1 TSRMLS_CC)) {
+                       if (!phar_get_efp(idata->internal_file, 1 TSRMLS_CC)) {
                                char *error;
-                               if (!phar_open_jit(phar->phar, phar->internal_file, phar->phar->fp, &error, 0 TSRMLS_CC)) {
+                               if (!phar_open_jit(idata->phar, idata->internal_file, phar_get_pharfp(idata->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;
+                               idata->fp = phar_get_efp(idata->internal_file, 1 TSRMLS_CC);
+                               idata->zero = phar_get_fp_offset(idata->internal_file TSRMLS_CC);
                        }
-                       phar_seek_efp(phar->internal_file, 0, SEEK_SET, 0, 1 TSRMLS_CC);
+                       phar_seek_efp(idata->internal_file, 0, SEEK_SET, 0, 1 TSRMLS_CC);
                        do {
-                               got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position));
+                               got = php_stream_read(idata->fp, buf, MIN(8192, idata->internal_file->uncompressed_filesize - idata->position));
                                PHPWRITE(buf, got);
-                               phar->position = php_stream_tell(phar->fp) - phar->zero;
-                               if (phar->position == (off_t) phar->internal_file->uncompressed_filesize) {
+                               idata->position = php_stream_tell(idata->fp) - idata->zero;
+                               if (idata->position == (off_t) idata->internal_file->uncompressed_filesize) {
                                        break;
                                }
                        } while (1);
 
-                       phar_entry_delref(phar TSRMLS_CC);
+                       phar_entry_delref(idata 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);
                                efree(basename);
                        }
-                       phar_entry_delref(phar TSRMLS_CC);
+                       phar_entry_delref(idata TSRMLS_CC);
                        if (entry[0] == '/') {
                                name_len = spprintf(&name, 4096, "phar://%s%s", arch, entry);
                        } else {
@@ -356,10 +356,13 @@ 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)) {
@@ -1082,7 +1085,10 @@ 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;
 }
 /* }}} */
@@ -1094,7 +1100,9 @@ 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);
+       }
 }
 /* }}} */
 
@@ -1199,7 +1207,9 @@ PHP_METHOD(Phar, __construct)
                return;
        }
        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;
 
@@ -1223,7 +1233,9 @@ PHP_METHOD(Phar, __construct)
                        &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1);
        }
 
-       phar_obj->arc.archive->is_data = is_data;
+       if (!phar_data->is_persistent) {
+               phar_obj->arc.archive->is_data = is_data;
+       }
        phar_obj->spl.info_class = phar_ce_entry;
 
        efree(fname);
@@ -1313,6 +1325,10 @@ 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;
@@ -1930,6 +1946,11 @@ 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)) {
index 4854aeb90706baaf747ac39ab73ea0c34c6b3507..262c3f207b8646ef788a0fbe703cb9ba348ba639 100644 (file)
@@ -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)
                {
@@ -489,10 +500,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;
@@ -544,72 +552,56 @@ 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 (SUCCESS == zend_hash_find(&(phar->virtual_dirs), internal_file, internal_file_len, (void **) &entry)) {
+               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;
+
+               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;
                        }
-               }
-               /* 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)) {
+                               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;
                                }
-                               PHAR_STR(key, str_key);
-                               if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) {
+                               test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen);
+                               if (SUCCESS != php_stream_stat_path(test, &ssbi)) {
+                                       efree(test);
                                        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;
                        }
                }
        }
@@ -777,7 +769,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);
@@ -787,6 +778,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;
 
index 92ecde46af9c2cf751b9ca67b9602c2b607105fc..4537f024b480e4e7d3b9483e9b4bb87174cb7832 100644 (file)
@@ -416,6 +416,7 @@ bail:
                }
                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) {
@@ -548,6 +549,7 @@ bail:
        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)) {
@@ -564,7 +566,6 @@ bail:
                        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) {
@@ -608,7 +609,7 @@ bail:
                                        return FAILURE;
                                }
                        }
-                       zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL);
+                       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 {
@@ -851,6 +852,12 @@ 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;
        }
diff --git a/ext/phar/tests/cached_manifest_1.phpt b/ext/phar/tests/cached_manifest_1.phpt
new file mode 100644 (file)
index 0000000..54ab6f0
--- /dev/null
@@ -0,0 +1,36 @@
+--TEST--
+Phar: phar.cache_list basic read test
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.cache_list={PWD}/files/nophar.phar
+--FILE--
+<?php
+$pname = 'phar://' . dirname(__FILE__) . '/files/nophar.phar';
+var_dump(file_get_contents($pname . '/b/c.php'));
+$a = opendir($pname);
+while (false !== ($b = readdir($a))) {
+var_dump($b);
+}
+foreach (new RecursiveIteratorIterator(new Phar($pname)) as $f) {
+       var_dump($f->getPathName());
+}
+var_dump(is_dir($pname . '/b'));
+var_dump(is_dir($pname . '/d'));
+var_dump(is_dir($pname . '/b/c.php'));
+?>
+===DONE===
+--EXPECTF--
+string(131) "<?php echo "in b\n";$a = fopen("index.php", "r", true);echo stream_get_contents($a);fclose($a);include dirname(__FILE__) . "/../d";"
+string(1) "b"
+string(1) "d"
+string(9) "index.php"
+string(7) "web.php"
+string(%d) "phar://%snophar.phar%cb%cc.php"
+string(%d) "phar://%snophar.phar%cd"
+string(%d) "phar://%snophar.phar%cindex.php"
+string(%d) "phar://%snophar.phar%cweb.php"
+bool(true)
+bool(false)
+bool(false)
+===DONE===
index cf789de89871736fd43a319d433e24ea45569c9f..de1eb347196e46a367a58235b66caee2912b0441 100644 (file)
@@ -95,14 +95,14 @@ 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 {
@@ -117,7 +117,7 @@ php_stream *phar_get_efp(phar_entry_info *entry, 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;
@@ -132,21 +132,22 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi
        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;
+                       temp = eoffset + entry->uncompressed_filesize + offset;
                        break;
                case SEEK_CUR :
-                       temp = entry->offset + position + offset;
+                       temp = eoffset + position + offset;
                        break;
                case SEEK_SET :
-                       temp = entry->offset + offset;
+                       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);
@@ -254,7 +255,8 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d
        if (*filename == '.') {
                int try_len;
 
-               if (SUCCESS != (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) {
+               if (SUCCESS != zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar) &&
+                       PHAR_G(manifest_cached) && SUCCESS != zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar)) {
                        efree(arch);
                        return phar_save_resolve_path(filename, filename_len TSRMLS_CC);
                }
@@ -537,6 +539,7 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char
                }
                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)) {
@@ -552,6 +555,16 @@ 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);
@@ -579,8 +592,10 @@ 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) {
@@ -621,9 +636,11 @@ 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;
 }
 /* }}} */
@@ -662,6 +679,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));
 
@@ -732,7 +756,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char
 /* initialize a phar_archive_data's read-only fp for existing phar data */
 int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) 
 {
-       if (phar->fp) {
+       if (phar_get_pharfp(phar TSRMLS_CC)) {
                return SUCCESS;
        }
 
@@ -746,8 +770,8 @@ int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC)
                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;
@@ -795,6 +819,7 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS
        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);
@@ -813,7 +838,7 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS
                /* 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;
@@ -822,16 +847,17 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS
        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;
        }
@@ -841,27 +867,26 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS
        }
        /* 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;
+       phar_set_fp_type(entry, PHAR_UFP, loc TSRMLS_CC);
 
        return SUCCESS;
 }
@@ -1010,7 +1035,7 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *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 */
@@ -1030,6 +1055,7 @@ 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);
 
@@ -1038,7 +1064,9 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch
        }
        *archive = NULL;
        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);
@@ -1052,12 +1080,16 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch
                        *archive = *fd_ptr;
                        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) {
@@ -1070,11 +1102,30 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch
                                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);
+                       }
+                       return SUCCESS;
+               }
+               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;
+                               }
                        }
                        return SUCCESS;
                }
-               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, (void**)&fd_ptr)) {
+               if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, fhash, (void**)&fd_ptr)) {
+                       *archive = *fd_ptr;
+                       return SUCCESS;
+               }
+               if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, save, save_len, fhash, (void**)&fd_ptr)) {
                        *archive = *fd_ptr;
                        return SUCCESS;
                }
@@ -1090,15 +1141,20 @@ 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);
                        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;
@@ -1827,12 +1883,12 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat
  */
 static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength)  /* {{{ */
 {
-       void *dummy = (void *) 1;
+       void *dummy = (char *) 1;
        if (SUCCESS == zend_hash_find(ht, arKey, nKeyLength, (void **)&dummy)) {
                dummy++;
        }
 
-       return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
+       return zend_hash_update(ht, arKey, nKeyLength, (char *) &dummy, sizeof(void *), NULL);
 }
 /* }}} */
 
@@ -1870,3 +1926,110 @@ void phar_delete_virtual_dirs(phar_archive_data *phar, char *filename, int filen
        }
 }
 /* }}} */
+
+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);
+       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;
+}
+/* }}} */
index 3932c7c9af13500f950cb6733a81b7ae4e6cf7a9..a32576231fd2066c4ff20d42c0096c60201c421e 100644 (file)
@@ -297,6 +297,7 @@ foundit:
                        /* 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);
@@ -521,6 +522,7 @@ foundit:
                }
                mydata->is_temporary_alias = 1;
        }
+
        if (pphar) {
                *pphar = mydata;
        }
@@ -844,6 +846,12 @@ 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;
        }