diff options
author | Greg Beaver <cellog@php.net> | 2008-06-18 06:38:47 +0000 |
---|---|---|
committer | Greg Beaver <cellog@php.net> | 2008-06-18 06:38:47 +0000 |
commit | cbe23b9f2041a6b37fbeadc49963baf6ffc5827a (patch) | |
tree | 2318f9bc03c9022fb2359ff27da66dc62fb7401b | |
parent | 31bfce6f19f7e484b0a901fe1d0e26a2f8439395 (diff) | |
download | php-git-cbe23b9f2041a6b37fbeadc49963baf6ffc5827a.tar.gz |
fix windows build and more performance jumps (these are minor)
implement real copy-on-write
use virtual_dirs for wrapper stat
-rw-r--r-- | ext/phar/dirstream.c | 18 | ||||
-rw-r--r-- | ext/phar/func_interceptors.c | 36 | ||||
-rw-r--r-- | ext/phar/phar.c | 204 | ||||
-rwxr-xr-x | ext/phar/phar.phar | bin | 15252 -> 15252 bytes | |||
-rwxr-xr-x | ext/phar/phar_internal.h | 131 | ||||
-rwxr-xr-x | ext/phar/phar_object.c | 59 | ||||
-rw-r--r-- | ext/phar/stream.c | 132 | ||||
-rw-r--r-- | ext/phar/tar.c | 11 | ||||
-rw-r--r-- | ext/phar/tests/cached_manifest_1.phpt | 36 | ||||
-rw-r--r-- | ext/phar/util.c | 249 | ||||
-rw-r--r-- | ext/phar/zip.c | 8 |
11 files changed, 608 insertions, 276 deletions
diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 19ac6f9cef..6c2bd7231c 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -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); diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index 621d87392a..0abc623963 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -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); diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 3e19e3d4fc..e4f917cce9 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -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)) { diff --git a/ext/phar/phar.phar b/ext/phar/phar.phar Binary files differindex 730a2d311c..336c929e64 100755 --- a/ext/phar/phar.phar +++ b/ext/phar/phar.phar diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 85a2b8551c..a14c707a9e 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -136,8 +136,12 @@ #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); diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 0c76b876d9..2f5b1635db 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -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)) { diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 4854aeb907..262c3f207b 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -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; diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 92ecde46af..4537f024b4 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -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 index 0000000000..54ab6f0065 --- /dev/null +++ b/ext/phar/tests/cached_manifest_1.phpt @@ -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=== diff --git a/ext/phar/util.c b/ext/phar/util.c index cf789de898..de1eb34719 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -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; +} +/* }}} */ diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 3932c7c9af..a32576231f 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -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; } |