/ext/phar/phar.c
C | 3739 lines | 2876 code | 521 blank | 342 comment | 960 complexity | e2d23dc05a55eb47f81518347ab21406 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- +----------------------------------------------------------------------+
- | phar php single-file executable PHP extension |
- +----------------------------------------------------------------------+
- | Copyright (c) 2005-2011 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 |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt. |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Gregory Beaver <cellog@php.net> |
- | Marcus Boerger <helly@php.net> |
- +----------------------------------------------------------------------+
- */
- /* $Id: phar.c 307915 2011-02-01 14:01:00Z iliaa $ */
- #define PHAR_MAIN 1
- #include "phar_internal.h"
- #include "SAPI.h"
- #include "func_interceptors.h"
- static void destroy_phar_data(void *pDest);
- ZEND_DECLARE_MODULE_GLOBALS(phar)
- #if PHP_VERSION_ID >= 50300
- char *(*phar_save_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
- #endif
- /**
- * set's phar->is_writeable based on the current INI value
- */
- static int phar_set_writeable_bit(void *pDest, void *argument TSRMLS_DC) /* {{{ */
- {
- zend_bool keep = *(zend_bool *)argument;
- phar_archive_data *phar = *(phar_archive_data **)pDest;
- 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 */
- ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
- {
- zend_bool old, ini;
- if (entry->name_length == 14) {
- 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;
- }
- else if (new_value_length == 3 && !strcasecmp("yes", new_value)) {
- ini = (zend_bool) 1;
- }
- else if (new_value_length == 4 && !strcasecmp("true", new_value)) {
- ini = (zend_bool) 1;
- }
- else {
- ini = (zend_bool) atoi(new_value);
- }
- /* do not allow unsetting in runtime */
- if (stage == ZEND_INI_STAGE_STARTUP) {
- if (entry->name_length == 14) {
- PHAR_G(readonly_orig) = ini;
- } else {
- PHAR_G(require_hash_orig) = ini;
- }
- } else if (old && !ini) {
- return FAILURE;
- }
- if (entry->name_length == 14) {
- PHAR_G(readonly) = ini;
- if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets) {
- zend_hash_apply_with_argument(&(PHAR_GLOBALS->phar_fname_map), phar_set_writeable_bit, (void *)&ini TSRMLS_CC);
- }
- } else {
- PHAR_G(require_hash) = ini;
- }
- return SUCCESS;
- }
- /* }}}*/
- /* this global stores the global cached pre-parsed manifests */
- HashTable cached_phars;
- HashTable cached_alias;
- static void phar_split_cache_list(TSRMLS_D) /* {{{ */
- {
- char *tmp;
- char *key, *lasts, *end;
- char ds[2];
- phar_archive_data *phar;
- uint i = 0;
- if (!PHAR_GLOBALS->cache_list || !(PHAR_GLOBALS->cache_list[0])) {
- return;
- }
- ds[0] = DEFAULT_DIR_SEPARATOR;
- ds[1] = '\0';
- tmp = estrdup(PHAR_GLOBALS->cache_list);
- /* fake request startup */
- PHAR_GLOBALS->request_init = 1;
- if (zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0) == SUCCESS) {
- EG(regular_list).nNextFreeElement=1; /* we don't want resource id 0 */
- }
- PHAR_G(has_bz2) = zend_hash_exists(&module_registry, "bz2", sizeof("bz2"));
- PHAR_G(has_zlib) = zend_hash_exists(&module_registry, "zlib", sizeof("zlib"));
- /* these two are dummies and will be destroyed later */
- zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1);
- zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
- /* these two are real and will be copied over cached_phars/cached_alias later */
- zend_hash_init(&(PHAR_GLOBALS->phar_fname_map), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data, 1);
- zend_hash_init(&(PHAR_GLOBALS->phar_alias_map), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
- PHAR_GLOBALS->manifest_cached = 1;
- PHAR_GLOBALS->persist = 1;
- for (key = php_strtok_r(tmp, ds, &lasts);
- key;
- key = php_strtok_r(NULL, ds, &lasts)) {
- end = strchr(key, DEFAULT_DIR_SEPARATOR);
- if (end) {
- if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
- finish_up:
- phar->phar_pos = i++;
- php_stream_close(phar->fp);
- phar->fp = NULL;
- } else {
- finish_error:
- PHAR_GLOBALS->persist = 0;
- PHAR_GLOBALS->manifest_cached = 0;
- efree(tmp);
- zend_hash_destroy(&(PHAR_G(phar_fname_map)));
- PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
- zend_hash_destroy(&(PHAR_G(phar_alias_map)));
- PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
- zend_hash_destroy(&cached_phars);
- zend_hash_destroy(&cached_alias);
- zend_hash_graceful_reverse_destroy(&EG(regular_list));
- memset(&EG(regular_list), 0, sizeof(HashTable));
- /* free cached manifests */
- PHAR_GLOBALS->request_init = 0;
- return;
- }
- } else {
- if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL TSRMLS_CC)) {
- goto finish_up;
- } else {
- goto finish_error;
- }
- }
- }
- PHAR_GLOBALS->persist = 0;
- PHAR_GLOBALS->request_init = 0;
- /* destroy dummy values from before */
- zend_hash_destroy(&cached_phars);
- zend_hash_destroy(&cached_alias);
- cached_phars = PHAR_GLOBALS->phar_fname_map;
- cached_alias = PHAR_GLOBALS->phar_alias_map;
- PHAR_GLOBALS->phar_fname_map.arBuckets = 0;
- PHAR_GLOBALS->phar_alias_map.arBuckets = 0;
- zend_hash_graceful_reverse_destroy(&EG(regular_list));
- memset(&EG(regular_list), 0, sizeof(HashTable));
- efree(tmp);
- }
- /* }}} */
- ZEND_INI_MH(phar_ini_cache_list) /* {{{ */
- {
- PHAR_G(cache_list) = new_value;
- if (stage == ZEND_INI_STAGE_STARTUP) {
- phar_split_cache_list(TSRMLS_C);
- }
- return SUCCESS;
- }
- /* }}} */
- PHP_INI_BEGIN()
- STD_PHP_INI_BOOLEAN( "phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
- STD_PHP_INI_BOOLEAN( "phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
- STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
- PHP_INI_END()
- /**
- * When all uses of a phar have been concluded, this frees the manifest
- * and the phar slot
- */
- void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
- {
- if (phar->alias && phar->alias != phar->fname) {
- 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) {
- /* for zip comments that are strings */
- free(phar->metadata);
- } else {
- zval_internal_ptr_dtor(&phar->metadata);
- }
- } else {
- zval_ptr_dtor(&phar->metadata);
- }
- phar->metadata_len = 0;
- phar->metadata = 0;
- }
- if (phar->fp) {
- php_stream_close(phar->fp);
- phar->fp = 0;
- }
- if (phar->ufp) {
- php_stream_close(phar->ufp);
- phar->ufp = 0;
- }
- pefree(phar, phar->is_persistent);
- }
- /* }}}*/
- /**
- * Delete refcount and destruct if needed. On destruct return 1 else 0.
- */
- int phar_archive_delref(phar_archive_data *phar TSRMLS_DC) /* {{{ */
- {
- if (phar->is_persistent) {
- return 0;
- }
- if (--phar->refcount < 0) {
- if (PHAR_GLOBALS->request_done
- || zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
- phar_destroy_phar_data(phar TSRMLS_CC);
- }
- return 1;
- } else if (!phar->refcount) {
- /* invalidate phar cache */
- PHAR_G(last_phar) = NULL;
- PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
- if (phar->fp && !(phar->flags & PHAR_FILE_COMPRESSION_MASK)) {
- /* close open file handle - allows removal or rename of
- the file on windows, which has greedy locking
- only close if the archive was not already compressed. If it
- was compressed, then the fp does not refer to the original file */
- 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 */
- if (zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), phar->fname, phar->fname_len) != SUCCESS) {
- phar_destroy_phar_data(phar TSRMLS_CC);
- }
- return 1;
- }
- }
- return 0;
- }
- /* }}}*/
- /**
- * Destroy phar's in shutdown, here we don't care about aliases
- */
- static void destroy_phar_data_only(void *pDest) /* {{{ */
- {
- phar_archive_data *phar_data = *(phar_archive_data **) pDest;
- TSRMLS_FETCH();
- if (EG(exception) || --phar_data->refcount < 0) {
- phar_destroy_phar_data(phar_data TSRMLS_CC);
- }
- }
- /* }}}*/
- /**
- * Delete aliases to phar's that got kicked out of the global table
- */
- static int phar_unalias_apply(void *pDest, void *argument TSRMLS_DC) /* {{{ */
- {
- return *(void**)pDest == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
- }
- /* }}} */
- /**
- * Delete aliases to phar's that got kicked out of the global table
- */
- static int phar_tmpclose_apply(void *pDest TSRMLS_DC) /* {{{ */
- {
- phar_entry_info *entry = (phar_entry_info *) pDest;
- 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;
- }
- /* }}} */
- /**
- * Filename map destructor
- */
- static void destroy_phar_data(void *pDest) /* {{{ */
- {
- phar_archive_data *phar_data = *(phar_archive_data **) pDest;
- TSRMLS_FETCH();
- if (PHAR_GLOBALS->request_ends) {
- /* first, iterate over the manifest and close all PHAR_TMP entry fp handles,
- this prevents unnecessary unfreed stream resources */
- zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply TSRMLS_CC);
- 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);
- }
- }
- /* }}}*/
- /**
- * destructor for the manifest hash, frees each file's entry
- */
- void destroy_phar_manifest_entry(void *pDest) /* {{{ */
- {
- phar_entry_info *entry = (phar_entry_info *)pDest;
- TSRMLS_FETCH();
- if (entry->cfp) {
- 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) {
- /* for zip comments that are strings */
- free(entry->metadata);
- } else {
- zval_internal_ptr_dtor(&entry->metadata);
- }
- } else {
- zval_ptr_dtor(&entry->metadata);
- }
- entry->metadata_len = 0;
- entry->metadata = 0;
- }
- if (entry->metadata_str.c) {
- smart_str_free(&entry->metadata_str);
- entry->metadata_str.c = 0;
- }
- 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;
- }
- }
- /* }}} */
- int phar_entry_delref(phar_entry_data *idata TSRMLS_DC) /* {{{ */
- {
- int ret = 0;
- if (idata->internal_file && !idata->internal_file->is_persistent) {
- if (--idata->internal_file->fp_refcount < 0) {
- idata->internal_file->fp_refcount = 0;
- }
- if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
- php_stream_close(idata->fp);
- }
- /* if phar_get_or_create_entry_data returns a sub-directory, we have to free it */
- if (idata->internal_file->is_temp_dir) {
- destroy_phar_manifest_entry((void *)idata->internal_file);
- efree(idata->internal_file);
- }
- }
- phar_archive_delref(idata->phar TSRMLS_CC);
- efree(idata);
- return ret;
- }
- /* }}} */
- /**
- * Removes an entry, either by actually removing it or by marking it.
- */
- 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);
- }
- zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
- idata->phar->refcount--;
- efree(idata);
- } else {
- idata->internal_file->is_deleted = 1;
- phar_entry_delref(idata TSRMLS_CC);
- }
- if (!phar->donotflush) {
- phar_flush(phar, 0, 0, 0, error TSRMLS_CC);
- }
- }
- /* }}} */
- #define MAPPHAR_ALLOC_FAIL(msg) \
- if (fp) {\
- php_stream_close(fp);\
- }\
- if (error) {\
- spprintf(error, 0, msg, fname);\
- }\
- return FAILURE;
- #define MAPPHAR_FAIL(msg) \
- efree(savebuf);\
- if (mydata) {\
- phar_destroy_phar_data(mydata TSRMLS_CC);\
- }\
- if (signature) {\
- pefree(signature, PHAR_G(persist));\
- }\
- MAPPHAR_ALLOC_FAIL(msg)
- #ifdef WORDS_BIGENDIAN
- # define PHAR_GET_32(buffer, var) \
- var = ((((unsigned char*)(buffer))[3]) << 24) \
- | ((((unsigned char*)(buffer))[2]) << 16) \
- | ((((unsigned char*)(buffer))[1]) << 8) \
- | (((unsigned char*)(buffer))[0]); \
- (buffer) += 4
- # define PHAR_GET_16(buffer, var) \
- var = ((((unsigned char*)(buffer))[1]) << 8) \
- | (((unsigned char*)(buffer))[0]); \
- (buffer) += 2
- #else
- # define PHAR_GET_32(buffer, var) \
- memcpy(&var, buffer, sizeof(var)); \
- buffer += 4
- # define PHAR_GET_16(buffer, var) \
- var = *(php_uint16*)(buffer); \
- buffer += 2
- #endif
- #define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
- (((php_uint16)var[1]) & 0xff) << 8))
- #define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
- (((php_uint32)var[1]) & 0xff) << 8 | \
- (((php_uint32)var[2]) & 0xff) << 16 | \
- (((php_uint32)var[3]) & 0xff) << 24))
- /**
- * Open an already loaded phar
- */
- int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
- {
- phar_archive_data *phar;
- #ifdef PHP_WIN32
- char *unixfname;
- #endif
- if (error) {
- *error = NULL;
- }
- #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)
- ) {
- phar_entry_info *stub;
- efree(unixfname);
- #else
- if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error TSRMLS_CC)
- && ((alias && fname_len == phar->fname_len
- && !strncmp(fname, phar->fname, fname_len)) || !alias)
- ) {
- phar_entry_info *stub;
- #endif
- /* logic above is as follows:
- If an explicit alias was requested, ensure the filename passed in
- matches the phar's filename.
- If no alias was passed in, then it can match either and be valid
- */
- if (!is_data) {
- /* prevent any ".phar" without a stub getting through */
- if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
- if (PHAR_G(readonly) && FAILURE == zend_hash_find(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
- if (error) {
- spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
- }
- return FAILURE;
- }
- }
- }
- if (pphar) {
- *pphar = phar;
- }
- return SUCCESS;
- } else {
- #ifdef PHP_WIN32
- efree(unixfname);
- #endif
- if (pphar) {
- *pphar = NULL;
- }
- if (phar && error && !(options & REPORT_ERRORS)) {
- efree(error);
- }
- return FAILURE;
- }
- }
- /* }}}*/
- /**
- * Parse out metadata from the manifest for a single file
- *
- * Meta-data is in this format:
- * [len32][data...]
- *
- * data is the serialized zval
- */
- int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSRMLS_DC) /* {{{ */
- {
- const unsigned char *p;
- php_uint32 buf_len;
- php_unserialize_data_t var_hash;
- if (!zip_metadata_len) {
- PHAR_GET_32(*buffer, buf_len);
- } else {
- buf_len = zip_metadata_len;
- }
- if (buf_len) {
- ALLOC_ZVAL(*metadata);
- INIT_ZVAL(**metadata);
- p = (const unsigned char*) *buffer;
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if (!php_var_unserialize(metadata, &p, p + buf_len, &var_hash TSRMLS_CC)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zval_ptr_dtor(metadata);
- *metadata = NULL;
- return FAILURE;
- }
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- if (PHAR_G(persist)) {
- /* lazy init metadata */
- zval_ptr_dtor(metadata);
- *metadata = (zval *) pemalloc(buf_len, 1);
- memcpy(*metadata, *buffer, buf_len);
- *buffer += buf_len;
- return SUCCESS;
- }
- } else {
- *metadata = NULL;
- }
- if (!zip_metadata_len) {
- *buffer += buf_len;
- }
- return SUCCESS;
- }
- /* }}}*/
- /**
- * 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
- * FAILURE, and setting pphar to the pointer to the manifest entry
- *
- * This is used by phar_open_from_filename to process the manifest, but can be called
- * directly.
- */
- static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */
- {
- char b32[4], *buffer, *endbuffer, *savebuf;
- phar_archive_data *mydata = NULL;
- phar_entry_info entry;
- php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
- php_uint16 manifest_ver;
- long offset;
- int register_alias, sig_len, temp_alias = 0;
- char *signature = NULL;
- if (pphar) {
- *pphar = NULL;
- }
- if (error) {
- *error = NULL;
- }
- /* check for ?>\n and increment accordingly */
- if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
- MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
- }
- 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') {
- MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
- }
- ++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\"")
- }
- /* 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]);
- buffer += 2;
- if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" is API version %1.u.%1.u.%1.u, and cannot be processed", fname, manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0x0F);
- }
- return FAILURE;
- }
- 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;
- /* The lowest nibble contains the phar wide flags. The compression flags can */
- /* be ignored on reading because it is being generated anyways. */
- if (manifest_flags & PHAR_HDR_SIGNATURE) {
- char sig_buf[8], *sig_ptr = sig_buf;
- off_t read_len;
- size_t end_of_phar;
- if (-1 == php_stream_seek(fp, -8, SEEK_END)
- || (read_len = php_stream_tell(fp)) < 20
- || 8 != php_stream_read(fp, sig_buf, 8)
- || memcmp(sig_buf+4, "GBMB", 4)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- 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);
- }
- return FAILURE;
- }
- 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;
- }
- 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;
- #if PHAR_HASH_OK
- 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);
- }
- 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);
- }
- return FAILURE;
- }
- break;
- }
- case PHAR_SIG_SHA256: {
- unsigned char digest[32];
- php_stream_seek(fp, -(8 + 32), SEEK_END);
- read_len = php_stream_tell(fp);
- if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
- if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error TSRMLS_CC)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- char *save = *error;
- spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
- efree(save);
- }
- return FAILURE;
- }
- break;
- }
- #else
- case PHAR_SIG_SHA512:
- case PHAR_SIG_SHA256:
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname);
- }
- return FAILURE;
- #endif
- case PHAR_SIG_SHA1: {
- unsigned char digest[20];
- 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);
- }
- return FAILURE;
- }
- if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error TSRMLS_CC)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- char *save = *error;
- spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
- efree(save);
- }
- return FAILURE;
- }
- break;
- }
- case PHAR_SIG_MD5: {
- unsigned char digest[16];
- php_stream_seek(fp, -(8 + 16), SEEK_END);
- read_len = php_stream_tell(fp);
- if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
- }
- return FAILURE;
- }
- if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error TSRMLS_CC)) {
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- char *save = *error;
- spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
- efree(save);
- }
- return FAILURE;
- }
- break;
- }
- default:
- efree(savebuf);
- php_stream_close(fp);
- if (error) {
- spprintf(error, 0, "phar \"%s\" has a broken 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);
- }
- return FAILURE;
- } else {
- sig_flags = 0;
- sig_len = 0;
- }
- /* 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) */
- if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len)))
- {
- 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;
- register_alias = 1;
- } else if (!alias_len || !alias) {
- /* if we neither have an explicit nor an implicit alias, we use the filename */
- alias = NULL;
- alias_len = 0;
- register_alias = 0;
- } else if (alias_len) {
- register_alias = 1;
- temp_alias = 1;
- }
- /* we have 5 32-bit items plus 1 byte at least */
- if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
- /* prevent serious memory issues */
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
- }
- mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
- mydata->is_persistent = PHAR_G(persist);
- /* check whether we have meta data, zero check works regardless of byte order */
- if (mydata->is_persistent) {
- PHAR_GET_32(buffer, mydata->metadata_len);
- if (phar_parse_metadata(&buffer, &mydata->metadata, mydata->metadata_len TSRMLS_CC) == FAILURE) {
- MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
- }
- } else {
- if (phar_parse_metadata(&buffer, &mydata->metadata, 0 TSRMLS_CC) == FAILURE) {
- MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
- }
- }
- /* set up our manifest */
- zend_hash_init(&mydata->manifest, manifest_count,
- zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
- zend_hash_init(&mydata->mounted_dirs, 5,
- zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
- zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
- zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
- mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
- #ifdef PHP_WIN32
- phar_unixify_path_separators(mydata->fname, fname_len);
- #endif
- mydata->fname_len = fname_len;
- offset = halt_offset + manifest_len + 4;
- memset(&entry, 0, sizeof(phar_entry_info));
- entry.phar = mydata;
- entry.fp_type = PHAR_FP;
- entry.is_persistent = mydata->is_persistent;
- for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) {
- if (buffer + 4 > endbuffer) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
- }
- PHAR_GET_32(buffer, entry.filename_len);
- if (entry.filename_len == 0) {
- MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
- }
- if (entry.is_persistent) {
- entry.manifest_pos = manifest_index;
- }
- if (buffer + entry.filename_len + 20 > endbuffer) {
- MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
- }
- if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
- entry.is_dir = 1;
- } else {
- entry.is_dir = 0;
- }
- 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;
- } else {
- if (mydata->min_timestamp > entry.timestamp) {
- mydata->min_timestamp = entry.timestamp;
- } else if (mydata->max_timestamp < entry.timestamp) {
- 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) {
- PHAR_GET_32(buffer, entry.metadata_len);
- if (!entry.metadata_len) buffer -= 4;
- if (phar_parse_metadata(&buffer, &entry.metadata, entry.metadata_len TSRMLS_CC) == FAILURE) {
- pefree(entry.filename, entry.is_persistent);
- MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
- }
- } else {
- if (phar_parse_metadata(&buffer, &entry.metadata, 0 TSRMLS_CC) == FAILURE) {
- pefree(entry.filename, entry.is_persistent);
- MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
- }
- }
- entry.offset = entry.offset_abs = offset;
- offset += entry.compressed_filesize;
- switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
- case PHAR_ENT_COMPRESSED_GZ:
- if (!PHAR_G(has_zlib)) {
- if (entry.metadata) {
- 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\"");
- }
- 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\"");
- }
- 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)");
- }
- break;
- }
- manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
- /* if signature matched, no need to check CRC32 for each file */
- entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
- phar_set_inode(&entry TSRMLS_CC);
- zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL);
- }
- snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
- mydata->internal_file_start = halt_offset + manifest_len + 4;
- 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) {
- mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
- }
- if (mydata->ext) {
- 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);
- mydata->alias_len = alias ? alias_len : fname_len;
- mydata->sig_flags = sig_flags;
- mydata->fp = fp;
- mydata->sig_len = sig_len;
- mydata->signature = signature;
- phar_request_initialize(TSRMLS_C);
- if (register_alias) {
- phar_archive_data **fd_ptr;
- mydata->is_temporary_alias = temp_alias;
- if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
- signature = NULL;
- fp = NULL;
- MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
- }
- if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
- if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
- signature = NULL;
- fp = NULL;
- MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
- }
- }
- zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- } else {
- mydata->is_temporary_alias = 1;
- }
- zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
- efree(savebuf);
- if (pphar) {
- *pphar = mydata;
- }
- return SUCCESS;
- }
- /* }}} */
- /**
- * Create or open a phar for writing
- */
- int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
- {
- const char *ext_str, *z;
- char *my_error;
- int ext_len;
- phar_archive_data **test, *unused = NULL;
- test = &unused;
- if (error) {
- *error = NULL;
- }
- /* first try to open an existing file */
- if (phar_detect_phar_fname_ext(fname, 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) {
- if (ext_len == -2) {
- spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
- } else {
- spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
- }
- }
- return FAILURE;
- }
- check_file:
- if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) {
- if (pphar) {
- *pphar = *test;
- }
- if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
- if (error) {
- spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
- }
- return FAILURE;
- }
- if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
- phar_entry_info *stub;
- if (FAILURE == zend_hash_find(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) {
- spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
- return FAILURE;
- }
- }
- if (!PHAR_G(readonly) || (*test)->is_data) {
- (*test)->is_writeable = 1;
- }
- return SUCCESS;
- } else if (my_error) {
- if (error) {
- *error = my_error;
- } else {
- efree(my_error);
- }
- return FAILURE;
- }
- if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
- /* assume zip-based phar */
- return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
- }
- if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
- /* assume tar-based phar */
- return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
- }
- return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC);
- }
- /* }}} */
- int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
- {
- phar_archive_data *mydata;
- php_stream *fp;
- char *actual = NULL, *p;
- if (!pphar) {
- pphar = &mydata;
- }
- 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);
- }
- if (fp) {
- if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC) == SUCCESS) {
- if ((*pphar)->is_data || !PHAR_G(readonly)) {
- (*pphar)->is_writeable = 1;
- }
- if (actual) {
- efree(actual);
- }
- return SUCCESS;
- } else {
- /* file exists, but is either corrupt or not a phar archive */
- if (actual) {
- efree(actual);
- }
- return FAILURE;
- }
- }
- if (actual) {
- efree(actual);
- }
- if (PHAR_G(readonly) && !is_data) {
- if (options & REPORT_ERRORS) {
- if (error) {
- spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
- }
- }
- return FAILURE;
- }
- /* 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) {
- mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
- }
- if (mydata->ext) {
- mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
- }
- }
- if (pphar) {
- *pphar = mydata;
- }
- zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
- zend_get_hash_value, destroy_phar_manifest_entry, 0);
- zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
- zend_get_hash_value, NULL, 0);
- zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
- zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
- mydata->fname_len = fname_len;
- snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
- mydata->is_temporary_alias = alias ? 0 : 1;
- mydata->internal_file_start = -1;
- mydata->fp = NULL;
- mydata->is_writeable = 1;
- 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;
- mydata->is_data = 1;
- /* assume tar format, PharData can specify other */
- mydata->is_tar = 1;
- } else {
- phar_archive_data **fd_ptr;
- if (alias && 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)) {
- 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) {
- if (error) {
- 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;
- }
- /* }}}*/
- /**
- * Return an already opened filename.
- *
- * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
- * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS
- * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
- */
- int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
- {
- php_stream *fp;
- char *actual;
- int ret, is_data = 0;
- if (error) {
- *error = NULL;
- }
- if (!strstr(fname, ".phar")) {
- is_data = 1;
- }
- if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC) == SUCCESS) {
- return SUCCESS;
- } else if (error && *error) {
- return FAILURE;
- }
- 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) {
- spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
- }
- }
- if (actual) {
- efree(actual);
- }
- return FAILURE;
- }
- if (actual) {
- fname = actual;
- fname_len = strlen(actual);
- }
- ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error TSRMLS_CC);
- if (actual) {
- efree(actual);
- }
- return ret;
- }
- /* }}}*/
- static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
- {
- const char *c;
- int so_far = 0;
- if (buf_len < search_len) {
- return NULL;
- }
- c = buf - 1;
- do {
- if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
- return (char *) NULL;
- }
- so_far = c - buf;
- if (so_far >= (buf_len - search_len)) {
- return (char *) NULL;
- }
- if (!memcmp(c, search, search_len)) {
- return (char *) c;
- }
- } while (1);
- }
- /* }}} */
- /**
- * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
- * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS
- * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
- */
- static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, int is_data, char **error TSRMLS_DC) /* {{{ */
- {
- const char token[] = "__HALT_COMPILER();";
- const char zip_magic[] = "PK\x03\x04";
- const char gz_magic[] = "\x1f\x8b\x08";
- const char bz_magic[] = "BZh";
- char *pos, buffer[1024 + sizeof(token)], test = '\0';
- const long readsize = sizeof(buffer) - sizeof(token);
- const long tokenlen = sizeof(token) - 1;
- long halt_offset;
- size_t got;
- php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
- if (error) {
- *error = NULL;
- }
- if (-1 == php_stream_rewind(fp)) {
- MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
- }
- buffer[sizeof(buffer)-1] = '\0';
- memset(buffer, 32, sizeof(token));
- halt_offset = 0;
- /* Maybe it's better to compile the file instead of just searching, */
- /* but we only want the offset. So we want a .re scanner to find it. */
- while(!php_stream_eof(fp)) {
- 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;
- if (!memcmp(pos, gz_magic, 3)) {
- char err = 0;
- php_stream_filter *filter;
- php_stream *temp;
- /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
- zval filterparams;
- if (!PHAR_G(has_zlib)) {
- 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")
- }
- } else {
- zval_dtor(&filterparams);
- }
- php_stream_filter_append(&temp->writefilters, filter);
- if (SUCCESS != phar_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
- if (err) {
- 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")
- }
- 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);
- fp = temp;
- php_stream_rewind(fp);
- compression = PHAR_FILE_COMPRESSED_GZ;
- /* now, start over */
- test = '\0';
- continue;
- } else if (!memcmp(pos, bz_magic, 3)) {
- php_stream_filter *filter;
- php_stream *temp;
- 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 (SUCCESS != phar_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
- 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);
- fp = temp;
- php_stream_rewind(fp);
- compression = PHAR_FILE_COMPRESSED_BZ2;
- /* now, start over */
- 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);
- return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error TSRMLS_CC);
- }
- }
- }
- 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, comp…
Large files files are truncated, but you can click here to view the full file