From 4bce48417b56b4e18a7fd7d1d93af1a5ca5a91db Mon Sep 17 00:00:00 2001 From: Greg Beaver Date: Tue, 8 Jan 2008 20:31:54 +0000 Subject: [PATCH] split stream file handlers into stream.c and directory handlers into dirstream.c --- ext/phar/config.m4 | 2 +- ext/phar/config.w32 | 2 +- ext/phar/dirstream.c | 393 +++++++++++ ext/phar/dirstream.h | 45 ++ ext/phar/phar.c | 1225 +--------------------------------- ext/phar/phar_internal.h | 26 +- ext/phar/php_stream_unlink.h | 23 + ext/phar/stream.c | 855 ++++++++++++++++++++++++ ext/phar/stream.h | 50 ++ ext/phar/zip.c | 22 +- 10 files changed, 1374 insertions(+), 1269 deletions(-) create mode 100644 ext/phar/dirstream.c create mode 100644 ext/phar/dirstream.h create mode 100644 ext/phar/php_stream_unlink.h create mode 100644 ext/phar/stream.c create mode 100644 ext/phar/stream.h diff --git a/ext/phar/config.m4 b/ext/phar/config.m4 index 7e35fc6180..742923994f 100644 --- a/ext/phar/config.m4 +++ b/ext/phar/config.m4 @@ -32,7 +32,7 @@ if test "$PHP_PHAR" != "no"; then else AC_MSG_RESULT([no]) fi - PHP_NEW_EXTENSION(phar, tar.c zip.c phar.c phar_object.c phar_path_check.c $PHP_PHAR_SOURCES, $ext_shared) + PHP_NEW_EXTENSION(phar, tar.c zip.c stream.c dirstream.c phar.c phar_object.c phar_path_check.c $PHP_PHAR_SOURCES, $ext_shared) PHP_ADD_BUILD_DIR($ext_builddir/lib, 1) PHP_SUBST(PHAR_SHARED_LIBADD) PHP_ADD_EXTENSION_DEP(phar, zlib, true) diff --git a/ext/phar/config.w32 b/ext/phar/config.w32 index f91cb76b9a..ac7c546056 100644 --- a/ext/phar/config.w32 +++ b/ext/phar/config.w32 @@ -4,7 +4,7 @@ ARG_ENABLE("phar", "enable phar support", "no"); if (PHP_PHAR != "no") { - EXTENSION("phar", "tar.c zip.c phar.c phar_object.c phar_path_check.c"); + EXTENSION("phar", "tar.c zip.c stream.c dirstream.c phar.c phar_object.c phar_path_check.c"); ADD_SOURCES(configure_module_dirname + "/lib", "zip_add.c zip_error.c zip_fclose.c \ zip_fread.c zip_open.c zip_source_filep.c \ zip_strerror.c zip_close.c zip_error_get.c \ diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c new file mode 100644 index 0000000000..63d960c337 --- /dev/null +++ b/ext/phar/dirstream.c @@ -0,0 +1,393 @@ +/* + +----------------------------------------------------------------------+ + | phar:// stream wrapper support | + +----------------------------------------------------------------------+ + | 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 | + | 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 | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +#define PHAR_DIRSTREAM 1 +#include "phar_internal.h" +#include "dirstream.h" + +BEGIN_EXTERN_C() +void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, + zend_bool is_dir, char *alias, int alias_len TSRMLS_DC); +END_EXTERN_C() + +php_stream_ops phar_dir_ops = { + phar_dir_write, /* write */ + phar_dir_read, /* read */ + phar_dir_close, /* close */ + phar_dir_flush, /* flush */ + "phar dir", + phar_dir_seek, /* seek */ + NULL, /* cast */ + phar_dir_stat, /* stat */ + NULL, /* set option */ +}; + +/** + * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle + */ +static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ +{ + HashTable *data = (HashTable *)stream->abstract; + + if (data && data->arBuckets) + { + zend_hash_destroy(data); + data->arBuckets = 0; + FREE_HASHTABLE(data); + stream->abstract = NULL; + } + return 0; +} +/* }}} */ + +/** + * Used for seeking on a phar directory handle + */ +static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */ +{ + HashTable *data = (HashTable *)stream->abstract; + + if (data) + { + if (whence == SEEK_END) { + whence = SEEK_SET; + offset = zend_hash_num_elements(data) + offset; + } + if (whence == SEEK_SET) { + zend_hash_internal_pointer_reset(data); + } + + if (offset < 0) { + php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: cannot seek because the resulting seek is negative"); + return -1; + } else { + *newoffset = 0; + while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) { + (*newoffset)++; + } + return 0; + } + } + return -1; +} +/* }}} */ + +/** + * Used for readdir() on an opendir()ed phar directory handle + */ +static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ +{ + size_t to_read; + HashTable *data = (HashTable *)stream->abstract; + char *key; + uint keylen; + ulong unused; + + 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; + } + 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, key, to_read); + ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; + + return sizeof(php_stream_dirent); +} +/* }}} */ + +/** + * Dummy: Used for writing to a phar directory (i.e. not used) + */ +static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */ +{ + return 0; +} +/* }}} */ + +/** + * Dummy: Used for flushing writes to a phar directory (i.e. not used) + */ +static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */ +{ + return EOF; +} +/* }}} */ + +/** + * Stat a dir in a phar + */ +static int phar_dir_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ +{ + phar_entry_data *data = (phar_entry_data *)stream->abstract; + + /* If ssb is NULL then someone is misbehaving */ + if (!ssb) { + return -1; + } + + phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC); + return 0; +} +/* }}} */ + +/** + * add an empty element with a char * key to a hash table, avoiding duplicates + * + * This is used to get a unique listing of virtual directories within a phar, + * for iterating over opendir()ed phar directories. + */ +static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength) /* {{{ */ +{ + void *dummy = (void *) 1; + + return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL); +} +/* }}} */ + +/** + * Used for sorting directories alphabetically + */ +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 + result = zend_binary_strcmp(f->key.arKey.s, f->nKeyLength, s->key.arKey.s, s->nKeyLength); +#endif + + if (result < 0) { + return -1; + } else if (result > 0) { + return 1; + } else { + return 0; + } +} +/* }}} */ + +/** + * Create a opendir() directory stream handle by iterating over each of the + * files in a phar and retrieving its relative path. From this, construct + * a list of files/directories that are "in" the directory represented by dir + */ +static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */ +{ + HashTable *data; + int dirlen = strlen(dir); + char *save, *found, *key; + uint keylen; + ulong unused; + char *entry; + ALLOC_HASHTABLE(data); + zend_hash_init(data, 64, zend_get_hash_value, NULL, 0); + + if (*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) { + /* make empty root directory for empty phar */ + 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; + } + if (*dir == '/') { + /* root directory */ + if (NULL != (found = (char *) memchr(key, '/', keylen))) { + /* the entry has a path separator and is a subdirectory */ + entry = (char *) safe_emalloc(found - key, 1, 1); + memcpy(entry, key, found - key); + keylen = found - key; + entry[keylen] = '\0'; + } else { + entry = (char *) safe_emalloc(keylen, 1, 1); + memcpy(entry, key, keylen); + entry[keylen] = '\0'; + } + goto PHAR_ADD_ENTRY; + } else { + if (0 != memcmp(key, dir, dirlen)) { + /* entry in directory not found */ + if (SUCCESS != zend_hash_move_forward(manifest)) { + break; + } + continue; + } else { + if (key[dirlen] != '/') { + if (SUCCESS != zend_hash_move_forward(manifest)) { + break; + } + continue; + } + } + } + save = 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; + entry = (char *) safe_emalloc(found - save + dirlen, 1, 1); + memcpy(entry, save + dirlen + 1, found - save - dirlen - 1); + keylen = found - save - dirlen - 1; + entry[keylen] = '\0'; + } else { + /* is file */ + save -= dirlen + 1; + entry = (char *) safe_emalloc(keylen - dirlen, 1, 1); + memcpy(entry, save + dirlen + 1, keylen - dirlen - 1); + entry[keylen - dirlen - 1] = '\0'; + keylen = keylen - dirlen - 1; + } +PHAR_ADD_ENTRY: + 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) { + FREE_HASHTABLE(data); + return NULL; + } + return php_stream_alloc(&phar_dir_ops, data, NULL, "r"); + } else { + efree(dir); + FREE_HASHTABLE(data); + return NULL; + } +} +/* }}}*/ + +/** + * 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_url *resource = NULL; + php_stream *ret; + char *internal_file, *key, *error, *plain_map; + uint keylen; + ulong unused; + phar_archive_data *phar; + phar_entry_info *entry; + uint host_len; + + if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) { + return NULL; + } + + /* we must have at the very least phar://alias.phar/ */ + if (!resource->scheme || !resource->host || !resource->path) { + if (resource->host && !resource->path) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, resource->host); + php_url_free(resource); + return NULL; + } + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); + return NULL; + } + + if (strcasecmp("phar", resource->scheme)) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path); + return NULL; + } + + host_len = strlen(resource->host); + phar_request_initialize(TSRMLS_C); + if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { + spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); + ret = php_stream_opendir(internal_file, options, context); + if (!ret) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); + } + php_url_free(resource); + efree(internal_file); + return ret; + } + + 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); + efree(error); + } + php_url_free(resource); + return NULL; + } + if (error) { + efree(error); + } + if (*internal_file == '\0') { + /* root directory requested */ + internal_file = estrndup(internal_file - 1, 1); + ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC); + 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)) { + php_url_free(resource); + return NULL; + } else { + /* search for directory */ + zend_hash_internal_pointer_reset(&phar->manifest); + while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) { + if (HASH_KEY_NON_EXISTANT != + zend_hash_get_current_key_ex( + &phar->manifest, &key, &keylen, &unused, 0, NULL)) { + if (0 == memcmp(key, internal_file, strlen(internal_file))) { + /* directory found */ + internal_file = estrndup(internal_file, + strlen(internal_file)); + php_url_free(resource); + return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC); + } + } + if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { + break; + } + } + } + + php_url_free(resource); + return NULL; +} +/* }}} */ diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h new file mode 100644 index 0000000000..d06a45a8c6 --- /dev/null +++ b/ext/phar/dirstream.h @@ -0,0 +1,45 @@ +/* + +----------------------------------------------------------------------+ + | phar php single-file executable PHP extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 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 | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +BEGIN_EXTERN_C() +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); + +#ifdef PHAR_DIRSTREAM +php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC); + +/* directory handlers */ +static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC); +static size_t phar_dir_read( php_stream *stream, char *buf, size_t count TSRMLS_DC); +static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC); +static int phar_dir_flush(php_stream *stream TSRMLS_DC); +static int phar_dir_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); +static int phar_dir_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); +#endif +END_EXTERN_C() + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 8f0310c0a9..ef2c7d8c7e 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.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 | @@ -19,33 +19,13 @@ /* $Id$ */ -#define PHAR_MAIN +#define PHAR_MAIN 1 #include "phar_internal.h" #include "SAPI.h" +#include "php_stream_unlink.h" ZEND_DECLARE_MODULE_GLOBALS(phar) -#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000) - -static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC) -{ - php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC); - - if (!wrapper || !wrapper->wops) { - return 0; - } - - if (!wrapper->wops->unlink) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper"); - return 0; - } - return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC); -} - -#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC) - -#endif - /* 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) /* {{{ */ @@ -1899,91 +1879,6 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le return SUCCESS; } /* }}} */ - -/** - * Open a phar file for streams API - */ -static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */ -{ - php_url *resource; - char *arch = NULL, *entry = NULL, *error; - int arch_len, entry_len; - - if (!strncasecmp(filename, "phar://", 7)) { - if (mode[0] == 'a') { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported"); - return NULL; - } - if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) { - if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - if (arch && !entry) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); - arch = NULL; - } else { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz/.phar.bz2)", filename); - } - } - if (arch) { - efree(arch); - } - if (entry) { - efree(entry); - } - return NULL; - } - resource = ecalloc(1, sizeof(php_url)); - resource->scheme = estrndup("phar", 4); - resource->host = arch; - - resource->path = entry; -#if MBO_0 - if (resource) { - fprintf(stderr, "Alias: %s\n", alias); - fprintf(stderr, "Scheme: %s\n", resource->scheme); -/* fprintf(stderr, "User: %s\n", resource->user);*/ -/* fprintf(stderr, "Pass: %s\n", resource->pass ? "***" : NULL);*/ - fprintf(stderr, "Host: %s\n", resource->host); -/* fprintf(stderr, "Port: %d\n", resource->port);*/ - fprintf(stderr, "Path: %s\n", resource->path); -/* fprintf(stderr, "Query: %s\n", resource->query);*/ -/* fprintf(stderr, "Fragment: %s\n", resource->fragment);*/ - } -#endif - if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_plain_map.arBuckets && zend_hash_exists(&(PHAR_GLOBALS->phar_plain_map), arch, arch_len+1)) { - return resource; - } - if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { - if (PHAR_G(readonly)) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting"); - php_url_free(resource); - return NULL; - } - if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE) - { - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - } - php_url_free(resource); - return NULL; - } - } else { - if (phar_open_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE) - { - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - } - php_url_free(resource); - return NULL; - } - } - return resource; - } - - return NULL; -} -/* }}} */ /** * Invoked when a user calls Phar::mapPhar() from within an executing .phar @@ -2038,53 +1933,10 @@ int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC) } /* }}} */ -static php_stream_ops phar_ops = { - phar_stream_write, /* write */ - phar_stream_read, /* read */ - phar_stream_close, /* close */ - phar_stream_flush, /* flush */ - "phar stream", - phar_stream_seek, /* seek */ - NULL, /* cast */ - phar_stream_stat, /* stat */ - NULL, /* set option */ -}; - -static php_stream_ops phar_dir_ops = { - phar_dir_write, /* write */ - phar_dir_read, /* read */ - phar_dir_close, /* close */ - phar_dir_flush, /* flush */ - "phar dir", - phar_dir_seek, /* seek */ - NULL, /* cast */ - phar_dir_stat, /* stat */ - NULL, /* set option */ -}; - -static 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 */ - NULL, /* create directory */ - NULL, /* remove directory */ -}; - -php_stream_wrapper php_stream_phar_wrapper = { - &phar_stream_wops, - NULL, - 0 /* is_url */ -}; - /** * Validate the CRC32 of a file opened from within the phar */ -static int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */ +int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC) /* {{{ */ { php_uint32 crc = ~0; int len = idata->internal_file->uncompressed_filesize; @@ -2330,379 +2182,6 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, return entry; } -/** - * used for fopen('phar://...') and company - */ -static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *idata; - char *internal_file; - char *error; - char *plain_map; - HashTable *pharcontext; - php_url *resource = NULL; - php_stream *fp, *fpf; - zval **pzoption, *metadata; - uint host_len; - - if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) { - return NULL; - } - - /* we must have at the very least phar://alias.phar/internalfile.php */ - if (!resource->scheme || !resource->host || !resource->path) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path); - return NULL; - } - - if (strcasecmp("phar", resource->scheme)) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path); - return NULL; - } - - host_len = strlen(resource->host); - phar_request_initialize(TSRMLS_C); - if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { - spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); - fp = php_stream_open_wrapper_ex(internal_file, mode, options, opened_path, context); - if (!fp) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); - } - php_url_free(resource); - efree(internal_file); - return fp; - } - - /* strip leading "/" */ - internal_file = estrdup(resource->path + 1); - if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { - if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, &error TSRMLS_CC))) { - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - } else { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host); - } - efree(internal_file); - php_url_free(resource); - return NULL; - } - if (error) { - efree(error); - } - fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); - php_url_free(resource); - efree(internal_file); - if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) { - pharcontext = HASH_OF(*pzoption); - if (idata->internal_file->uncompressed_filesize == 0 - && idata->internal_file->compressed_filesize == 0 - && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS - && Z_TYPE_PP(pzoption) == IS_LONG - && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0 - ) { - idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK; - idata->internal_file->flags |= Z_LVAL_PP(pzoption); - } - if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) { - if (idata->internal_file->metadata) { - zval_ptr_dtor(&idata->internal_file->metadata); - idata->internal_file->metadata = NULL; - } - - MAKE_STD_ZVAL(idata->internal_file->metadata); - metadata = *pzoption; - ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0); - idata->phar->is_modified = 1; - } - } - return fpf; - } else { - if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) || !idata) { - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - } - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host); - efree(internal_file); - php_url_free(resource); - return NULL; - } - } - php_url_free(resource); - -#if MBO_0 - fprintf(stderr, "Pharname: %s\n", idata->phar->filename); - fprintf(stderr, "Filename: %s\n", internal_file); - fprintf(stderr, "Entry: %s\n", idata->internal_file->filename); - fprintf(stderr, "Size: %u\n", idata->internal_file->uncompressed_filesize); - fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags); - fprintf(stderr, "Offset: %u\n", idata->internal_file->offset_within_phar); - fprintf(stderr, "Cached: %s\n", idata->internal_file->filedata ? "yes" : "no"); -#endif - - /* do we have the data already? */ - if (idata->fp) { - fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); - efree(internal_file); - return fpf; - } - -#if PHP_MAJOR_VERSION < 6 - if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { - phar_entry_delref(idata TSRMLS_CC); - efree(internal_file); - return NULL; - } -#endif - - if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) { - phar_entry_delref(idata TSRMLS_CC); - efree(internal_file); - return NULL; - } - - fp = idata->phar->fp; - - if (!idata->phar->is_zip && !fp) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot open phar \"%s\"", idata->phar->fname); - phar_entry_delref(idata TSRMLS_CC); - efree(internal_file); - return NULL; - } - - if (!phar_open_jit(idata->phar, idata->internal_file, fp, &error, idata->for_write TSRMLS_CC)) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - phar_entry_delref(idata TSRMLS_CC); - efree(internal_file); - return NULL; - } - idata->fp = idata->internal_file->fp; - if (idata->fp == idata->phar->fp) { - idata->zero = idata->internal_file->offset_within_phar + idata->phar->internal_file_start; - } - - /* check length, crc32 */ - if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) { - /* already issued the error */ - phar_entry_delref(idata TSRMLS_CC); - efree(internal_file); - return NULL; - } - idata->internal_file->is_crc_checked = 1; - - fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); - efree(internal_file); - return fpf; -} -/* }}} */ - -/** - * Used for fclose($fp) where $fp is a phar archive - */ -static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ -{ - phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC); - - return 0; -} -/* }}} */ - -/** - * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle - */ -static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ -{ - HashTable *data = (HashTable *)stream->abstract; - - if (data && data->arBuckets) - { - zend_hash_destroy(data); - data->arBuckets = 0; - FREE_HASHTABLE(data); - stream->abstract = NULL; - } - return 0; -} -/* }}} */ - -/** - * Used for seeking on a phar directory handle - */ -static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */ -{ - HashTable *data = (HashTable *)stream->abstract; - - if (data) - { - if (whence == SEEK_END) { - whence = SEEK_SET; - offset = zend_hash_num_elements(data) + offset; - } - if (whence == SEEK_SET) { - zend_hash_internal_pointer_reset(data); - } - - if (offset < 0) { - php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: cannot seek because the resulting seek is negative"); - return -1; - } else { - *newoffset = 0; - while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) { - (*newoffset)++; - } - return 0; - } - } - return -1; -} -/* }}} */ - -/** - * used for fread($fp) and company on a fopen()ed phar file handle - */ -static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *)stream->abstract; - size_t got; - - if (data->internal_file->is_deleted) { - stream->eof = 1; - return 0; - } - - /* use our proxy position */ - php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); - - if (!data->zero) { - got = php_stream_read(data->fp, buf, count); - if (data->fp->eof) { - stream->eof = 1; - } - /* note the position, and restore the stream for the next fp */ - data->position = php_stream_tell(data->fp); - } else { - got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position)); - data->position = php_stream_tell(data->fp) - data->zero; - stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize); - } - - - return got; -} -/* }}} */ - -/** - * Used for readdir() on an opendir()ed phar directory handle - */ -static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ -{ - size_t to_read; - HashTable *data = (HashTable *)stream->abstract; - char *key; - uint keylen; - ulong unused; - - 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; - } - 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, key, to_read); - ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; - - return sizeof(php_stream_dirent); -} -/* }}} */ - -/** - * Used for fseek($fp) on a phar file handle - */ -static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *)stream->abstract; - - int res; - if (data->zero) { - off_t temp; - switch (whence) { - case SEEK_END : - temp = data->zero + data->internal_file->uncompressed_filesize + offset; - break; - case SEEK_CUR : - temp = data->zero + data->position + offset; - break; - case SEEK_SET : - temp = data->zero + offset; - break; - } - if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) { - *newoffset = -1; - return -1; - } - if (temp < data->zero) { - *newoffset = -1; - return -1; - } - res = php_stream_seek(data->fp, temp, SEEK_SET); - *newoffset = php_stream_tell(data->fp) - data->zero; - data->position = *newoffset; - return res; - } - if (whence != SEEK_SET) { - /* use our proxy position, so the relative stuff works */ - php_stream_seek(data->fp, data->position, SEEK_SET); - } - /* now do the actual seek */ - res = php_stream_seek(data->fp, offset, whence); - *newoffset = php_stream_tell(data->fp); - data->position = *newoffset; - return res; -} -/* }}} */ - -/** - * Used for writing to a phar file - */ -static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *) stream->abstract; - - php_stream_seek(data->fp, data->position, SEEK_SET); - if (count != php_stream_write(data->fp, buf, count)) { - php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname); - return -1; - } - data->position = php_stream_tell(data->fp); - if (data->position > (off_t)data->internal_file->uncompressed_filesize) { - data->internal_file->uncompressed_filesize = data->position; - } - data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize; - data->internal_file->old_flags = data->internal_file->flags; - data->internal_file->is_modified = 1; - return count; -} -/* }}} */ - -/** - * Dummy: Used for writing to a phar directory (i.e. not used) - */ -static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */ -{ - return 0; -} -/* }}} */ - static inline void phar_set_32(char *buffer, int var) /* {{{ */ { #ifdef WORDS_BIGENDIAN @@ -3393,702 +2872,6 @@ int phar_flush(phar_archive_data *archive, char *user_stub, long len, char **err } /* }}} */ -/** - * Used to save work done on a writeable phar - */ -static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */ -{ - char *error; - int ret; - if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) { - ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, &error TSRMLS_CC); - if (error) { - php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, error); - efree(error); - } - return ret; - } else { - return EOF; - } -} -/* }}} */ - -/** - * Dummy: Used for flushing writes to a phar directory (i.e. not used) - */ -static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */ -{ - return EOF; -} -/* }}} */ - - /* {{{ phar_dostat */ -/** - * stat an opened phar file handle stream, used by phar_stat() - */ -static void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, - zend_bool is_dir, char *alias, int alias_len TSRMLS_DC) -{ - char *tmp; - int tmp_len; - memset(ssb, 0, sizeof(php_stream_statbuf)); - - if (!is_dir && !data->is_dir) { - ssb->sb.st_size = data->uncompressed_filesize; - ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; - ssb->sb.st_mode |= S_IFREG; /* regular file */ - /* timestamp is just the timestamp when this was added to the phar */ -#ifdef NETWARE - ssb->sb.st_mtime.tv_sec = data->timestamp; - ssb->sb.st_atime.tv_sec = data->timestamp; - ssb->sb.st_ctime.tv_sec = data->timestamp; -#else - ssb->sb.st_mtime = data->timestamp; - ssb->sb.st_atime = data->timestamp; - ssb->sb.st_ctime = data->timestamp; -#endif - } else if (!is_dir && data->is_dir && (data->is_tar || data->is_zip)) { - ssb->sb.st_size = 0; - ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; - ssb->sb.st_mode |= S_IFDIR; /* regular directory */ - /* timestamp is just the timestamp when this was added to the phar */ -#ifdef NETWARE - ssb->sb.st_mtime.tv_sec = data->timestamp; - ssb->sb.st_atime.tv_sec = data->timestamp; - ssb->sb.st_ctime.tv_sec = data->timestamp; -#else - ssb->sb.st_mtime = data->timestamp; - ssb->sb.st_atime = data->timestamp; - ssb->sb.st_ctime = data->timestamp; -#endif - } else { - ssb->sb.st_size = 0; - ssb->sb.st_mode = 0777; - ssb->sb.st_mode |= S_IFDIR; /* regular directory */ -#ifdef NETWARE - ssb->sb.st_mtime.tv_sec = phar->max_timestamp; - ssb->sb.st_atime.tv_sec = phar->max_timestamp; - ssb->sb.st_ctime.tv_sec = phar->max_timestamp; -#else - ssb->sb.st_mtime = phar->max_timestamp; - ssb->sb.st_atime = phar->max_timestamp; - ssb->sb.st_ctime = phar->max_timestamp; -#endif - } - if (!phar->is_writeable) { - ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777); - } - - ssb->sb.st_nlink = 1; - ssb->sb.st_rdev = -1; - if (data) { - tmp_len = data->filename_len + alias_len; - } else { - tmp_len = alias_len + 1; - } - tmp = (char *) emalloc(tmp_len); - memcpy(tmp, alias, alias_len); - if (data) { - memcpy(tmp + alias_len, data->filename, data->filename_len); - } else { - *(tmp+alias_len) = '/'; - } - /* this is only for APC, so use /dev/null device - no chance of conflict there! */ - ssb->sb.st_dev = 0xc; - /* generate unique inode number for alias/filename, so no phars will conflict */ - ssb->sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len); - efree(tmp); -#ifndef PHP_WIN32 - ssb->sb.st_blksize = -1; - ssb->sb.st_blocks = -1; -#endif -} -/* }}}*/ - -/** - * Stat an opened phar file handle - */ -static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *)stream->abstract; - - /* If ssb is NULL then someone is misbehaving */ - if (!ssb) { - return -1; - } - - phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC); - return 0; -} -/* }}} */ - -/** - * Stat a dir in a phar - */ -static int phar_dir_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ -{ - phar_entry_data *data = (phar_entry_data *)stream->abstract; - - /* If ssb is NULL then someone is misbehaving */ - if (!ssb) { - return -1; - } - - phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC); - return 0; -} -/* }}} */ - -/** - * Stream wrapper stat implementation of stat() - */ -static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, - php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */ -{ - php_url *resource = NULL; - char *internal_file, *key, *error, *plain_map; - uint keylen; - ulong unused; - phar_archive_data *phar; - phar_entry_info *entry; - uint host_len; - int retval; - - if ((resource = phar_open_url(wrapper, url, "r", flags TSRMLS_CC)) == NULL) { - return -1; - } - - /* we must have at the very least phar://alias.phar/internalfile.php */ - if (!resource->scheme || !resource->host || !resource->path) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: invalid url \"%s\"", url); - return -1; - } - - if (strcasecmp("phar", resource->scheme)) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: not a phar url \"%s\"", url); - return -1; - } - - host_len = strlen(resource->host); - phar_request_initialize(TSRMLS_C); - if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { - spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); - retval = php_stream_stat_path_ex(internal_file, flags, ssb, context); - if (retval == -1) { - php_stream_wrapper_log_error(wrapper, 0/* TODO:options */ TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); - } - php_url_free(resource); - efree(internal_file); - return retval; - } - - internal_file = resource->path + 1; /* strip leading "/" */ - /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */ - if (FAILURE == phar_get_archive(&phar, resource->host, strlen(resource->host), NULL, 0, &error TSRMLS_CC)) { - php_url_free(resource); - if (error) { - php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, error); - efree(error); - } - return 0; - } - if (error) { - efree(error); - } - if (*internal_file == '\0') { - /* root directory requested */ - phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); - php_url_free(resource); - return 0; - } - if (!phar->manifest.arBuckets) { - php_url_free(resource); - return 0; - } - /* search through the manifest of files, and if we have an exact match, it's a file */ - if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry)) { - phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC); - } else { - /* search for directory (partial match of a file) */ - zend_hash_internal_pointer_reset(&phar->manifest); - while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) { - if (HASH_KEY_NON_EXISTANT != - zend_hash_get_current_key_ex( - &phar->manifest, &key, &keylen, &unused, 0, NULL)) { - if (0 == memcmp(internal_file, key, strlen(internal_file))) { - /* directory found, all dirs have the same stat */ - if (key[strlen(internal_file)] == '/') { - phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); - break; - } - } - } - if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { - break; - } - } - } - - php_url_free(resource); - return 0; -} -/* }}} */ - -/** - * add an empty element with a char * key to a hash table, avoiding duplicates - * - * This is used to get a unique listing of virtual directories within a phar, - * for iterating over opendir()ed phar directories. - */ -static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength) /* {{{ */ -{ - void *dummy = (void *) 1; - - return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL); -} -/* }}} */ - -/** - * Used for sorting directories alphabetically - */ -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 - result = zend_binary_strcmp(f->key.arKey.s, f->nKeyLength, s->key.arKey.s, s->nKeyLength); -#endif - - if (result < 0) { - return -1; - } else if (result > 0) { - return 1; - } else { - return 0; - } -} -/* }}} */ - -/** - * Create a opendir() directory stream handle by iterating over each of the - * files in a phar and retrieving its relative path. From this, construct - * a list of files/directories that are "in" the directory represented by dir - */ -static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */ -{ - HashTable *data; - int dirlen = strlen(dir); - char *save, *found, *key; - uint keylen; - ulong unused; - char *entry; - ALLOC_HASHTABLE(data); - zend_hash_init(data, 64, zend_get_hash_value, NULL, 0); - - if (*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) { - /* make empty root directory for empty phar */ - 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; - } - if (*dir == '/') { - /* root directory */ - if (NULL != (found = (char *) memchr(key, '/', keylen))) { - /* the entry has a path separator and is a subdirectory */ - entry = (char *) safe_emalloc(found - key, 1, 1); - memcpy(entry, key, found - key); - keylen = found - key; - entry[keylen] = '\0'; - } else { - entry = (char *) safe_emalloc(keylen, 1, 1); - memcpy(entry, key, keylen); - entry[keylen] = '\0'; - } - goto PHAR_ADD_ENTRY; - } else { - if (0 != memcmp(key, dir, dirlen)) { - /* entry in directory not found */ - if (SUCCESS != zend_hash_move_forward(manifest)) { - break; - } - continue; - } else { - if (key[dirlen] != '/') { - if (SUCCESS != zend_hash_move_forward(manifest)) { - break; - } - continue; - } - } - } - save = 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; - entry = (char *) safe_emalloc(found - save + dirlen, 1, 1); - memcpy(entry, save + dirlen + 1, found - save - dirlen - 1); - keylen = found - save - dirlen - 1; - entry[keylen] = '\0'; - } else { - /* is file */ - save -= dirlen + 1; - entry = (char *) safe_emalloc(keylen - dirlen, 1, 1); - memcpy(entry, save + dirlen + 1, keylen - dirlen - 1); - entry[keylen - dirlen - 1] = '\0'; - keylen = keylen - dirlen - 1; - } -PHAR_ADD_ENTRY: - 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) { - FREE_HASHTABLE(data); - return NULL; - } - return php_stream_alloc(&phar_dir_ops, data, NULL, "r"); - } else { - efree(dir); - FREE_HASHTABLE(data); - return NULL; - } -} -/* }}}*/ - -/** - * Unlink a file within a phar archive - */ -static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */ -{ - php_url *resource; - char *internal_file, *error, *plain_map; - phar_entry_data *idata; - uint host_len; - int retval; - - if ((resource = phar_open_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) { - return 0; - } - - /* we must have at the very least phar://alias.phar/internalfile.php */ - if (!resource->scheme || !resource->host || !resource->path) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url); - return 0; - } - - if (strcasecmp("phar", resource->scheme)) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url); - return 0; - } - - host_len = strlen(resource->host); - phar_request_initialize(TSRMLS_C); - if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { - spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); - retval = php_stream_unlink(internal_file, options, context); - if (!retval) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); - } - php_url_free(resource); - efree(internal_file); - return retval; - } - - if (PHAR_G(readonly)) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting"); - return 0; - } - - /* need to copy to strip leading "/", will get touched again */ - internal_file = estrdup(resource->path + 1); - if (FAILURE == phar_get_entry_data(&idata, resource->host, strlen(resource->host), internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) { - /* constraints of fp refcount were not met */ - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - } - efree(internal_file); - php_url_free(resource); - return 0; - } - if (error) { - efree(error); - } - if (!idata) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\", cannot unlink", internal_file, resource->host); - efree(internal_file); - php_url_free(resource); - return 0; - } - if (idata->internal_file->fp_refcount > 1) { - /* 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); - phar_entry_delref(idata TSRMLS_CC); - return 0; - } - php_url_free(resource); - efree(internal_file); - phar_entry_remove(idata, &error TSRMLS_CC); - if (error) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); - efree(error); - } - return 1; -} -/* }}} */ - -static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */ -{ - php_url *resource_from, *resource_to; - char *error, *plain_map; - phar_archive_data *phar; - phar_entry_info *entry; - uint host_len; - - error = NULL; - if (PHAR_G(readonly)) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting"); - return 0; - } - - if ((resource_from = phar_open_url(wrapper, url_from, "r+b", options TSRMLS_CC)) == NULL) { - return 0; - } - - if ((resource_to = phar_open_url(wrapper, url_to, "wb", options TSRMLS_CC)) == NULL) { - php_url_free(resource_from); - return 0; - } - - /* we must have at the very least phar://alias.phar/internalfile.php */ - if (!resource_from->scheme || !resource_from->host || !resource_from->path) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from); - return 0; - } - - if (!resource_to->scheme || !resource_to->host || !resource_to->path) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_to); - return 0; - } - - if (strcasecmp("phar", resource_from->scheme)) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from); - return 0; - } - - if (strcasecmp("phar", resource_to->scheme)) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_to); - return 0; - } - - if (strcmp(resource_from->host, resource_to->host)) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to); - return 0; - } - - host_len = strlen(resource_from->host); - phar_request_initialize(TSRMLS_C); - if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource_from->host, host_len+1, (void **)&plain_map) == SUCCESS) { - /*TODO:use php_stream_rename() once available*/ - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive", url_from, url_to); - return 0; - } - - if (SUCCESS != phar_get_archive(&phar, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); - efree(error); - return 0; - } - - if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) { - phar_entry_info new; - - /* perform rename magic */ - if (entry->is_deleted) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to); - return 0; - } - /* transfer all data over to the new entry */ - memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info)); - /* mark the old one for deletion */ - entry->is_deleted = 1; - entry->fp = NULL; - entry->metadata = 0; - entry->link = NULL; -#if HAVE_PHAR_ZIP - entry->zip = NULL; -#endif - - zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry); - if (!entry->is_modified) { - /* copy file contents into a new temp stream */ - if (!phar_open_jit(phar, entry, phar->fp, &error, 1 TSRMLS_CC)) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); - efree(error); - return 0; - } - } - entry->is_modified = 1; - entry->filename = estrdup(resource_to->path+1); - entry->filename_len = strlen(entry->filename); - phar_flush(phar, 0, 0, &error TSRMLS_CC); - if (error) { - php_url_free(resource_from); - php_url_free(resource_to); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); - efree(error); - return 0; - } - } - php_url_free(resource_from); - php_url_free(resource_to); - return 1; -} -/* }}} */ - -/** - * Open a directory handle within a phar archive - */ -static 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; - char *internal_file, *key, *error, *plain_map; - uint keylen; - ulong unused; - phar_archive_data *phar; - phar_entry_info *entry; - uint host_len; - - if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) { - return NULL; - } - - /* we must have at the very least phar://alias.phar/ */ - if (!resource->scheme || !resource->host || !resource->path) { - if (resource->host && !resource->path) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, resource->host); - php_url_free(resource); - return NULL; - } - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); - return NULL; - } - - if (strcasecmp("phar", resource->scheme)) { - php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path); - return NULL; - } - - host_len = strlen(resource->host); - phar_request_initialize(TSRMLS_C); - if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { - spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); - ret = php_stream_opendir(internal_file, options, context); - if (!ret) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); - } - php_url_free(resource); - efree(internal_file); - return ret; - } - - 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); - efree(error); - } - php_url_free(resource); - return NULL; - } - if (error) { - efree(error); - } - if (*internal_file == '\0') { - /* root directory requested */ - internal_file = estrndup(internal_file - 1, 1); - ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC); - 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)) { - php_url_free(resource); - return NULL; - } else { - /* search for directory */ - zend_hash_internal_pointer_reset(&phar->manifest); - while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) { - if (HASH_KEY_NON_EXISTANT != - zend_hash_get_current_key_ex( - &phar->manifest, &key, &keylen, &unused, 0, NULL)) { - if (0 == memcmp(key, internal_file, strlen(internal_file))) { - /* directory found */ - internal_file = estrndup(internal_file, - strlen(internal_file)); - php_url_free(resource); - return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC); - } - } - if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { - break; - } - } - } - - php_url_free(resource); - return NULL; -} -/* }}} */ - #ifdef COMPILE_DL_PHAR ZEND_GET_MODULE(phar) #endif diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index ba2cca2a18..8b3bb96692 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -317,31 +317,7 @@ int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char * #ifdef PHAR_MAIN static void phar_fopen(INTERNAL_FUNCTION_PARAMETERS); static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC); - -static php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC); - -static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); -static 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); -static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC); -static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC); -static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC); - -/* file/stream handlers */ -static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC); -static size_t phar_stream_read( php_stream *stream, char *buf, size_t count TSRMLS_DC); -static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC); -static int phar_stream_flush(php_stream *stream TSRMLS_DC); -static int phar_stream_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); -static int phar_stream_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); - -/* directory handlers */ -static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC); -static size_t phar_dir_read( php_stream *stream, char *buf, size_t count TSRMLS_DC); -static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC); -static int phar_dir_flush(php_stream *stream TSRMLS_DC); -static int phar_dir_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); -static int phar_dir_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); - +extern php_stream_wrapper php_stream_phar_wrapper; #endif int phar_archive_delref(phar_archive_data *phar TSRMLS_DC); diff --git a/ext/phar/php_stream_unlink.h b/ext/phar/php_stream_unlink.h new file mode 100644 index 0000000000..31a126ee45 --- /dev/null +++ b/ext/phar/php_stream_unlink.h @@ -0,0 +1,23 @@ +#ifndef PHAR_UNLINK +#define PHAR_UNLINK +#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000) + +static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC) +{ + php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC); + + if (!wrapper || !wrapper->wops) { + return 0; + } + + if (!wrapper->wops->unlink) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper"); + return 0; + } + return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC); +} + +#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC) + +#endif /* defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000) */ +#endif /* PHAR_UNLINK */ diff --git a/ext/phar/stream.c b/ext/phar/stream.c new file mode 100644 index 0000000000..4e59ddb0df --- /dev/null +++ b/ext/phar/stream.c @@ -0,0 +1,855 @@ +/* + +----------------------------------------------------------------------+ + | phar:// stream wrapper support | + +----------------------------------------------------------------------+ + | 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 | + | 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 | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +#define PHAR_STREAM 1 +#include "phar_internal.h" +#include "php_stream_unlink.h" +#include "stream.h" +#include "dirstream.h" + +php_stream_ops phar_ops = { + phar_stream_write, /* write */ + phar_stream_read, /* read */ + phar_stream_close, /* close */ + phar_stream_flush, /* flush */ + "phar stream", + phar_stream_seek, /* seek */ + NULL, /* cast */ + phar_stream_stat, /* stat */ + NULL, /* set option */ +}; + +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 */ + NULL, /* create directory */ + NULL, /* remove directory */ +}; + +php_stream_wrapper php_stream_phar_wrapper = { + &phar_stream_wops, + NULL, + 0 /* is_url */ +}; + +/** + * Open a phar file for streams API + */ +php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */ +{ + php_url *resource; + char *arch = NULL, *entry = NULL, *error; + int arch_len, entry_len; + + if (!strncasecmp(filename, "phar://", 7)) { + if (mode[0] == 'a') { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported"); + return NULL; + } + if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) { + if (!(options & PHP_STREAM_URL_STAT_QUIET)) { + if (arch && !entry) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); + arch = NULL; + } else { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz/.phar.bz2)", filename); + } + } + if (arch) { + efree(arch); + } + if (entry) { + efree(entry); + } + return NULL; + } + resource = ecalloc(1, sizeof(php_url)); + resource->scheme = estrndup("phar", 4); + resource->host = arch; + + resource->path = entry; +#if MBO_0 + if (resource) { + fprintf(stderr, "Alias: %s\n", alias); + fprintf(stderr, "Scheme: %s\n", resource->scheme); +/* fprintf(stderr, "User: %s\n", resource->user);*/ +/* fprintf(stderr, "Pass: %s\n", resource->pass ? "***" : NULL);*/ + fprintf(stderr, "Host: %s\n", resource->host); +/* fprintf(stderr, "Port: %d\n", resource->port);*/ + fprintf(stderr, "Path: %s\n", resource->path); +/* fprintf(stderr, "Query: %s\n", resource->query);*/ +/* fprintf(stderr, "Fragment: %s\n", resource->fragment);*/ + } +#endif + if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_plain_map.arBuckets && zend_hash_exists(&(PHAR_GLOBALS->phar_plain_map), arch, arch_len+1)) { + return resource; + } + if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { + if (PHAR_G(readonly)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting"); + php_url_free(resource); + return NULL; + } + if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE) + { + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + } + php_url_free(resource); + return NULL; + } + } else { + if (phar_open_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE) + { + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + } + php_url_free(resource); + return NULL; + } + } + return resource; + } + + return NULL; +} +/* }}} */ + +/** + * used for fopen('phar://...') and company + */ +static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ +{ + phar_entry_data *idata; + char *internal_file; + char *error; + char *plain_map; + HashTable *pharcontext; + php_url *resource = NULL; + php_stream *fp, *fpf; + zval **pzoption, *metadata; + uint host_len; + + if ((resource = phar_open_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) { + return NULL; + } + + /* we must have at the very least phar://alias.phar/internalfile.php */ + if (!resource->scheme || !resource->host || !resource->path) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path); + return NULL; + } + + if (strcasecmp("phar", resource->scheme)) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path); + return NULL; + } + + host_len = strlen(resource->host); + phar_request_initialize(TSRMLS_C); + if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { + spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); + fp = php_stream_open_wrapper_ex(internal_file, mode, options, opened_path, context); + if (!fp) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); + } + php_url_free(resource); + efree(internal_file); + return fp; + } + + /* strip leading "/" */ + internal_file = estrdup(resource->path + 1); + if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { + if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, &error TSRMLS_CC))) { + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + } else { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host); + } + efree(internal_file); + php_url_free(resource); + return NULL; + } + if (error) { + efree(error); + } + fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); + php_url_free(resource); + efree(internal_file); + if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) { + pharcontext = HASH_OF(*pzoption); + if (idata->internal_file->uncompressed_filesize == 0 + && idata->internal_file->compressed_filesize == 0 + && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS + && Z_TYPE_PP(pzoption) == IS_LONG + && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0 + ) { + idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK; + idata->internal_file->flags |= Z_LVAL_PP(pzoption); + } + if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) { + if (idata->internal_file->metadata) { + zval_ptr_dtor(&idata->internal_file->metadata); + idata->internal_file->metadata = NULL; + } + + MAKE_STD_ZVAL(idata->internal_file->metadata); + metadata = *pzoption; + ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0); + idata->phar->is_modified = 1; + } + } + return fpf; + } else { + if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) || !idata) { + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + } + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host); + efree(internal_file); + php_url_free(resource); + return NULL; + } + } + php_url_free(resource); + +#if MBO_0 + fprintf(stderr, "Pharname: %s\n", idata->phar->filename); + fprintf(stderr, "Filename: %s\n", internal_file); + fprintf(stderr, "Entry: %s\n", idata->internal_file->filename); + fprintf(stderr, "Size: %u\n", idata->internal_file->uncompressed_filesize); + fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags); + fprintf(stderr, "Offset: %u\n", idata->internal_file->offset_within_phar); + fprintf(stderr, "Cached: %s\n", idata->internal_file->filedata ? "yes" : "no"); +#endif + + /* do we have the data already? */ + if (idata->fp) { + fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); + efree(internal_file); + return fpf; + } + +#if PHP_MAJOR_VERSION < 6 + if (PG(safe_mode) && (!php_checkuid(idata->phar->fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { + phar_entry_delref(idata TSRMLS_CC); + efree(internal_file); + return NULL; + } +#endif + + if (php_check_open_basedir(idata->phar->fname TSRMLS_CC)) { + phar_entry_delref(idata TSRMLS_CC); + efree(internal_file); + return NULL; + } + + fp = idata->phar->fp; + + if (!idata->phar->is_zip && !fp) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot open phar \"%s\"", idata->phar->fname); + phar_entry_delref(idata TSRMLS_CC); + efree(internal_file); + return NULL; + } + + if (!phar_open_jit(idata->phar, idata->internal_file, fp, &error, idata->for_write TSRMLS_CC)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + phar_entry_delref(idata TSRMLS_CC); + efree(internal_file); + return NULL; + } + idata->fp = idata->internal_file->fp; + if (idata->fp == idata->phar->fp) { + idata->zero = idata->internal_file->offset_within_phar + idata->phar->internal_file_start; + } + + /* check length, crc32 */ + if (!idata->internal_file->is_crc_checked && phar_postprocess_file(wrapper, options, idata, idata->internal_file->crc32 TSRMLS_CC) != SUCCESS) { + /* already issued the error */ + phar_entry_delref(idata TSRMLS_CC); + efree(internal_file); + return NULL; + } + idata->internal_file->is_crc_checked = 1; + + fpf = php_stream_alloc(&phar_ops, idata, NULL, mode); + efree(internal_file); + return fpf; +} +/* }}} */ + +/** + * Used for fclose($fp) where $fp is a phar archive + */ +static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ +{ + phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC); + + return 0; +} +/* }}} */ + +/** + * used for fread($fp) and company on a fopen()ed phar file handle + */ +static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ +{ + phar_entry_data *data = (phar_entry_data *)stream->abstract; + size_t got; + + if (data->internal_file->is_deleted) { + stream->eof = 1; + return 0; + } + + /* use our proxy position */ + php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); + + if (!data->zero) { + got = php_stream_read(data->fp, buf, count); + if (data->fp->eof) { + stream->eof = 1; + } + /* note the position, and restore the stream for the next fp */ + data->position = php_stream_tell(data->fp); + } else { + got = php_stream_read(data->fp, buf, MIN(count, data->internal_file->uncompressed_filesize - data->position)); + data->position = php_stream_tell(data->fp) - data->zero; + stream->eof = (data->position == (off_t) data->internal_file->uncompressed_filesize); + } + + + return got; +} +/* }}} */ + +/** + * Used for fseek($fp) on a phar file handle + */ +static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */ +{ + phar_entry_data *data = (phar_entry_data *)stream->abstract; + + int res; + if (data->zero) { + off_t temp; + switch (whence) { + case SEEK_END : + temp = data->zero + data->internal_file->uncompressed_filesize + offset; + break; + case SEEK_CUR : + temp = data->zero + data->position + offset; + break; + case SEEK_SET : + temp = data->zero + offset; + break; + } + if (temp > data->zero + (off_t) data->internal_file->uncompressed_filesize) { + *newoffset = -1; + return -1; + } + if (temp < data->zero) { + *newoffset = -1; + return -1; + } + res = php_stream_seek(data->fp, temp, SEEK_SET); + *newoffset = php_stream_tell(data->fp) - data->zero; + data->position = *newoffset; + return res; + } + if (whence != SEEK_SET) { + /* use our proxy position, so the relative stuff works */ + php_stream_seek(data->fp, data->position, SEEK_SET); + } + /* now do the actual seek */ + res = php_stream_seek(data->fp, offset, whence); + *newoffset = php_stream_tell(data->fp); + data->position = *newoffset; + return res; +} +/* }}} */ + +/** + * Used for writing to a phar file + */ +static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */ +{ + phar_entry_data *data = (phar_entry_data *) stream->abstract; + + php_stream_seek(data->fp, data->position, SEEK_SET); + if (count != php_stream_write(data->fp, buf, count)) { + php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname); + return -1; + } + data->position = php_stream_tell(data->fp); + if (data->position > (off_t)data->internal_file->uncompressed_filesize) { + data->internal_file->uncompressed_filesize = data->position; + } + data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize; + data->internal_file->old_flags = data->internal_file->flags; + data->internal_file->is_modified = 1; + return count; +} +/* }}} */ + +/** + * Used to save work done on a writeable phar + */ +static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */ +{ + char *error; + int ret; + if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) { + ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, &error TSRMLS_CC); + if (error) { + php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, error); + efree(error); + } + return ret; + } else { + return EOF; + } +} +/* }}} */ + + /* {{{ phar_dostat */ +/** + * stat an opened phar file handle stream, used by phar_stat() + */ +void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, + zend_bool is_dir, char *alias, int alias_len TSRMLS_DC) +{ + char *tmp; + int tmp_len; + memset(ssb, 0, sizeof(php_stream_statbuf)); + + if (!is_dir && !data->is_dir) { + ssb->sb.st_size = data->uncompressed_filesize; + ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; + ssb->sb.st_mode |= S_IFREG; /* regular file */ + /* timestamp is just the timestamp when this was added to the phar */ +#ifdef NETWARE + ssb->sb.st_mtime.tv_sec = data->timestamp; + ssb->sb.st_atime.tv_sec = data->timestamp; + ssb->sb.st_ctime.tv_sec = data->timestamp; +#else + ssb->sb.st_mtime = data->timestamp; + ssb->sb.st_atime = data->timestamp; + ssb->sb.st_ctime = data->timestamp; +#endif + } else if (!is_dir && data->is_dir && (data->is_tar || data->is_zip)) { + ssb->sb.st_size = 0; + ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; + ssb->sb.st_mode |= S_IFDIR; /* regular directory */ + /* timestamp is just the timestamp when this was added to the phar */ +#ifdef NETWARE + ssb->sb.st_mtime.tv_sec = data->timestamp; + ssb->sb.st_atime.tv_sec = data->timestamp; + ssb->sb.st_ctime.tv_sec = data->timestamp; +#else + ssb->sb.st_mtime = data->timestamp; + ssb->sb.st_atime = data->timestamp; + ssb->sb.st_ctime = data->timestamp; +#endif + } else { + ssb->sb.st_size = 0; + ssb->sb.st_mode = 0777; + ssb->sb.st_mode |= S_IFDIR; /* regular directory */ +#ifdef NETWARE + ssb->sb.st_mtime.tv_sec = phar->max_timestamp; + ssb->sb.st_atime.tv_sec = phar->max_timestamp; + ssb->sb.st_ctime.tv_sec = phar->max_timestamp; +#else + ssb->sb.st_mtime = phar->max_timestamp; + ssb->sb.st_atime = phar->max_timestamp; + ssb->sb.st_ctime = phar->max_timestamp; +#endif + } + if (!phar->is_writeable) { + ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777); + } + + ssb->sb.st_nlink = 1; + ssb->sb.st_rdev = -1; + if (data) { + tmp_len = data->filename_len + alias_len; + } else { + tmp_len = alias_len + 1; + } + tmp = (char *) emalloc(tmp_len); + memcpy(tmp, alias, alias_len); + if (data) { + memcpy(tmp + alias_len, data->filename, data->filename_len); + } else { + *(tmp+alias_len) = '/'; + } + /* this is only for APC, so use /dev/null device - no chance of conflict there! */ + ssb->sb.st_dev = 0xc; + /* generate unique inode number for alias/filename, so no phars will conflict */ + ssb->sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len); + efree(tmp); +#ifndef PHP_WIN32 + ssb->sb.st_blksize = -1; + ssb->sb.st_blocks = -1; +#endif +} +/* }}}*/ + +/** + * Stat an opened phar file handle + */ +static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ +{ + phar_entry_data *data = (phar_entry_data *)stream->abstract; + + /* If ssb is NULL then someone is misbehaving */ + if (!ssb) { + return -1; + } + + phar_dostat(data->phar, data->internal_file, ssb, 0, data->phar->alias, data->phar->alias_len TSRMLS_CC); + return 0; +} +/* }}} */ + +/** + * Stream wrapper stat implementation of stat() + */ +static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, + php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */ +{ + php_url *resource = NULL; + char *internal_file, *key, *error, *plain_map; + uint keylen; + ulong unused; + phar_archive_data *phar; + phar_entry_info *entry; + uint host_len; + int retval; + + if ((resource = phar_open_url(wrapper, url, "r", flags TSRMLS_CC)) == NULL) { + return -1; + } + + /* we must have at the very least phar://alias.phar/internalfile.php */ + if (!resource->scheme || !resource->host || !resource->path) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: invalid url \"%s\"", url); + return -1; + } + + if (strcasecmp("phar", resource->scheme)) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, "phar error: not a phar url \"%s\"", url); + return -1; + } + + host_len = strlen(resource->host); + phar_request_initialize(TSRMLS_C); + if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { + spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); + retval = php_stream_stat_path_ex(internal_file, flags, ssb, context); + if (retval == -1) { + php_stream_wrapper_log_error(wrapper, 0/* TODO:options */ TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); + } + php_url_free(resource); + efree(internal_file); + return retval; + } + + internal_file = resource->path + 1; /* strip leading "/" */ + /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */ + if (FAILURE == phar_get_archive(&phar, resource->host, strlen(resource->host), NULL, 0, &error TSRMLS_CC)) { + php_url_free(resource); + if (error) { + php_stream_wrapper_log_error(wrapper, flags TSRMLS_CC, error); + efree(error); + } + return 0; + } + if (error) { + efree(error); + } + if (*internal_file == '\0') { + /* root directory requested */ + phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); + php_url_free(resource); + return 0; + } + if (!phar->manifest.arBuckets) { + php_url_free(resource); + return 0; + } + /* search through the manifest of files, and if we have an exact match, it's a file */ + if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry)) { + phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC); + } else { + /* search for directory (partial match of a file) */ + zend_hash_internal_pointer_reset(&phar->manifest); + while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) { + if (HASH_KEY_NON_EXISTANT != + zend_hash_get_current_key_ex( + &phar->manifest, &key, &keylen, &unused, 0, NULL)) { + if (0 == memcmp(internal_file, key, strlen(internal_file))) { + /* directory found, all dirs have the same stat */ + if (key[strlen(internal_file)] == '/') { + phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); + break; + } + } + } + if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { + break; + } + } + } + + php_url_free(resource); + return 0; +} +/* }}} */ + +/** + * Unlink a file within a phar archive + */ +static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */ +{ + php_url *resource; + char *internal_file, *error, *plain_map; + phar_entry_data *idata; + uint host_len; + int retval; + + if ((resource = phar_open_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) { + return 0; + } + + /* we must have at the very least phar://alias.phar/internalfile.php */ + if (!resource->scheme || !resource->host || !resource->path) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url); + return 0; + } + + if (strcasecmp("phar", resource->scheme)) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url); + return 0; + } + + host_len = strlen(resource->host); + phar_request_initialize(TSRMLS_C); + if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource->host, host_len+1, (void **)&plain_map) == SUCCESS) { + spprintf(&internal_file, 0, "%s%s", plain_map, resource->path); + retval = php_stream_unlink(internal_file, options, context); + if (!retval) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" extracted from \"%s\" could not be opened", internal_file, resource->host); + } + php_url_free(resource); + efree(internal_file); + return retval; + } + + if (PHAR_G(readonly)) { + php_url_free(resource); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting"); + return 0; + } + + /* need to copy to strip leading "/", will get touched again */ + internal_file = estrdup(resource->path + 1); + if (FAILURE == phar_get_entry_data(&idata, resource->host, strlen(resource->host), internal_file, strlen(internal_file), "r", &error TSRMLS_CC)) { + /* constraints of fp refcount were not met */ + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + } + efree(internal_file); + php_url_free(resource); + return 0; + } + if (error) { + efree(error); + } + if (!idata) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\", cannot unlink", internal_file, resource->host); + efree(internal_file); + php_url_free(resource); + return 0; + } + if (idata->internal_file->fp_refcount > 1) { + /* 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); + phar_entry_delref(idata TSRMLS_CC); + return 0; + } + php_url_free(resource); + efree(internal_file); + phar_entry_remove(idata, &error TSRMLS_CC); + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error); + efree(error); + } + return 1; +} +/* }}} */ + +static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */ +{ + php_url *resource_from, *resource_to; + char *error, *plain_map; + phar_archive_data *phar; + phar_entry_info *entry; + uint host_len; + + error = NULL; + if (PHAR_G(readonly)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting"); + return 0; + } + + if ((resource_from = phar_open_url(wrapper, url_from, "r+b", options TSRMLS_CC)) == NULL) { + return 0; + } + + if ((resource_to = phar_open_url(wrapper, url_to, "wb", options TSRMLS_CC)) == NULL) { + php_url_free(resource_from); + return 0; + } + + /* we must have at the very least phar://alias.phar/internalfile.php */ + if (!resource_from->scheme || !resource_from->host || !resource_from->path) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from); + return 0; + } + + if (!resource_to->scheme || !resource_to->host || !resource_to->path) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_to); + return 0; + } + + if (strcasecmp("phar", resource_from->scheme)) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from); + return 0; + } + + if (strcasecmp("phar", resource_to->scheme)) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_to); + return 0; + } + + if (strcmp(resource_from->host, resource_to->host)) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to); + return 0; + } + + host_len = strlen(resource_from->host); + phar_request_initialize(TSRMLS_C); + if (zend_hash_find(&(PHAR_GLOBALS->phar_plain_map), resource_from->host, host_len+1, (void **)&plain_map) == SUCCESS) { + /*TODO:use php_stream_rename() once available*/ + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive", url_from, url_to); + return 0; + } + + if (SUCCESS != phar_get_archive(&phar, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + efree(error); + return 0; + } + + if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) { + phar_entry_info new; + + /* perform rename magic */ + if (entry->is_deleted) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to); + return 0; + } + /* transfer all data over to the new entry */ + memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info)); + /* mark the old one for deletion */ + entry->is_deleted = 1; + entry->fp = NULL; + entry->metadata = 0; + entry->link = NULL; +#if HAVE_PHAR_ZIP + entry->zip = NULL; +#endif + + zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry); + if (!entry->is_modified) { + /* copy file contents into a new temp stream */ + if (!phar_open_jit(phar, entry, phar->fp, &error, 1 TSRMLS_CC)) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + efree(error); + return 0; + } + } + entry->is_modified = 1; + entry->filename = estrdup(resource_to->path+1); + entry->filename_len = strlen(entry->filename); + phar_flush(phar, 0, 0, &error TSRMLS_CC); + if (error) { + php_url_free(resource_from); + php_url_free(resource_to); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + efree(error); + return 0; + } + } + php_url_free(resource_from); + php_url_free(resource_to); + return 1; +} +/* }}} */ diff --git a/ext/phar/stream.h b/ext/phar/stream.h new file mode 100644 index 0000000000..b9cf92a250 --- /dev/null +++ b/ext/phar/stream.h @@ -0,0 +1,50 @@ +/* + +----------------------------------------------------------------------+ + | phar php single-file executable PHP extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 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 | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +BEGIN_EXTERN_C() +int phar_postprocess_file(php_stream_wrapper *wrapper, int options, phar_entry_data *idata, php_uint32 crc32 TSRMLS_DC); + +php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC); +void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC); + +static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); +static 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); +static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC); +static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC); +static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC); + +/* file/stream handlers */ +static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC); +static size_t phar_stream_read( php_stream *stream, char *buf, size_t count TSRMLS_DC); +static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC); +static int phar_stream_flush(php_stream *stream TSRMLS_DC); +static int phar_stream_seek( php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); +static int phar_stream_stat( php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); +END_EXTERN_C() + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 25859200e7..a7e212c270 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -17,27 +17,7 @@ */ #include "phar_internal.h" - -#if defined(PHP_VERSION_ID) && (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 60000) - -static int _php_stream_unlink(char *url, int options, php_stream_context *context TSRMLS_DC) -{ - php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(url, NULL, options TSRMLS_CC); - - if (!wrapper || !wrapper->wops) { - return 0; - } - - if (!wrapper->wops->unlink) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper"); - return 0; - } - return wrapper->wops->unlink(wrapper, url, ENFORCE_SAFE_MODE | REPORT_ERRORS, context TSRMLS_CC); -} - -#define php_stream_unlink(url, options, context) _php_stream_unlink((url), (options), (context) TSRMLS_CC) - -#endif +#include "php_stream_unlink.h" /** * Does not check for a previously opened phar in the cache. -- 2.40.0