diff options
-rwxr-xr-x | ext/phar/build_precommand.php | 17 | ||||
-rw-r--r-- | ext/phar/dirstream.c | 80 | ||||
-rw-r--r-- | ext/phar/dirstream.h | 2 | ||||
-rw-r--r-- | ext/phar/func_interceptors.c | 5 | ||||
-rw-r--r-- | ext/phar/func_interceptors.h | 2 | ||||
-rw-r--r-- | ext/phar/makestub.php | 2 | ||||
-rw-r--r-- | ext/phar/phar.c | 717 | ||||
-rwxr-xr-x | ext/phar/phar_internal.h | 18 | ||||
-rwxr-xr-x | ext/phar/phar_object.c | 630 | ||||
-rwxr-xr-x | ext/phar/phar_path_check.c | 2 | ||||
-rwxr-xr-x | ext/phar/phar_path_check.re | 2 | ||||
-rw-r--r-- | ext/phar/php_phar.h | 6 | ||||
-rw-r--r-- | ext/phar/shortarc.php | 564 | ||||
-rw-r--r-- | ext/phar/stream.c | 80 | ||||
-rw-r--r-- | ext/phar/stream.h | 2 | ||||
-rw-r--r-- | ext/phar/tar.c | 115 | ||||
-rw-r--r-- | ext/phar/util.c | 283 | ||||
-rw-r--r-- | ext/phar/zip.c | 180 |
18 files changed, 1871 insertions, 836 deletions
diff --git a/ext/phar/build_precommand.php b/ext/phar/build_precommand.php index 8904afdf05..c2fe9c8e52 100755 --- a/ext/phar/build_precommand.php +++ b/ext/phar/build_precommand.php @@ -5,14 +5,12 @@ * @ingroup Phar * @brief class Phar Pre Command * @author Marcus Boerger - * @date 2007 - 2007 + * @date 2007 - 2008 * * Phar Command */ -foreach(array("SPL", "Reflection", "Phar") as $ext) -{ - if (!extension_loaded($ext)) - { +foreach(array("SPL", "Reflection", "Phar") as $ext) { + if (!extension_loaded($ext)) { echo "$argv[0] requires PHP extension $ext.\n"; exit(1); } @@ -28,14 +26,12 @@ $classes = array( 'PharCommand', ); -foreach($classes as $name) -{ +foreach($classes as $name) { echo "if (!class_exists('$name', 0))\n{\n"; $f = file(dirname(__FILE__) . '/phar/' . strtolower($name) . '.inc'); unset($f[0]); $c = count($f); - while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) - { + while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) { unset($f[$c--]); } if (substr($f[$c], -2) == "\r\n") { @@ -47,8 +43,7 @@ foreach($classes as $name) if (substr($f[$c], -2) == '?>') { $f[$c] = substr($f[$c], 0,-2); } - while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) - { + while ($c && (strlen($f[$c]) == 0 || $f[$c] == "\n" || $f[$c] == "\r\n")) { unset($f[$c--]); } echo join('', $f); diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 8218c9ca71..e72bd5b2d2 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -45,13 +45,13 @@ static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{ { HashTable *data = (HashTable *)stream->abstract; - if (data && data->arBuckets) - { + if (data && data->arBuckets) { zend_hash_destroy(data); data->arBuckets = 0; FREE_HASHTABLE(data); stream->abstract = NULL; } + return 0; } /* }}} */ @@ -63,14 +63,15 @@ static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *ne { HashTable *data = (HashTable *)stream->abstract; - if (!data) - { + if (!data) { return -1; } + if (whence == SEEK_END) { whence = SEEK_SET; offset = zend_hash_num_elements(data) + offset; } + if (whence == SEEK_SET) { zend_hash_internal_pointer_reset(data); } @@ -102,15 +103,19 @@ static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_D if (FAILURE == zend_hash_has_more_elements(data)) { return 0; } + if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(data, &key, &keylen, &unused, 0, NULL)) { return 0; } + PHAR_STR(key, str_key); zend_hash_move_forward(data); to_read = MIN(keylen, count); + if (to_read == 0 || count < keylen) { return 0; } + memset(buf, 0, sizeof(php_stream_dirent)); memcpy(((php_stream_dirent *) buf)->d_name, str_key, to_read); ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; @@ -159,10 +164,9 @@ static int phar_compare_dir_name(const void *a, const void *b TSRMLS_DC) /* {{{ Bucket *f; Bucket *s; int result; - + f = *((Bucket **) a); s = *((Bucket **) b); - #if (PHP_MAJOR_VERSION < 6) result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength); #else @@ -202,12 +206,16 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) efree(dir); return php_stream_alloc(&phar_dir_ops, data, NULL, "r"); } + zend_hash_internal_pointer_reset(manifest); + while (FAILURE != zend_hash_has_more_elements(manifest)) { if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) { break; } + PHAR_STR(key, str_key); + if (keylen <= (uint)dirlen) { if (keylen < (uint)dirlen || !strncmp(str_key, dir, dirlen)) { if (SUCCESS != zend_hash_move_forward(manifest)) { @@ -216,6 +224,7 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) continue; } } + if (*dir == '/') { /* root directory */ if (keylen >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) { @@ -225,6 +234,7 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) } continue; } + if (NULL != (found = (char *) memchr(str_key, '/', keylen))) { /* the entry has a path separator and is a subdirectory */ entry = (char *) safe_emalloc(found - str_key, 1, 1); @@ -236,6 +246,7 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) memcpy(entry, str_key, keylen); entry[keylen] = '\0'; } + goto PHAR_ADD_ENTRY; } else { if (0 != memcmp(str_key, dir, dirlen)) { @@ -253,8 +264,10 @@ static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) } } } + save = str_key; save += dirlen + 1; /* seek to just past the path separator */ + if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) { /* is subdirectory */ save -= dirlen + 1; @@ -274,11 +287,14 @@ PHAR_ADD_ENTRY: if (keylen) { phar_add_empty(data, entry, keylen); } + efree(entry); + if (SUCCESS != zend_hash_move_forward(manifest)) { break; } } + if (FAILURE != zend_hash_has_more_elements(data)) { efree(dir); if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) { @@ -296,8 +312,7 @@ PHAR_ADD_ENTRY: /** * Open a directory handle within a phar archive */ -php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, - int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ +php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ { php_url *resource = NULL; php_stream *ret; @@ -334,8 +349,8 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char host_len = strlen(resource->host); phar_request_initialize(TSRMLS_C); - internal_file = resource->path + 1; /* strip leading "/" */ + if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) { if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); @@ -346,9 +361,11 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char php_url_free(resource); return NULL; } + if (error) { efree(error); } + if (*internal_file == '\0') { /* root directory requested */ internal_file = estrndup(internal_file - 1, 1); @@ -356,10 +373,12 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char php_url_free(resource); return ret; } + if (!phar->manifest.arBuckets) { php_url_free(resource); return NULL; } + if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry) && !entry->is_dir) { php_url_free(resource); return NULL; @@ -389,6 +408,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC); } } + if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { break; } @@ -417,11 +437,14 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { phar = NULL; } + efree(arch); efree(entry2); + if (PHAR_G(readonly) && (!phar || !phar->is_data)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", write operations disabled", url_from); return 0; @@ -463,18 +486,21 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in php_url_free(resource); return 0; } + if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); efree(error); php_url_free(resource); return 0; } + if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC))) { /* entry exists as a file */ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", resource->path+1, resource->host); php_url_free(resource); return 0; } + if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); efree(error); @@ -494,11 +520,14 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in if (phar->is_zip) { entry.is_zip = 1; } + entry.filename = estrdup(resource->path + 1); + if (phar->is_tar) { entry.is_tar = 1; entry.tar_type = TAR_DIR; } + entry.filename_len = strlen(resource->path + 1); php_url_free(resource); entry.is_dir = 1; @@ -507,19 +536,23 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in entry.is_crc_checked = 1; entry.flags = PHAR_ENT_PERM_DEF_DIR; entry.old_flags = PHAR_ENT_PERM_DEF_DIR; + if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", entry.filename, phar->fname); efree(error); efree(entry.filename); return 0; } + phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry.filename, phar->fname, error); zend_hash_del(&phar->manifest, entry.filename, entry.filename_len); efree(error); return 0; } + phar_add_virtual_dirs(phar, entry.filename, entry.filename_len TSRMLS_CC); return 1; } @@ -547,11 +580,14 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } + if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { phar = NULL; } + efree(arch); efree(entry2); + if (PHAR_G(readonly) && (!phar || !phar->is_data)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); return 0; @@ -584,7 +620,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ } path_len = strlen(resource->path+1); - + if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, path_len, 2, &error, 1 TSRMLS_CC))) { if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); @@ -604,13 +640,13 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ } for (zend_hash_internal_pointer_reset(&phar->manifest); - HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)); - zend_hash_move_forward(&phar->manifest)) { + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)); + zend_hash_move_forward(&phar->manifest)) { - if (!entry->is_deleted && - key_len > path_len && - memcmp(key, resource->path+1, path_len) == 0 && - IS_SLASH(key[path_len])) { + if (!entry->is_deleted && + key_len > path_len && + memcmp(key, resource->path+1, path_len) == 0 && + IS_SLASH(key[path_len])) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty"); if (entry->is_temp_dir) { efree(entry->filename); @@ -622,13 +658,13 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ } for (zend_hash_internal_pointer_reset(&phar->virtual_dirs); - HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL)); - zend_hash_move_forward(&phar->virtual_dirs)) { + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL)); + zend_hash_move_forward(&phar->virtual_dirs)) { - if (!entry->is_deleted && - key_len > path_len && - memcmp(key, resource->path+1, path_len) == 0 && - IS_SLASH(key[path_len])) { + if (!entry->is_deleted && + key_len > path_len && + memcmp(key, resource->path+1, path_len) == 0 && + IS_SLASH(key[path_len])) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty"); if (entry->is_temp_dir) { efree(entry->filename); diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h index 6467b55bce..b6b49699b9 100644 --- a/ext/phar/dirstream.h +++ b/ext/phar/dirstream.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index 266bbb0d8a..d3308b571a 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -37,6 +37,7 @@ PHAR_FUNC(phar_opendir) /* {{{ */ && !cached_phars.arBuckets) { goto skip_phar; } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &filename, &filename_len, &zcontext) == FAILURE) { return; } @@ -110,10 +111,12 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */ && !cached_phars.arBuckets) { goto skip_phar; } + /* Parse arguments */ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|br!ll", &filename, &filename_len, &use_include_path, &zcontext, &offset, &maxlen) == FAILURE) { goto skip_phar; } + if (use_include_path || (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://"))) { char *arch, *entry, *fname; int arch_len, entry_len, fname_len; @@ -783,7 +786,7 @@ statme_baby: if (!phar->is_writeable) { sb.st_mode = (sb.st_mode & 0555) | (sb.st_mode & ~0777); } - + sb.st_nlink = 1; sb.st_rdev = -1; /* this is only for APC, so use /dev/null device - no chance of conflict there! */ diff --git a/ext/phar/func_interceptors.h b/ext/phar/func_interceptors.h index e161df6d34..d67d5f6380 100644 --- a/ext/phar/func_interceptors.h +++ b/ext/phar/func_interceptors.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/phar/makestub.php b/ext/phar/makestub.php index f1e7b46dc3..2bb30e96d8 100644 --- a/ext/phar/makestub.php +++ b/ext/phar/makestub.php @@ -34,7 +34,7 @@ $stub = '/* +----------------------------------------------------------------------+ | phar php single-file executable PHP extension generated stub | +----------------------------------------------------------------------+ - | Copyright (c) 2005-' . date('Y') . ' The PHP Group | + | Copyright (c) 2005-' . date('Y') . ' The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/phar/phar.c b/ext/phar/phar.c index f9126c7dd0..9cb0b74916 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -42,12 +42,12 @@ static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC) /* {{{ if (!phar->is_data) { phar->is_writeable = !keep; } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ -/* if the original value is 0 (disabled), then allow setting/unsetting at will - otherwise, only allow 1 (enabled), and error on disabling */ +/* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ { zend_bool old, ini; @@ -56,7 +56,7 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ old = PHAR_G(readonly_orig); } else { old = PHAR_G(require_hash_orig); - } + } if (new_value_length == 2 && !strcasecmp("on", new_value)) { ini = (zend_bool) 1; @@ -74,10 +74,10 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ /* do not allow unsetting in runtime */ if (stage == ZEND_INI_STAGE_STARTUP) { if (entry->name_length == 14) { - PHAR_G(readonly_orig) = ini; + PHAR_G(readonly_orig) = ini; } else { PHAR_G(require_hash_orig) = ini; - } + } } else if (old && !ini) { return FAILURE; } @@ -89,7 +89,8 @@ ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */ } } else { PHAR_G(require_hash) = ini; - } + } + return SUCCESS; } /* }}}*/ @@ -119,6 +120,7 @@ static void phar_split_cache_list(TSRMLS_D) /* {{{ */ if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) { EG(regular_list).nNextFreeElement=1; /* we don't want resource id 0 */ } + PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2")); PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib")); /* these two are dummies and will be destroyed later */ @@ -132,9 +134,9 @@ static void phar_split_cache_list(TSRMLS_D) /* {{{ */ for (key = php_strtok_r(tmp, ds, &lasts); key; - key = php_strtok_r(NULL, ds, &lasts)) - { + key = php_strtok_r(NULL, ds, &lasts)) { end = strchr(key, DEFAULT_DIR_SEPARATOR); + if (end) { if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) { finish_up: @@ -166,6 +168,7 @@ finish_error: } } } + PHAR_GLOBALS->persist = 0; PHAR_GLOBALS->request_init = 0; /* destroy dummy values from before */ @@ -194,9 +197,9 @@ ZEND_INI_MH(phar_ini_cache_list) /* {{{ */ /* }}} */ PHP_INI_BEGIN() - STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals) + STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals) STD_PHP_INI_BOOLEAN( "phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals) - STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals) + STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals) PHP_INI_END() /** @@ -209,26 +212,32 @@ void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */ pefree(phar->alias, phar->is_persistent); phar->alias = NULL; } + if (phar->fname) { pefree(phar->fname, phar->is_persistent); phar->fname = NULL; } + if (phar->signature) { pefree(phar->signature, phar->is_persistent); phar->signature = NULL; } + if (phar->manifest.arBuckets) { zend_hash_destroy(&phar->manifest); phar->manifest.arBuckets = NULL; } + if (phar->mounted_dirs.arBuckets) { zend_hash_destroy(&phar->mounted_dirs); phar->mounted_dirs.arBuckets = NULL; } + if (phar->virtual_dirs.arBuckets) { zend_hash_destroy(&phar->virtual_dirs); phar->virtual_dirs.arBuckets = NULL; } + if (phar->metadata) { if (phar->is_persistent) { if (phar->metadata_len) { @@ -243,14 +252,17 @@ void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */ phar->metadata_len = 0; phar->metadata = 0; } + if (phar->fp) { php_stream_close(phar->fp); phar->fp = 0; } + if (phar->ufp) { php_stream_close(phar->ufp); phar->ufp = 0; } + pefree(phar, phar->is_persistent); } /* }}}*/ @@ -260,7 +272,10 @@ void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */ */ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ { - if (phar->is_persistent) return 0; + if (phar->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) { @@ -271,6 +286,7 @@ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) { /* close open file handle - allows removal or rename of the file on windows, which has greedy locking @@ -279,6 +295,7 @@ int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */ php_stream_close(phar->fp); phar->fp = NULL; } + if (!zend_hash_num_elements(&phar->manifest)) { /* this is a new phar that has perhaps had an alias/metadata set, but has never been flushed */ @@ -325,10 +342,12 @@ static int phar_tmpclose_apply(void *pDest TSRMLS_DC) /* {{{ */ if (entry->fp_type != PHAR_TMP) { return ZEND_HASH_APPLY_KEEP; } + if (entry->fp && !entry->fp_refcount) { php_stream_close(entry->fp); entry->fp = NULL; } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -348,7 +367,9 @@ static void destroy_phar_data(void *pDest) /* {{{ */ destroy_phar_data_only(pDest); return; } + zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_alias_map), phar_unalias_apply, phar_data TSRMLS_CC); + if (--phar_data->refcount < 0) { phar_destroy_phar_data(phar_data TSRMLS_CC); } @@ -367,10 +388,12 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */ php_stream_close(entry->cfp); entry->cfp = 0; } + if (entry->fp) { php_stream_close(entry->fp); entry->fp = 0; } + if (entry->metadata) { if (entry->is_persistent) { if (entry->metadata_len) { @@ -385,15 +408,19 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */ entry->metadata_len = 0; entry->metadata = 0; } + if (entry->metadata_str.c) { smart_str_free(&entry->metadata_str); entry->metadata_str.c = 0; } + pefree(entry->filename, entry->is_persistent); + if (entry->link) { pefree(entry->link, entry->is_persistent); entry->link = 0; } + if (entry->tmp) { pefree(entry->tmp, entry->is_persistent); entry->tmp = 0; @@ -409,10 +436,12 @@ int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */ if (--idata->internal_file->fp_refcount < 0) { idata->internal_file->fp_refcount = 0; } + if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) { php_stream_close(idata->fp); } } + phar_archive_delref(idata->phar TSRMLS_CC); efree(idata); return ret; @@ -427,6 +456,7 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */ phar_archive_data *phar; phar = idata->phar; + if (idata->internal_file->fp_refcount < 2) { if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) { php_stream_close(idata->fp); @@ -508,6 +538,7 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len #ifdef PHP_WIN32 unixfname = estrndup(fname, fname_len); phar_unixify_path_separators(unixfname, fname_len); + if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error TSRMLS_CC) && ((alias && fname_len == phar->fname_len && !strncmp(unixfname, phar->fname, fname_len)) || !alias) @@ -538,9 +569,11 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len } } } + if (pphar) { *pphar = phar; } + return SUCCESS; } else { #ifdef PHP_WIN32 @@ -549,9 +582,11 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len if (pphar) { *pphar = NULL; } + if (phar && error && !(options & REPORT_ERRORS)) { efree(error); } + return FAILURE; } } @@ -582,13 +617,16 @@ int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSR INIT_ZVAL(**metadata); p = (const unsigned char*) *buffer; PHP_VAR_UNSERIALIZE_INIT(var_hash); - if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) { + + if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); zval_ptr_dtor(metadata); *metadata = NULL; return FAILURE; } + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (PHAR_G(persist)) { /* lazy init metadata */ zval_ptr_dtor(metadata); @@ -602,9 +640,11 @@ int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSR } else { *metadata = NULL; } + if (!zip_metadata_len) { *buffer += buf_len; } + return SUCCESS; } /* }}}*/ @@ -612,7 +652,7 @@ int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSR /** * Does not check for a previously opened phar in the cache. * - * Parse a new one and add it to the cache, returning either SUCCESS or + * Parse a new one and add it to the cache, returning either SUCCESS or * FAILURE, and setting pphar to the pointer to the manifest entry * * This is used by phar_open_from_filename to process the manifest, but can be called @@ -632,6 +672,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char if (pphar) { *pphar = NULL; } + if (error) { *error = NULL; } @@ -642,15 +683,18 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char } buffer = b32; + if (3 != php_stream_read(fp, buffer, 3)) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)") } + if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') { int nextchar; halt_offset += 3; if (EOF == (nextchar = php_stream_getc(fp))) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)") } + if ((char) nextchar == '\r') { /* if we have an \r we require an \n as well */ if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') { @@ -658,10 +702,12 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char } ++halt_offset; } + if ((char) nextchar == '\n') { ++halt_offset; } } + /* make sure we are at the right location to read the manifest */ if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) { MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"") @@ -669,31 +715,38 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char /* read in manifest */ buffer = b32; + if (4 != php_stream_read(fp, buffer, 4)) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)") } + PHAR_GET_32(buffer, manifest_len); + if (manifest_len > 1048576 * 100) { /* prevent serious memory issues by limiting manifest to at most 100 MB in length */ MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"") } + buffer = (char *)emalloc(manifest_len); savebuf = buffer; endbuffer = buffer + manifest_len; + if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)") } /* extract the number of entries */ PHAR_GET_32(buffer, manifest_count); + if (manifest_count == 0) { MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries. Phars must have at least 1 entry"); } /* extract API version, lowest nibble currently unused */ - manifest_ver = (((unsigned char)buffer[0]) << 8) - + ((unsigned char)buffer[1]); + manifest_ver = (((unsigned char)buffer[0]) << 8) + + ((unsigned char)buffer[1]); buffer += 2; + if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) { efree(savebuf); php_stream_close(fp); @@ -706,7 +759,6 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char PHAR_GET_32(buffer, manifest_flags); manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK; - manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK; /* remember whether this entire phar was compressed with gz/bzip2 */ manifest_flags |= compression; @@ -720,7 +772,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char if (-1 == php_stream_seek(fp, -8, SEEK_END) || (read_len = php_stream_tell(fp)) < 20 - || 8 != php_stream_read(fp, sig_buf, 8) + || 8 != php_stream_read(fp, sig_buf, 8) || memcmp(sig_buf+4, "GBMB", 4)) { efree(savebuf); php_stream_close(fp); @@ -729,185 +781,191 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char } return FAILURE; } + PHAR_GET_32(sig_ptr, sig_flags); + switch(sig_flags) { - case PHAR_SIG_OPENSSL: { - php_uint32 signature_len; - char *sig; - off_t whence; - - /* we store the signature followed by the signature length */ - if (-1 == php_stream_seek(fp, -12, SEEK_CUR) - || 4 != php_stream_read(fp, sig_buf, 4)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname); + case PHAR_SIG_OPENSSL: { + php_uint32 signature_len; + char *sig; + off_t whence; + + /* we store the signature followed by the signature length */ + if (-1 == php_stream_seek(fp, -12, SEEK_CUR) + || 4 != php_stream_read(fp, sig_buf, 4)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname); + } + return FAILURE; } - return FAILURE; - } - - sig_ptr = sig_buf; - PHAR_GET_32(sig_ptr, signature_len); - sig = (char *) emalloc(signature_len); + sig_ptr = sig_buf; + PHAR_GET_32(sig_ptr, signature_len); + sig = (char *) emalloc(signature_len); + whence = signature_len + 4; + whence = -whence; + + if (-1 == php_stream_seek(fp, whence, SEEK_CUR) + || !(end_of_phar = php_stream_tell(fp)) + || signature_len != php_stream_read(fp, sig, signature_len)) { + efree(savebuf); + efree(sig); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname); + } + return FAILURE; + } - whence = signature_len + 4; - whence = -whence; - if (-1 == php_stream_seek(fp, whence, SEEK_CUR) - || !(end_of_phar = php_stream_tell(fp)) - || signature_len != php_stream_read(fp, sig, signature_len)) { - efree(savebuf); - efree(sig); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname); + if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + efree(sig); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; } - return FAILURE; - } - if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error TSRMLS_CC)) { - efree(savebuf); efree(sig); - php_stream_close(fp); - if (error) { - char *save = *error; - spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error); - efree(save); - } - return FAILURE; } - efree(sig); - } - break; + break; #if HAVE_HASH_EXT - case PHAR_SIG_SHA512: { - unsigned char digest[64]; + case PHAR_SIG_SHA512: { + unsigned char digest[64]; - php_stream_seek(fp, -(8 + 64), SEEK_END); - read_len = php_stream_tell(fp); - if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + php_stream_seek(fp, -(8 + 64), SEEK_END); + read_len = php_stream_tell(fp); + + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } - return FAILURE; - } - if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - char *save = *error; - spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error); - efree(save); + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; } - return FAILURE; + break; } - break; - } - case PHAR_SIG_SHA256: { - unsigned char digest[32]; + case PHAR_SIG_SHA256: { + unsigned char digest[32]; - php_stream_seek(fp, -(8 + 32), SEEK_END); - read_len = php_stream_tell(fp); + php_stream_seek(fp, -(8 + 32), SEEK_END); + read_len = php_stream_tell(fp); - if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } - return FAILURE; - } - if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) { + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; + } + break; + } +#else + case PHAR_SIG_SHA512: + case PHAR_SIG_SHA256: efree(savebuf); php_stream_close(fp); + if (error) { - char *save = *error; - spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error); - efree(save); + spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname); } return FAILURE; - } - break; - } -#else - case PHAR_SIG_SHA512: - case PHAR_SIG_SHA256: - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname); - } - return FAILURE; #endif - case PHAR_SIG_SHA1: { - unsigned char digest[20]; + case PHAR_SIG_SHA1: { + unsigned char digest[20]; - php_stream_seek(fp, -(8 + 20), SEEK_END); - read_len = php_stream_tell(fp); + php_stream_seek(fp, -(8 + 20), SEEK_END); + read_len = php_stream_tell(fp); - if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } - return FAILURE; - } - if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - char *save = *error; - spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error); - efree(save); + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; } - return FAILURE; + break; } - break; - } - case PHAR_SIG_MD5: { - unsigned char digest[16]; + case PHAR_SIG_MD5: { + unsigned char digest[16]; - php_stream_seek(fp, -(8 + 16), SEEK_END); - read_len = php_stream_tell(fp); + php_stream_seek(fp, -(8 + 16), SEEK_END); + read_len = php_stream_tell(fp); - if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" has a broken signature", fname); + } + return FAILURE; } - return FAILURE; - } - if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) { + if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) { + efree(savebuf); + php_stream_close(fp); + if (error) { + char *save = *error; + spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error); + efree(save); + } + return FAILURE; + } + break; + } + default: efree(savebuf); php_stream_close(fp); + if (error) { - char *save = *error; - spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error); - efree(save); + spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname); } return FAILURE; - } - break; - } - default: - efree(savebuf); - php_stream_close(fp); - if (error) { - spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname); - } - return FAILURE; } } else if (PHAR_G(require_hash)) { efree(savebuf); php_stream_close(fp); + if (error) { spprintf(error, 0, "phar \"%s\" does not have a signature", fname); } @@ -919,12 +977,15 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char /* extract alias */ PHAR_GET_32(buffer, tmp_len); + if (buffer + tmp_len > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)"); } + if (manifest_len < 10 + tmp_len) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)") } + /* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */ if (tmp_len) { /* if the alias is stored we enforce it (implicit overrides explicit) */ @@ -932,15 +993,19 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char { buffer[tmp_len] = '\0'; php_stream_close(fp); + if (signature) { efree(signature); } + if (error) { spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias); } + efree(savebuf); return FAILURE; } + alias_len = tmp_len; alias = buffer; buffer += tmp_len; @@ -1000,24 +1065,33 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char if (buffer + 4 > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)") } + PHAR_GET_32(buffer, entry.filename_len); + if (entry.filename_len == 0) { MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\""); } - if (entry.is_persistent) entry.manifest_pos = manifest_index; + + if (entry.is_persistent) { + entry.manifest_pos = manifest_index; + } + if (buffer + entry.filename_len + 20 > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)"); } + if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') { entry.is_dir = 1; } else { entry.is_dir = 0; } + phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC); entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent); buffer += entry.filename_len; PHAR_GET_32(buffer, entry.uncompressed_filesize); PHAR_GET_32(buffer, entry.timestamp); + if (offset == halt_offset + (int)manifest_len + 4) { mydata->min_timestamp = entry.timestamp; mydata->max_timestamp = entry.timestamp; @@ -1028,13 +1102,16 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char mydata->max_timestamp = entry.timestamp; } } + PHAR_GET_32(buffer, entry.compressed_filesize); PHAR_GET_32(buffer, entry.crc32); PHAR_GET_32(buffer, entry.flags); + if (entry.is_dir) { entry.filename_len--; entry.flags |= PHAR_ENT_PERM_DEF_DIR; } + if (entry.is_persistent) { if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) { pefree(entry.filename, entry.is_persistent); @@ -1046,49 +1123,52 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\""); } } + entry.offset = entry.offset_abs = offset; offset += entry.compressed_filesize; + switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) { - case PHAR_ENT_COMPRESSED_GZ: - if (!PHAR_G(has_zlib)) { - if (entry.metadata) { - if (entry.is_persistent) { - free(entry.metadata); - } else { - zval_ptr_dtor(&entry.metadata); + case PHAR_ENT_COMPRESSED_GZ: + if (!PHAR_G(has_zlib)) { + if (entry.metadata) { + if (entry.is_persistent) { + free(entry.metadata); + } else { + zval_ptr_dtor(&entry.metadata); + } } + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\""); } - pefree(entry.filename, entry.is_persistent); - MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\""); - } - break; - case PHAR_ENT_COMPRESSED_BZ2: - if (!PHAR_G(has_bz2)) { - if (entry.metadata) { - if (entry.is_persistent) { - free(entry.metadata); - } else { - zval_ptr_dtor(&entry.metadata); + break; + case PHAR_ENT_COMPRESSED_BZ2: + if (!PHAR_G(has_bz2)) { + if (entry.metadata) { + if (entry.is_persistent) { + free(entry.metadata); + } else { + zval_ptr_dtor(&entry.metadata); + } } + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\""); } - pefree(entry.filename, entry.is_persistent); - MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\""); - } - break; - default: - if (entry.uncompressed_filesize != entry.compressed_filesize) { - if (entry.metadata) { - if (entry.is_persistent) { - free(entry.metadata); - } else { - zval_ptr_dtor(&entry.metadata); + break; + default: + if (entry.uncompressed_filesize != entry.compressed_filesize) { + if (entry.metadata) { + if (entry.is_persistent) { + free(entry.metadata); + } else { + zval_ptr_dtor(&entry.metadata); + } } + pefree(entry.filename, entry.is_persistent); + MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)"); } - pefree(entry.filename, entry.is_persistent); - MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)"); - } - break; + break; } + manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK); /* if signature matched, no need to check CRC32 for each file */ entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0); @@ -1101,6 +1181,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char mydata->halt_offset = halt_offset; mydata->flags = manifest_flags; endbuffer = strrchr(mydata->fname, '/'); + if (endbuffer) { mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer); if (mydata->ext == endbuffer) { @@ -1110,6 +1191,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext; } } + mydata->alias = alias ? pestrndup(alias, alias_len, mydata->is_persistent) : pestrndup(mydata->fname, fname_len, mydata->is_persistent); @@ -1119,15 +1201,18 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char mydata->sig_len = sig_len; mydata->signature = signature; phar_request_initialize(TSRMLS_C); + if (register_alias) { phar_archive_data **fd_ptr; mydata->is_temporary_alias = temp_alias; + if (!phar_validate_alias(mydata->alias, mydata->alias_len)) { signature = NULL; fp = NULL; MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias"); } + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { signature = NULL; @@ -1135,10 +1220,12 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive"); } } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); } else { mydata->is_temporary_alias = 1; } + zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); efree(savebuf); @@ -1161,6 +1248,7 @@ int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int al phar_archive_data **test, *unused = NULL; test = &unused; + if (error) { *error = NULL; } @@ -1169,6 +1257,7 @@ int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int al if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) { goto check_file; } + /* next try to create a new file */ if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) { if (error) { @@ -1176,18 +1265,19 @@ int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int al } return FAILURE; } - check_file: if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) { if (pphar) { *pphar = *test; } + if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) { if (error) { spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname); } return FAILURE; } + if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) { phar_entry_info *stub; if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) { @@ -1195,6 +1285,7 @@ check_file: return FAILURE; } } + if (!PHAR_G(readonly) || (*test)->is_data) { (*test)->is_writeable = 1; } @@ -1219,7 +1310,6 @@ check_file: } return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC); - } /* }}} */ @@ -1237,13 +1327,13 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a return FAILURE; } #endif - if (php_check_open_basedir(fname TSRMLS_CC)) { return FAILURE; } /* first open readonly so it won't be created if not present */ fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual); + if (actual) { fname = actual; fname_len = strlen(actual); @@ -1266,6 +1356,7 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a return FAILURE; } } + if (actual) { efree(actual); } @@ -1281,13 +1372,13 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a /* set up our manifest */ mydata = ecalloc(1, sizeof(phar_archive_data)); - mydata->fname = expand_filepath(fname, NULL TSRMLS_CC); fname_len = strlen(mydata->fname); #ifdef PHP_WIN32 phar_unixify_path_separators(mydata->fname, fname_len); #endif p = strrchr(mydata->fname, '/'); + if (p) { mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p); if (mydata->ext == p) { @@ -1301,6 +1392,7 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a if (pphar) { *pphar = mydata; } + zend_hash_init(&mydata->manifest, sizeof(phar_entry_info), zend_get_hash_value, destroy_phar_manifest_entry, 0); zend_hash_init(&mydata->mounted_dirs, sizeof(char *), @@ -1316,6 +1408,7 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a mydata->is_brandnew = 1; phar_request_initialize(TSRMLS_C); zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (is_data) { alias = NULL; alias_len = 0; @@ -1330,16 +1423,21 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a if (error) { spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias); } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + if (pphar) { *pphar = NULL; } + return FAILURE; } } + mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len); mydata->alias_len = alias ? alias_len : fname_len; } + if (alias_len && alias) { if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) { if (options & REPORT_ERRORS) { @@ -1347,13 +1445,17 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias); } } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + if (pphar) { *pphar = NULL; } + return FAILURE; } } + return SUCCESS; } /* }}}*/ @@ -1384,18 +1486,17 @@ int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_l } else if (error && *error) { return FAILURE; } - #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { return FAILURE; } #endif - if (php_check_open_basedir(fname TSRMLS_CC)) { return FAILURE; } fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual); + if (!fp) { if (options & REPORT_ERRORS) { if (error) { @@ -1414,9 +1515,11 @@ int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_l } ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC); + if (actual) { efree(actual); } + return ret; } /* }}}*/ @@ -1429,15 +1532,20 @@ static inline char *phar_strnstr(const char *buf, int buf_len, const char *searc if (buf_len < search_len) { return NULL; } + c = buf - 1; + do { if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) { return (char *) NULL; } + so_far = c - buf; + if (so_far >= (buf_len - search_len)) { return (char *) NULL; } + if (!memcmp(c, search, search_len)) { return (char *) c; } @@ -1466,6 +1574,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a if (error) { *error = NULL; } + if (-1 == php_stream_rewind(fp)) { MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"") } @@ -1480,6 +1589,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") } + if (!test) { test = '\1'; pos = buffer+tokenlen; @@ -1494,23 +1604,26 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini") } array_init(&filterparams); - /* this is defined in zlib's zconf.h */ #ifndef MAX_WBITS #define MAX_WBITS 15 #endif add_assoc_long(&filterparams, "window", MAX_WBITS + 32); + /* entire file is gzip-compressed, uncompress to temporary file */ if (!(temp = php_stream_fopen_tmpfile())) { MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"") } + php_stream_rewind(fp); filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { err = 1; add_assoc_long(&filterparams, "window", MAX_WBITS); filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC); zval_dtor(&filterparams); + if (!filter) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6") @@ -1518,7 +1631,9 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a } else { zval_dtor(&filterparams); } + php_stream_filter_append(&temp->writefilters, filter); + if (0 == php_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL)) { if (err) { php_stream_close(temp); @@ -1527,6 +1642,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file") } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); php_stream_close(fp); @@ -1544,21 +1660,27 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a if (!PHAR_G(has_bz2)) { MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini") } + /* entire file is bzip-compressed, uncompress to temporary file */ if (!(temp = php_stream_fopen_tmpfile())) { MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"") } + php_stream_rewind(fp); filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed") } + php_stream_filter_append(&temp->writefilters, filter); + if (0 == php_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL)) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file") } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); php_stream_close(fp); @@ -1570,10 +1692,12 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a test = '\0'; continue; } + if (!memcmp(pos, zip_magic, 4)) { php_stream_seek(fp, 0, SEEK_END); return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC); } + if (got > 512) { if (phar_is_tar(pos, fname)) { php_stream_rewind(fp); @@ -1581,6 +1705,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a } } } + if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) { halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */ return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC); @@ -1589,7 +1714,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a halt_offset += got; memmove(buffer, buffer + tokenlen, got + 1); } - + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)") } /* }}} */ @@ -1610,6 +1735,7 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in old = *a; *a = '\0'; + if ((realpath = expand_filepath(fname, NULL TSRMLS_CC))) { #ifdef PHP_WIN32 phar_unixify_path_separators(realpath, strlen(realpath)); @@ -1619,6 +1745,7 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in efree(realpath); return SUCCESS; } + if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_phars, realpath, strlen(realpath))) { *a = old; efree(realpath); @@ -1626,14 +1753,18 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in } efree(realpath); } + if (SUCCESS == php_stream_stat_path((char *) fname, &ssb)) { *a = old; + if (ssb.sb.st_mode & S_IFDIR) { return FAILURE; } + if (for_create == 1) { return FAILURE; } + return SUCCESS; } else { char *slash; @@ -1642,12 +1773,15 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in *a = old; return FAILURE; } + slash = (char *) strrchr(fname, '/'); *a = old; + if (slash) { old = *slash; *slash = '\0'; } + if (SUCCESS != php_stream_stat_path((char *) fname, &ssb)) { if (slash) { *slash = old; @@ -1661,29 +1795,37 @@ static int phar_analyze_path(const char *fname, const char *ext, int ext_len, in a = strstr(realpath, fname) + ((ext - fname) + ext_len); *a = '\0'; slash = strrchr(realpath, '/'); + if (slash) { *slash = '\0'; } else { efree(realpath); return FAILURE; } + if (SUCCESS != php_stream_stat_path(realpath, &ssb)) { efree(realpath); return FAILURE; } + efree(realpath); + if (ssb.sb.st_mode & S_IFDIR) { return SUCCESS; } } + return FAILURE; } + if (slash) { *slash = old; } + if (ssb.sb.st_mode & S_IFDIR) { return SUCCESS; } + return FAILURE; } } @@ -1698,6 +1840,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i if (ext_len >= 50) { return FAILURE; } + if (executable == 1) { /* copy "." as well */ memcpy(test, ext_str - 1, ext_len + 1); @@ -1705,6 +1848,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */ /* (phar://hi/there/.phar/oops is also invalid) */ pos = strstr(test, ".phar"); + if (pos && (*(pos - 1) != '/') && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) { return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC); @@ -1712,6 +1856,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i return FAILURE; } } + /* data phars need only contain a single non-"." to be valid */ if (!executable) { pos = strstr(ext_str, ".phar"); @@ -1724,6 +1869,7 @@ static int phar_check_str(const char *fname, const char *ext_str, int ext_len, i return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC); } } + return FAILURE; } /* }}} */ @@ -1750,15 +1896,18 @@ int phar_detect_phar_fname_ext(const char *filename, int filename_len, const cha if (!filename_len || filename_len == 1) { return FAILURE; } + phar_request_initialize(TSRMLS_C); /* first check for alias in first segment */ pos = memchr(filename, '/', filename_len); + if (pos && pos != filename) { if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), (char *) filename, pos - filename)) { *ext_str = pos; *ext_len = -1; return FAILURE; } + if (PHAR_G(manifest_cached) && zend_hash_exists(&cached_alias, (char *) filename, pos - filename)) { *ext_str = pos; *ext_len = -1; @@ -1774,17 +1923,22 @@ int phar_detect_phar_fname_ext(const char *filename, int filename_len, const cha *ext_str = filename + (filename_len - (*pphar)->ext_len); woohoo: *ext_len = (*pphar)->ext_len; + if (executable == 2) { return SUCCESS; } + if (executable == 1 && !(*pphar)->is_data) { return SUCCESS; } + if (!executable && (*pphar)->is_data) { return SUCCESS; } + return FAILURE; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, (char *) filename, filename_len, (void **)&pphar)) { *ext_str = filename + (filename_len - (*pphar)->ext_len); goto woohoo; @@ -1796,6 +1950,7 @@ woohoo: ulong unused; zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_fname_map)); + while (FAILURE != zend_hash_has_more_elements(&(PHAR_GLOBALS->phar_fname_map))) { if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_fname_map), &key, &keylen, &unused, 0, NULL)) { break; @@ -1807,6 +1962,7 @@ woohoo: zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map)); continue; } + if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen || filename[keylen] == '/' || filename[keylen] == '\0')) { if (FAILURE == zend_hash_get_current_data(&(PHAR_GLOBALS->phar_fname_map), (void **) &pphar)) { @@ -1815,10 +1971,13 @@ woohoo: *ext_str = filename + (keylen - (*pphar)->ext_len); goto woohoo; } + zend_hash_move_forward(&(PHAR_GLOBALS->phar_fname_map)); } + if (PHAR_G(manifest_cached)) { zend_hash_internal_pointer_reset(&cached_phars); + while (FAILURE != zend_hash_has_more_elements(&cached_phars)) { if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&cached_phars, &key, &keylen, &unused, 0, NULL)) { break; @@ -1830,6 +1989,7 @@ woohoo: zend_hash_move_forward(&cached_phars); continue; } + if (!memcmp(filename, str_key, keylen) && ((uint)filename_len == keylen || filename[keylen] == '/' || filename[keylen] == '\0')) { if (FAILURE == zend_hash_get_current_data(&cached_phars, (void **) &pphar)) { @@ -1849,6 +2009,7 @@ next_extension: if (!pos) { return FAILURE; } + while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) { pos = memchr(pos + 1, '.', filename_len - (pos - filename) + 1); if (!pos) { @@ -1857,26 +2018,30 @@ next_extension: } slash = memchr(pos, '/', filename_len - (pos - filename)); + if (!slash) { /* this is a url like "phar://blah.phar" with no directory */ *ext_str = pos; *ext_len = strlen(pos); + /* file extension must contain "phar" */ switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) { - case SUCCESS : + case SUCCESS: return SUCCESS; - case FAILURE : + case FAILURE: /* we are at the end of the string, so we fail */ return FAILURE; } } + /* we've found an extension that ends at a directory separator */ *ext_str = pos; *ext_len = slash - pos; + switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) { - case SUCCESS : + case SUCCESS: return SUCCESS; - case FAILURE : + case FAILURE: /* look for more extensions */ pos = strchr(pos + 1, '.'); if (pos) { @@ -1885,11 +2050,12 @@ next_extension: } goto next_extension; } + return FAILURE; } /* }}} */ -static int php_check_dots(const char *element, int n) /* {{{ */ +static int php_check_dots(const char *element, int n) /* {{{ */ { for(n--; n >= 0; --n) { if (element[n] != '.') { @@ -1906,7 +2072,7 @@ static int php_check_dots(const char *element, int n) /* {{{ */ #define IS_DIRECTORY_CURRENT(element, len) \ (len == 1 && element[0] == '.') -#define IS_BACKSLASH(c) ((c) == '/') +#define IS_BACKSLASH(c) ((c) == '/') #ifdef COMPILE_DL_PHAR /* stupid-ass non-extern declaration in tsrm_strtok.h breaks dumbass MS compiler */ @@ -1933,6 +2099,7 @@ char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */ while (*s && in_character_class(*s, delim)) { ++s; } + if (!*s) { return NULL; } @@ -1942,12 +2109,14 @@ char *tsrm_strtok_r(char *s, const char *delim, char **last) /* {{{ */ while (*s && !in_character_class(*s, delim)) { ++s; } + if (!*s) { *last = s; } else { *s = '\0'; *last = s + 1; } + return token; } /* }}} */ @@ -1971,22 +2140,29 @@ char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ newpath[0] = '/'; newpath_len = 1; } + ptr = path; - if (*ptr == '/') ++ptr; + + if (*ptr == '/') { + ++ptr; + } + tok = ptr; + do { ptr = memchr(ptr, '/', path_length - (ptr - path)); } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok); + if (!ptr && (path_length - (tok - path))) { switch (path_length - (tok - path)) { - case 1 : + case 1: if (*tok == '.') { efree(path); *new_len = 1; return estrndup("/", 1); } break; - case 2 : + case 2: if (tok[0] == '.' && tok[1] == '.') { efree(path); *new_len = 1; @@ -1995,6 +2171,7 @@ char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ } return path; } + while (ptr) { ptr_length = ptr - tok; last_time: @@ -2020,19 +2197,24 @@ last_time: newpath_len += ptr_length; } + if (ptr == path + path_length) { break; } + tok = ++ptr; + do { ptr = memchr(ptr, '/', path_length - (ptr - path)); } while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok); + if (!ptr && (path_length - (tok - path))) { ptr_length = path_length - (tok - path); ptr = path + path_length; goto last_time; } } + efree(path); *new_len = newpath_len; return estrndup(newpath, newpath_len); @@ -2081,17 +2263,21 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le *arch = filename; #endif } + if (free_filename) { efree(filename); } + return FAILURE; } + ext_len = 0; /* no extension detected - instead we are dealing with an alias */ } *arch_len = ext_str - filename + ext_len; *arch = estrndup(filename, *arch_len); + if (ext_str[ext_len]) { *entry_len = filename_len - *arch_len; *entry = estrndup(ext_str+ext_len, *entry_len); @@ -2103,9 +2289,11 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le *entry_len = 1; *entry = estrndup("/", 1); } + if (free_filename) { efree(filename); } + return SUCCESS; } /* }}} */ @@ -2127,6 +2315,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ if (error) { *error = NULL; } + fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); @@ -2142,6 +2331,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ } MAKE_STD_ZVAL(halt_constant); + if (0 == zend_get_constant("__COMPILER_HALT_OFFSET__", 24, halt_constant TSRMLS_CC)) { FREE_ZVAL(halt_constant); if (error) { @@ -2149,6 +2339,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ } return FAILURE; } + halt_offset = Z_LVAL(*halt_constant); FREE_ZVAL(halt_constant); @@ -2163,6 +2354,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ } fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual); + if (!fp) { if (error) { spprintf(error, 0, "unable to open phar for reading \"%s\"", fname); @@ -2179,9 +2371,11 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error TSRMLS_ } ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, error TSRMLS_CC); + if (actual) { efree(actual); } + return ret; } /* }}} */ @@ -2199,6 +2393,7 @@ int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_d if (error) { *error = NULL; } + if (entry->is_zip) { /* verify local file header */ phar_zip_file_header local; @@ -2214,23 +2409,30 @@ int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_d spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename); return FAILURE; } + /* verify local header */ if (entry->filename_len != PHAR_ZIP_16(local.filename_len) || entry->crc32 != PHAR_ZIP_32(local.crc32) || entry->uncompressed_filesize != PHAR_ZIP_32(local.uncompsize) || entry->compressed_filesize != PHAR_ZIP_32(local.compsize)) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local head of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename); return FAILURE; } + /* construct actual offset to file start - local extra_len can be different from central extra_len */ entry->offset = entry->offset_abs = sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len); + if (idata->zero && idata->zero != entry->offset_abs) { idata->zero = entry->offset_abs; } } - php_stream_seek(fp, idata->zero, SEEK_SET); + + php_stream_seek(fp, idata->zero, SEEK_SET); + while (len--) { CRC32(crc, php_stream_getc(fp)); } + php_stream_seek(fp, idata->zero, SEEK_SET); + if (~crc == crc32) { entry->is_crc_checked = 1; return SUCCESS; @@ -2352,6 +2554,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, zval_dtor(openssl); return FAILURE; } + zval_dtor(openssl); efree(openssl); @@ -2363,11 +2566,13 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, ++(zkey->refcount); #else Z_ADDREF_P(zdata); + if (is_sign) { Z_SET_ISREF_P(zsig); } else { Z_ADDREF_P(zsig); } + Z_ADDREF_P(zkey); #endif fci.retval_ptr_ptr = &retval_ptr; @@ -2387,20 +2592,23 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, --(zkey->refcount); #else Z_DELREF_P(zdata); + if (is_sign) { Z_UNSET_ISREF_P(zsig); } else { Z_DELREF_P(zsig); } + Z_DELREF_P(zkey); #endif zval_dtor(zdata); efree(zdata); zval_dtor(zkey); efree(zkey); + switch (Z_TYPE_P(retval_ptr)) { default: - case IS_LONG : + case IS_LONG: zval_dtor(zsig); efree(zsig); if (1 == Z_LVAL_P(retval_ptr)) { @@ -2409,7 +2617,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, } efree(retval_ptr); return FAILURE; - case IS_BOOL : + case IS_BOOL: efree(retval_ptr); if (Z_BVAL_P(retval_ptr)) { *signature = estrndup(Z_STRVAL_P(zsig), Z_STRLEN_P(zsig)); @@ -2546,7 +2754,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } len = pos - user_stub + 18; if ((size_t)len != php_stream_write(newfile, user_stub, len) - || 5 != php_stream_write(newfile, " ?>\r\n", 5)) { + || 5 != php_stream_write(newfile, " ?>\r\n", 5)) { if (closeoldfile) { php_stream_close(oldfile); } @@ -2615,8 +2823,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, new_manifest_count = 0; offset = 0; for (zend_hash_internal_pointer_reset(&phar->manifest); - zend_hash_has_more_elements(&phar->manifest) == SUCCESS; - zend_hash_move_forward(&phar->manifest)) { + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } @@ -2808,55 +3016,67 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, /* write the manifest header */ if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest)) || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) { + if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); phar->alias_len = restore_alias_len; + if (error) { spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname); } + return EOF; } - + phar->alias_len = restore_alias_len; - + phar_set_32(manifest, main_metadata_str.len); if (4 != php_stream_write(newfile, manifest, 4) || (main_metadata_str.len && main_metadata_str.len != php_stream_write(newfile, main_metadata_str.c, main_metadata_str.len))) { smart_str_free(&main_metadata_str); + if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); phar->alias_len = restore_alias_len; + if (error) { spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname); } + return EOF; - } + } smart_str_free(&main_metadata_str); - + /* re-calculate the manifest location to simplify later code */ manifest_ftell = php_stream_tell(newfile); - + /* now write the manifest */ for (zend_hash_internal_pointer_reset(&phar->manifest); - zend_hash_has_more_elements(&phar->manifest) == SUCCESS; - zend_hash_move_forward(&phar->manifest)) { + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } + if (entry->is_deleted || entry->is_mounted) { /* remove this from the new phar if deleted, ignore if mounted */ continue; } + if (entry->is_dir) { /* add 1 for trailing slash */ phar_set_32(entry_buffer, entry->filename_len + 1); } else { phar_set_32(entry_buffer, entry->filename_len); } + if (4 != php_stream_write(newfile, entry_buffer, 4) || entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len) || (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) { @@ -2873,6 +3093,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } return EOF; } + /* set the manifest meta-data: 4: uncompressed filesize 4: creation timestamp @@ -2889,30 +3110,37 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, phar_set_32(entry_buffer+12, entry->crc32); phar_set_32(entry_buffer+16, entry->flags); phar_set_32(entry_buffer+20, entry->metadata_str.len); + if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer)) || entry->metadata_str.len != php_stream_write(newfile, entry->metadata_str.c, entry->metadata_str.len)) { if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); + if (error) { spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname); } + return EOF; } } - + /* now copy the actual file data to the new phar */ offset = php_stream_tell(newfile); for (zend_hash_internal_pointer_reset(&phar->manifest); - zend_hash_has_more_elements(&phar->manifest) == SUCCESS; - zend_hash_move_forward(&phar->manifest)) { + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } + if (entry->is_deleted || entry->is_dir || entry->is_mounted) { continue; } + if (entry->cfp) { file = entry->cfp; php_stream_rewind(file); @@ -2929,6 +3157,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, return EOF; } } + if (!file) { if (closeoldfile) { php_stream_close(oldfile); @@ -2939,32 +3168,39 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } return EOF; } - /* this will have changed for all files that have either - changed compression or been modified */ + + /* this will have changed for all files that have either changed compression or been modified */ entry->offset = entry->offset_abs = offset; offset += entry->compressed_filesize; wrote = php_stream_copy_to_stream(file, newfile, entry->compressed_filesize); + if (entry->compressed_filesize != wrote) { if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); + if (error) { spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname); } + return EOF; } + entry->is_modified = 0; + if (entry->cfp) { php_stream_close(entry->cfp); entry->cfp = NULL; } + if (entry->fp_type == PHAR_MOD) { - /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed - when the phar_entry_data is phar_entry_delref'ed */ + /* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed when the phar_entry_data is phar_entry_delref'ed */ if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) { php_stream_close(entry->fp); } + entry->fp = NULL; entry->fp_type = PHAR_FP; } else if (entry->fp_type == PHAR_UFP) { @@ -3034,12 +3270,14 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, if (phar->fp && free_fp) { php_stream_close(phar->fp); } + if (phar->ufp) { if (free_ufp) { php_stream_close(phar->ufp); } phar->ufp = NULL; } + if (closeoldfile) { php_stream_close(oldfile); } @@ -3062,6 +3300,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, } return EOF; } + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { php_stream_filter *filter; /* to properly compress, we have to tell zlib to add a zlib header */ @@ -3071,12 +3310,14 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert, add_assoc_long(&filterparams, "window", MAX_WBITS+16); filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); zval_dtor(&filterparams); + if (!filter) { if (error) { spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); } return EOF; } + php_stream_filter_append(&phar->fp->writefilters, filter); php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); php_stream_filter_flush(filter, 1); @@ -3122,7 +3363,7 @@ ZEND_GET_MODULE(phar) * Every user visible function must have an entry in phar_functions[]. */ function_entry phar_functions[] = { - {NULL, NULL, NULL} /* Must be the last line in phar_functions[] */ + {NULL, NULL, NULL} /* Must be the last line in phar_functions[] */ }; /* }}}*/ @@ -3222,18 +3463,22 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type } } } + zend_try { failed = 0; res = phar_orig_compile_file(file_handle, type TSRMLS_CC); } zend_catch { failed = 1; } zend_end_try(); + if (name) { efree(name); } + if (failed) { zend_bailout(); } + return res; } /* }}} */ @@ -3253,6 +3498,7 @@ int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); + if (fname_len > 7 && !strncasecmp(fname, "phar://", 7)) { if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); @@ -3263,11 +3509,13 @@ int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* efree(entry); } } + /* retrieving an include within the current directory, so use this if possible */ if (!(entry = phar_find_in_include_path((char *) filename, strlen(filename), NULL TSRMLS_CC))) { /* this file is not in the phar, use the original path */ goto skip_phar; } + if (SUCCESS == phar_orig_zend_open(entry, handle TSRMLS_CC)) { if (!handle->opened_path) { handle->opened_path = entry; @@ -3277,9 +3525,11 @@ int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* } return SUCCESS; } + if (entry != filename) { efree(entry); } + return FAILURE; } skip_phar: @@ -3404,6 +3654,7 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */ PHAR_GLOBALS->request_done = 0; zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), 5, zend_get_hash_value, destroy_phar_data, 0); zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), 5, zend_get_hash_value, NULL, 0); + if (PHAR_G(manifest_cached)) { phar_archive_data **pphar; phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp)); @@ -3413,8 +3664,10 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */ zend_hash_move_forward(&cached_phars)) { stuff[pphar[0]->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar[0]->manifest)), sizeof(phar_entry_fp_info)); } + PHAR_GLOBALS->cached_fp = stuff; } + PHAR_GLOBALS->phar_SERVER_mung_list = 0; PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; @@ -3429,6 +3682,7 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ int i; PHAR_GLOBALS->request_ends = 1; + if (PHAR_GLOBALS->request_init) { phar_release_functions(TSRMLS_C); @@ -3437,6 +3691,7 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map)); PHAR_GLOBALS->phar_fname_map.arBuckets = NULL; PHAR_GLOBALS->phar_SERVER_mung_list = 0; + if (PHAR_GLOBALS->cached_fp) { for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) { if (PHAR_GLOBALS->cached_fp[i].fp) { @@ -3450,14 +3705,18 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ efree(PHAR_GLOBALS->cached_fp); PHAR_GLOBALS->cached_fp = 0; } + PHAR_GLOBALS->request_init = 0; + if (PHAR_G(cwd)) { efree(PHAR_G(cwd)); } + PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; PHAR_G(cwd_init) = 0; } + PHAR_GLOBALS->request_done = 1; return SUCCESS; } @@ -3474,11 +3733,13 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */ php_info_print_table_row(2, "Phar-based phar archives", "enabled"); php_info_print_table_row(2, "Tar-based phar archives", "enabled"); php_info_print_table_row(2, "ZIP-based phar archives", "enabled"); + if (PHAR_G(has_zlib)) { php_info_print_table_row(2, "gzip compression", "enabled"); } else { php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)"); } + if (PHAR_G(has_bz2)) { php_info_print_table_row(2, "bzip2 compression", "enabled"); } else { @@ -3497,10 +3758,10 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */ php_info_print_box_start(0); PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik."); - PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n"); + PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n"); PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger."); - PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n"); - PUTS("Portions of tar implementation Copyright (c) 2003-2007 Tim Kientzle."); + PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n"); + PUTS("Portions of tar implementation Copyright (c) 2003-2008 Tim Kientzle."); php_info_print_box_end(); DISPLAY_INI_ENTRIES(); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 52242c4231..d97dee0adb 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -163,7 +163,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phar) int has_bz2; zend_bool readonly_orig; zend_bool require_hash_orig; - zend_bool intercepted; + zend_bool intercepted; int request_init; int require_hash; int request_done; @@ -493,8 +493,8 @@ union _phar_archive_object { zend_object std; spl_filesystem_object spl; struct { - zend_object std; - phar_archive_data *archive; + zend_object std; + phar_archive_data *archive; } arc; }; #endif @@ -505,8 +505,8 @@ union _phar_entry_object { zend_object std; spl_filesystem_object spl; struct { - zend_object std; - phar_entry_info *entry; + zend_object std; + phar_entry_info *entry; } ent; }; #endif @@ -520,11 +520,15 @@ extern char *(*phar_save_resolve_path)(const char *filename, int filename_len TS #if PHP_VERSION_ID >= 60000 typedef zstr phar_zstr; #define PHAR_STR(a, b) \ - spprintf(&b, 0, "%r", a.s); + spprintf(&b, 0, "%s", a.s); +#define PHAR_ZSTR(a, b) \ + b = ZSTR(a); #else typedef char *phar_zstr; #define PHAR_STR(a, b) \ b = a; +#define PHAR_ZSTR(a, b) \ + b = a; #endif BEGIN_EXTERN_C() diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index add4495c49..fe8eff2661 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2005-2007 The PHP Group | + | Copyright (c) 2005-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -65,42 +65,47 @@ static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char _SERVER = Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]); /* PATH_INFO and PATH_TRANSLATED should always be munged */ - if (SUCCESS == zend_hash_find(_SERVER, "PATH_INFO", sizeof("PATH_INFO"), (void **) &stuff)) { - int code; - zval *temp; + if (SUCCESS == zend_hash_find(_SERVER, "PATH_INFO", sizeof("PATH_INFO"), (void **) &stuff)) { + int code; + zval *temp; path_info = Z_STRVAL_PP(stuff); code = Z_STRLEN_PP(stuff); + if (Z_STRLEN_PP(stuff) > entry_len && !memcmp(Z_STRVAL_PP(stuff), entry, entry_len)) { ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + entry_len, request_uri_len, 1); - MAKE_STD_ZVAL(temp); + MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); zend_hash_update(_SERVER, "PHAR_PATH_INFO", sizeof("PHAR_PATH_INFO"), (void *) &temp, sizeof(zval **), NULL); } } - if (SUCCESS == zend_hash_find(_SERVER, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"), (void **) &stuff)) { - int code; - zval *temp; - path_info = Z_STRVAL_PP(stuff); - code = Z_STRLEN_PP(stuff); + if (SUCCESS == zend_hash_find(_SERVER, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"), (void **) &stuff)) { + int code; + zval *temp; + + path_info = Z_STRVAL_PP(stuff); + code = Z_STRLEN_PP(stuff); Z_STRLEN_PP(stuff) = spprintf(&(Z_STRVAL_PP(stuff)), 4096, "phar://%s%s", fname, entry); MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(_SERVER, "PHAR_PATH_TRANSLATED", sizeof("PHAR_PATH_TRANSLATED"), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_PATH_TRANSLATED", sizeof("PHAR_PATH_TRANSLATED"), (void *) &temp, sizeof(zval **), NULL); } + if (!PHAR_GLOBALS->phar_SERVER_mung_list) { return; } + if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_REQUEST_URI) { - if (SUCCESS == zend_hash_find(_SERVER, "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &stuff)) { + if (SUCCESS == zend_hash_find(_SERVER, "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &stuff)) { int code; zval *temp; path_info = Z_STRVAL_PP(stuff); code = Z_STRLEN_PP(stuff); + if (Z_STRLEN_PP(stuff) > basename_len && !memcmp(Z_STRVAL_PP(stuff), basename, basename_len)) { ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + basename_len, Z_STRLEN_PP(stuff) - basename_len, 1); @@ -110,13 +115,15 @@ static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char } } } + if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_PHP_SELF) { - if (SUCCESS == zend_hash_find(_SERVER, "PHP_SELF", sizeof("PHP_SELF"), (void **) &stuff)) { + if (SUCCESS == zend_hash_find(_SERVER, "PHP_SELF", sizeof("PHP_SELF"), (void **) &stuff)) { int code; zval *temp; path_info = Z_STRVAL_PP(stuff); code = Z_STRLEN_PP(stuff); + if (Z_STRLEN_PP(stuff) > basename_len && !memcmp(Z_STRVAL_PP(stuff), basename, basename_len)) { ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + basename_len, Z_STRLEN_PP(stuff) - basename_len, 1); @@ -128,32 +135,32 @@ static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char } if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_SCRIPT_NAME) { - if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void **) &stuff)) { - int code; - zval *temp; - - path_info = Z_STRVAL_PP(stuff); - code = Z_STRLEN_PP(stuff); + if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void **) &stuff)) { + int code; + zval *temp; + + path_info = Z_STRVAL_PP(stuff); + code = Z_STRLEN_PP(stuff); ZVAL_STRINGL(*stuff, entry, entry_len, 1); - + MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(_SERVER, "PHAR_SCRIPT_NAME", sizeof("PHAR_SCRIPT_NAME"), (void *) &temp, sizeof(zval **), NULL); - } + zend_hash_update(_SERVER, "PHAR_SCRIPT_NAME", sizeof("PHAR_SCRIPT_NAME"), (void *) &temp, sizeof(zval **), NULL); + } } if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_SCRIPT_FILENAME) { if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &stuff)) { - int code; - zval *temp; + int code; + zval *temp; - path_info = Z_STRVAL_PP(stuff); - code = Z_STRLEN_PP(stuff); + path_info = Z_STRVAL_PP(stuff); + code = Z_STRLEN_PP(stuff); Z_STRLEN_PP(stuff) = spprintf(&(Z_STRVAL_PP(stuff)), 4096, "phar://%s%s", fname, entry); MAKE_STD_ZVAL(temp); ZVAL_STRINGL(temp, path_info, code, 0); - zend_hash_update(_SERVER, "PHAR_SCRIPT_FILENAME", sizeof("PHAR_SCRIPT_FILENAME"), (void *) &temp, sizeof(zval **), NULL); + zend_hash_update(_SERVER, "PHAR_SCRIPT_FILENAME", sizeof("PHAR_SCRIPT_FILENAME"), (void *) &temp, sizeof(zval **), NULL); } } } @@ -199,12 +206,14 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char ctr.line_len = spprintf(&(ctr.line), 0, "Content-length: %d", info->uncompressed_filesize); sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); efree(ctr.line); + if (FAILURE == sapi_send_headers(TSRMLS_C)) { zend_bailout(); } /* prepare to output */ fp = phar_get_efp(info, 1 TSRMLS_CC); + if (!fp) { char *error; if (!phar_open_jit(phar, info, phar_get_pharfp(phar TSRMLS_CC), &error, 0 TSRMLS_CC)) { @@ -218,6 +227,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char } position = 0; phar_seek_efp(info, 0, SEEK_SET, 0, 1 TSRMLS_CC); + do { got = php_stream_read(fp, buf, MIN(8192, info->uncompressed_filesize - position)); if (got > 0) { @@ -235,6 +245,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char phar_mung_server_vars(arch, entry, entry_len, basename, ru, ru_len TSRMLS_CC); efree(basename); } + if (entry[0] == '/') { name_len = spprintf(&name, 4096, "phar://%s%s", arch, entry); } else { @@ -249,6 +260,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; + if (zend_hash_add(&EG(included_files), name, name_len+1, (void *)&dummy, sizeof(int), NULL) == SUCCESS) { if ((cwd = zend_memrchr(entry, '/', entry_len))) { PHAR_G(cwd_init) = 1; @@ -264,11 +276,15 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len)); } } + new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC); + if (!new_op_array) { zend_hash_del(&EG(included_files), name, name_len+1); } + zend_destroy_file_handle(&file_handle TSRMLS_CC); + } else { efree(name); new_op_array = NULL; @@ -286,18 +302,23 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char } zend_end_try(); destroy_op_array(new_op_array TSRMLS_CC); efree(new_op_array); + if (PHAR_G(cwd)) { efree(PHAR_G(cwd)); PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; } + PHAR_G(cwd_init) = 0; efree(name); + if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { zval_ptr_dtor(EG(return_value_ptr_ptr)); } + zend_bailout(); } + return PHAR_MIME_PHP; } return -1; @@ -355,6 +376,7 @@ static void phar_postprocess_ru_web(char *fname, int fname_len, char **entry, in /* 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); } @@ -373,6 +395,7 @@ static void phar_postprocess_ru_web(char *fname, int fname_len, char **entry, in *entry_len = e_len + 1; return; } + if (u) { u1 = strrchr(e, '/'); u[0] = '/'; @@ -391,9 +414,11 @@ static void phar_postprocess_ru_web(char *fname, int fname_len, char **entry, in return; } } + u[0] = '\0'; u_len = strlen(u + 1); e_len -= u_len + 1; + if (e_len < 0) { if (saveu) { saveu[0] = '/'; @@ -431,6 +456,7 @@ PHP_METHOD(Phar, running) RETURN_STRINGL(arch, arch_len, 0); } } + RETURN_STRINGL("", 0, 1); } /* }}} */ @@ -461,6 +487,7 @@ PHP_METHOD(Phar, mount) if (fname_len > 7 && !memcmp(fname, "phar://", 7) && SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { efree(entry); entry = NULL; + if (path_len > 7 && !memcmp(path, "phar://", 7)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Can only mount internal paths within a phar archive, use a relative path instead of \"%s\"", path); efree(arch); @@ -473,7 +500,9 @@ carry_on2: goto carry_on; } } + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s is not a phar archive, cannot mount", arch); + if (arch) { efree(arch); } @@ -485,17 +514,22 @@ carry_on: if (path && path == entry) { efree(entry); } + if (arch) { efree(arch); } + return; } + if (entry && path && path == entry) { efree(entry); } + if (arch) { efree(arch); } + return; } else if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void **)&pphar)) { goto carry_on; @@ -503,12 +537,14 @@ carry_on: if (SUCCESS == phar_copy_on_write(pphar TSRMLS_CC)) { goto carry_on; } + goto carry_on; } else if (SUCCESS == phar_split_fname(path, path_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { path = entry; path_len = entry_len; goto carry_on2; } + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Mounting of %s to %s failed", path, actual); } /* }}} */ @@ -537,6 +573,7 @@ PHP_METHOD(Phar, webPhar) phar_request_initialize(TSRMLS_C); fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); + if (phar_open_executed_filename(alias, alias_len, &error TSRMLS_CC) != SUCCESS) { if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); @@ -549,11 +586,13 @@ PHP_METHOD(Phar, webPhar) if (!(SG(request_info).request_method && SG(request_info).request_uri && (!strcmp(SG(request_info).request_method, "GET") || !strcmp(SG(request_info).request_method, "POST")))) { return; } + #ifdef PHP_WIN32 fname = estrndup(fname, fname_len); phar_unixify_path_separators(fname, fname_len); #endif basename = zend_memrchr(fname, '/', fname_len); + if (!basename) { basename = fname; } else { @@ -568,12 +607,13 @@ PHP_METHOD(Phar, webPhar) zval **z_script_name, **z_path_info; if (SUCCESS != zend_hash_find(ht, "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void**)&z_script_name) || - IS_STRING != Z_TYPE_PP(z_script_name) || - !strstr(Z_STRVAL_PP(z_script_name), basename)) { + IS_STRING != Z_TYPE_PP(z_script_name) || + !strstr(Z_STRVAL_PP(z_script_name), basename)) { return; } + if (SUCCESS == zend_hash_find(ht, "PATH_INFO", sizeof("PATH_INFO"), (void**)&z_path_info) && - IS_STRING == Z_TYPE_PP(z_path_info)) { + IS_STRING == Z_TYPE_PP(z_path_info)) { entry_len = Z_STRLEN_PP(z_path_info); entry = estrndup(Z_STRVAL_PP(z_path_info), entry_len); path_info = emalloc(Z_STRLEN_PP(z_script_name) + entry_len + 1); @@ -585,7 +625,9 @@ PHP_METHOD(Phar, webPhar) entry = estrndup("", 0); path_info = Z_STRVAL_PP(z_script_name); } + pt = estrndup(Z_STRVAL_PP(z_script_name), Z_STRLEN_PP(z_script_name)); + } else { char *testit; @@ -594,7 +636,9 @@ PHP_METHOD(Phar, webPhar) efree(testit); return; } + path_info = sapi_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); + if (path_info) { entry = path_info; entry_len = strlen(entry); @@ -606,6 +650,7 @@ PHP_METHOD(Phar, webPhar) entry = estrndup("", 0); entry_len = 0; } + pt = estrndup(testit, (pt - testit) + (fname_len - (basename - fname))); } not_cgi = 0; @@ -616,8 +661,8 @@ PHP_METHOD(Phar, webPhar) /* this can happen with rewrite rules - and we have no idea what to do then, so return */ return; } - entry_len = strlen(path_info); + entry_len = strlen(path_info); entry_len -= (pt - path_info) + (fname_len - (basename - fname)); entry = estrndup(pt + (fname_len - (basename - fname)), entry_len); @@ -640,9 +685,11 @@ PHP_METHOD(Phar, webPhar) if (FAILURE == zend_fcall_info_init(rewrite, 0, &fci, &fcc, NULL, NULL TSRMLS_CC)) { #endif zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: invalid rewrite callback"); + if (free_pathinfo) { efree(path_info); } + return; } @@ -659,11 +706,14 @@ PHP_METHOD(Phar, webPhar) if (!EG(exception)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: failed to call rewrite callback"); } + if (free_pathinfo) { efree(path_info); } + return; } + if (!fci.retval_ptr_ptr || !retval_ptr) { if (free_pathinfo) { efree(path_info); @@ -671,9 +721,16 @@ PHP_METHOD(Phar, webPhar) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: rewrite callback must return a string or false"); return; } + switch (Z_TYPE_P(retval_ptr)) { - case IS_STRING : +#if PHP_VERSION_ID >= 60000 + case IS_UNICODE: + zval_unicode_to_string(retval_ptr TSRMLS_CC); + /* break intentionally omitted */ +#endif + case IS_STRING: efree(entry); + if (fci.retval_ptr_ptr != &retval_ptr) { entry = estrndup(Z_STRVAL_PP(fci.retval_ptr_ptr), Z_STRLEN_PP(fci.retval_ptr_ptr)); entry_len = Z_STRLEN_PP(fci.retval_ptr_ptr); @@ -681,19 +738,24 @@ PHP_METHOD(Phar, webPhar) entry = Z_STRVAL_P(retval_ptr); entry_len = Z_STRLEN_P(retval_ptr); } + break; - case IS_BOOL : + case IS_BOOL: phar_do_403(entry, entry_len TSRMLS_CC); + if (free_pathinfo) { efree(path_info); } + zend_bailout(); return; default: efree(retval_ptr); + if (free_pathinfo) { efree(path_info); } + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar error: rewrite callback must return a string or false"); return; } @@ -702,6 +764,7 @@ PHP_METHOD(Phar, webPhar) if (entry_len) { phar_postprocess_ru_web(fname, fname_len, &entry, &entry_len, &ru, &ru_len TSRMLS_CC); } + if (!entry_len || (entry_len == 1 && entry[0] == '/')) { efree(entry); /* direct request */ @@ -721,9 +784,11 @@ PHP_METHOD(Phar, webPhar) if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL TSRMLS_CC) || (info = phar_get_entry_info(phar, entry, entry_len, NULL, 0 TSRMLS_CC)) == NULL) { phar_do_404(phar, fname, fname_len, f404, f404_len, entry, entry_len TSRMLS_CC); + if (free_pathinfo) { efree(path_info); } + zend_bailout(); } else { char *tmp, sa; @@ -738,18 +803,23 @@ PHP_METHOD(Phar, webPhar) sa = *tmp; *tmp = '\0'; } + ctr.response_code = 0; + if (path_info[strlen(path_info)-1] == '/') { ctr.line_len = spprintf(&(ctr.line), 4096, "Location: %s%s", path_info, entry + 1); } else { ctr.line_len = spprintf(&(ctr.line), 4096, "Location: %s%s", path_info, entry); } + if (not_cgi) { *tmp = sa; } + if (free_pathinfo) { efree(path_info); } + sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); sapi_send_headers(TSRMLS_C); efree(ctr.line); @@ -775,7 +845,7 @@ PHP_METHOD(Phar, webPhar) if (SUCCESS == zend_hash_find(Z_ARRVAL_P(mimeoverride), ext, strlen(ext)+1, (void **) &val)) { switch (Z_TYPE_PP(val)) { - case IS_LONG : + case IS_LONG: if (Z_LVAL_PP(val) == PHAR_MIME_PHP || Z_LVAL_PP(val) == PHAR_MIME_PHPS) { mime_type = ""; code = Z_LVAL_PP(val); @@ -787,11 +857,11 @@ PHP_METHOD(Phar, webPhar) RETURN_FALSE; } break; - case IS_STRING : + case IS_STRING: mime_type = Z_STRVAL_PP(val); code = PHAR_MIME_OTHER; break; - default : + default: zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Unknown mime type specifier used (not a string or int), only Phar::PHP, Phar::PHPS and a mime type string are allowed"); #ifdef PHP_WIN32 efree(fname); @@ -818,6 +888,7 @@ PHP_METHOD(Phar, webPhar) PHP_METHOD(Phar, mungServer) { zval *mungvalues; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &mungvalues) == FAILURE) { return; } @@ -826,6 +897,7 @@ PHP_METHOD(Phar, mungServer) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "No values passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME"); return; } + if (zend_hash_num_elements(Z_ARRVAL_P(mungvalues)) > 4) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Too many values passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME"); return; @@ -840,13 +912,16 @@ PHP_METHOD(Phar, mungServer) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "unable to retrieve array value in Phar::mungServer()"); return; } + if (Z_TYPE_PP(data) != IS_STRING) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Non-string value passed to Phar::mungServer(), expecting an array of any of these strings: PHP_SELF, REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME"); return; } + if (Z_STRLEN_PP(data) == sizeof("PHP_SELF")-1 && !strncmp(Z_STRVAL_PP(data), "PHP_SELF", sizeof("PHP_SELF")-1)) { PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_PHP_SELF; } + if (Z_STRLEN_PP(data) == sizeof("REQUEST_URI")-1) { if (!strncmp(Z_STRVAL_PP(data), "REQUEST_URI", sizeof("REQUEST_URI")-1)) { PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_REQUEST_URI; @@ -855,6 +930,7 @@ PHP_METHOD(Phar, mungServer) PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_SCRIPT_NAME; } } + if (Z_STRLEN_PP(data) == sizeof("SCRIPT_FILENAME")-1 && !strncmp(Z_STRVAL_PP(data), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1)) { PHAR_GLOBALS->phar_SERVER_mung_list |= PHAR_MUNG_SCRIPT_FILENAME; } @@ -909,6 +985,7 @@ PHP_METHOD(Phar, mapPhar) char *alias = NULL, *error; int alias_len = 0; long dataoffset = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!l", &alias, &alias_len, &dataoffset) == FAILURE) { return; } @@ -916,6 +993,7 @@ PHP_METHOD(Phar, mapPhar) phar_request_initialize(TSRMLS_C); RETVAL_BOOL(phar_open_executed_filename(alias, alias_len, &error TSRMLS_CC) == SUCCESS); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -936,6 +1014,7 @@ PHP_METHOD(Phar, loadPhar) phar_request_initialize(TSRMLS_C); RETVAL_BOOL(phar_open_from_filename(fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, &error TSRMLS_CC) == SUCCESS); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -968,14 +1047,12 @@ PHP_METHOD(Phar, canCompress) } else { RETURN_FALSE; } - case PHAR_ENT_COMPRESSED_BZ2: if (PHAR_G(has_bz2)) { RETURN_TRUE; } else { RETURN_FALSE; } - default: if (PHAR_G(has_zlib) || PHAR_G(has_bz2)) { RETURN_TRUE; @@ -1019,9 +1096,11 @@ PHP_METHOD(Phar, isValidPharFilename) static void phar_spl_foreign_dtor(spl_filesystem_object *object TSRMLS_DC) /* {{{ */ { phar_archive_data *phar = (phar_archive_data *) object->oth; + if (!phar->is_persistent) { phar_archive_delref(phar TSRMLS_CC); } + object->oth = NULL; } /* }}} */ @@ -1040,8 +1119,8 @@ static void phar_spl_foreign_clone(spl_filesystem_object *src, spl_filesystem_ob /* }}} */ static spl_other_handler phar_spl_foreign_handler = { - phar_spl_foreign_dtor, - phar_spl_foreign_clone + phar_spl_foreign_dtor, + phar_spl_foreign_clone }; #endif /* HAVE_SPL */ @@ -1057,7 +1136,12 @@ PHP_METHOD(Phar, __construct) #else char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname; int fname_len, alias_len = 0, arch_len, entry_len, is_data; - long flags = SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS, format = 0; +#if PHP_VERSION_ID < 50300 + long flags = 0; +#else + long flags = SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS; +#endif + long format = 0; phar_archive_object *phar_obj; phar_archive_data *phar_data; zval *zobj = getThis(), arg1, arg2; @@ -1127,6 +1211,7 @@ PHP_METHOD(Phar, __construct) phar_data->is_zip = 1; phar_data->is_tar = 0; } + if (fname == arch) { efree(arch); fname = save_fname; @@ -1143,10 +1228,13 @@ PHP_METHOD(Phar, __construct) efree(entry); return; } + is_data = phar_data->is_data; + if (!phar_data->is_persistent) { ++(phar_data->refcount); } + phar_obj->arc.archive = phar_data; phar_obj->spl.oth_handler = &phar_spl_foreign_handler; @@ -1159,22 +1247,21 @@ PHP_METHOD(Phar, __construct) INIT_PZVAL(&arg1); ZVAL_STRINGL(&arg1, fname, fname_len, 0); - INIT_PZVAL(&arg2); ZVAL_LONG(&arg2, flags); + zend_call_method_with_2_params(&zobj, Z_OBJCE_P(zobj), &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg1, &arg2); if (!phar_data->is_persistent) { phar_obj->arc.archive->is_data = is_data; } - phar_obj->spl.info_class = phar_ce_entry; + phar_obj->spl.info_class = phar_ce_entry; efree(fname); #endif /* HAVE_SPL */ } /* }}} */ -/* }}} */ /* {{{ proto array Phar::getSupportedSignatures() * Return array of supported signature types @@ -1185,12 +1272,10 @@ PHP_METHOD(Phar, getSupportedSignatures) add_next_index_stringl(return_value, "MD5", 3, 1); add_next_index_stringl(return_value, "SHA-1", 5, 1); - #if HAVE_HASH_EXT add_next_index_stringl(return_value, "SHA-256", 7, 1); add_next_index_stringl(return_value, "SHA-512", 7, 1); #endif - #if PHAR_HAVE_OPENSSL add_next_index_stringl(return_value, "OpenSSL", 7, 1); #else @@ -1207,11 +1292,12 @@ PHP_METHOD(Phar, getSupportedSignatures) PHP_METHOD(Phar, getSupportedCompression) { array_init(return_value); - phar_request_initialize(TSRMLS_C); + if (PHAR_G(has_zlib)) { add_next_index_stringl(return_value, "GZ", 2, 1); } + if (PHAR_G(has_bz2)) { add_next_index_stringl(return_value, "BZIP2", 5, 1); } @@ -1264,11 +1350,14 @@ PHP_METHOD(Phar, unlinkArchive) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar archive \"%s\" is in phar.cache_list, cannot unlinkArchive()", fname); return; } + if (phar->refcount) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar archive \"%s\" has open file handles or objects. fclose() all file handles, and unset() all objects prior to calling unlinkArchive()", fname); return; } + fname = estrndup(phar->fname, phar->fname_len); + /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; @@ -1319,23 +1408,33 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ char *str = "[stream]"; iter->funcs->get_current_data(iter, &value TSRMLS_CC); + if (EG(exception)) { return ZEND_HASH_APPLY_STOP; } + if (!value) { /* failure in get_current_data */ - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned no value", ce->name); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned no value", ce->name); return ZEND_HASH_APPLY_STOP; } + switch (Z_TYPE_PP(value)) { - case IS_STRING : +#if PHP_VERSION_ID >= 60000 + case IS_UNICODE: + zval_unicode_to_string(*(value) TSRMLS_CC); + /* break intentionally omitted */ +#endif + case IS_STRING: break; - case IS_RESOURCE : + case IS_RESOURCE: php_stream_from_zval_no_verify(fp, value); + if (!fp) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %s returned an invalid stream handle", ce->name); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returned an invalid stream handle", ce->name); return ZEND_HASH_APPLY_STOP; } + if (iter->funcs->get_current_key) { key_type = iter->funcs->get_current_key(iter, &key, &str_key_len, &int_key TSRMLS_CC); @@ -1343,31 +1442,42 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ return ZEND_HASH_APPLY_STOP; } - PHAR_STR(key, str_key); - if (key_type == HASH_KEY_IS_LONG) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } + + if (key_type > 9) { /* IS_UNICODE == 10 */ + spprintf(&str_key, 0, "%v", key); + } else { + PHAR_STR(key, str_key); + } + save = str_key; - if (str_key[str_key_len - 1] == '\0') str_key_len--; + + if (str_key[str_key_len - 1] == '\0') { + str_key_len--; + } + } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } + close_fp = 0; opened = (char *) estrndup(str, sizeof("[stream]") + 1); goto after_open_fp; - case IS_OBJECT : + case IS_OBJECT: if (instanceof_function(Z_OBJCE_PP(value), spl_ce_SplFileInfo TSRMLS_CC)) { char *test = NULL; zval dummy; spl_filesystem_object *intern = (spl_filesystem_object*)zend_object_store_get_object(*value TSRMLS_CC); if (!base_len) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %s returns an SplFileInfo object, so base directory must be specified", ce->name); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returns an SplFileInfo object, so base directory must be specified", ce->name); return ZEND_HASH_APPLY_STOP; } + switch (intern->type) { case SPL_FS_DIR: #if PHP_VERSION_ID >= 60000 @@ -1379,23 +1489,31 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ #endif fname_len = spprintf(&fname, 0, "%s%c%s", test, DEFAULT_SLASH, intern->u.dir.entry.d_name); php_stat(fname, fname_len, FS_IS_DIR, &dummy TSRMLS_CC); + if (Z_BVAL(dummy)) { /* ignore directories */ efree(fname); return ZEND_HASH_APPLY_KEEP; } + test = expand_filepath(fname, NULL TSRMLS_CC); + if (test) { efree(fname); fname = test; fname_len = strlen(fname); } + save = fname; is_splfileinfo = 1; goto phar_spl_fileinfo; case SPL_FS_INFO: case SPL_FS_FILE: +#if PHP_VERSION_ID >= 60000 + fname = expand_filepath(intern->file_name.s, NULL TSRMLS_CC); +#else fname = expand_filepath(intern->file_name, NULL TSRMLS_CC); +#endif fname_len = strlen(fname); save = fname; is_splfileinfo = 1; @@ -1403,8 +1521,8 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ } } /* fall-through */ - default : - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid value (must return a string)", ce->name); + default: + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid value (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } @@ -1416,8 +1534,10 @@ phar_spl_fileinfo: temp = expand_filepath(base, NULL TSRMLS_CC); base = temp; base_len = strlen(base); + if (strstr(fname, base)) { str_key_len = fname_len - base_len; + if (str_key_len <= 0) { if (save) { efree(save); @@ -1425,17 +1545,22 @@ phar_spl_fileinfo: } return ZEND_HASH_APPLY_KEEP; } + str_key = fname + base_len; + if (*str_key == '/' || *str_key == '\\') { str_key++; str_key_len--; } + } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that is not in the base directory \"%s\"", ce->name, fname, base); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that is not in the base directory \"%s\"", ce->name, fname, base); + if (save) { efree(save); efree(temp); } + return ZEND_HASH_APPLY_STOP; } } else { @@ -1446,89 +1571,115 @@ phar_spl_fileinfo: return ZEND_HASH_APPLY_STOP; } - PHAR_STR(key, str_key); - if (key_type == HASH_KEY_IS_LONG) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } + + if (key_type > 9) { /* IS_UNICODE == 10 */ + spprintf(&str_key, 0, "%v", key); + } else { + PHAR_STR(key, str_key); + } + save = str_key; + if (str_key[str_key_len - 1] == '\0') str_key_len--; } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } } #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that safe mode prevents opening", ce->name, fname); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that safe mode prevents opening", ce->name, fname); + if (save) { efree(save); } + if (temp) { efree(temp); } + return ZEND_HASH_APPLY_STOP; } #endif if (php_check_open_basedir(fname TSRMLS_CC)) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that open_basedir prevents opening", ce->name, fname); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that open_basedir prevents opening", ce->name, fname); + if (save) { efree(save); } + if (temp) { efree(temp); } + return ZEND_HASH_APPLY_STOP; } /* try to open source file, then create internal phar file and copy contents */ fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened); + if (!fp) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a file that could not be opened \"%s\"", ce->name, fname); + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a file that could not be opened \"%s\"", ce->name, fname); + if (save) { efree(save); } + if (temp) { efree(temp); } + return ZEND_HASH_APPLY_STOP; } - after_open_fp: if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) { /* silently skip any files that would be added to the magic .phar directory */ if (save) { efree(save); } + if (temp) { efree(temp); } + if (opened) { efree(opened); } + if (close_fp) { php_stream_close(fp); } + return ZEND_HASH_APPLY_KEEP; } + if (!(data = phar_get_or_create_entry_data(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, str_key, str_key_len, "w+b", 0, &error, 1 TSRMLS_CC))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s cannot be created: %s", str_key, error); efree(error); + if (save) { efree(save); } + if (opened) { efree(opened); } + if (temp) { efree(temp); } + if (close_fp) { php_stream_close(fp); } + return ZEND_HASH_APPLY_STOP; + } else { if (error) { efree(error); @@ -1537,6 +1688,7 @@ after_open_fp: if (data->internal_file->fp_type == PHAR_MOD) { php_stream_close(data->internal_file->fp); } + data->internal_file->fp = NULL; data->internal_file->fp_type = PHAR_UFP; data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp); @@ -1545,6 +1697,7 @@ after_open_fp: data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize = php_stream_tell(p_obj->fp) - data->internal_file->offset; } + if (close_fp) { php_stream_close(fp); } @@ -1554,6 +1707,7 @@ after_open_fp: if (save) { efree(save); } + if (temp) { efree(temp); } @@ -1602,7 +1756,11 @@ PHP_METHOD(Phar, buildFromDirectory) INIT_PZVAL(&arg); ZVAL_STRINGL(&arg, dir, dir_len, 0); INIT_PZVAL(&arg2); +#if PHP_VERSION_ID < 50300 + ZVAL_LONG(&arg2, 0); +#else ZVAL_LONG(&arg2, SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS); +#endif zend_call_method_with_2_params(&iter, spl_ce_RecursiveDirectoryIterator, &spl_ce_RecursiveDirectoryIterator->constructor, "__construct", NULL, &arg, &arg2); @@ -1662,15 +1820,19 @@ PHP_METHOD(Phar, buildFromDirectory) if (SUCCESS == spl_iterator_apply((apply_reg ? regexiter : iteriter), (spl_iterator_apply_func_t) phar_build, (void *) &pass TSRMLS_CC)) { zval_ptr_dtor(&iteriter); + if (apply_reg) { zval_ptr_dtor(®exiter); } + phar_obj->arc.archive->ufp = pass.fp; phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + } else { zval_ptr_dtor(&iteriter); php_stream_close(pass.fp); @@ -1728,7 +1890,6 @@ PHP_METHOD(Phar, buildFromIterator) } else { php_stream_close(pass.fp); } - } /* }}} */ @@ -1738,7 +1899,7 @@ PHP_METHOD(Phar, buildFromIterator) PHP_METHOD(Phar, count) { PHAR_ARCHIVE_OBJECT(); - + RETURN_LONG(zend_hash_num_elements(&phar_obj->arc.archive->manifest)); } /* }}} */ @@ -1755,6 +1916,7 @@ PHP_METHOD(Phar, isFileFormat) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) { RETURN_FALSE; } + switch (type) { case PHAR_FORMAT_TAR: RETURN_BOOL(phar_obj->arc.archive->is_tar); @@ -1785,23 +1947,28 @@ static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS } return FAILURE; } + /* copy old contents in entirety */ phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC); offset = php_stream_tell(fp); link = phar_get_link_source(entry TSRMLS_CC); + if (!link) { link = entry; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename); return FAILURE; } + if (entry->fp_type == PHAR_MOD) { /* save for potential restore on error */ entry->cfp = entry->fp; entry->fp = NULL; } + /* set new location of file contents */ entry->fp_type = PHAR_FP; entry->offset = offset; @@ -1823,12 +1990,15 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c if (!ext) { if (phar->is_zip) { + if (phar->is_data) { ext = "zip"; } else { ext = "phar.zip"; } + } else if (phar->is_tar) { + switch (phar->flags) { case PHAR_FILE_COMPRESSED_GZ: if (phar->is_data) { @@ -1852,6 +2022,7 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c } } } else { + switch (phar->flags) { case PHAR_FILE_COMPRESSED_GZ: ext = "phar.gz"; @@ -1864,6 +2035,7 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c } } } else if (phar_path_check(&ext, &ext_len, &pcr_error) > pcr_is_ok) { + if (phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar converted from \"%s\" has invalid extension %s", phar->fname, ext); } else { @@ -1913,11 +2085,11 @@ static zval *phar_rename_archive(phar_archive_data *phar, char *ext, zend_bool c goto its_ok; } } + efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to add newly converted phar \"%s\" to the list of phars, a phar with that name already exists", phar->fname); return NULL; } - its_ok: if (!phar->is_data) { if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 1, 1, 1 TSRMLS_CC)) { @@ -1925,6 +2097,7 @@ its_ok: zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "phar \"%s\" has invalid extension %s", phar->fname, ext); return NULL; } + if (phar->alias) { if (phar->is_temporary_alias) { phar->alias = NULL; @@ -1936,12 +2109,15 @@ its_ok: zend_hash_update(&(PHAR_GLOBALS->phar_alias_map), newpath, phar->fname_len, (void*)&phar, sizeof(phar_archive_data*), NULL); } } + } else { + if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &(phar->ext_len), 0, 1, 1 TSRMLS_CC)) { efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "data phar \"%s\" has invalid extension %s", phar->fname, ext); return NULL; } + phar->alias = NULL; phar->alias_len = 0; } @@ -1953,6 +2129,7 @@ its_ok: } phar_flush(phar, 0, 0, 1, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, error); efree(error); @@ -1969,16 +2146,17 @@ its_ok: } MAKE_STD_ZVAL(ret); + if (SUCCESS != object_init_ex(ret, ce)) { zval_dtor(ret); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname); return NULL; } + INIT_PZVAL(&arg1); ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len, 0); zend_call_method_with_1_params(&ret, ce, &ce->constructor, "__construct", NULL, &arg1); - return ret; } /* }}} */ @@ -1996,16 +2174,16 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char phar = (phar_archive_data *) ecalloc(1, sizeof(phar_archive_data)); /* set whole-archive compression and type from parameter */ phar->flags = flags; - phar->is_data = source->is_data; + switch (convert) { - case PHAR_FORMAT_TAR : + case PHAR_FORMAT_TAR: phar->is_tar = 1; break; - case PHAR_FORMAT_ZIP : + case PHAR_FORMAT_ZIP: phar->is_zip = 1; break; - default : + default: phar->is_data = 0; break; } @@ -2022,6 +2200,7 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char phar->fname_len = source->fname_len; phar->is_temporary_alias = source->is_temporary_alias; phar->alias = source->alias; + /* first copy each file's uncompressed contents to a temporary file and set per-file flags */ for (zend_hash_internal_pointer_reset(&source->manifest); SUCCESS == zend_hash_has_more_elements(&source->manifest); zend_hash_move_forward(&source->manifest)) { @@ -2033,16 +2212,21 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char "Cannot convert phar archive \"%s\"", source->fname); return NULL; } + newentry = *entry; + if (newentry.link) { newentry.link = estrdup(newentry.link); goto no_copy; } + if (newentry.tmp) { newentry.tmp = estrdup(newentry.tmp); goto no_copy; } + newentry.metadata_str.c = 0; + if (FAILURE == phar_copy_file_contents(&newentry, phar->fp TSRMLS_CC)) { zend_hash_destroy(&(phar->manifest)); php_stream_close(phar->fp); @@ -2052,6 +2236,7 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char } no_copy: newentry.filename = estrndup(newentry.filename, newentry.filename_len); + if (newentry.metadata) { zval *t; @@ -2068,11 +2253,14 @@ no_copy: newentry.metadata_str.c = NULL; newentry.metadata_str.len = 0; } + newentry.is_zip = phar->is_zip; newentry.is_tar = phar->is_tar; + if (newentry.is_tar) { newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE); } + newentry.is_modified = 1; newentry.phar = phar; newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */ @@ -2096,7 +2284,7 @@ no_copy: /* }}} */ /* {{{ proto object Phar::convertToExecutable([int format[, int compression [, string file_ext]]]) - * Convert a phar.tar or phar.zip archive to the phar file format. The + * Convert a phar.tar or phar.zip archive to the phar file format. The * optional parameter allows the user to determine the new * filename extension (default is phar). */ @@ -2155,25 +2343,28 @@ PHP_METHOD(Phar, convertToExecutable) "Cannot compress entire archive with gzip, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with gzip, enable ext/zlib in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_GZ; break; - case PHAR_ENT_COMPRESSED_BZ2: if (format == PHAR_FORMAT_ZIP) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_BZ2; break; default: @@ -2186,6 +2377,7 @@ PHP_METHOD(Phar, convertToExecutable) phar_obj->arc.archive->is_data = 0; ret = phar_convert_to_other(phar_obj->arc.archive, format, ext, flags TSRMLS_CC); phar_obj->arc.archive->is_data = is_data; + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2253,25 +2445,28 @@ PHP_METHOD(Phar, convertToData) "Cannot compress entire archive with gzip, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with gzip, enable ext/zlib in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_GZ; break; - case PHAR_ENT_COMPRESSED_BZ2: if (format == PHAR_FORMAT_ZIP) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, zip archives do not support whole-archive compression"); return; } + if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress entire archive with bz2, enable ext/bz2 in php.ini"); return; } + flags = PHAR_FILE_COMPRESSED_BZ2; break; default: @@ -2284,6 +2479,7 @@ PHP_METHOD(Phar, convertToData) phar_obj->arc.archive->is_data = 1; ret = phar_convert_to_other(phar_obj->arc.archive, format, ext, flags TSRMLS_CC); phar_obj->arc.archive->is_data = is_data; + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2299,13 +2495,15 @@ PHP_METHOD(Phar, convertToData) PHP_METHOD(Phar, isCompressed) { PHAR_ARCHIVE_OBJECT(); - + if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_GZ) { RETURN_LONG(PHAR_ENT_COMPRESSED_GZ); } + if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_BZ2) { RETURN_LONG(PHAR_ENT_COMPRESSED_BZ2); } + RETURN_FALSE; } /* }}} */ @@ -2317,10 +2515,11 @@ PHP_METHOD(Phar, isWritable) { php_stream_statbuf ssb; PHAR_ARCHIVE_OBJECT(); - + if (!phar_obj->arc.archive->is_writeable) { RETURN_FALSE; } + if (SUCCESS != php_stream_stat_path(phar_obj->arc.archive->fname, &ssb)) { if (phar_obj->arc.archive->is_brandnew) { /* assume it works if the file doesn't exist yet */ @@ -2328,6 +2527,7 @@ PHP_METHOD(Phar, isWritable) } RETURN_FALSE; } + RETURN_BOOL((ssb.sb.st_mode & (S_IWOTH | S_IWGRP | S_IWUSR)) != 0); } /* }}} */ @@ -2374,7 +2574,7 @@ PHP_METHOD(Phar, delete) zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } - + RETURN_TRUE; } /* }}} */ @@ -2412,6 +2612,7 @@ PHP_METHOD(Phar, setAlias) char *alias, *error, *oldalias; phar_archive_data **fd_ptr; int alias_len, oldalias_len, old_temp, readd = 0; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -2425,8 +2626,13 @@ PHP_METHOD(Phar, setAlias) PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; if (phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "A Phar alias cannot be set in a plain %s archive", phar_obj->arc.archive->is_tar ? "tar" : "zip"); + if (phar_obj->arc.archive->is_tar) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar alias cannot be set in a plain tar archive"); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar alias cannot be set in a plain zip archive"); + } RETURN_FALSE; } @@ -2458,15 +2664,17 @@ valid_alias: oldalias = phar_obj->arc.archive->alias; oldalias_len = phar_obj->arc.archive->alias_len; old_temp = phar_obj->arc.archive->is_temporary_alias; + if (alias_len) { phar_obj->arc.archive->alias = estrndup(alias, alias_len); } else { phar_obj->arc.archive->alias = NULL; } + phar_obj->arc.archive->alias_len = alias_len; phar_obj->arc.archive->is_temporary_alias = 0; - phar_flush(phar_obj->arc.archive, NULL, 0, 0, &error TSRMLS_CC); + if (error) { phar_obj->arc.archive->alias = oldalias; phar_obj->arc.archive->alias_len = oldalias_len; @@ -2478,10 +2686,13 @@ valid_alias: efree(error); RETURN_FALSE; } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(phar_obj->arc.archive), sizeof(phar_archive_data*), NULL); + if (oldalias) { efree(oldalias); } + RETURN_TRUE; } @@ -2495,7 +2706,7 @@ valid_alias: PHP_METHOD(Phar, getVersion) { PHAR_ARCHIVE_OBJECT(); - + RETURN_STRING(phar_obj->arc.archive->version, 1); } /* }}} */ @@ -2506,7 +2717,7 @@ PHP_METHOD(Phar, getVersion) PHP_METHOD(Phar, startBuffering) { PHAR_ARCHIVE_OBJECT(); - + phar_obj->arc.archive->donotflush = 1; } /* }}} */ @@ -2517,7 +2728,7 @@ PHP_METHOD(Phar, startBuffering) PHP_METHOD(Phar, isBuffering) { PHAR_ARCHIVE_OBJECT(); - + RETURN_BOOL(!phar_obj->arc.archive->donotflush); } /* }}} */ @@ -2528,6 +2739,7 @@ PHP_METHOD(Phar, isBuffering) PHP_METHOD(Phar, stopBuffering) { char *error; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -2537,8 +2749,8 @@ PHP_METHOD(Phar, stopBuffering) } phar_obj->arc.archive->donotflush = 0; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -2566,8 +2778,13 @@ PHP_METHOD(Phar, setStub) } if (phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "A Phar stub cannot be set in a plain %s archive", phar_obj->arc.archive->is_tar ? "tar" : "zip"); + if (phar_obj->arc.archive->is_tar) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain tar archive"); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain zip archive"); + } return; } @@ -2590,10 +2807,12 @@ PHP_METHOD(Phar, setStub) } } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &stub, &stub_len) == SUCCESS) { phar_flush(phar_obj->arc.archive, stub, stub_len, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } @@ -2614,7 +2833,7 @@ PHP_METHOD(Phar, setStub) * files cannot be loaded via this kind of stub, no parameters are accepted * when the Phar object is zip- or tar-based. */ - PHP_METHOD(Phar, setDefaultStub) +PHP_METHOD(Phar, setDefaultStub) { char *index = NULL, *webindex = NULL, *error = NULL, *stub = NULL; int index_len = 0, webindex_len = 0, created_stub = 0; @@ -2622,8 +2841,13 @@ PHP_METHOD(Phar, setStub) PHAR_ARCHIVE_OBJECT(); if (phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, - "A Phar stub cannot be set in a plain %s archive", phar_obj->arc.archive->is_tar ? "tar" : "zip"); + if (phar_obj->arc.archive->is_tar) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain tar archive"); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "A Phar stub cannot be set in a plain zip archive"); + } return; } @@ -2643,7 +2867,6 @@ PHP_METHOD(Phar, setStub) } if (!phar_obj->arc.archive->is_tar && !phar_obj->arc.archive->is_zip) { - stub = phar_create_default_stub(index, webindex, &stub_len, &error TSRMLS_CC); if (error) { @@ -2654,6 +2877,7 @@ PHP_METHOD(Phar, setStub) } RETURN_FALSE; } + created_stub = 1; } @@ -2684,6 +2908,7 @@ PHP_METHOD(Phar, setSignatureAlgorithm) long algo; char *error, *key = NULL; int key_len = 0; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -2703,16 +2928,16 @@ PHP_METHOD(Phar, setSignatureAlgorithm) } switch (algo) { - case PHAR_SIG_SHA256 : - case PHAR_SIG_SHA512 : + case PHAR_SIG_SHA256: + case PHAR_SIG_SHA512: #if !HAVE_HASH_EXT zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "SHA-256 and SHA-512 signatures are only supported if the hash extension is enabled"); return; #endif - case PHAR_SIG_MD5 : - case PHAR_SIG_SHA1 : - case PHAR_SIG_OPENSSL : + case PHAR_SIG_MD5: + case PHAR_SIG_SHA1: + case PHAR_SIG_OPENSSL: phar_obj->arc.archive->sig_flags = algo; phar_obj->arc.archive->is_modified = 1; PHAR_G(openssl_privatekey) = key; @@ -2724,7 +2949,7 @@ PHP_METHOD(Phar, setSignatureAlgorithm) efree(error); } break; - default : + default: zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Unknown signature algorithm specified"); } @@ -2745,25 +2970,25 @@ PHP_METHOD(Phar, getSignature) array_init(return_value); add_assoc_stringl(return_value, "hash", phar_obj->arc.archive->signature, phar_obj->arc.archive->sig_len, 1); switch(phar_obj->arc.archive->sig_flags) { - case PHAR_SIG_MD5: - add_assoc_stringl(return_value, "hash_type", "MD5", 3, 1); - break; - case PHAR_SIG_SHA1: - add_assoc_stringl(return_value, "hash_type", "SHA-1", 5, 1); - break; - case PHAR_SIG_SHA256: - add_assoc_stringl(return_value, "hash_type", "SHA-256", 7, 1); - break; - case PHAR_SIG_SHA512: - add_assoc_stringl(return_value, "hash_type", "SHA-512", 7, 1); - break; - case PHAR_SIG_OPENSSL: - add_assoc_stringl(return_value, "hash_type", "OpenSSL", 7, 1); - break; - default: - unknown_len = spprintf(&unknown, 0, "Unknown (%d)", phar_obj->arc.archive->sig_flags); - add_assoc_stringl(return_value, "hash_type", unknown, unknown_len, 0); - break; + case PHAR_SIG_MD5: + add_assoc_stringl(return_value, "hash_type", "MD5", 3, 1); + break; + case PHAR_SIG_SHA1: + add_assoc_stringl(return_value, "hash_type", "SHA-1", 5, 1); + break; + case PHAR_SIG_SHA256: + add_assoc_stringl(return_value, "hash_type", "SHA-256", 7, 1); + break; + case PHAR_SIG_SHA512: + add_assoc_stringl(return_value, "hash_type", "SHA-512", 7, 1); + break; + case PHAR_SIG_OPENSSL: + add_assoc_stringl(return_value, "hash_type", "OpenSSL", 7, 1); + break; + default: + unknown_len = spprintf(&unknown, 0, "Unknown (%d)", phar_obj->arc.archive->sig_flags); + add_assoc_stringl(return_value, "hash_type", unknown, unknown_len, 0); + break; } } else { RETURN_FALSE; @@ -2790,6 +3015,7 @@ static int phar_set_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ */ if (entry->is_deleted) { return ZEND_HASH_APPLY_KEEP; } + entry->old_flags = entry->flags; entry->flags &= ~PHAR_ENT_COMPRESSION_MASK; entry->flags |= compress; @@ -2805,16 +3031,19 @@ static int phar_test_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ * if (entry->is_deleted) { return ZEND_HASH_APPLY_KEEP; } + if (!PHAR_G(has_bz2)) { if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) { *(int *) argument = 0; } } + if (!PHAR_G(has_zlib)) { if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { *(int *) argument = 0; } } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -2828,6 +3057,7 @@ static void pharobj_set_compression(HashTable *manifest, php_uint32 compress TSR static int pharobj_cancompress(HashTable *manifest TSRMLS_DC) /* {{{ */ { int test; + test = 1; zend_hash_apply_with_argument(manifest, phar_test_compression, &test TSRMLS_CC); return test; @@ -2851,7 +3081,7 @@ PHP_METHOD(Phar, compress) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &method, &ext, &ext_len) == FAILURE) { return; } - + if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot compress phar archive, phar is read-only"); @@ -2876,7 +3106,7 @@ PHP_METHOD(Phar, compress) } flags = PHAR_FILE_COMPRESSED_GZ; break; - + case PHAR_ENT_COMPRESSED_BZ2: if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -2896,6 +3126,7 @@ PHP_METHOD(Phar, compress) } else { ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_PHAR, ext, flags TSRMLS_CC); } + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2935,6 +3166,7 @@ PHP_METHOD(Phar, decompress) } else { ret = phar_convert_to_other(phar_obj->arc.archive, PHAR_FORMAT_PHAR, ext, PHAR_FILE_COMPRESSED_NONE TSRMLS_CC); } + if (ret) { RETURN_ZVAL(ret, 1, 1); } else { @@ -2988,11 +3220,13 @@ PHP_METHOD(Phar, compressFiles) "Unknown compression specified, please pass one of Phar::GZ or Phar::BZ2"); return; } + if (phar_obj->arc.archive->is_tar) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with Gzip compression, tar archives cannot compress individual files, use compress() to compress the whole archive"); return; } + if (!pharobj_cancompress(&phar_obj->arc.archive->manifest TSRMLS_CC)) { if (flags == PHAR_FILE_COMPRESSED_GZ) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -3003,11 +3237,11 @@ PHP_METHOD(Phar, compressFiles) } return; } - pharobj_set_compression(&phar_obj->arc.archive->manifest, flags TSRMLS_CC); + pharobj_set_compression(&phar_obj->arc.archive->manifest, flags TSRMLS_CC); phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, error); efree(error); @@ -3028,11 +3262,13 @@ PHP_METHOD(Phar, decompressFiles) "Phar is readonly, cannot change compression"); return; } + if (!pharobj_cancompress(&phar_obj->arc.archive->manifest TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress all files, some are compressed as bzip2 or gzip and cannot be decompressed"); return; } + if (phar_obj->arc.archive->is_tar) { RETURN_TRUE; } else { @@ -3040,12 +3276,13 @@ PHP_METHOD(Phar, decompressFiles) } phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } /* }}} */ @@ -3059,6 +3296,7 @@ PHP_METHOD(Phar, copy) const char *pcr_error; int oldfile_len, newfile_len; phar_entry_info *oldentry, newentry = {0}, *temp; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) { @@ -3106,6 +3344,7 @@ PHP_METHOD(Phar, copy) } memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info)); + if (newentry.metadata) { zval *t; @@ -3122,6 +3361,7 @@ PHP_METHOD(Phar, copy) newentry.metadata_str.c = NULL; newentry.metadata_str.len = 0; } + newentry.filename = estrndup(newfile, newfile_len); newentry.filename_len = newfile_len; newentry.fp_refcount = 0; @@ -3138,8 +3378,8 @@ PHP_METHOD(Phar, copy) zend_hash_add(&oldentry->phar->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL); phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3157,6 +3397,7 @@ PHP_METHOD(Phar, offsetExists) char *fname; int fname_len; phar_entry_info *entry; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { @@ -3205,12 +3446,12 @@ PHP_METHOD(Phar, offsetGet) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot get stub \".phar/stub.php\" directly in phar \"%s\", use getStub", phar_obj->arc.archive->fname); return; } - + if (fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot get alias \".phar/alias.txt\" directly in phar \"%s\", use getAlias", phar_obj->arc.archive->fname); return; } - + if (fname_len >= sizeof(".phar")-1 && !memcmp(fname, ".phar", sizeof(".phar")-1)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot directly get any files or directories in magic \".phar\" directory", phar_obj->arc.archive->fname); return; @@ -3220,13 +3461,13 @@ PHP_METHOD(Phar, offsetGet) efree(entry->filename); efree(entry); } + fname_len = spprintf(&fname, 0, "phar://%s/%s", phar_obj->arc.archive->fname, fname); MAKE_STD_ZVAL(zfname); ZVAL_STRINGL(zfname, fname, fname_len, 0); spl_instantiate_arg_ex1(phar_obj->spl.info_class, &return_value, 0, zfname TSRMLS_CC); zval_ptr_dtor(&zfname); } - } /* }}} */ @@ -3256,6 +3497,7 @@ static void phar_add_file(phar_archive_data *phar, char *filename, int filename_ if (error) { efree(error); } + if (!data->internal_file->is_dir) { if (cont_str) { contents_len = php_stream_write(data->fp, cont_str, cont_len); @@ -3270,10 +3512,13 @@ static void phar_add_file(phar_archive_data *phar, char *filename, int filename_ } contents_len = php_stream_copy_to_stream(contents_file, data->fp, PHP_STREAM_COPY_ALL); } + data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len; } + phar_entry_delref(data TSRMLS_CC); phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3296,13 +3541,16 @@ static void phar_mkdir(phar_archive_data *phar, char *dirname, int dirname_len T } else { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Directory %s does not exist and cannot be created", dirname); } + return; } else { if (error) { efree(error); } + phar_entry_delref(data TSRMLS_CC); phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3311,7 +3559,6 @@ static void phar_mkdir(phar_archive_data *phar, char *dirname, int dirname_len T } /* }}} */ - /* {{{ proto int Phar::offsetSet(string entry, string value) * set the contents of an internal file to those of an external file */ @@ -3326,7 +3573,7 @@ PHP_METHOD(Phar, offsetSet) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by INI setting"); return; } - + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sr", &fname, &fname_len, &zresource) == FAILURE && zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) { return; @@ -3346,6 +3593,7 @@ PHP_METHOD(Phar, offsetSet) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set any files or directories in magic \".phar\" directory", phar_obj->arc.archive->fname); return; } + phar_add_file(phar_obj->arc.archive, fname, fname_len, cont_str, cont_len, zresource TSRMLS_CC); } /* }}} */ @@ -3375,14 +3623,17 @@ PHP_METHOD(Phar, offsetUnset) /* entry is deleted, but has not been flushed to disk yet */ return; } + entry->is_modified = 0; entry->is_deleted = 1; /* we need to "flush" the stream to save the newly deleted file on disk */ phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } } else { @@ -3398,6 +3649,7 @@ PHP_METHOD(Phar, addEmptyDir) { char *dirname; int dirname_len; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &dirname, &dirname_len) == FAILURE) { @@ -3408,6 +3660,7 @@ PHP_METHOD(Phar, addEmptyDir) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot create a directory in magic \".phar\" directory"); return; } + phar_mkdir(phar_obj->arc.archive, dirname, dirname_len TSRMLS_CC); } /* }}} */ @@ -3421,6 +3674,7 @@ PHP_METHOD(Phar, addFile) int fname_len, localname_len = 0; php_stream *resource; zval *zresource; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &fname, &fname_len, &localname, &localname_len) == FAILURE) { @@ -3443,6 +3697,7 @@ PHP_METHOD(Phar, addFile) zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "phar error: unable to open file \"%s\" to add to phar archive", fname); return; } + if (localname) { fname = localname; fname_len = localname_len; @@ -3463,6 +3718,7 @@ PHP_METHOD(Phar, addFromString) { char *localname, *cont_str; int localname_len, cont_len; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &localname, &localname_len, &cont_str, &cont_len) == FAILURE) { @@ -3483,6 +3739,7 @@ PHP_METHOD(Phar, getStub) php_stream *fp; php_stream_filter *filter = NULL; phar_entry_info *stub; + PHAR_ARCHIVE_OBJECT(); if (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) { @@ -3538,6 +3795,7 @@ PHP_METHOD(Phar, getStub) php_stream_rewind(fp); carry_on: buf = safe_emalloc(len, 1, 1); + if (len != php_stream_read(fp, buf, len)) { if (fp != phar_obj->arc.archive->fp) { php_stream_close(fp); @@ -3547,15 +3805,17 @@ carry_on: efree(buf); return; } + if (filter) { php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); } + if (fp != phar_obj->arc.archive->fp) { php_stream_close(fp); } - buf[len] = '\0'; + buf[len] = '\0'; RETURN_STRINGL(buf, len, 0); } /* }}}*/ @@ -3591,6 +3851,7 @@ PHP_METHOD(Phar, setMetadata) { char *error; zval *metadata; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -3610,8 +3871,8 @@ PHP_METHOD(Phar, setMetadata) MAKE_STD_ZVAL(phar_obj->arc.archive->metadata); ZVAL_ZVAL(phar_obj->arc.archive->metadata, metadata, 1, 0); phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3625,6 +3886,7 @@ PHP_METHOD(Phar, setMetadata) PHP_METHOD(Phar, delMetadata) { char *error; + PHAR_ARCHIVE_OBJECT(); if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { @@ -3636,8 +3898,8 @@ PHP_METHOD(Phar, delMetadata) zval_ptr_dtor(&phar_obj->arc.archive->metadata); phar_obj->arc.archive->metadata = NULL; phar_obj->arc.archive->is_modified = 1; - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -3645,6 +3907,7 @@ PHP_METHOD(Phar, delMetadata) } else { RETURN_TRUE; } + } else { RETURN_TRUE; } @@ -3653,7 +3916,7 @@ PHP_METHOD(Phar, delMetadata) #if (PHP_MAJOR_VERSION < 6) #define OPENBASEDIR_CHECKPATH(filename) \ (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC) -#else +#else #define OPENBASEDIR_CHECKPATH(filename) \ php_check_open_basedir(filename TSRMLS_CC) #endif @@ -3670,10 +3933,13 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * /* silently ignore mounted entries */ return SUCCESS; } + if (entry->filename_len >= sizeof(".phar")-1 && !memcmp(entry->filename, ".phar", sizeof(".phar")-1)) { return SUCCESS; } + len = spprintf(&fullpath, 0, "%s/%s", dest, entry->filename); + if (len >= MAXPATHLEN) { char *tmp; /* truncate for error message */ @@ -3688,6 +3954,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * efree(fullpath); return FAILURE; } + if (!len) { spprintf(error, 4096, "Cannot extract \"%s\", internal error", entry->filename); efree(fullpath); @@ -3706,13 +3973,16 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * efree(fullpath); return FAILURE; } + /* perform dirname */ slash = zend_memrchr(entry->filename, '/', entry->filename_len); + if (slash) { fullpath[dest_len + (slash - entry->filename) + 1] = '\0'; } else { fullpath[dest_len] = '\0'; } + if (FAILURE == php_stream_stat_path(fullpath, &ssb)) { if (entry->is_dir) { if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK, PHP_STREAM_MKDIR_RECURSIVE, NULL)) { @@ -3728,6 +3998,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char * } } } + if (slash) { fullpath[dest_len + (slash - entry->filename) + 1] = '/'; } else { @@ -3804,6 +4075,7 @@ PHP_METHOD(Phar, extractTo) int nelems; zval *zval_files = NULL; zend_bool overwrite = 0; + PHAR_ARCHIVE_OBJECT(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!b", &pathto, &pathto_len, &zval_files, &overwrite) == FAILURE) { @@ -3811,11 +4083,13 @@ PHP_METHOD(Phar, extractTo) } fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual); + if (!fp) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Invalid argument, %s cannot be found", phar_obj->arc.archive->fname); return; } + efree(actual); php_stream_close(fp); @@ -3824,6 +4098,7 @@ PHP_METHOD(Phar, extractTo) "Invalid argument, extraction path must be non-zero length"); return; } + if (pathto_len >= MAXPATHLEN) { char *tmp = estrndup(pathto, 50); /* truncate for error message */ @@ -3849,6 +4124,11 @@ PHP_METHOD(Phar, extractTo) switch (Z_TYPE_P(zval_files)) { case IS_NULL: goto all_files; +#if PHP_VERSION_ID >= 60000 + case IS_UNICODE: + zval_unicode_to_string(zval_files TSRMLS_CC); + /* break intentionally omitted */ +#endif case IS_STRING: filename = Z_STRVAL_P(zval_files); filename_len = Z_STRLEN_P(zval_files); @@ -3862,6 +4142,11 @@ PHP_METHOD(Phar, extractTo) zval **zval_file; if (zend_hash_index_find(Z_ARRVAL_P(zval_files), i, (void **) &zval_file) == SUCCESS) { switch (Z_TYPE_PP(zval_file)) { +#if PHP_VERSION_ID >= 60000 + case IS_UNICODE: + zval_unicode_to_string(*(zval_file) TSRMLS_CC); + /* break intentionally omitted */ +#endif case IS_STRING: break; default: @@ -3887,11 +4172,13 @@ PHP_METHOD(Phar, extractTo) "Invalid argument, expected a filename (string) or array of filenames"); return; } + if (FAILURE == zend_hash_find(&phar_obj->arc.archive->manifest, filename, filename_len, (void **)&entry)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Phar Error: attempted to extract non-existent file \"%s\" from phar \"%s\"", filename, phar_obj->arc.archive->fname); return; } + if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Extraction from phar \"%s\" failed: %s", phar_obj->arc.archive->fname, error); @@ -3900,7 +4187,6 @@ PHP_METHOD(Phar, extractTo) } } else { phar_archive_data *phar = phar_obj->arc.archive; - all_files: /* Extract all files */ if (!zend_hash_num_elements(&(phar->manifest))) { @@ -3911,9 +4197,11 @@ all_files: zend_hash_has_more_elements(&phar->manifest) == SUCCESS; zend_hash_move_forward(&phar->manifest)) { phar_entry_info *entry; + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } + if (FAILURE == phar_extract_file(overwrite, entry, pathto, pathto_len, &error TSRMLS_CC)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Extraction from phar \"%s\" failed: %s", phar->fname, error); @@ -3934,15 +4222,15 @@ PHP_METHOD(PharFileInfo, __construct) { char *fname, *arch, *entry, *error; int fname_len, arch_len, entry_len; - phar_entry_object *entry_obj; - phar_entry_info *entry_info; + phar_entry_object *entry_obj; + phar_entry_info *entry_info; phar_archive_data *phar_data; zval *zobj = getThis(), arg1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { return; } - + entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC); if (entry_obj->ent.entry) { @@ -3972,7 +4260,7 @@ PHP_METHOD(PharFileInfo, __construct) if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, 1 TSRMLS_CC)) == NULL) { zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, - "Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error?", ":"", error?error:""); + "Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : ""); efree(arch); efree(entry); return; @@ -4011,6 +4299,7 @@ PHP_METHOD(PharFileInfo, __destruct) efree(entry_obj->ent.entry->filename); entry_obj->ent.entry->filename = NULL; } + efree(entry_obj->ent.entry); entry_obj->ent.entry = NULL; } @@ -4067,6 +4356,7 @@ PHP_METHOD(PharFileInfo, getCRC32) "Phar entry is a directory, does not have a CRC"); \ return; } + if (entry_obj->ent.entry->is_crc_checked) { RETURN_LONG(entry_obj->ent.entry->crc32); } else { @@ -4082,7 +4372,7 @@ PHP_METHOD(PharFileInfo, getCRC32) PHP_METHOD(PharFileInfo, isCRCChecked) { PHAR_ENTRY_OBJECT(); - + RETURN_BOOL(entry_obj->ent.entry->is_crc_checked); } /* }}} */ @@ -4093,7 +4383,7 @@ PHP_METHOD(PharFileInfo, isCRCChecked) PHP_METHOD(PharFileInfo, getPharFlags) { PHAR_ENTRY_OBJECT(); - + RETURN_LONG(entry_obj->ent.entry->flags & ~(PHAR_ENT_PERM_MASK|PHAR_ENT_COMPRESSION_MASK)); } /* }}} */ @@ -4112,32 +4402,38 @@ PHP_METHOD(PharFileInfo, chmod) "Phar entry \"%s\" is a temporary directory (not an actual entry in the archive), cannot chmod", entry_obj->ent.entry->filename); \ return; } + if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "Cannot modify permissions for file \"%s\" in phar \"%s\", write operations are prohibited", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); return; } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perms) == FAILURE) { return; - } - /* clear permissions */ + } + + /* clear permissions */ entry_obj->ent.entry->flags &= ~PHAR_ENT_PERM_MASK; perms &= 0777; entry_obj->ent.entry->flags |= perms; entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags; entry_obj->ent.entry->phar->is_modified = 1; entry_obj->ent.entry->is_modified = 1; + /* hackish cache in php_stat needs to be cleared */ /* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */ if (BG(CurrentLStatFile)) { efree(BG(CurrentLStatFile)); } + if (BG(CurrentStatFile)) { efree(BG(CurrentStatFile)); } + BG(CurrentLStatFile) = NULL; BG(CurrentStatFile) = NULL; - phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4151,7 +4447,7 @@ PHP_METHOD(PharFileInfo, chmod) PHP_METHOD(PharFileInfo, hasMetadata) { PHAR_ENTRY_OBJECT(); - + RETURN_BOOL(entry_obj->ent.entry->metadata != NULL); } /* }}} */ @@ -4162,7 +4458,7 @@ PHP_METHOD(PharFileInfo, hasMetadata) PHP_METHOD(PharFileInfo, getMetadata) { PHAR_ENTRY_OBJECT(); - + if (entry_obj->ent.entry->metadata) { RETURN_ZVAL(entry_obj->ent.entry->metadata, 1, 0); } @@ -4176,17 +4472,20 @@ PHP_METHOD(PharFileInfo, setMetadata) { char *error; zval *metadata; + PHAR_ENTRY_OBJECT(); if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by phar.readonly INI setting"); return; } + if (entry_obj->ent.entry->is_temp_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ "Phar entry is a temporary directory (not an actual entry in the archive), cannot set metadata"); \ return; } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &metadata) == FAILURE) { return; } @@ -4202,6 +4501,7 @@ PHP_METHOD(PharFileInfo, setMetadata) entry_obj->ent.entry->is_modified = 1; entry_obj->ent.entry->phar->is_modified = 1; phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4215,17 +4515,20 @@ PHP_METHOD(PharFileInfo, setMetadata) PHP_METHOD(PharFileInfo, delMetadata) { char *error; + PHAR_ENTRY_OBJECT(); if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by phar.readonly INI setting"); return; } + if (entry_obj->ent.entry->is_temp_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ "Phar entry is a temporary directory (not an actual entry in the archive), cannot delete metadata"); \ return; } + if (entry_obj->ent.entry->metadata) { zval_ptr_dtor(&entry_obj->ent.entry->metadata); entry_obj->ent.entry->metadata = NULL; @@ -4233,6 +4536,7 @@ PHP_METHOD(PharFileInfo, delMetadata) entry_obj->ent.entry->phar->is_modified = 1; phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -4240,6 +4544,7 @@ PHP_METHOD(PharFileInfo, delMetadata) } else { RETURN_TRUE; } + } else { RETURN_TRUE; } @@ -4254,6 +4559,7 @@ PHP_METHOD(PharFileInfo, getContent) char *error; php_stream *fp; phar_entry_info *link; + PHAR_ENTRY_OBJECT(); if (entry_obj->ent.entry->is_dir) { @@ -4261,24 +4567,30 @@ PHP_METHOD(PharFileInfo, getContent) "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\" is a directory", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); return; } + link = phar_get_link_source(entry_obj->ent.entry TSRMLS_CC); + if (!link) { link = entry_obj->ent.entry; } + if (SUCCESS != phar_open_entry_fp(link, &error, 0 TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\": %s", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname, error); efree(error); return; } + if (!(fp = phar_get_efp(link, 0 TSRMLS_CC))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar error: Cannot retrieve contents of \"%s\" in phar \"%s\"", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); return; } + phar_seek_efp(link, 0, SEEK_SET, 0, 0 TSRMLS_CC); Z_TYPE_P(return_value) = IS_STRING; Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0); + if (!Z_STRVAL_P(return_value)) { Z_STRVAL_P(return_value) = estrndup("", 0); } @@ -4303,16 +4615,19 @@ PHP_METHOD(PharFileInfo, compress) "Cannot compress with Gzip compression, not possible with tar-based phar archives"); return; } + if (entry_obj->ent.entry->is_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ "Phar entry is a directory, cannot set compression"); \ return; } + if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar is readonly, cannot change compression"); return; } + if (entry_obj->ent.entry->is_deleted) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress deleted file"); @@ -4325,12 +4640,14 @@ PHP_METHOD(PharFileInfo, compress) RETURN_TRUE; return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0) { if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with gzip compression, file is already compressed with bzip2 compression and bz2 extension is not enabled, cannot decompress"); return; } + /* decompress this file indirectly */ if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -4339,11 +4656,13 @@ PHP_METHOD(PharFileInfo, compress) return; } } + if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with gzip compression, zlib extension is not enabled"); return; } + entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags; entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK; entry_obj->ent.entry->flags |= PHAR_ENT_COMPRESSED_GZ; @@ -4353,12 +4672,14 @@ PHP_METHOD(PharFileInfo, compress) RETURN_TRUE; return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0) { if (!PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with bzip2 compression, file is already compressed with gzip compression and zlib extension is not enabled, cannot decompress"); return; } + /* decompress this file indirectly */ if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -4367,6 +4688,7 @@ PHP_METHOD(PharFileInfo, compress) return; } } + if (!PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress with bzip2 compression, bz2 extension is not enabled"); @@ -4383,12 +4705,13 @@ PHP_METHOD(PharFileInfo, compress) entry_obj->ent.entry->phar->is_modified = 1; entry_obj->ent.entry->is_modified = 1; - phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); } + RETURN_TRUE; } /* }}} */ @@ -4406,30 +4729,36 @@ PHP_METHOD(PharFileInfo, decompress) "Phar entry is a directory, cannot set compression"); \ return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSION_MASK) == 0) { RETURN_TRUE; return; } + if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Phar is readonly, cannot decompress"); return; } + if (entry_obj->ent.entry->is_deleted) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot compress deleted file"); return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0 && !PHAR_G(has_zlib)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress Gzip-compressed file, zlib extension is not enabled"); return; } + if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0 && !PHAR_G(has_bz2)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress Bzip2-compressed file, bz2 extension is not enabled"); return; } + if (!entry_obj->ent.entry->fp) { if (FAILURE == phar_open_archive_fp(entry_obj->ent.entry->phar TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress entry \"%s\", phar error: Cannot open phar archive \"%s\" for reading", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname); @@ -4437,12 +4766,13 @@ PHP_METHOD(PharFileInfo, decompress) } entry_obj->ent.entry->fp_type = PHAR_FP; } + entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags; entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK; entry_obj->ent.entry->phar->is_modified = 1; entry_obj->ent.entry->is_modified = 1; - phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); diff --git a/ext/phar/phar_path_check.c b/ext/phar/phar_path_check.c index c1a273281b..480eed717b 100755 --- a/ext/phar/phar_path_check.c +++ b/ext/phar/phar_path_check.c @@ -4,7 +4,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2007 The PHP Group | + | Copyright (c) 2007-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/phar/phar_path_check.re b/ext/phar/phar_path_check.re index 97a6b5d565..9e89151fff 100755 --- a/ext/phar/phar_path_check.re +++ b/ext/phar/phar_path_check.re @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2007 The PHP Group | + | Copyright (c) 2007-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/phar/php_phar.h b/ext/phar/php_phar.h index 89023972f6..f0a34a2e01 100644 --- a/ext/phar/php_phar.h +++ b/ext/phar/php_phar.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2005 The PHP Group | + | Copyright (c) 2005-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -22,7 +22,7 @@ #ifndef PHP_PHAR_H #define PHP_PHAR_H -#define PHP_PHAR_VERSION "2.0.0b2-dev" +#define PHP_PHAR_VERSION "2.0.0b2-dev" #include "ext/standard/basic_functions.h" extern zend_module_entry phar_module_entry; @@ -34,7 +34,7 @@ extern zend_module_entry phar_module_entry; #define PHP_PHAR_API #endif -#endif /* PHP_PHAR_H */ +#endif /* PHP_PHAR_H */ /* diff --git a/ext/phar/shortarc.php b/ext/phar/shortarc.php index 0f1679b992..1bf3baa8aa 100644 --- a/ext/phar/shortarc.php +++ b/ext/phar/shortarc.php @@ -3,293 +3,293 @@ $web = '000'; if (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) { - Phar::interceptFileFuncs(); - set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path()); - Phar::webPhar(null, $web); - include 'phar://' . __FILE__ . '/' . Extract_Phar::START; - return; + Phar::interceptFileFuncs(); + set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path()); + Phar::webPhar(null, $web); + include 'phar://' . __FILE__ . '/' . Extract_Phar::START; + return; } if (@(isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST'))) { - Extract_Phar::go(true); - $mimes = array( - 'phps' => 2, - 'c' => 'text/plain', - 'cc' => 'text/plain', - 'cpp' => 'text/plain', - 'c++' => 'text/plain', - 'dtd' => 'text/plain', - 'h' => 'text/plain', - 'log' => 'text/plain', - 'rng' => 'text/plain', - 'txt' => 'text/plain', - 'xsd' => 'text/plain', - 'php' => 1, - 'inc' => 1, - 'avi' => 'video/avi', - 'bmp' => 'image/bmp', - 'css' => 'text/css', - 'gif' => 'image/gif', - 'htm' => 'text/html', - 'html' => 'text/html', - 'htmls' => 'text/html', - 'ico' => 'image/x-ico', - 'jpe' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'js' => 'application/x-javascript', - 'midi' => 'audio/midi', - 'mid' => 'audio/midi', - 'mod' => 'audio/mod', - 'mov' => 'movie/quicktime', - 'mp3' => 'audio/mp3', - 'mpg' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'pdf' => 'application/pdf', - 'png' => 'image/png', - 'swf' => 'application/shockwave-flash', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'wav' => 'audio/wav', - 'xbm' => 'image/xbm', - 'xml' => 'text/xml', - ); - - header("Cache-Control: no-cache, must-revalidate"); - header("Pragma: no-cache"); - - $basename = basename(__FILE__); - if (!strpos($_SERVER['REQUEST_URI'], $basename)) { - chdir(Extract_Phar::$temp); - include $web; - return; - } - $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); - if (!$pt || $pt == '/') { - $pt = $web; - header('HTTP/1.1 301 Moved Permanently'); - header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); - exit; - } - $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); - if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { - header('HTTP/1.0 404 Not Found'); - echo "<html>\n <head>\n <title>File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; - exit; - } - $b = pathinfo($a); - if (!isset($b['extension'])) { - header('Content-Type: text/plain'); - header('Content-Length: ' . filesize($a)); - readfile($a); - exit; - } - if (isset($mimes[$b['extension']])) { - if ($mimes[$b['extension']] === 1) { - include $a; - exit; - } - if ($mimes[$b['extension']] === 2) { - highlight_file($a); - exit; - } - header('Content-Type: ' .$mimes[$b['extension']]); - header('Content-Length: ' . filesize($a)); - readfile($a); - exit; - } + Extract_Phar::go(true); + $mimes = array( + 'phps' => 2, + 'c' => 'text/plain', + 'cc' => 'text/plain', + 'cpp' => 'text/plain', + 'c++' => 'text/plain', + 'dtd' => 'text/plain', + 'h' => 'text/plain', + 'log' => 'text/plain', + 'rng' => 'text/plain', + 'txt' => 'text/plain', + 'xsd' => 'text/plain', + 'php' => 1, + 'inc' => 1, + 'avi' => 'video/avi', + 'bmp' => 'image/bmp', + 'css' => 'text/css', + 'gif' => 'image/gif', + 'htm' => 'text/html', + 'html' => 'text/html', + 'htmls' => 'text/html', + 'ico' => 'image/x-ico', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'js' => 'application/x-javascript', + 'midi' => 'audio/midi', + 'mid' => 'audio/midi', + 'mod' => 'audio/mod', + 'mov' => 'movie/quicktime', + 'mp3' => 'audio/mp3', + 'mpg' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'pdf' => 'application/pdf', + 'png' => 'image/png', + 'swf' => 'application/shockwave-flash', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'wav' => 'audio/wav', + 'xbm' => 'image/xbm', + 'xml' => 'text/xml', + ); + + header("Cache-Control: no-cache, must-revalidate"); + header("Pragma: no-cache"); + + $basename = basename(__FILE__); + if (!strpos($_SERVER['REQUEST_URI'], $basename)) { + chdir(Extract_Phar::$temp); + include $web; + return; + } + $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); + if (!$pt || $pt == '/') { + $pt = $web; + header('HTTP/1.1 301 Moved Permanently'); + header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); + exit; + } + $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); + if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { + header('HTTP/1.0 404 Not Found'); + echo "<html>\n <head>\n <title>File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; + exit; + } + $b = pathinfo($a); + if (!isset($b['extension'])) { + header('Content-Type: text/plain'); + header('Content-Length: ' . filesize($a)); + readfile($a); + exit; + } + if (isset($mimes[$b['extension']])) { + if ($mimes[$b['extension']] === 1) { + include $a; + exit; + } + if ($mimes[$b['extension']] === 2) { + highlight_file($a); + exit; + } + header('Content-Type: ' .$mimes[$b['extension']]); + header('Content-Length: ' . filesize($a)); + readfile($a); + exit; + } } class Extract_Phar { - static $temp; - static $origdir; - const GZ = 0x1000; - const BZ2 = 0x2000; - const MASK = 0x3000; - const START = 'index.php'; - const LEN = XXXX; - - static function go($return = false) - { - $fp = fopen(__FILE__, 'rb'); - fseek($fp, self::LEN); - $L = unpack('V', $a = (binary)fread($fp, 4)); - $m = (binary)''; - - do { - $read = 8192; - if ($L[1] - strlen($m) < 8192) { - $read = $L[1] - strlen($m); - } - $last = (binary)fread($fp, $read); - $m .= $last; - } while (strlen($last) && strlen($m) < $L[1]); - - if (strlen($m) < $L[1]) { - die('ERROR: manifest length read was "' . - strlen($m) .'" should be "' . - $L[1] . '"'); - } - - $info = self::_unpack($m); - $f = $info['c']; - - if ($f & self::GZ) { - if (!function_exists('gzinflate')) { - die('Error: zlib extension is not enabled -' . - ' gzinflate() function needed for zlib-compressed .phars'); - } - } - - if ($f & self::BZ2) { - if (!function_exists('bzdecompress')) { - die('Error: bzip2 extension is not enabled -' . - ' bzdecompress() function needed for bz2-compressed .phars'); - } - } - - $temp = self::tmpdir(); - - if (!$temp || !is_writable($temp)) { - $sessionpath = session_save_path(); - if (strpos ($sessionpath, ";") !== false) - $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); - if (!file_exists($sessionpath) || !is_dir($sessionpath)) { - die('Could not locate temporary directory to extract phar'); - } - $temp = $sessionpath; - } - - $temp .= '/pharextract/'.basename(__FILE__, '.phar'); - self::$temp = $temp; - self::$origdir = getcwd(); - @mkdir($temp, 0777, true); - $temp = realpath($temp); - - if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { - self::_removeTmpFiles($temp, getcwd()); - @mkdir($temp, 0777, true); - @file_put_contents($temp . '/' . md5_file(__FILE__), ''); - - foreach ($info['m'] as $path => $file) { - $a = !file_exists(dirname($temp . '/' . $path)); - @mkdir(dirname($temp . '/' . $path), 0777, true); - clearstatcache(); - - if ($path[strlen($path) - 1] == '/') { - @mkdir($temp . '/' . $path, 0777); - } else { - file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); - @chmod($temp . '/' . $path, 0666); - } - } - } - - chdir($temp); - - if (!$return) { - include self::START; - } - } - - static function tmpdir() - { - if (strpos(PHP_OS, 'WIN') !== false) { - if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { - return $var; - } - if (is_dir('/temp') || mkdir('/temp')) { - return realpath('/temp'); - } - return false; - } - if ($var = getenv('TMPDIR')) { - return $var; - } - return realpath('/tmp'); - } - - static function _unpack($m) - { - $info = unpack('V', substr($m, 0, 4)); - // skip API version, phar flags, alias, metadata - $l = unpack('V', substr($m, 10, 4)); - $m = substr($m, 14 + $l[1]); - $s = unpack('V', substr($m, 0, 4)); - $o = 0; - $start = 4 + $s[1]; - $ret['c'] = 0; - - for ($i = 0; $i < $info[1]; $i++) { - // length of the file name - $len = unpack('V', substr($m, $start, 4)); - $start += 4; - // file name - $savepath = substr($m, $start, $len[1]); - $start += $len[1]; - // retrieve manifest data: - // 0 = size, 1 = timestamp, 2 = compressed size, 3 = crc32, 4 = flags - // 5 = metadata length - $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); - $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] - & 0xffffffff); - $ret['m'][$savepath][7] = $o; - $o += $ret['m'][$savepath][2]; - $start += 24 + $ret['m'][$savepath][5]; - $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; - } - return $ret; - } - - static function extractFile($path, $entry, $fp) - { - $data = ''; - $c = $entry[2]; - - while ($c) { - if ($c < 8192) { - $data .= @fread($fp, $c); - $c = 0; - } else { - $c -= 8192; - $data .= @fread($fp, 8192); - } - } - - if ($entry[4] & self::GZ) { - $data = gzinflate($data); - } elseif ($entry[4] & self::BZ2) { - $data = bzdecompress($data); - } - - if (strlen($data) != $entry[0]) { - die("Invalid internal .phar file (size error " . strlen($data) . " != " . - $stat[7] . ")"); - } - - if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { - die("Invalid internal .phar file (checksum error)"); - } - - return $data; - } - - static function _removeTmpFiles($temp, $origdir) - { - chdir($temp); - - foreach (glob('*') as $f) { - if (file_exists($f)) { - is_dir($f) ? @rmdir($f) : @unlink($f); - if (file_exists($f) && is_dir($f)) { - self::_removeTmpFiles($f, getcwd()); - } - } - } - - @rmdir($temp); - clearstatcache(); - chdir($origdir); - } + static $temp; + static $origdir; + const GZ = 0x1000; + const BZ2 = 0x2000; + const MASK = 0x3000; + const START = 'index.php'; + const LEN = XXXX; + + static function go($return = false) + { + $fp = fopen(__FILE__, 'rb'); + fseek($fp, self::LEN); + $L = unpack('V', $a = (binary)fread($fp, 4)); + $m = (binary)''; + + do { + $read = 8192; + if ($L[1] - strlen($m) < 8192) { + $read = $L[1] - strlen($m); + } + $last = (binary)fread($fp, $read); + $m .= $last; + } while (strlen($last) && strlen($m) < $L[1]); + + if (strlen($m) < $L[1]) { + die('ERROR: manifest length read was "' . + strlen($m) .'" should be "' . + $L[1] . '"'); + } + + $info = self::_unpack($m); + $f = $info['c']; + + if ($f & self::GZ) { + if (!function_exists('gzinflate')) { + die('Error: zlib extension is not enabled -' . + ' gzinflate() function needed for zlib-compressed .phars'); + } + } + + if ($f & self::BZ2) { + if (!function_exists('bzdecompress')) { + die('Error: bzip2 extension is not enabled -' . + ' bzdecompress() function needed for bz2-compressed .phars'); + } + } + + $temp = self::tmpdir(); + + if (!$temp || !is_writable($temp)) { + $sessionpath = session_save_path(); + if (strpos ($sessionpath, ";") !== false) + $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); + if (!file_exists($sessionpath) || !is_dir($sessionpath)) { + die('Could not locate temporary directory to extract phar'); + } + $temp = $sessionpath; + } + + $temp .= '/pharextract/'.basename(__FILE__, '.phar'); + self::$temp = $temp; + self::$origdir = getcwd(); + @mkdir($temp, 0777, true); + $temp = realpath($temp); + + if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { + self::_removeTmpFiles($temp, getcwd()); + @mkdir($temp, 0777, true); + @file_put_contents($temp . '/' . md5_file(__FILE__), ''); + + foreach ($info['m'] as $path => $file) { + $a = !file_exists(dirname($temp . '/' . $path)); + @mkdir(dirname($temp . '/' . $path), 0777, true); + clearstatcache(); + + if ($path[strlen($path) - 1] == '/') { + @mkdir($temp . '/' . $path, 0777); + } else { + file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); + @chmod($temp . '/' . $path, 0666); + } + } + } + + chdir($temp); + + if (!$return) { + include self::START; + } + } + + static function tmpdir() + { + if (strpos(PHP_OS, 'WIN') !== false) { + if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { + return $var; + } + if (is_dir('/temp') || mkdir('/temp')) { + return realpath('/temp'); + } + return false; + } + if ($var = getenv('TMPDIR')) { + return $var; + } + return realpath('/tmp'); + } + + static function _unpack($m) + { + $info = unpack('V', substr($m, 0, 4)); + // skip API version, phar flags, alias, metadata + $l = unpack('V', substr($m, 10, 4)); + $m = substr($m, 14 + $l[1]); + $s = unpack('V', substr($m, 0, 4)); + $o = 0; + $start = 4 + $s[1]; + $ret['c'] = 0; + + for ($i = 0; $i < $info[1]; $i++) { + // length of the file name + $len = unpack('V', substr($m, $start, 4)); + $start += 4; + // file name + $savepath = substr($m, $start, $len[1]); + $start += $len[1]; + // retrieve manifest data: + // 0 = size, 1 = timestamp, 2 = compressed size, 3 = crc32, 4 = flags + // 5 = metadata length + $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); + $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] + & 0xffffffff); + $ret['m'][$savepath][7] = $o; + $o += $ret['m'][$savepath][2]; + $start += 24 + $ret['m'][$savepath][5]; + $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; + } + return $ret; + } + + static function extractFile($path, $entry, $fp) + { + $data = ''; + $c = $entry[2]; + + while ($c) { + if ($c < 8192) { + $data .= @fread($fp, $c); + $c = 0; + } else { + $c -= 8192; + $data .= @fread($fp, 8192); + } + } + + if ($entry[4] & self::GZ) { + $data = gzinflate($data); + } elseif ($entry[4] & self::BZ2) { + $data = bzdecompress($data); + } + + if (strlen($data) != $entry[0]) { + die("Invalid internal .phar file (size error " . strlen($data) . " != " . + $stat[7] . ")"); + } + + if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { + die("Invalid internal .phar file (checksum error)"); + } + + return $data; + } + + static function _removeTmpFiles($temp, $origdir) + { + chdir($temp); + + foreach (glob('*') as $f) { + if (file_exists($f)) { + is_dir($f) ? @rmdir($f) : @unlink($f); + if (file_exists($f) && is_dir($f)) { + self::_removeTmpFiles($f, getcwd()); + } + } + } + + @rmdir($temp); + clearstatcache(); + chdir($origdir); + } } diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 0c77583e21..725a9e1500 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -35,22 +35,22 @@ php_stream_ops phar_ops = { }; php_stream_wrapper_ops phar_stream_wops = { - phar_wrapper_open_url, - NULL, /* phar_wrapper_close */ - NULL, /* phar_wrapper_stat, */ - phar_wrapper_stat, /* stat_url */ - phar_wrapper_open_dir, /* opendir */ - "phar", - phar_wrapper_unlink, /* unlink */ - phar_wrapper_rename, /* rename */ - phar_wrapper_mkdir, /* create directory */ - phar_wrapper_rmdir, /* remove directory */ + phar_wrapper_open_url, + NULL, /* phar_wrapper_close */ + NULL, /* phar_wrapper_stat, */ + phar_wrapper_stat, /* stat_url */ + phar_wrapper_open_dir, /* opendir */ + "phar", + phar_wrapper_unlink, /* unlink */ + phar_wrapper_rename, /* rename */ + phar_wrapper_mkdir, /* create directory */ + phar_wrapper_rmdir, /* remove directory */ }; -php_stream_wrapper php_stream_phar_wrapper = { - &phar_stream_wops, - NULL, - 0 /* is_url */ +php_stream_wrapper php_stream_phar_wrapper = { + &phar_stream_wops, + NULL, + 0 /* is_url */ }; /** @@ -69,8 +69,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, if (!(options & PHP_STREAM_URL_STAT_QUIET)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported"); } - return NULL; - } + return NULL; + } if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0) TSRMLS_CC) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { @@ -147,7 +147,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, php_url_free(resource); return NULL; } - } + } return resource; } /* }}} */ @@ -296,7 +296,6 @@ idata_error: } } php_url_free(resource); - #if MBO_0 fprintf(stderr, "Pharname: %s\n", idata->phar->filename); fprintf(stderr, "Filename: %s\n", internal_file); @@ -366,7 +365,7 @@ static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRML { phar_entry_data *data = (phar_entry_data *)stream->abstract; size_t got; - + if (data->internal_file->is_deleted) { stream->eof = 1; return 0; @@ -379,7 +378,6 @@ static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRML data->position = php_stream_tell(data->fp) - data->zero; stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize); - return got; } /* }}} */ @@ -629,7 +627,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, } PHAR_STR(key, str_key); if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) { - zend_hash_move_forward_ex(&phar->mounted_dirs, &pos); + zend_hash_move_forward_ex(&phar->mounted_dirs, &pos); continue; } else { char *test; @@ -646,7 +644,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen); if (SUCCESS != php_stream_stat_path(test, &ssbi)) { efree(test); - zend_hash_move_forward_ex(&phar->mounted_dirs, &pos); + zend_hash_move_forward_ex(&phar->mounted_dirs, &pos); continue; } /* mount the file/directory just in time */ @@ -664,7 +662,6 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, } } } - free_resource: php_url_free(resource); return FAILURE; @@ -730,7 +727,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio efree(error); } if (idata->internal_file->fp_refcount > 1) { - /* more than just our fp resource is open for this file */ + /* more than just our fp resource is open for this file */ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host); efree(internal_file); php_url_free(resource); @@ -807,7 +804,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); return 0; } - + if (!resource_to->scheme || !resource_to->host || !resource_to->path) { php_url_free(resource_from); php_url_free(resource_to); @@ -895,14 +892,14 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char uint to_len = strlen(resource_to->path+1); for (zend_hash_internal_pointer_reset(&phar->manifest); - HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)) && - SUCCESS == zend_hash_get_current_data(&phar->manifest, (void **) &entry); - zend_hash_move_forward(&phar->manifest)) { + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)) && + SUCCESS == zend_hash_get_current_data(&phar->manifest, (void **) &entry); + zend_hash_move_forward(&phar->manifest)) { if (!entry->is_deleted && - key_len > from_len && - memcmp(key, resource_from->path+1, from_len) == 0 && - IS_SLASH(key[from_len])) { + key_len > from_len && + memcmp(key, resource_from->path+1, from_len) == 0 && + IS_SLASH(key[from_len])) { new_key_len = key_len + to_len - from_len; new_key = emalloc(new_key_len+1); @@ -916,15 +913,15 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char entry->filename_len = new_key_len; zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, NULL); } - } + } for (zend_hash_internal_pointer_reset(&phar->virtual_dirs); - HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL)); - zend_hash_move_forward(&phar->virtual_dirs)) { + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL)); + zend_hash_move_forward(&phar->virtual_dirs)) { if (key_len >= from_len && - memcmp(key, resource_from->path+1, from_len) == 0 && - (key_len == from_len || IS_SLASH(key[from_len]))) { + memcmp(key, resource_from->path+1, from_len) == 0 && + (key_len == from_len || IS_SLASH(key[from_len]))) { new_key_len = key_len + to_len - from_len; new_key = emalloc(new_key_len+1); @@ -937,13 +934,13 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char } for (zend_hash_internal_pointer_reset(&phar->mounted_dirs); - HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &key_len, &unused, 0, NULL)) && - SUCCESS == zend_hash_get_current_data(&phar->mounted_dirs, (void **) &entry); - zend_hash_move_forward(&phar->mounted_dirs)) { + HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &key_len, &unused, 0, NULL)) && + SUCCESS == zend_hash_get_current_data(&phar->mounted_dirs, (void **) &entry); + zend_hash_move_forward(&phar->mounted_dirs)) { if (key_len >= from_len && - memcmp(key, resource_from->path+1, from_len) == 0 && - (key_len == from_len || IS_SLASH(key[from_len]))) { + memcmp(key, resource_from->path+1, from_len) == 0 && + (key_len == from_len || IS_SLASH(key[from_len]))) { new_key_len = key_len + to_len - from_len; new_key = emalloc(new_key_len+1); @@ -969,6 +966,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char php_url_free(resource_from); php_url_free(resource_to); + return 1; } /* }}} */ diff --git a/ext/phar/stream.h b/ext/phar/stream.h index 49f6b612f1..c69202bfcf 100644 --- a/ext/phar/stream.h +++ b/ext/phar/stream.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2007 The PHP Group | + | Copyright (c) 2006-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/phar/tar.c b/ext/phar/tar.c index e1e733582d..e5159dab92 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -27,19 +27,19 @@ static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */ while (i < len && buf[i] == ' ') { ++i; } - while (i < len && - buf[i] >= '0' && - buf[i] <= '7') { + + while (i < len && buf[i] >= '0' && buf[i] <= '7') { num = num * 8 + (buf[i] - '0'); ++i; } + return num; } /* }}} */ /* adapted from format_octal() in libarchive * - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -176,6 +176,7 @@ int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) php_stream_seek(fp, save, SEEK_SET); return FAILURE; } + if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) { entry->phar->metadata = entry->metadata; entry->metadata = NULL; @@ -184,6 +185,7 @@ int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp TSRMLS_DC) mentry->metadata = entry->metadata; entry->metadata = NULL; } + efree(metadata); php_stream_seek(fp, save, SEEK_SET); return SUCCESS; @@ -207,6 +209,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, totalsize = php_stream_tell(fp); php_stream_seek(fp, 0, SEEK_SET); read = php_stream_read(fp, buf, sizeof(buf)); + if (read != sizeof(buf)) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname); @@ -214,6 +217,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, php_stream_close(fp); return FAILURE; } + hdr = (tar_header*)buf; old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0); @@ -234,6 +238,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, entry.is_crc_checked = 1; entry.phar = myphar; pos += sizeof(buf); + do { phar_entry_info *newentry; @@ -298,7 +303,9 @@ bail: return FAILURE; } } + read = php_stream_read(fp, buf, sizeof(buf)); + if (read != sizeof(buf)) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); @@ -307,16 +314,21 @@ bail: phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } + hdr = (tar_header*) buf; sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum)); + if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) { break; } + if (error) { spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname); } + goto bail; } + if (!old && hdr->prefix[0] != 0) { char name[256]; @@ -327,7 +339,9 @@ bail: } else { strcat(name, hdr->name); } + entry.filename_len = strlen(hdr->prefix) + 100; + if (name[entry.filename_len - 1] == '/') { /* some tar programs store directories with trailing slash */ entry.filename_len--; @@ -336,13 +350,16 @@ bail: } else { entry.filename = pestrdup(hdr->name, myphar->is_persistent); entry.filename_len = strlen(entry.filename); + if (entry.filename[entry.filename_len - 1] == '/') { /* some tar programs store directories with trailing slash */ entry.filename[entry.filename_len - 1] = '\0'; entry.filename_len--; } } + phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC); + if (sum1 != sum2) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename); @@ -359,13 +376,13 @@ bail: entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK; entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime)); entry.is_persistent = myphar->is_persistent; - #ifndef S_ISDIR #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) #endif if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) { entry.tar_type = TAR_DIR; } + if (entry.tar_type == TAR_DIR) { entry.is_dir = 1; } else { @@ -373,6 +390,7 @@ bail: } entry.link = NULL; + if (entry.tar_type == TAR_LINK) { if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) { if (error) { @@ -389,7 +407,11 @@ 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.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) { @@ -400,6 +422,7 @@ bail: return FAILURE; } } + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { size_t read; /* found explicit alias */ @@ -411,7 +434,9 @@ bail: phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } + read = php_stream_read(fp, buf, size); + if (read == size) { buf[size] = '\0'; if (!phar_validate_alias(buf, size)) { @@ -421,13 +446,16 @@ bail: buf[52] = '.'; buf[53] = '\0'; } + if (error) { spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname); } + php_stream_close(fp); phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } + actual_alias = pestrndup(buf, size, myphar->is_persistent); myphar->alias = actual_alias; myphar->alias_len = size; @@ -436,12 +464,15 @@ bail: if (error) { spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname); } + php_stream_close(fp); phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } } + size = (size+511)&~511; + if (((hdr->typeflag == 0) || (hdr->typeflag == TAR_FILE)) && size > 0) { /* this is not good enough - seek succeeds even on truncated tars */ php_stream_seek(fp, size, SEEK_CUR); @@ -454,7 +485,9 @@ bail: return FAILURE; } } + read = php_stream_read(fp, buf, sizeof(buf)); + if (read != sizeof(buf)) { if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname); @@ -488,6 +521,7 @@ bail: } else { myphar->is_data = 1; } + if (p) { myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p); if (myphar->ext == p) { @@ -497,7 +531,9 @@ bail: myphar->ext_len = (myphar->fname + fname_len) - myphar->ext; } } + phar_request_initialize(TSRMLS_C); + if (SUCCESS != zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len, (void*)&myphar, sizeof(phar_archive_data*), (void **)&actual)) { if (error) { spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname); @@ -506,11 +542,14 @@ bail: phar_destroy_phar_data(myphar TSRMLS_CC); return FAILURE; } + myphar = *actual; + if (actual_alias) { phar_archive_data **fd_ptr; myphar->is_temporary_alias = 0; + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) { if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) { if (error) { @@ -520,6 +559,7 @@ bail: return FAILURE; } } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); } else { phar_archive_data **fd_ptr; @@ -541,11 +581,14 @@ bail: myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent); myphar->alias_len = fname_len; } + myphar->is_temporary_alias = 1; } + if (pphar) { *pphar = myphar; } + return SUCCESS; } /* }}} */ @@ -569,6 +612,7 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ if (entry->is_mounted) { return ZEND_HASH_APPLY_KEEP; } + if (entry->is_deleted) { if (entry->fp_refcount <= 0) { return ZEND_HASH_APPLY_REMOVE; @@ -579,6 +623,7 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ } memset((char *) &header, 0, sizeof(header)); + if (entry->filename_len > 100) { if (entry->filename_len > 255) { if (fp->error) { @@ -591,28 +636,35 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ } else { memcpy(header.name, entry->filename, entry->filename_len); } + phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1); + if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file modification time of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + /* calc checksum */ header.typeflag = entry->tar_type; + if (entry->link) { strncpy(header.linkname, entry->link, strlen(entry->link)); } + strncpy(header.magic, "ustar", sizeof("ustar")-1); strncpy(header.version, "00", sizeof("00")-1); strncpy(header.checksum, " ", sizeof(" ")-1); entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header)); + if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, checksum of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename); @@ -622,12 +674,14 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ /* write header */ entry->header_offset = php_stream_tell(fp->new); + if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + pos = php_stream_tell(fp->new); /* save start of file within tar */ /* write contents */ @@ -635,22 +689,25 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) { return ZEND_HASH_APPLY_STOP; } + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } + if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize)) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename); } return ZEND_HASH_APPLY_STOP; } - + memset(padding, 0, 512); php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize); } + if (!entry->is_modified && entry->fp_refcount) { /* open file pointers refer to this fp, do not free the stream */ switch (entry->fp_type) { @@ -665,12 +722,14 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC) /* {{{ */ } entry->is_modified = 0; + if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) { if (!entry->fp_refcount) { php_stream_close(entry->fp); } entry->fp = NULL; } + entry->fp_type = PHAR_FP; /* note new location within tar */ @@ -686,24 +745,29 @@ int phar_tar_setmetadata(zval *metadata, phar_entry_info *entry, char **error, p if (entry->metadata_str.c) { smart_str_free(&entry->metadata_str); } + entry->metadata_str.c = 0; entry->metadata_str.len = 0; PHP_VAR_SERIALIZE_INIT(metadata_hash); php_var_serialize(&entry->metadata_str, &metadata, &metadata_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(metadata_hash); entry->uncompressed_filesize = entry->compressed_filesize = entry->metadata_str.len; + if (entry->fp && entry->fp_type == PHAR_MOD) { php_stream_close(entry->fp); } + entry->fp_type = PHAR_MOD; entry->is_modified = 1; entry->fp = php_stream_fopen_tmpfile(); entry->offset = entry->offset_abs = 0; + if (entry->metadata_str.len != php_stream_write(entry->fp, entry->metadata_str.c, entry->metadata_str.len)) { spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename); zend_hash_del(&(entry->phar->manifest), entry->filename, entry->filename_len); return ZEND_HASH_APPLY_STOP; } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -732,13 +796,16 @@ int phar_tar_setupmetadata(void *pDest, void *argument TSRMLS_DC) /* {{{ */ if (!entry->is_modified) { return ZEND_HASH_APPLY_KEEP; } + /* now we are dealing with regular files, so look for metadata */ lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename); + if (!entry->metadata) { zend_hash_del(&(entry->phar->manifest), lookfor, lookfor_len); efree(lookfor); return ZEND_HASH_APPLY_KEEP; } + if (SUCCESS == zend_hash_find(&(entry->phar->manifest), lookfor, lookfor_len, (void **)&metadata)) { int ret; ret = phar_tar_setmetadata(entry->metadata, metadata, error, fp TSRMLS_CC); @@ -786,6 +853,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + if (phar->is_data) { goto nostub; } @@ -795,13 +863,16 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); entry.filename_len = sizeof(".phar/alias.txt")-1; entry.fp = php_stream_fopen_tmpfile(); + if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) { if (error) { spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); } return EOF; } + entry.uncompressed_filesize = phar->alias_len; + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { if (error) { spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname); @@ -839,8 +910,8 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } else { free_user_stub = 0; } - if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) - { + + if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) { if (error) { spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname); } @@ -849,6 +920,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + len = pos - user_stub + 18; entry.fp = php_stream_fopen_tmpfile(); entry.uncompressed_filesize = len + 5; @@ -864,9 +936,11 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau php_stream_close(entry.fp); return EOF; } + entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); entry.filename_len = sizeof(".phar/stub.php")-1; zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + if (free_user_stub) { efree(user_stub); } @@ -911,9 +985,7 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defau } } } - nostub: - if (phar->fp && !phar->is_brandnew) { oldfile = phar->fp; closeoldfile = 0; @@ -922,7 +994,9 @@ nostub: oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL); closeoldfile = oldfile != NULL; } + newfile = php_stream_fopen_tmpfile(); + if (!newfile) { if (error) { spprintf(error, 0, "unable to create temporary file"); @@ -964,6 +1038,7 @@ nostub: } return EOF; } + if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(phar->metadata, mentry, error, oldfile TSRMLS_CC)) { zend_hash_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1); if (closeoldfile) { @@ -973,12 +1048,14 @@ nostub: } } } + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_setupmetadata, (void *) &pass TSRMLS_CC); if (error && *error) { if (closeoldfile) { php_stream_close(oldfile); } + /* on error in the hash iterator above, error is set */ php_stream_close(newfile); return EOF; @@ -994,12 +1071,15 @@ nostub: spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save); efree(save); } + if (closeoldfile) { php_stream_close(oldfile); } + php_stream_close(newfile); return EOF; } + entry.filename = ".phar/signature.bin"; entry.filename_len = sizeof(".phar/signature.bin")-1; entry.fp = php_stream_fopen_tmpfile(); @@ -1008,26 +1088,28 @@ nostub: # define PHAR_SET_32(var, buffer) \ *(php_uint32 *)(var) = (((((unsigned char*)(buffer))[3]) << 24) \ | ((((unsigned char*)(buffer))[2]) << 16) \ - | ((((unsigned char*)(buffer))[1]) << 8) \ + | ((((unsigned char*)(buffer))[1]) << 8) \ | (((unsigned char*)(buffer))[0])) #else # define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer) #endif PHAR_SET_32(sigbuf, phar->sig_flags); PHAR_SET_32(sigbuf + 4, signature_length); + if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) { efree(signature); if (error) { spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname); } + if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); return EOF; } - efree(signature); + efree(signature); entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8; /* throw out return value and write the signature */ entry.filename_len = phar_tar_writeheaders((void *)&entry, (void *)&pass TSRMLS_CC); @@ -1050,14 +1132,17 @@ nostub: if (closeoldfile) { php_stream_close(oldfile); } + /* on error in the hash iterator above, error is set */ if (error && *error) { php_stream_close(newfile); return EOF; } + if (phar->fp && pass.free_fp) { php_stream_close(phar->fp); } + if (phar->ufp) { if (pass.free_ufp) { php_stream_close(phar->ufp); @@ -1066,7 +1151,6 @@ nostub: } phar->is_brandnew = 0; - php_stream_rewind(newfile); if (phar->donotflush) { @@ -1081,6 +1165,7 @@ nostub: } return EOF; } + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { php_stream_filter *filter; /* to properly compress, we have to tell zlib to add a zlib header */ @@ -1094,6 +1179,7 @@ nostub: add_assoc_long(&filterparams, "window", MAX_WBITS + 16); filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); zval_dtor(&filterparams); + if (!filter) { /* copy contents uncompressed rather than lose them */ php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); @@ -1103,6 +1189,7 @@ nostub: } return EOF; } + php_stream_filter_append(&phar->fp->writefilters, filter); php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); php_stream_filter_flush(filter, 1); diff --git a/ext/phar/util.c b/ext/phar/util.c index 909f4053aa..008bd3f5ea 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -96,6 +96,7 @@ php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC) /* return phar_get_efp(link_entry, 1 TSRMLS_CC); } } + 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 */ @@ -124,6 +125,7 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi if (!fp) { return -1; } + if (follow_links) { phar_entry_info *t; t = phar_get_link_source(entry TSRMLS_CC); @@ -131,27 +133,33 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi entry = t; } } + if (entry->is_dir) { return 0; } + eoffset = phar_get_fp_offset(entry TSRMLS_CC); + switch (whence) { - case SEEK_END : + case SEEK_END: temp = eoffset + entry->uncompressed_filesize + offset; break; - case SEEK_CUR : + case SEEK_CUR: temp = eoffset + position + offset; break; - case SEEK_SET : + case SEEK_SET: temp = eoffset + offset; break; } + if (temp > eoffset + (off_t) entry->uncompressed_filesize) { return -1; } + if (temp < eoffset) { return -1; } + return php_stream_seek(fp, temp, SEEK_SET); } /* }}} */ @@ -199,12 +207,14 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, filename_len = strlen(entry.tmp); filename = entry.tmp; + /* only check openbasedir for files, not for phar streams */ if (!is_phar && php_check_open_basedir(filename TSRMLS_CC)) { efree(entry.tmp); efree(entry.filename); return FAILURE; } + entry.is_mounted = 1; entry.is_crc_checked = 1; entry.fp_type = PHAR_TMP; @@ -214,6 +224,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, efree(entry.filename); return FAILURE; } + if (ssb.sb.st_mode & S_IFDIR) { entry.is_dir = 1; if (SUCCESS != zend_hash_add(&phar->mounted_dirs, entry.filename, path_len, (void *)&(entry.filename), sizeof(char *), NULL)) { @@ -226,10 +237,13 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, entry.is_dir = 0; entry.uncompressed_filesize = entry.compressed_filesize = ssb.sb.st_size; } + entry.flags = ssb.sb.st_mode; + if (SUCCESS == zend_hash_add(&phar->manifest, entry.filename, path_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { return SUCCESS; } + efree(entry.tmp); efree(entry.filename); return FAILURE; @@ -252,6 +266,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d if (!zend_is_executing(TSRMLS_C) || !PHAR_G(cwd)) { return phar_save_resolve_path(filename, filename_len TSRMLS_CC); } + fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); @@ -279,8 +294,10 @@ splitted: if (pphar) { *pphar = phar; } + try_len = filename_len; test = phar_fix_filepath(estrndup(filename, filename_len), &try_len, 1 TSRMLS_CC); + if (*test == '/') { if (zend_hash_exists(&(phar->manifest), test + 1, try_len - 1)) { spprintf(&ret, 0, "phar://%s%s", arch, test); @@ -298,10 +315,12 @@ splitted: } efree(test); } + spprintf(&path, MAXPATHLEN, "phar://%s/%s%c%s", arch, PHAR_G(cwd), DEFAULT_DIR_SEPARATOR, PG(include_path)); efree(arch); ret = php_resolve_path(filename, filename_len, path TSRMLS_CC); efree(path); + if (ret && strlen(ret) > 8 && !strncmp(ret, "phar://", 7)) { char *arch; int arch_len, ret_len; @@ -312,13 +331,17 @@ splitted: if (SUCCESS != phar_split_fname(ret, ret_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return ret; } + zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + if (!pphar && PHAR_G(manifest_cached)) { zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); } + efree(arch); efree(entry); } + return ret; #else /* PHP 5.2 */ char resolved_path[MAXPATHLEN]; @@ -338,12 +361,15 @@ splitted: if (!zend_is_executing(TSRMLS_C) || !PHAR_G(cwd)) { goto doit; } + fname = zend_get_executed_filename(TSRMLS_C); + if (SUCCESS != phar_split_fname(fname, strlen(fname), &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { goto doit; } efree(entry); + if (*filename == '.') { int try_len; @@ -351,8 +377,10 @@ splitted: efree(arch); goto doit; } + try_len = filename_len; test = phar_fix_filepath(estrndup(filename, filename_len), &try_len, 1 TSRMLS_CC); + if (*test == '/') { if (zend_hash_exists(&(phar->manifest), test + 1, try_len - 1)) { spprintf(&ret, 0, "phar://%s%s", arch, test); @@ -368,23 +396,23 @@ splitted: return ret; } } + efree(test); } - efree(arch); + efree(arch); doit: - if (*filename == '.' || - IS_ABSOLUTE_PATH(filename, filename_len) || - !path || - !*path) { + if (*filename == '.' || IS_ABSOLUTE_PATH(filename, filename_len) || !path || !*path) { if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) { return estrdup(resolved_path); } else { return NULL; } } + /* test for stream wrappers and return */ for (p = filename; p - filename < filename_len && (isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'); ++p, ++n); + if (n < filename_len - 3 && (*p == ':') && (!strncmp("//", p+1, 2) || ( filename_len > 4 && !memcmp("data", filename, 4)))) { /* found stream wrapper, this is an absolute path until stream wrappers implement realpath */ return estrndup(filename, filename_len); @@ -401,6 +429,7 @@ doit: maybe_stream = 0; goto not_stream; } + for (p = ptr, n = 0; p < end && (isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'); ++p, ++n); if (n == end - ptr && *p && !strncmp("//", p+1, 2)) { @@ -417,6 +446,7 @@ not_stream: ptr = end + 1; continue; } + memcpy(trypath, ptr, end-ptr); len = end-ptr; trypath[end-ptr] = '/'; @@ -428,6 +458,7 @@ not_stream: if (len + 1 + filename_len + 1 >= MAXPATHLEN) { break; } + memcpy(trypath, ptr, len); trypath[len] = '/'; memcpy(trypath+len+1, filename, filename_len+1); @@ -456,19 +487,23 @@ not_stream: if (wrapper == &php_stream_phar_wrapper) { char *arch, *entry; int arch_len, entry_len, ret_len; - + ret_len = strlen(trypath); /* found phar:// */ if (SUCCESS != phar_split_fname(trypath, ret_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { return estrndup(trypath, ret_len); } + zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); + if (!pphar && PHAR_G(manifest_cached)) { zend_hash_find(&cached_phars, arch, arch_len, (void **) &pphar); } + efree(arch); efree(entry); + return estrndup(trypath, ret_len); } return estrdup(trypath); @@ -483,8 +518,7 @@ not_stream: } } /* end provided path */ - /* check in calling scripts' current working directory as a fall back case - */ + /* check in calling scripts' current working directory as a fall back case */ if (zend_is_executing(TSRMLS_C)) { char *exec_fname = zend_get_executed_filename(TSRMLS_C); int exec_fname_length = strlen(exec_fname); @@ -492,29 +526,32 @@ not_stream: int n = 0; while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); - if (exec_fname && exec_fname[0] != '[' && - exec_fname_length > 0 && - exec_fname_length + 1 + filename_len + 1 < MAXPATHLEN) { + if (exec_fname && exec_fname[0] != '[' && + exec_fname_length > 0 && + exec_fname_length + 1 + filename_len + 1 < MAXPATHLEN) { memcpy(trypath, exec_fname, exec_fname_length + 1); memcpy(trypath+exec_fname_length + 1, filename, filename_len+1); /* search for stream wrapper */ for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; ++p, ++n); + if (n < exec_fname_length - 3 && (*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || !memcmp("data", trypath, 4))) { char *actual; - + wrapper = php_stream_locate_url_wrapper(trypath, &actual, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + if (wrapper == &php_plain_files_wrapper) { /* this should never technically happen, but we'll leave it here for completeness */ strncpy(trypath, actual, MAXPATHLEN); } else if (!wrapper) { - /* if wrapper is NULL, there was a mal-formed include_path stream wrapper + /* if wrapper is NULL, there was a malformed include_path stream wrapper this also should be impossible */ return NULL; } else { return estrdup(trypath); } } + if (tsrm_realpath(trypath, resolved_path TSRMLS_CC)) { return estrdup(resolved_path); } @@ -546,19 +583,24 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char if (!ret) { return FAILURE; } + *ret = NULL; + if (error) { *error = NULL; } + if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, error TSRMLS_CC)) { return FAILURE; } + if (for_write && PHAR_G(readonly) && !phar->is_data) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, disabled by ini setting", path, fname); } return FAILURE; } + if (!path_len) { if (error) { spprintf(error, 4096, "phar error: file \"\" in phar \"%s\" cannot be empty", fname); @@ -581,6 +623,7 @@ really_get_entry: return FAILURE; } } + if (for_write && phar->is_persistent) { if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) { if (error) { @@ -591,24 +634,28 @@ really_get_entry: goto really_get_entry; } } + if (entry->is_modified && !for_write) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for reading, writable file pointers are open", path, fname); } return FAILURE; } + if (entry->fp_refcount && for_write) { if (error) { spprintf(error, 4096, "phar error: file \"%s\" in phar \"%s\" cannot be opened for writing, readable file pointers are open", path, fname); } return FAILURE; } + if (entry->is_deleted) { if (!for_create) { return FAILURE; } entry->is_deleted = 0; } + if (entry->is_dir) { *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); (*ret)->position = 0; @@ -618,12 +665,15 @@ really_get_entry: (*ret)->internal_file = entry; (*ret)->is_zip = entry->is_zip; (*ret)->is_tar = entry->is_tar; + if (!phar->is_persistent) { ++(entry->phar->refcount); ++(entry->fp_refcount); } + return SUCCESS; } + if (entry->fp_type == PHAR_MOD) { if (for_trunc) { if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) { @@ -638,6 +688,7 @@ really_get_entry: entry->link = NULL; entry->tar_type = (entry->tar_type ? TAR_FILE : 0); } + if (for_write) { if (for_trunc) { if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) { @@ -654,6 +705,7 @@ really_get_entry: } } } + *ret = (phar_entry_data *) emalloc(sizeof(phar_entry_data)); (*ret)->position = 0; (*ret)->phar = phar; @@ -663,10 +715,12 @@ really_get_entry: (*ret)->is_tar = entry->is_tar; (*ret)->fp = phar_get_efp(entry, 1 TSRMLS_CC); (*ret)->zero = phar_get_fp_offset(entry TSRMLS_CC); + if (!phar->is_persistent) { ++(entry->fp_refcount); ++(entry->phar->refcount); } + return SUCCESS; } /* }}} */ @@ -720,6 +774,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char etemp.filename_len = path_len; etemp.fp_type = PHAR_MOD; etemp.fp = php_stream_fopen_tmpfile(); + if (!etemp.fp) { if (error) { spprintf(error, 0, "phar error: unable to create temporary file"); @@ -727,6 +782,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char efree(ret); return NULL; } + etemp.fp_refcount = 1; if (allow_dir == 2) { @@ -739,6 +795,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char } else { etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE; } + phar_add_virtual_dirs(phar, path, path_len TSRMLS_CC); etemp.is_modified = 1; etemp.timestamp = time(0); @@ -746,10 +803,12 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char etemp.phar = phar; etemp.filename = estrndup(path, path_len); etemp.is_zip = phar->is_zip; + if (phar->is_tar) { etemp.is_tar = phar->is_tar; etemp.tar_type = TAR_FILE; } + if (FAILURE == zend_hash_add(&phar->manifest, etemp.filename, path_len, (void*)&etemp, sizeof(phar_entry_info), (void **) &entry)) { php_stream_close(etemp.fp); if (error) { @@ -759,7 +818,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char efree(etemp.filename); return NULL; } - + if (!entry) { php_stream_close(etemp.fp); efree(etemp.filename); @@ -775,23 +834,23 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char ret->is_zip = entry->is_zip; ret->is_tar = entry->is_tar; ret->internal_file = entry; + return ret; } /* }}} */ /* initialize a phar_archive_data's read-only fp for existing phar data */ -int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ +int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ { if (phar_get_pharfp(phar TSRMLS_CC)) { return SUCCESS; } - #if PHP_MAJOR_VERSION < 6 if (PG(safe_mode) && (!php_checkuid(phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { return FAILURE; } #endif - + if (php_check_open_basedir(phar->fname TSRMLS_CC)) { return FAILURE; } @@ -801,6 +860,7 @@ int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC) /* {{{ */ if (!phar_get_pharfp(phar TSRMLS_CC)) { return FAILURE; } + return SUCCESS; } /* }}} */ @@ -813,21 +873,24 @@ int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **er if (FAILURE == phar_open_entry_fp(source, error, 1 TSRMLS_CC)) { return FAILURE; } + if (dest->link) { efree(dest->link); dest->link = NULL; dest->tar_type = (dest->tar_type ? TAR_FILE : 0); } + dest->fp_type = PHAR_MOD; dest->offset = 0; dest->is_modified = 1; dest->fp = php_stream_fopen_tmpfile(); - phar_seek_efp(source, 0, SEEK_SET, 0, 1 TSRMLS_CC); link = phar_get_link_source(source TSRMLS_CC); + if (!link) { link = source; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), dest->fp, link->uncompressed_filesize)) { php_stream_close(dest->fp); dest->fp_type = PHAR_FP; @@ -836,13 +899,14 @@ int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **er } return FAILURE; } + return SUCCESS; } /* }}} */ /* open and decompress a compressed phar entry */ -int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) /* {{{ */ +int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) /* {{{ */ { php_stream_filter *filter; phar_archive_data *phar = entry->phar; @@ -852,30 +916,34 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS if (follow_links && entry->link) { phar_entry_info *link_entry = phar_get_link_source(entry TSRMLS_CC); - if (link_entry && link_entry != entry) { return phar_open_entry_fp(link_entry, error, 1 TSRMLS_CC); } } + if (entry->fp_type == PHAR_TMP) { if (!entry->fp) { entry->fp = php_stream_open_wrapper(entry->tmp, "rb", STREAM_MUST_SEEK|0, NULL); } return SUCCESS; } + if (entry->fp_type != PHAR_FP) { /* either newly created or already modified */ return SUCCESS; } + if (!phar_get_pharfp(phar TSRMLS_CC)) { if (FAILURE == phar_open_archive_fp(phar TSRMLS_CC)) { spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname); return FAILURE; } } + if ((entry->old_flags && !(entry->old_flags & PHAR_ENT_COMPRESSION_MASK)) || !(entry->flags & PHAR_ENT_COMPRESSION_MASK)) { return SUCCESS; } + if (!phar_get_entrypufp(entry TSRMLS_CC)) { phar_set_entrypufp(entry, php_stream_fopen_tmpfile() TSRMLS_CC); if (!phar_get_entrypufp(entry TSRMLS_CC)) { @@ -885,38 +953,44 @@ int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TS } ufp = phar_get_entrypufp(entry TSRMLS_CC); + if ((filtername = phar_decompress_filter(entry, 0)) != NULL) { filter = php_stream_filter_create(filtername, NULL, 0 TSRMLS_CC); } else { filter = NULL; } + if (!filter) { spprintf(error, 4096, "phar error: unable to read phar \"%s\" (cannot create %s filter while decompressing file \"%s\")", phar->fname, phar_decompress_filter(entry, 1), entry->filename); return FAILURE; } + /* now we can safely use proper decompression */ /* save the new offset location within ufp */ php_stream_seek(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(ufp); php_stream_filter_remove(filter, 1 TSRMLS_CC); + 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; + /* this is now the new location of the file contents within this fp */ phar_set_fp_type(entry, PHAR_UFP, loc TSRMLS_CC); - return SUCCESS; } /* }}} */ @@ -974,22 +1048,27 @@ int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, entry->offset = 0; return SUCCESS; } + if (error) { *error = NULL; } + /* open a new temp file for writing */ if (entry->link) { efree(entry->link); entry->link = NULL; entry->tar_type = (entry->tar_type ? TAR_FILE : 0); } + entry->fp = php_stream_fopen_tmpfile(); + if (!entry->fp) { if (error) { spprintf(error, 0, "phar error: unable to create temporary file"); } return FAILURE; } + entry->old_flags = entry->flags; entry->is_modified = 1; phar->is_modified = 1; @@ -1020,9 +1099,11 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) /* {{ fp = php_stream_fopen_tmpfile(); phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC); link = phar_get_link_source(entry TSRMLS_CC); + if (!link) { link = entry; } + if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) { if (error) { spprintf(error, 4096, "phar error: cannot separate entry file \"%s\" contents in phar archive \"%s\" for write access", entry->filename, entry->phar->fname); @@ -1069,13 +1150,16 @@ int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len TSRMLS_D if (phar->refcount || phar->is_persistent) { return FAILURE; } + /* this archive has no open references, so emit an E_STRICT and remove it */ if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) { return FAILURE; } + /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + return SUCCESS; } /* }}} */ @@ -1096,10 +1180,13 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch if (error) { *error = NULL; } + *archive = NULL; + if (PHAR_G(last_phar) && fname_len == PHAR_G(last_phar_name_len) && !memcmp(fname, PHAR_G(last_phar_name), fname_len)) { *archive = PHAR_G(last_phar); if (alias && alias_len) { + if (!PHAR_G(last_phar)->is_temporary_alias && (alias_len != PHAR_G(last_phar)->alias_len || memcmp(PHAR_G(last_phar)->alias, alias, alias_len))) { if (error) { spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, PHAR_G(last_phar)->fname, fname); @@ -1107,20 +1194,25 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch *archive = NULL; return FAILURE; } + if (PHAR_G(last_phar)->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len); } - zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(*archive), sizeof(phar_archive_data*), NULL); + + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&(*archive), sizeof(phar_archive_data*), NULL); PHAR_G(last_alias) = alias; PHAR_G(last_alias_len) = alias_len; } + return SUCCESS; } + if (alias && alias_len && PHAR_G(last_phar) && alias_len == PHAR_G(last_alias_len) && !memcmp(alias, PHAR_G(last_alias), alias_len)) { fd = PHAR_G(last_phar); fd_ptr = &fd; goto alias_success; } + if (alias && alias_len) { 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)) { @@ -1135,6 +1227,7 @@ alias_success: } return FAILURE; } + *archive = *fd_ptr; fd = *fd_ptr; PHAR_G(last_phar) = fd; @@ -1142,20 +1235,25 @@ alias_success: PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = alias; PHAR_G(last_alias_len) = alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, alias, alias_len, ahash, (void **)&fd_ptr)) { goto alias_success; } } + fhash = zend_inline_hash_func(fname, fname_len); my_realpath = NULL; save = fname; save_len = fname_len; + if (fname && fname_len) { if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, fhash, (void**)&fd_ptr)) { *archive = *fd_ptr; fd = *fd_ptr; + if (alias && alias_len) { if (!fd->is_temporary_alias && (alias_len != fd->alias_len || memcmp(fd->alias, alias, alias_len))) { if (error) { @@ -1163,21 +1261,27 @@ alias_success: } return FAILURE; } + if (fd->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len); } - zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); + + zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); } + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (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) { @@ -1188,34 +1292,43 @@ alias_success: return FAILURE; } } + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (SUCCESS == zend_hash_quick_find(&(PHAR_GLOBALS->phar_alias_map), save, save_len, fhash, (void**)&fd_ptr)) { fd = *archive = *fd_ptr; + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_alias, save, save_len, fhash, (void**)&fd_ptr)) { fd = *archive = *fd_ptr; + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } /* not found, try converting \ to / */ my_realpath = expand_filepath(fname, my_realpath TSRMLS_CC); + if (my_realpath) { fname_len = strlen(my_realpath); fname = my_realpath; @@ -1226,26 +1339,34 @@ alias_success: phar_unixify_path_separators(fname, fname_len); #endif 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_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); + zend_hash_quick_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, ahash, (void*)&fd, sizeof(phar_archive_data*), NULL); } + efree(my_realpath); + PHAR_G(last_phar) = fd; PHAR_G(last_phar_name) = fd->fname; PHAR_G(last_phar_name_len) = fd->fname_len; PHAR_G(last_alias) = fd->alias; PHAR_G(last_alias_len) = fd->alias_len; + return SUCCESS; } + if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_quick_find(&cached_phars, fname, fname_len, fhash, (void**)&fd_ptr)) { goto realpath_success; } + efree(my_realpath); } + return FAILURE; } /* }}} */ @@ -1278,13 +1399,14 @@ char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{ } else { flags = entry->flags; } + switch (flags & PHAR_ENT_COMPRESSION_MASK) { - case PHAR_ENT_COMPRESSED_GZ: - return "zlib.inflate"; - case PHAR_ENT_COMPRESSED_BZ2: - return "bzip2.decompress"; - default: - return return_unknown ? "unknown" : NULL; + case PHAR_ENT_COMPRESSED_GZ: + return "zlib.inflate"; + case PHAR_ENT_COMPRESSED_BZ2: + return "bzip2.decompress"; + default: + return return_unknown ? "unknown" : NULL; } } /* }}} */ @@ -1324,12 +1446,14 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if (!path_len && !dir) { if (error) { spprintf(error, 4096, "phar error: invalid path \"%s\" must not be empty", path); } return NULL; } + if (phar_path_check(&path, &path_len, &pcr_error) > pcr_is_ok) { if (error) { spprintf(error, 4096, "phar error: invalid path \"%s\" contains %s", path, pcr_error); @@ -1340,12 +1464,14 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in if (!phar->manifest.arBuckets) { return NULL; } + if (is_dir) { if (!path_len || path_len == 1) { return NULL; } path_len--; } + if (SUCCESS == zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) { if (entry->is_deleted) { /* entry is deleted, but has not been flushed to disk yet */ @@ -1366,6 +1492,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return entry; } + if (dir) { if (zend_hash_exists(&phar->virtual_dirs, path, path_len)) { /* a file or directory exists in a sub-directory of this path */ @@ -1378,6 +1505,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in return entry; } } + if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) { phar_zstr key; char *str_key; @@ -1406,17 +1534,21 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if (!entry->tmp || !entry->is_mounted) { if (error) { spprintf(error, 4096, "phar internal error: mounted path \"%s\" is not properly initialized as a mounted path", str_key); } return NULL; } + test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, path + keylen); + if (SUCCESS != php_stream_stat_path(test, &ssb)) { efree(test); return NULL; } + if (ssb.sb.st_mode & S_IFDIR && !dir) { efree(test); if (error) { @@ -1424,6 +1556,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + if ((ssb.sb.st_mode & S_IFDIR) == 0 && dir) { efree(test); /* user requested a directory, we must return one */ @@ -1432,6 +1565,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + /* mount the file just in time */ if (SUCCESS != phar_mount_entry(phar, test, test_len, path, path_len TSRMLS_CC)) { efree(test); @@ -1440,7 +1574,9 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } + efree(test); + if (SUCCESS != zend_hash_find(&phar->manifest, path, path_len, (void**)&entry)) { if (error) { spprintf(error, 4096, "phar error: path \"%s\" exists as file \"%s\" and could not be retrieved after being mounted", path, test); @@ -1451,6 +1587,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } } } + return NULL; } /* }}} */ @@ -1494,13 +1631,13 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, php_stream_rewind(fp); Z_TYPE_P(zdata) = IS_STRING; Z_STRLEN_P(zdata) = end; + if (end != (off_t) php_stream_copy_to_mem(fp, &(Z_STRVAL_P(zdata)), (size_t) end, 0)) { zval_dtor(zdata); zval_dtor(zsig); zval_dtor(zkey); return FAILURE; } - #if PHP_VERSION_ID < 50300 if (FAILURE == zend_fcall_info_init(openssl, &fci, &fcc TSRMLS_CC)) { #else @@ -1542,9 +1679,9 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, efree(zsig); return FAILURE; } + zval_dtor(openssl); efree(openssl); - #if PHP_VERSION_ID < 50300 --(zdata->refcount); --(zsig->refcount); @@ -1562,9 +1699,10 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, efree(zdata); zval_dtor(zkey); efree(zkey); + switch (Z_TYPE_P(retval_ptr)) { default: - case IS_LONG : + case IS_LONG: zval_dtor(zsig); efree(zsig); if (1 == Z_LVAL_P(retval_ptr)) { @@ -1573,7 +1711,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, off_t end, } efree(retval_ptr); return FAILURE; - case IS_BOOL : + case IS_BOOL: efree(retval_ptr); if (Z_BVAL_P(retval_ptr)) { *signature = estrndup(Z_STRVAL_P(zsig), Z_STRLEN_P(zsig)); @@ -1611,7 +1749,6 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ php_uint32 pubkey_len; char *pubkey = NULL, *pfile; php_stream *pfp; - #ifndef PHAR_HAVE_OPENSSL if (!zend_hash_exists(&module_registry, "openssl", sizeof("openssl"))) { if (error) { @@ -1624,30 +1761,38 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ spprintf(&pfile, 0, "%s.pubkey", fname); pfp = php_stream_open_wrapper(pfile, "rb", 0, NULL); efree(pfile); + if (!pfp || !(pubkey_len = php_stream_copy_to_mem(pfp, &pubkey, PHP_STREAM_COPY_ALL, 0)) || !pubkey) { if (error) { spprintf(error, 0, "openssl public key could not be read"); } return FAILURE; } + php_stream_close(pfp); #ifndef PHAR_HAVE_OPENSSL tempsig = sig_len; + if (FAILURE == phar_call_openssl_signverify(0, fp, end_of_phar, pubkey, pubkey_len, &sig, &tempsig TSRMLS_CC)) { if (pubkey) { efree(pubkey); } + if (error) { spprintf(error, 0, "openssl signature could not be verified"); } + return FAILURE; } + if (pubkey) { efree(pubkey); } + sig_len = tempsig; #else in = BIO_new_mem_buf(pubkey, pubkey_len); + if (NULL == in) { efree(pubkey); if (error) { @@ -1655,9 +1800,11 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ } return FAILURE; } + key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL); BIO_free(in); efree(pubkey); + if (NULL == key) { if (error) { spprintf(error, 0, "openssl signature could not be processed"); @@ -1666,47 +1813,56 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ } EVP_VerifyInit(&md_ctx, mdtype); - read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + php_stream_seek(fp, 0, SEEK_SET); + while (read_size && (len = php_stream_read(fp, (char*)buf, read_size)) > 0) { EVP_VerifyUpdate (&md_ctx, buf, len); read_len -= (off_t)len; + if (read_len < read_size) { read_size = (int)read_len; } } + if (EVP_VerifyFinal(&md_ctx, (unsigned char *)sig, sig_len, key) != 1) { /* 1: signature verified, 0: signature does not match, -1: failed signature operation */ EVP_MD_CTX_cleanup(&md_ctx); + if (error) { spprintf(error, 0, "broken openssl signature"); } + return FAILURE; } + EVP_MD_CTX_cleanup(&md_ctx); #endif - + *signature_len = phar_hex_str((const char*)sig, sig_len, signature TSRMLS_CC); } break; #if HAVE_HASH_EXT case PHAR_SIG_SHA512: { unsigned char digest[64]; - PHP_SHA512_CTX context; + PHP_SHA512_CTX context; PHP_SHA512Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_SHA512Update(&context, buf, len); read_len -= (off_t)len; @@ -1714,6 +1870,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_SHA512Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1728,15 +1885,17 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ } case PHAR_SIG_SHA256: { unsigned char digest[32]; - PHP_SHA256_CTX context; + PHP_SHA256_CTX context; PHP_SHA256Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_SHA256Update(&context, buf, len); read_len -= (off_t)len; @@ -1744,6 +1903,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_SHA256Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1770,11 +1930,13 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ PHP_SHA1Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_SHA1Update(&context, buf, len); read_len -= (off_t)len; @@ -1782,6 +1944,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_SHA1Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1800,11 +1963,13 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ PHP_MD5Init(&context); read_len = end_of_phar; + if (read_len > sizeof(buf)) { read_size = sizeof(buf); } else { read_size = (int)read_len; } + while ((len = php_stream_read(fp, (char*)buf, read_size)) > 0) { PHP_MD5Update(&context, buf, len); read_len -= (off_t)len; @@ -1812,6 +1977,7 @@ int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_typ read_size = (int)read_len; } } + PHP_MD5Final(digest, &context); if (memcmp(digest, sig, sizeof(digest))) { @@ -1850,12 +2016,14 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat #if HAVE_HASH_EXT case PHAR_SIG_SHA512: { unsigned char digest[64]; - PHP_SHA512_CTX context; + PHP_SHA512_CTX context; PHP_SHA512Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_SHA512Update(&context, buf, sig_len); } + PHP_SHA512Final(digest, &context); *signature = estrndup((char *) digest, 64); *signature_length = 64; @@ -1864,11 +2032,13 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat case PHAR_SIG_SHA256: { unsigned char digest[32]; PHP_SHA256_CTX context; - + PHP_SHA256Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_SHA256Update(&context, buf, sig_len); } + PHP_SHA256Final(digest, &context); *signature = estrndup((char *) digest, 32); *signature_length = 32; @@ -1880,6 +2050,7 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat if (error) { spprintf(error, 0, "unable to write to phar \"%s\" with requested hash type", phar->fname); } + return FAILURE; #endif case PHAR_SIG_OPENSSL: { @@ -1899,9 +2070,10 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat } return FAILURE; } - key = PEM_read_bio_PrivateKey(in, NULL,NULL, ""); + key = PEM_read_bio_PrivateKey(in, NULL,NULL, ""); BIO_free(in); + if (!key) { if (error) { spprintf(error, 0, "unable to process private key"); @@ -1911,11 +2083,12 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat siglen = EVP_PKEY_size(key); sigbuf = emalloc(siglen + 1); - EVP_SignInit(&md_ctx, mdtype); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { EVP_SignUpdate(&md_ctx, buf, sig_len); } + if (!EVP_SignFinal (&md_ctx, sigbuf,(unsigned int *)&siglen, key)) { efree(sigbuf); if (error) { @@ -1923,12 +2096,14 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat } return FAILURE; } + sigbuf[siglen] = '\0'; EVP_MD_CTX_cleanup(&md_ctx); #else sigbuf = NULL; siglen = 0; php_stream_seek(fp, 0, SEEK_END); + if (FAILURE == phar_call_openssl_signverify(1, fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen TSRMLS_CC)) { if (error) { spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", phar->fname); @@ -1947,9 +2122,11 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat PHP_SHA1_CTX context; PHP_SHA1Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_SHA1Update(&context, buf, sig_len); } + PHP_SHA1Final(digest, &context); *signature = estrndup((char *) digest, 20); *signature_length = 20; @@ -1960,15 +2137,18 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat PHP_MD5_CTX context; PHP_MD5Init(&context); + while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { PHP_MD5Update(&context, buf, sig_len); } + PHP_MD5Final(digest, &context); *signature = estrndup((char *) digest, 16); *signature_length = 16; break; } } + phar->sig_len = phar_hex_str((const char *)*signature, *signature_length, &phar->signature TSRMLS_CC); return SUCCESS; } @@ -1993,15 +2173,19 @@ static void phar_update_cached_entry(void *data, void *argument) /* {{{ */ 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 */ @@ -2018,7 +2202,6 @@ static void phar_update_cached_entry(void *data, void *argument) /* {{{ */ #else Z_SET_REFCOUNT_P(entry->metadata, 1); #endif - entry->metadata_str.c = NULL; entry->metadata_str.len = 0; } @@ -2038,12 +2221,15 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ 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) { @@ -2062,6 +2248,7 @@ static void phar_copy_cached_phar(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ #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)); @@ -2089,10 +2276,12 @@ int phar_copy_on_write(phar_archive_data **pphar TSRMLS_DC) /* {{{ */ /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + if (newpphar[0]->alias_len && FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), newpphar[0]->alias, newpphar[0]->alias_len, (void*)newpphar, sizeof(phar_archive_data*), NULL)) { zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), (*pphar)->fname, (*pphar)->fname_len); return FAILURE; } + *pphar = *newpphar; return SUCCESS; } diff --git a/ext/phar/zip.c b/ext/phar/zip.c index f235297da9..07725875f4 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -46,30 +46,38 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_ui if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) { return FAILURE; } + if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') { /* skip to next header */ php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR); len -= PHAR_GET_16(h.header.size) + 4; continue; } + /* unix3 header found */ read = php_stream_read(fp, (char *) &(h.unix3.crc32), sizeof(h.unix3) - sizeof(h.header)); len -= read + 4; + if (sizeof(h.unix3) - sizeof(h.header) != read) { return FAILURE; } + if (PHAR_GET_16(h.unix3.size) > sizeof(h.unix3) - 4) { /* skip symlink filename - we may add this support in later */ php_stream_seek(fp, h.unix3.size - sizeof(h.unix3.size), SEEK_CUR); } + /* set permissions */ entry->flags &= PHAR_ENT_COMPRESSION_MASK; + if (entry->is_dir) { entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK; } else { entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK; } + } while (len); + return SUCCESS; } /* }}} */ @@ -94,7 +102,7 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_ui 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -109,38 +117,38 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_ui */ static time_t phar_zip_d2u_time(int dtime, int ddate) /* {{{ */ { - struct tm *tm, tmbuf; - time_t now; + struct tm *tm, tmbuf; + time_t now; - now = time(NULL); - tm = php_localtime_r(&now, &tmbuf); - - tm->tm_year = ((ddate>>9)&127) + 1980 - 1900; - tm->tm_mon = ((ddate>>5)&15) - 1; - tm->tm_mday = ddate&31; + now = time(NULL); + tm = php_localtime_r(&now, &tmbuf); - tm->tm_hour = (dtime>>11)&31; - tm->tm_min = (dtime>>5)&63; - tm->tm_sec = (dtime<<1)&62; + tm->tm_year = ((ddate>>9)&127) + 1980 - 1900; + tm->tm_mon = ((ddate>>5)&15) - 1; + tm->tm_mday = ddate&31; - return mktime(tm); + tm->tm_hour = (dtime>>11)&31; + tm->tm_min = (dtime>>5)&63; + tm->tm_sec = (dtime<<1)&62; + + return mktime(tm); } /* }}} */ static void phar_zip_u2d_time(time_t time, php_uint16 *dtime, php_uint16 *ddate) /* {{{ */ { - struct tm *tm, tmbuf; + struct tm *tm, tmbuf; - tm = php_localtime_r(&time, &tmbuf); - *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday; - *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1); + tm = php_localtime_r(&time, &tmbuf); + *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday; + *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1); } /* }}} */ /** * Does not check for a previously opened phar in the cache. * - * Parse a new one and add it to the cache, returning either SUCCESS or + * Parse a new one and add it to the cache, returning either SUCCESS or * FAILURE, and setting pphar to the pointer to the manifest entry * * This is used by phar_open_from_fp to process a zip-based phar, but can be called @@ -158,6 +166,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, char *p = buf, *ext, *actual_alias = NULL; size = php_stream_tell(fp); + if (size > sizeof(locator) + 65536) { /* seek to max comment length + end of central directory record */ size = sizeof(locator) + 65536; @@ -171,6 +180,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } else { php_stream_seek(fp, 0, SEEK_SET); } + if (!(read = php_stream_read(fp, buf, size))) { php_stream_close(fp); if (error) { @@ -178,6 +188,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } return FAILURE; } + while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) { if (!memcmp(p + 1, "K\5\6", 3)) { memcpy((void *)&locator, (void *) p, sizeof(locator)); @@ -189,6 +200,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } return FAILURE; } + if (locator.counthere != locator.count) { if (error) { spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname); @@ -196,6 +208,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, php_stream_close(fp); return FAILURE; } + mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist)); mydata->is_persistent = PHAR_G(persist); @@ -204,6 +217,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, char *metadata; metadata = p + sizeof(locator); + if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) { if (error) { spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname); @@ -212,7 +226,9 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, pefree(mydata, mydata->is_persistent); return FAILURE; } + mydata->metadata_len = PHAR_GET_16(locator.comment_len); + if (phar_parse_metadata(&metadata, &mydata->metadata, PHAR_GET_16(locator.comment_len) TSRMLS_CC) == FAILURE) { mydata->metadata_len = 0; /* if not valid serialized data, it is a regular string */ @@ -230,13 +246,17 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, } else { mydata->metadata = NULL; } + goto foundit; } } + php_stream_close(fp); + if (error) { spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname); } + return FAILURE; foundit: mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent); @@ -246,6 +266,7 @@ foundit: mydata->is_zip = 1; mydata->fname_len = fname_len; ext = strrchr(mydata->fname, '/'); + if (ext) { mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext); if (mydata->ext == ext) { @@ -255,6 +276,7 @@ foundit: mydata->ext_len = (mydata->fname + fname_len) - mydata->ext; } } + /* clean up on big-endian systems */ /* seek to central directory */ php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET); @@ -297,12 +319,17 @@ foundit: if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) { PHAR_ZIP_FAIL("unable to read central directory entry, truncated"); } + /* clean up for bigendian systems */ if (memcmp("PK\1\2", zipentry.signature, 4)) { /* corrupted entry */ PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature"); } - if (entry.is_persistent) entry.manifest_pos = i; + + 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); @@ -312,19 +339,25 @@ foundit: entry.header_offset = PHAR_GET_32(zipentry.offset); entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) + PHAR_GET_16(zipentry.extra_len); + if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) { PHAR_ZIP_FAIL("Cannot process encrypted zip files"); } + if (!PHAR_GET_16(zipentry.filename_len)) { PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)"); } + entry.filename_len = PHAR_GET_16(zipentry.filename_len); entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent); + if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated"); } + entry.filename[entry.filename_len] = '\0'; + if (entry.filename[entry.filename_len - 1] == '/') { entry.is_dir = 1; entry.filename_len--; @@ -332,7 +365,9 @@ foundit: } else { entry.is_dir = 0; } + phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC); + if (PHAR_GET_16(zipentry.extra_len)) { off_t loc = php_stream_tell(fp); if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len) TSRMLS_CC)) { @@ -341,6 +376,7 @@ foundit: } php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET); } + switch (zipentry.compressed) { case PHAR_ZIP_COMP_NONE : /* compression flag already set */ @@ -399,14 +435,17 @@ foundit: pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip"); } + /* get file metadata */ if (zipentry.comment_len) { if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in file comment, truncated"); } + p = buf; entry.metadata_len = zipentry.comment_len; + if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len) TSRMLS_CC) == FAILURE) { entry.metadata_len = 0; /* if not valid serialized data, it is a regular string */ @@ -423,41 +462,51 @@ foundit: } else { entry.metadata = NULL; } + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { php_stream_filter *filter; off_t saveloc; - /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but - not very. */ + /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but not very. */ saveloc = php_stream_tell(fp); php_stream_seek(fp, PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET); mydata->alias_len = entry.uncompressed_filesize; + if (entry.flags & PHAR_ENT_COMPRESSED_GZ) { filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed"); } + php_stream_filter_append(&fp->readfilters, filter); + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); + } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) { php_stream_filter *filter; filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed"); } + php_stream_filter_append(&fp->readfilters, filter); php_stream_filter_append(&fp->readfilters, filter); + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } + php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); } else { @@ -470,16 +519,21 @@ foundit: /* return to central directory parsing */ php_stream_seek(fp, saveloc, SEEK_SET); } + phar_set_inode(&entry TSRMLS_CC); zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL); } + mydata->fp = fp; + if (zend_hash_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) { mydata->is_data = 0; } else { mydata->is_data = 1; } + zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (actual_alias) { phar_archive_data **fd_ptr; @@ -491,7 +545,9 @@ foundit: zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); return FAILURE; } + mydata->is_temporary_alias = 0; + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void **)&fd_ptr)) { if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, mydata->alias_len TSRMLS_CC)) { if (error) { @@ -502,10 +558,13 @@ foundit: return FAILURE; } } + mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias; + if (entry.is_persistent) { efree(actual_alias); } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); } else { phar_archive_data **fd_ptr; @@ -520,6 +579,7 @@ foundit: return FAILURE; } } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent); mydata->alias_len = alias_len; @@ -527,12 +587,14 @@ foundit: mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent); mydata->alias_len = fname_len; } + mydata->is_temporary_alias = 1; } if (pphar) { *pphar = mydata; } + return SUCCESS; } /* }}} */ @@ -570,6 +632,7 @@ int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_l if (error) { spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname); } + return FAILURE; } /* }}} */ @@ -595,9 +658,11 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ entry = (phar_entry_info *)data; p = (struct _phar_zip_pass*) arg; + if (entry->is_mounted) { return ZEND_HASH_APPLY_KEEP; } + if (entry->is_deleted) { if (entry->fp_refcount <= 0) { return ZEND_HASH_APPLY_REMOVE; @@ -606,6 +671,7 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ return ZEND_HASH_APPLY_KEEP; } } + memset(&local, 0, sizeof(local)); memset(¢ral, 0, sizeof(central)); memset(&perms, 0, sizeof(perms)); @@ -620,18 +686,22 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ CRC32(perms.crc32, (char)perms.perms & 0xFF); CRC32(perms.crc32, (char)perms.perms & 0xFF00 >> 8); perms.crc32 = PHAR_SET_32(~(perms.crc32)); + if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { local.compressed = central.compressed = PHAR_SET_16(PHAR_ZIP_COMP_DEFLATE); } + if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) { local.compressed = central.compressed = PHAR_SET_16(PHAR_ZIP_COMP_BZIP2); } + /* do not use PHAR_SET_16 on either field of the next line */ phar_zip_u2d_time(entry->timestamp, &local.timestamp, &local.datestamp); central.timestamp = local.timestamp; central.datestamp = local.datestamp; central.filename_len = local.filename_len = PHAR_SET_16(entry->filename_len + (entry->is_dir ? 1 : 0)); central.offset = PHAR_SET_32(php_stream_tell(p->filefp)); + /* do extra field for perms later */ if (entry->is_modified) { php_uint32 loc; @@ -647,29 +717,36 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ } goto continue_dir; } + if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) { spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } - efp = phar_get_efp(entry, 0 TSRMLS_CC); + efp = phar_get_efp(entry, 0 TSRMLS_CC); newcrc32 = ~0; + for (loc = 0;loc < entry->uncompressed_filesize; ++loc) { CRC32(newcrc32, php_stream_getc(efp)); } + entry->crc32 = ~newcrc32; central.uncompsize = local.uncompsize = PHAR_SET_32(entry->uncompressed_filesize); + if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) { /* not compressed */ entry->compressed_filesize = entry->uncompressed_filesize; central.compsize = local.compsize = PHAR_SET_32(entry->compressed_filesize); goto not_compressed; } + filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC); + if (!filter) { if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); @@ -683,20 +760,26 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ /* work around inability to specify freedom in write and strictness in read count */ entry->cfp = php_stream_fopen_tmpfile(); + if (!entry->cfp) { spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_flush(efp); + if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) { spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_filter_append((&entry->cfp->writefilters), filter); + if (entry->uncompressed_filesize != php_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize)) { spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_filter_flush(filter, 1); php_stream_flush(entry->cfp); php_stream_filter_remove(filter, 1 TSRMLS_CC); @@ -710,6 +793,7 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */ } else { central.uncompsize = local.uncompsize = PHAR_SET_32(entry->uncompressed_filesize); central.compsize = local.compsize = PHAR_SET_32(entry->compressed_filesize); + if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) { spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; @@ -732,29 +816,36 @@ continue_dir: PHP_VAR_SERIALIZE_DESTROY(metadata_hash); central.comment_len = PHAR_SET_16(entry->metadata_str.len); } + entry->header_offset = php_stream_tell(p->filefp); offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms); + if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) { spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) { spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->is_dir) { if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) { spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (1 != php_stream_write(p->filefp, "/", 1)) { spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) { spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (1 != php_stream_write(p->centralfp, "/", 1)) { spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; @@ -764,40 +855,49 @@ continue_dir: spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) { spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } } + if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) { spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) { spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + if (entry->is_modified) { if (entry->cfp) { if (entry->compressed_filesize != php_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize)) { spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } + php_stream_close(entry->cfp); entry->cfp = NULL; } else { if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) { return ZEND_HASH_APPLY_STOP; } + phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC); + if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), p->filefp, entry->uncompressed_filesize)) { spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } } + if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) { php_stream_close(entry->fp); } + entry->is_modified = 0; } else { if (entry->fp_refcount) { @@ -812,22 +912,27 @@ continue_dir: break; } } + if (!entry->is_dir && entry->compressed_filesize && entry->compressed_filesize != php_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize)) { spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } } + entry->fp = NULL; entry->offset = entry->offset_abs = offset; entry->fp_type = PHAR_FP; + if (entry->metadata_str.c) { if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) { spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); smart_str_free(&entry->metadata_str); return ZEND_HASH_APPLY_STOP; } + smart_str_free(&entry->metadata_str); } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -859,6 +964,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + if (phar->is_data) { goto nostub; } @@ -866,15 +972,18 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau /* set alias */ if (!phar->is_temporary_alias && phar->alias_len) { entry.fp = php_stream_fopen_tmpfile(); + if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) { if (error) { spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname); } return EOF; } + entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len; entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); entry.filename_len = sizeof(".phar/alias.txt")-1; + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { if (error) { spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname); @@ -884,6 +993,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } else { zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1); } + /* register alias */ if (phar->alias_len) { if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error TSRMLS_CC)) { @@ -901,12 +1011,15 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + if (len == -1) { len = PHP_STREAM_COPY_ALL; } else { len = -len; } + user_stub = 0; + if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) { if (error) { spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname); @@ -917,6 +1030,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } else { free_user_stub = 0; } + if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) { if (error) { @@ -927,6 +1041,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + len = pos - user_stub + 18; entry.fp = php_stream_fopen_tmpfile(); entry.uncompressed_filesize = len + 5; @@ -942,8 +1057,10 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau php_stream_close(entry.fp); return EOF; } + entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); entry.filename_len = sizeof(".phar/stub.php")-1; + if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) { if (free_user_stub) { efree(user_stub); @@ -953,6 +1070,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } return EOF; } + if (free_user_stub) { efree(user_stub); } @@ -997,9 +1115,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defau } } } - nostub: - if (phar->fp && !phar->is_brandnew) { oldfile = phar->fp; closeoldfile = 0; @@ -1012,6 +1128,7 @@ nostub: /* save modified files to the zip */ pass.old = oldfile; pass.filefp = php_stream_fopen_tmpfile(); + if (!pass.filefp) { if (closeoldfile) { php_stream_close(oldfile); @@ -1021,7 +1138,9 @@ nostub: } return EOF; } + pass.centralfp = php_stream_fopen_tmpfile(); + if (!pass.centralfp) { if (closeoldfile) { php_stream_close(oldfile); @@ -1031,12 +1150,14 @@ nostub: } return EOF; } + pass.free_fp = pass.free_ufp = 1; memset(&eocd, 0, sizeof(eocd)); strncpy(eocd.signature, "PK\5\6", 4); eocd.counthere = eocd.count = zend_hash_num_elements(&phar->manifest); zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC); + if (temperr) { php_stream_close(pass.filefp); php_stream_close(pass.centralfp); @@ -1054,6 +1175,7 @@ nostub: eocd.cdir_size = php_stream_tell(pass.centralfp); eocd.cdir_offset = php_stream_tell(pass.filefp); php_stream_seek(pass.centralfp, 0, SEEK_SET); + if (eocd.cdir_size != php_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL)) { php_stream_close(pass.filefp); php_stream_close(pass.centralfp); @@ -1065,13 +1187,16 @@ nostub: } return EOF; } + php_stream_close(pass.centralfp); + if (phar->metadata) { /* set phar metadata */ PHP_VAR_SERIALIZE_INIT(metadata_hash); php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(metadata_hash); eocd.comment_len = PHAR_SET_16(main_metadata_str.len); + if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) { php_stream_close(pass.filefp); if (error) { @@ -1083,6 +1208,7 @@ nostub: smart_str_free(&main_metadata_str); return EOF; } + if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) { php_stream_close(pass.filefp); if (error) { @@ -1094,7 +1220,9 @@ nostub: smart_str_free(&main_metadata_str); return EOF; } + smart_str_free(&main_metadata_str); + } else { if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) { php_stream_close(pass.filefp); @@ -1107,17 +1235,21 @@ nostub: return EOF; } } + if (phar->fp && pass.free_fp) { php_stream_close(phar->fp); } + if (phar->ufp) { if (pass.free_ufp) { php_stream_close(phar->ufp); } phar->ufp = NULL; } + /* re-open */ phar->is_brandnew = 0; + if (phar->donotflush) { /* deferred flush */ phar->fp = pass.filefp; |