From 8fb4205a8d5d435d7632d1a3342f853a4e63b891 Mon Sep 17 00:00:00 2001 From: Greg Beaver Date: Fri, 11 Jan 2008 07:30:03 +0000 Subject: [PATCH] add Phar::interceptFileFuncs() To intercept fopen(), file_get_contents(), opendir(), and all the stat-based functions so that code like "if (is_readable('./config.inc.php'))" actually works inside of a phar [DOC] --- ext/phar/config.m4 | 2 +- ext/phar/config.w32 | 2 +- ext/phar/func_interceptors.c | 936 +++++++++++++++++++++++++++++++++++ ext/phar/func_interceptors.h | 34 ++ ext/phar/package.php | 4 +- ext/phar/phar.c | 373 +------------- ext/phar/phar_internal.h | 23 +- ext/phar/phar_object.c | 16 + ext/phar/stream.c | 9 +- ext/phar/tar.c | 4 +- 10 files changed, 1025 insertions(+), 378 deletions(-) create mode 100644 ext/phar/func_interceptors.c create mode 100644 ext/phar/func_interceptors.h diff --git a/ext/phar/config.m4 b/ext/phar/config.m4 index 742923994f..b60e121714 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 stream.c dirstream.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 func_interceptors.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 ac7c546056..e4c52e75e5 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 stream.c dirstream.c phar.c phar_object.c phar_path_check.c"); + EXTENSION("phar", "tar.c zip.c stream.c dirstream.c func_interceptors.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/func_interceptors.c b/ext/phar/func_interceptors.c new file mode 100644 index 0000000000..f80f39ad0a --- /dev/null +++ b/ext/phar/func_interceptors.c @@ -0,0 +1,936 @@ +/* + +----------------------------------------------------------------------+ + | phar php single-file executable PHP extension | + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "phar_internal.h" + +#define PHAR_FUNC(name) \ + static PHP_NAMED_FUNCTION(name) + +PHAR_FUNC(phar_fopen); +PHAR_FUNC(phar_file_get_contents); +PHAR_FUNC(phar_is_file); +PHAR_FUNC(phar_is_link); +PHAR_FUNC(phar_is_dir); +PHAR_FUNC(phar_opendir); +PHAR_FUNC(phar_file_exists); +PHAR_FUNC(phar_fileperms); +PHAR_FUNC(phar_fileinode); +PHAR_FUNC(phar_filesize); +PHAR_FUNC(phar_fileowner); +PHAR_FUNC(phar_filegroup); +PHAR_FUNC(phar_fileatime); +PHAR_FUNC(phar_filemtime); +PHAR_FUNC(phar_filectime); +PHAR_FUNC(phar_filetype); +PHAR_FUNC(phar_is_writable); +PHAR_FUNC(phar_is_readable); +PHAR_FUNC(phar_is_executable); +PHAR_FUNC(phar_lstat); +PHAR_FUNC(phar_stat); + +#define PHAR_INTERCEPT(func) \ + PHAR_G(orig_##func) = NULL; \ + if (SUCCESS == zend_hash_find(CG(function_table), #func, sizeof(#func), (void **)&orig)) { \ + PHAR_G(orig_##func) = orig->internal_function.handler; \ + orig->internal_function.handler = phar_##func; \ + } + + +void phar_intercept_functions(TSRMLS_D) /* {{{ */ +{ + zend_function *orig; + + if (!PHAR_G(request_init)) { + PHAR_G(cwd) = NULL; + PHAR_G(cwd_len) = 0; + } else if (PHAR_G(orig_fopen)) { + /* don't double-intercept */ + return; + } + PHAR_INTERCEPT(fopen); + PHAR_INTERCEPT(file_get_contents); + PHAR_INTERCEPT(is_file); + PHAR_INTERCEPT(is_link); + PHAR_INTERCEPT(is_dir); + PHAR_INTERCEPT(opendir); + PHAR_INTERCEPT(file_exists); + PHAR_INTERCEPT(fileperms); + PHAR_INTERCEPT(fileinode); + PHAR_INTERCEPT(filesize); + PHAR_INTERCEPT(fileowner); + PHAR_INTERCEPT(filegroup); + PHAR_INTERCEPT(fileatime); + PHAR_INTERCEPT(filemtime); + PHAR_INTERCEPT(filectime); + PHAR_INTERCEPT(filetype); + PHAR_INTERCEPT(is_writable); + PHAR_INTERCEPT(is_readable); + PHAR_INTERCEPT(is_executable); + PHAR_INTERCEPT(lstat); + PHAR_INTERCEPT(stat); +} + +#define PHAR_RELEASE(func) \ + if (PHAR_G(orig_##func) && SUCCESS == zend_hash_find(CG(function_table), #func, sizeof(#func), (void **)&orig)) { \ + orig->internal_function.handler = PHAR_G(orig_##func); \ + } \ + PHAR_G(orig_##func) = NULL; + + +void phar_release_functions(TSRMLS_D) /* {{{ */ +{ + zend_function *orig; + + PHAR_RELEASE(fopen); + PHAR_RELEASE(file_get_contents); + PHAR_RELEASE(is_file); + PHAR_RELEASE(is_dir); + PHAR_RELEASE(opendir); + PHAR_RELEASE(file_exists); + PHAR_RELEASE(fileperms); + PHAR_RELEASE(fileinode); + PHAR_RELEASE(filesize); + PHAR_RELEASE(fileowner); + PHAR_RELEASE(filegroup); + PHAR_RELEASE(fileatime); + PHAR_RELEASE(filemtime); + PHAR_RELEASE(filectime); + PHAR_RELEASE(filetype); + PHAR_RELEASE(is_writable); + PHAR_RELEASE(is_readable); + PHAR_RELEASE(is_executable); + PHAR_RELEASE(lstat); + PHAR_RELEASE(stat); +} +/* }}} */ + +PHAR_FUNC(phar_opendir) +{ + char *filename; + int filename_len; + zval *zcontext = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &filename, &filename_len, &zcontext) == FAILURE) { + return; + } + + if (!IS_ABSOLUTE_PATH(filename, filename_len)) { + char *arch, *entry, *fname; + int arch_len, entry_len, fname_len; + fname = zend_get_executed_filename(TSRMLS_C); + + /* we are checking for existence of a file within the relative path. Chances are good that this is + retrieving something from within the phar archive */ + + if (strncasecmp(fname, "phar://", 7)) { + goto skip_phar; + } + fname_len = strlen(fname); + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + php_stream_context *context = NULL; + php_stream *stream; + char *name; + + efree(entry); + entry = filename; + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = filename_len; + if (strstr(entry, "://")) { + efree(arch); + goto skip_phar; + } + /* retrieving a file within the current directory, so use this if possible */ + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + + spprintf(&name, 4096, "phar://%s/%s", arch, entry); + efree(entry); + efree(arch); + if (zcontext) { + context = php_stream_context_from_zval(zcontext, 0); + } + stream = php_stream_opendir(name, REPORT_ERRORS, context); + efree(name); + if (!stream) { + RETURN_FALSE; + } + php_stream_to_zval(stream, return_value); + efree(entry); + return; + } + } +skip_phar: + PHAR_G(orig_opendir)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; +} + +PHAR_FUNC(phar_file_get_contents) +{ + char *filename; + int filename_len; + char *contents; + zend_bool use_include_path = 0; + php_stream *stream; + int len, newlen; + long offset = -1; + long maxlen = PHP_STREAM_COPY_ALL; + zval *zcontext = NULL; + + /* Parse arguments */ + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|br!ll", &filename, &filename_len, &use_include_path, &zcontext, &offset, &maxlen) == FAILURE) { + return; + } + if (use_include_path || !IS_ABSOLUTE_PATH(filename, filename_len)) { + char *arch, *entry, *fname; + int arch_len, entry_len, fname_len, free_filename = 0; + php_stream_context *context = NULL; + fname = zend_get_executed_filename(TSRMLS_C); + + if (strncasecmp(fname, "phar://", 7)) { + goto skip_phar; + } + fname_len = strlen(fname); + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + char *name; + phar_archive_data **pphar; + + efree(entry); + entry = filename; + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = filename_len; + if (strstr(entry, "://")) { + efree(arch); + if (free_filename) { + efree(filename); + } + goto skip_phar; + } + + if (ZEND_NUM_ARGS() == 5 && maxlen < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "length must be greater than or equal to zero"); + RETURN_FALSE; + } + + if (!IS_ABSOLUTE_PATH(filename, filename_len)) { + /* retrieving a file defaults to within the current directory, so use this if possible */ + if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + name = entry; + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { + /* this file is not in the current directory, use the original path */ + efree(entry); + entry = name; + } + } + } + /* auto-convert to phar:// */ + spprintf(&name, 4096, "phar://%s/%s", arch, entry); + if (entry != filename) { + efree(entry); + } + efree(arch); + if (zcontext) { + context = php_stream_context_from_zval(zcontext, 0); + } + stream = php_stream_open_wrapper_ex(name, "rb", 0 | REPORT_ERRORS, NULL, context); + if (free_filename) { + efree(filename); + } + efree(name); + + + if (!stream) { + RETURN_FALSE; + } + + if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek to position %ld in the stream", offset); + php_stream_close(stream); + RETURN_FALSE; + } + + /* uses mmap if possible */ + if ((len = php_stream_copy_to_mem(stream, &contents, maxlen, 0)) > 0) { + + if (PG(magic_quotes_runtime)) { + contents = php_addslashes(contents, len, &newlen, 1 TSRMLS_CC); /* 1 = free source string */ + len = newlen; + } + + RETVAL_STRINGL(contents, len, 0); + } else if (len == 0) { + RETVAL_EMPTY_STRING(); + } else { + RETVAL_FALSE; + } + + php_stream_close(stream); + return; + } + } +skip_phar: + PHAR_G(orig_file_get_contents)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; +} + +PHAR_FUNC(phar_fopen) +{ + char *filename, *mode; + int filename_len, mode_len; + zend_bool use_include_path = 0; + zval *zcontext = NULL; + php_stream *stream; + + if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { + /* no need to check, include_path not even specified in fopen/ no active phars */ + goto skip_phar; + } + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "ss|br", &filename, &filename_len, &mode, &mode_len, &use_include_path, &zcontext) == FAILURE) { + goto skip_phar; + } + if (use_include_path || !IS_ABSOLUTE_PATH(filename, filename_len)) { + char *arch, *entry, *fname; + int arch_len, entry_len, fname_len, free_filename = 0; + php_stream_context *context = NULL; + fname = zend_get_executed_filename(TSRMLS_C); + + if (strncasecmp(fname, "phar://", 7)) { + goto skip_phar; + } + fname_len = strlen(fname); + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + char *name; + phar_archive_data **pphar; + + efree(entry); + entry = filename; + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = filename_len; + if (strstr(entry, "://")) { + efree(arch); + if (free_filename) { + efree(filename); + } + goto skip_phar; + } + /* retrieving a file defaults to within the current directory, so use this if possible */ + if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + name = entry; + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { + /* this file is not in the current directory, use the original path */ + efree(entry); + entry = name; + } + } + /* auto-convert to phar:// */ + spprintf(&name, 4096, "phar://%s/%s", arch, entry); + if (entry != filename) { + efree(entry); + } + efree(arch); + context = php_stream_context_from_zval(zcontext, 0); + stream = php_stream_open_wrapper_ex(name, mode, 0 | REPORT_ERRORS, NULL, context); + efree(name); + if (stream == NULL) { + RETURN_FALSE; + } + php_stream_to_zval(stream, return_value); + if (zcontext) { + zend_list_addref(Z_RESVAL_P(zcontext)); + } + if (free_filename) { + efree(filename); + } + return; + } + } +skip_phar: + PHAR_G(orig_fopen)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; +} + +#define IS_LINK_OPERATION(__t) ((__t) == FS_TYPE || (__t) == FS_IS_LINK || (__t) == FS_LSTAT) +#define IS_EXISTS_CHECK(__t) ((__t) == FS_EXISTS || (__t) == FS_IS_W || (__t) == FS_IS_R || (__t) == FS_IS_X || (__t) == FS_IS_FILE || (__t) == FS_IS_DIR || (__t) == FS_IS_LINK) +#define IS_ABLE_CHECK(__t) ((__t) == FS_IS_R || (__t) == FS_IS_W || (__t) == FS_IS_X) +#define IS_ACCESS_CHECK(__t) (IS_ABLE_CHECK(type) || (__t) == FS_EXISTS) + +/* {{{ php_stat + */ +PHPAPI void phar_fancy_stat(struct stat *stat_sb, int type, zval *return_value TSRMLS_DC) +{ + zval *stat_dev, *stat_ino, *stat_mode, *stat_nlink, *stat_uid, *stat_gid, *stat_rdev, + *stat_size, *stat_atime, *stat_mtime, *stat_ctime, *stat_blksize, *stat_blocks; + php_stream_statbuf ssb; + int rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */ + char *stat_sb_names[13] = { + "dev", "ino", "mode", "nlink", "uid", "gid", "rdev", + "size", "atime", "mtime", "ctime", "blksize", "blocks" + }; + +#ifndef NETWARE + if (type >= FS_IS_W && type <= FS_IS_X) { + if(stat_sb->st_uid==getuid()) { + rmask=S_IRUSR; + wmask=S_IWUSR; + xmask=S_IXUSR; + } else if(stat_sb->st_gid==getgid()) { + rmask=S_IRGRP; + wmask=S_IWGRP; + xmask=S_IXGRP; + } else { + int groups, n, i; + gid_t *gids; + + groups = getgroups(0, NULL); + if(groups > 0) { + gids=(gid_t *)safe_emalloc(groups, sizeof(gid_t), 0); + n=getgroups(groups, gids); + for(i=0;ist_gid==gids[i]) { + rmask=S_IRGRP; + wmask=S_IWGRP; + xmask=S_IXGRP; + break; + } + } + efree(gids); + } + } + } +#endif + + switch (type) { + case FS_PERMS: + RETURN_LONG((long)stat_sb->st_mode); + case FS_INODE: + RETURN_LONG((long)stat_sb->st_ino); + case FS_SIZE: + RETURN_LONG((long)stat_sb->st_size); + case FS_OWNER: + RETURN_LONG((long)stat_sb->st_uid); + case FS_GROUP: + RETURN_LONG((long)stat_sb->st_gid); + case FS_ATIME: +#ifdef NETWARE + RETURN_LONG((long)stat_sb->st_atime.tv_sec); +#else + RETURN_LONG((long)stat_sb->st_atime); +#endif + case FS_MTIME: +#ifdef NETWARE + RETURN_LONG((long)stat_sb->st_mtime.tv_sec); +#else + RETURN_LONG((long)stat_sb->st_mtime); +#endif + case FS_CTIME: +#ifdef NETWARE + RETURN_LONG((long)stat_sb->st_ctime.tv_sec); +#else + RETURN_LONG((long)stat_sb->st_ctime); +#endif + case FS_TYPE: + if (S_ISLNK(stat_sb->st_mode)) { + RETURN_STRING("link", 1); + } + switch(stat_sb->st_mode & S_IFMT) { + case S_IFIFO: RETURN_STRING("fifo", 1); + case S_IFCHR: RETURN_STRING("char", 1); + case S_IFDIR: RETURN_STRING("dir", 1); + case S_IFBLK: RETURN_STRING("block", 1); + case S_IFREG: RETURN_STRING("file", 1); +#if defined(S_IFSOCK) && !defined(ZEND_WIN32)&&!defined(__BEOS__) + case S_IFSOCK: RETURN_STRING("socket", 1); +#endif + } + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown file type (%d)", stat_sb->st_mode&S_IFMT); + RETURN_STRING("unknown", 1); + case FS_IS_W: + RETURN_BOOL((stat_sb->st_mode & wmask) != 0); + case FS_IS_R: + RETURN_BOOL((stat_sb->st_mode&rmask)!=0); + case FS_IS_X: + RETURN_BOOL((stat_sb->st_mode&xmask)!=0 && !S_ISDIR(stat_sb->st_mode)); + case FS_IS_FILE: + RETURN_BOOL(S_ISREG(stat_sb->st_mode)); + case FS_IS_DIR: + RETURN_BOOL(S_ISDIR(stat_sb->st_mode)); + case FS_IS_LINK: + RETURN_BOOL(S_ISLNK(stat_sb->st_mode)); + case FS_EXISTS: + RETURN_TRUE; /* the false case was done earlier */ + case FS_LSTAT: + /* FALLTHROUGH */ + case FS_STAT: + array_init(return_value); + + MAKE_LONG_ZVAL_INCREF(stat_dev, stat_sb->st_dev); + MAKE_LONG_ZVAL_INCREF(stat_ino, stat_sb->st_ino); + MAKE_LONG_ZVAL_INCREF(stat_mode, stat_sb->st_mode); + MAKE_LONG_ZVAL_INCREF(stat_nlink, stat_sb->st_nlink); + MAKE_LONG_ZVAL_INCREF(stat_uid, stat_sb->st_uid); + MAKE_LONG_ZVAL_INCREF(stat_gid, stat_sb->st_gid); +#ifdef HAVE_ST_RDEV + MAKE_LONG_ZVAL_INCREF(stat_rdev, stat_sb->st_rdev); +#else + MAKE_LONG_ZVAL_INCREF(stat_rdev, -1); +#endif + MAKE_LONG_ZVAL_INCREF(stat_size, stat_sb->st_size); +#ifdef NETWARE + MAKE_LONG_ZVAL_INCREF(stat_atime, (stat_sb->st_atime).tv_sec); + MAKE_LONG_ZVAL_INCREF(stat_mtime, (stat_sb->st_mtime).tv_sec); + MAKE_LONG_ZVAL_INCREF(stat_ctime, (stat_sb->st_ctime).tv_sec); +#else + MAKE_LONG_ZVAL_INCREF(stat_atime, stat_sb->st_atime); + MAKE_LONG_ZVAL_INCREF(stat_mtime, stat_sb->st_mtime); + MAKE_LONG_ZVAL_INCREF(stat_ctime, stat_sb->st_ctime); +#endif +#ifdef HAVE_ST_BLKSIZE + MAKE_LONG_ZVAL_INCREF(stat_blksize, stat_sb->st_blksize); +#else + MAKE_LONG_ZVAL_INCREF(stat_blksize,-1); +#endif +#ifdef HAVE_ST_BLOCKS + MAKE_LONG_ZVAL_INCREF(stat_blocks, stat_sb->st_blocks); +#else + MAKE_LONG_ZVAL_INCREF(stat_blocks,-1); +#endif + /* Store numeric indexes in propper order */ + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_dev, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_ino, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_mode, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_nlink, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_uid, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_gid, sizeof(zval *), NULL); + + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_rdev, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_size, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_atime, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_mtime, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_ctime, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_blksize, sizeof(zval *), NULL); + zend_hash_next_index_insert(HASH_OF(return_value), (void *)&stat_blocks, sizeof(zval *), NULL); + + /* Store string indexes referencing the same zval*/ + zend_hash_update(HASH_OF(return_value), stat_sb_names[0], strlen(stat_sb_names[0])+1, (void *) &stat_dev, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[1], strlen(stat_sb_names[1])+1, (void *) &stat_ino, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[2], strlen(stat_sb_names[2])+1, (void *) &stat_mode, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[3], strlen(stat_sb_names[3])+1, (void *) &stat_nlink, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[4], strlen(stat_sb_names[4])+1, (void *) &stat_uid, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[5], strlen(stat_sb_names[5])+1, (void *) &stat_gid, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[6], strlen(stat_sb_names[6])+1, (void *) &stat_rdev, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[7], strlen(stat_sb_names[7])+1, (void *) &stat_size, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[8], strlen(stat_sb_names[8])+1, (void *) &stat_atime, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[9], strlen(stat_sb_names[9])+1, (void *) &stat_mtime, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[10], strlen(stat_sb_names[10])+1, (void *) &stat_ctime, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[11], strlen(stat_sb_names[11])+1, (void *) &stat_blksize, sizeof(zval *), NULL); + zend_hash_update(HASH_OF(return_value), stat_sb_names[12], strlen(stat_sb_names[12])+1, (void *) &stat_blocks, sizeof(zval *), NULL); + + return; + } + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Didn't understand stat call"); + RETURN_FALSE; +} +/* }}} */ + +PHPAPI void phar_file_stat(const char *filename, php_stat_len filename_length, int type, void (*orig_stat_func)(INTERNAL_FUNCTION_PARAMETERS), INTERNAL_FUNCTION_PARAMETERS) +{ + if (!filename_length) { + RETURN_FALSE; + } + + if (!IS_ABSOLUTE_PATH(filename, filename_len)) { + char *arch, *entry, *fname; + int arch_len, entry_len, fname_len; + fname = zend_get_executed_filename(TSRMLS_C); + struct stat sb = {0}; + phar_entry_info *data = NULL; + char *tmp; + int tmp_len; + + /* we are checking for existence of a file within the relative path. Chances are good that this is + retrieving something from within the phar archive */ + + if (strncasecmp(fname, "phar://", 7)) { + goto skip_phar; + } + fname_len = strlen(fname); + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + phar_archive_data **pphar; + + efree(entry); + entry = (char *)filename; + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = (int) filename_length; + if (strstr(entry, "://")) { + efree(arch); + goto skip_phar; + } + if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &data)) { + efree(entry); + goto stat_entry; + } else { + char *save = PHAR_G(cwd), *save2 = entry; + int save_len = PHAR_G(cwd_len), save2_len = entry_len; + + /* this file is not in the current directory, use the original path */ + entry = (char *)filename; + PHAR_G(cwd) = "/"; + PHAR_G(cwd_len) = 0; + /* clean path without cwd */ + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &data)) { + PHAR_G(cwd) = save; + PHAR_G(cwd_len) = save_len; + efree(entry); + efree(save2); + goto stat_entry; + } else { + phar_archive_data *phar = *pphar; + char *key; + uint keylen; + ulong unused; + + /* original not found either, this is possibly a directory relative to cwd */ + 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(save2, key, save2_len)) { + PHAR_G(cwd) = save; + PHAR_G(cwd_len) = save_len; + efree(save2); + efree(entry); + /* directory found, all dirs have the same stat */ + if (key[save2_len] == '/') { + sb.st_size = 0; + sb.st_mode = 0777; + sb.st_mode |= S_IFDIR; /* regular directory */ +#ifdef NETWARE + sb.st_mtime.tv_sec = phar->max_timestamp; + sb.st_atime.tv_sec = phar->max_timestamp; + sb.st_ctime.tv_sec = phar->max_timestamp; +#else + sb.st_mtime = phar->max_timestamp; + sb.st_atime = phar->max_timestamp; + sb.st_ctime = phar->max_timestamp; +#endif + goto statme_baby; + } + } + } + if (SUCCESS != zend_hash_move_forward(&phar->manifest)) { + break; + } + } + } + PHAR_G(cwd) = save; + PHAR_G(cwd_len) = save_len; + efree(entry); + efree(save2); + /* Error Occured */ + if (!IS_EXISTS_CHECK(type)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", filename); + } + RETURN_FALSE; + } + } +stat_entry: + if (!data->is_dir) { + sb.st_size = data->uncompressed_filesize; + sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; + sb.st_mode |= S_IFREG; /* regular file */ + /* timestamp is just the timestamp when this was added to the phar */ +#ifdef NETWARE + sb.st_mtime.tv_sec = data->timestamp; + sb.st_atime.tv_sec = data->timestamp; + sb.st_ctime.tv_sec = data->timestamp; +#else + sb.st_mtime = data->timestamp; + sb.st_atime = data->timestamp; + sb.st_ctime = data->timestamp; +#endif + } else { + sb.st_size = 0; + sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; + sb.st_mode |= S_IFDIR; /* regular directory */ + /* timestamp is just the timestamp when this was added to the phar */ +#ifdef NETWARE + sb.st_mtime.tv_sec = data->timestamp; + sb.st_atime.tv_sec = data->timestamp; + sb.st_ctime.tv_sec = data->timestamp; +#else + sb.st_mtime = data->timestamp; + sb.st_atime = data->timestamp; + sb.st_ctime = data->timestamp; +#endif + } + +statme_baby: + efree(arch); + if (!(*pphar)->is_writeable) { + sb.st_mode = (sb.st_mode & 0555) | (sb.st_mode & ~0777); + } + + sb.st_nlink = 1; + sb.st_rdev = -1; + if (data) { + tmp_len = data->filename_len + (*pphar)->alias_len; + } else { + tmp_len = (*pphar)->alias_len + 1; + } + tmp = (char *) emalloc(tmp_len); + memcpy(tmp, (*pphar)->alias, (*pphar)->alias_len); + if (data) { + memcpy(tmp + (*pphar)->alias_len, data->filename, data->filename_len); + } else { + *(tmp + (*pphar)->alias_len) = '/'; + } + /* this is only for APC, so use /dev/null device - no chance of conflict there! */ + sb.st_dev = 0xc; + /* generate unique inode number for alias/filename, so no phars will conflict */ + sb.st_ino = (unsigned short)zend_get_hash_value(tmp, tmp_len); + efree(tmp); +#ifndef PHP_WIN32 + sb.st_blksize = -1; + sb.st_blocks = -1; +#endif + phar_fancy_stat(&sb, type, return_value TSRMLS_CC); + return; + } + } +skip_phar: + orig_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; +} + +#define PharFileFunction(fname, funcnum, orig) \ +void fname(INTERNAL_FUNCTION_PARAMETERS) { \ + char *filename; \ + int filename_len; \ + \ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { \ + return; \ + } \ + \ + phar_file_stat(filename, (php_stat_len) filename_len, funcnum, PHAR_G(orig), INTERNAL_FUNCTION_PARAM_PASSTHRU); \ +} +/* }}} */ + +/* {{{ proto int fileperms(string filename) + Get file permissions */ +PharFileFunction(phar_fileperms, FS_PERMS, orig_fileperms) +/* }}} */ + +/* {{{ proto int fileinode(string filename) + Get file inode */ +PharFileFunction(phar_fileinode, FS_INODE, orig_fileinode) +/* }}} */ + +/* {{{ proto int filesize(string filename) + Get file size */ +PharFileFunction(phar_filesize, FS_SIZE, orig_filesize) +/* }}} */ + +/* {{{ proto int fileowner(string filename) + Get file owner */ +PharFileFunction(phar_fileowner, FS_OWNER, orig_fileowner) +/* }}} */ + +/* {{{ proto int filegroup(string filename) + Get file group */ +PharFileFunction(phar_filegroup, FS_GROUP, orig_filegroup) +/* }}} */ + +/* {{{ proto int fileatime(string filename) + Get last access time of file */ +PharFileFunction(phar_fileatime, FS_ATIME, orig_fileatime) +/* }}} */ + +/* {{{ proto int filemtime(string filename) + Get last modification time of file */ +PharFileFunction(phar_filemtime, FS_MTIME, orig_filemtime) +/* }}} */ + +/* {{{ proto int filectime(string filename) + Get inode modification time of file */ +PharFileFunction(phar_filectime, FS_CTIME, orig_filectime) +/* }}} */ + +/* {{{ proto string filetype(string filename) + Get file type */ +PharFileFunction(phar_filetype, FS_TYPE, orig_filetype) +/* }}} */ + +/* {{{ proto bool is_writable(string filename) + Returns true if file can be written */ +PharFileFunction(phar_is_writable, FS_IS_W, orig_is_writable) +/* }}} */ + +/* {{{ proto bool is_readable(string filename) + Returns true if file can be read */ +PharFileFunction(phar_is_readable, FS_IS_R, orig_is_readable) +/* }}} */ + +/* {{{ proto bool is_executable(string filename) + Returns true if file is executable */ +PharFileFunction(phar_is_executable, FS_IS_X, orig_is_executable) +/* }}} */ + +/* {{{ proto bool is_executable(string filename) + Returns true if file is executable */ +PharFileFunction(phar_file_exists, FS_EXISTS, orig_file_exists) +/* }}} */ + +/* {{{ proto bool is_executable(string filename) + Returns true if file is executable */ +PharFileFunction(phar_is_dir, FS_IS_DIR, orig_is_dir) +/* }}} */ + +PHAR_FUNC(phar_is_file) +{ + char *filename; + int filename_len; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + goto skip_phar; + } + if (!IS_ABSOLUTE_PATH(filename, filename_len)) { + char *arch, *entry, *fname; + int arch_len, entry_len, fname_len, free_filename = 0; + fname = zend_get_executed_filename(TSRMLS_C); + + /* we are checking for existence of a file within the relative path. Chances are good that this is + retrieving something from within the phar archive */ + + if (strncasecmp(fname, "phar://", 7)) { + goto skip_phar; + } + fname_len = strlen(fname); + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + phar_archive_data **pphar; + + efree(entry); + entry = filename; + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = filename_len; + if (strstr(entry, "://")) { + efree(arch); + if (free_filename) { + efree(filename); + } + goto skip_phar; + } + /* retrieving a file within the current directory, so use this if possible */ + if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + if (zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { + /* this file is not in the current directory, use the original path */ + efree(entry); + efree(arch); + RETURN_TRUE; + } + } + efree(entry); + efree(arch); + RETURN_FALSE; + } + } +skip_phar: + PHAR_G(orig_is_file)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; +} + +PHAR_FUNC(phar_is_link) +{ + char *filename; + int filename_len; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + goto skip_phar; + } + if (!IS_ABSOLUTE_PATH(filename, filename_len)) { + char *arch, *entry, *fname; + int arch_len, entry_len, fname_len, free_filename = 0; + fname = zend_get_executed_filename(TSRMLS_C); + + /* we are checking for existence of a file within the relative path. Chances are good that this is + retrieving something from within the phar archive */ + + if (strncasecmp(fname, "phar://", 7)) { + goto skip_phar; + } + fname_len = strlen(fname); + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + phar_archive_data **pphar; + + efree(entry); + entry = filename; + /* fopen within phar, if :// is not in the url, then prepend phar:/// */ + entry_len = filename_len; + if (strstr(entry, "://")) { + efree(arch); + if (free_filename) { + efree(filename); + } + goto skip_phar; + } + /* retrieving a file within the current directory, so use this if possible */ + if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { + phar_entry_info *etemp; + + entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); + if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &etemp)) { + /* this file is not in the current directory, use the original path */ + efree(entry); + efree(arch); + RETURN_BOOL(etemp->link); + } + } + efree(entry); + efree(arch); + RETURN_FALSE; + } + } +skip_phar: + PHAR_G(orig_file_exists)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; +} + +/* {{{ proto array lstat(string filename) + Give information about a file or symbolic link */ +PharFileFunction(phar_lstat, FS_LSTAT, orig_lstat) +/* }}} */ + +/* {{{ proto array stat(string filename) + Give information about a file */ +PharFileFunction(phar_stat, FS_STAT, orig_stat) +/* }}} */ + +/* + * 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/func_interceptors.h b/ext/phar/func_interceptors.h new file mode 100644 index 0000000000..b48be591ed --- /dev/null +++ b/ext/phar/func_interceptors.h @@ -0,0 +1,34 @@ +/* + +----------------------------------------------------------------------+ + | 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() +void phar_intercept_functions(TSRMLS_D); +void phar_release_functions(TSRMLS_D); +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/package.php b/ext/phar/package.php index 2eff70381a..82d807e36a 100644 --- a/ext/phar/package.php +++ b/ext/phar/package.php @@ -6,7 +6,9 @@ Major feature functionality release * add Phar::isTar(), Phar::isZip(), and Phar::isPhar() [Greg] * add Phar::webPhar() for running a web-based application unmodified directly from a phar archive [Greg] - * include/fopen with include_path all work unmodified within a phar [Greg] + * file functions (fopen-based and stat-based) can be instructed to only look for + relative paths within a phar via Phar::interceptFileFuncs() + * include works unmodified within a phar [Greg] * paths with . and .. work (phar://blah.phar/a/../b.php => phar://blah.phar/b.php) [Greg] * add support for mkdir()/rmdir() and support for empty directories to phar file format [Greg] * add option to compress the entire phar file for phar/tar file format [Greg] diff --git a/ext/phar/phar.c b/ext/phar/phar.c index b62547a507..579697834a 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -22,6 +22,7 @@ #define PHAR_MAIN 1 #include "phar_internal.h" #include "SAPI.h" +#include "func_interceptors.h" ZEND_DECLARE_MODULE_GLOBALS(phar) @@ -1893,13 +1894,13 @@ char *tsrm_strtok_r(char *s, const char *delim, char **last) /** * Remove .. and . references within a phar filename */ -static char *phar_fix_filepath(char *path, int *new_len, int cwd TSRMLS_DC) /* {{{ */ +char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ */ { char *ptr, *free_path, *new_phar; char *tok; int ptr_length, new_phar_len = 1, path_length = *new_len; - if (cwd) { + if (use_cwd) { free_path = path = estrndup(path, path_length); new_phar_len = PHAR_G(cwd_len); new_phar = estrndup(PHAR_G(cwd), new_phar_len); @@ -1948,9 +1949,7 @@ static char *phar_fix_filepath(char *path, int *new_len, int cwd TSRMLS_DC) /* { ptr = tsrm_strtok_r(NULL, "/", &tok); } - if (free_path) { - efree(free_path); - } + efree(free_path); if (path[path_length-1] == '/' && new_phar_len > 1) { new_phar = (char*)erealloc(new_phar, new_phar_len + 2); @@ -3215,337 +3214,11 @@ skip_phar: } /* }}} */ -static void phar_is_dir(INTERNAL_FUNCTION_PARAMETERS) -{ - char *filename; - int filename_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { - return; - } - - if (!IS_ABSOLUTE_PATH(filename, filename_len)) { - char *arch, *entry, *fname; - int arch_len, entry_len, fname_len, free_filename = 0; - fname = zend_get_executed_filename(TSRMLS_C); - - /* we are checking for existence of a file within the relative path. Chances are good that this is - retrieving something from within the phar archive */ - - if (strncasecmp(fname, "phar://", 7)) { - goto skip_phar; - } - fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { - phar_archive_data **pphar; - - efree(entry); - entry = filename; - /* fopen within phar, if :// is not in the url, then prepend phar:/// */ - entry_len = filename_len; - if (strstr(entry, "://")) { - efree(arch); - if (free_filename) { - efree(filename); - } - goto skip_phar; - } - /* retrieving a file within the current directory, so use this if possible */ - if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { - phar_entry_info *etemp; - - entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); - if (zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &etemp)) { - /* this file is not in the current directory, use the original path */ - efree(entry); - efree(arch); - RETURN_BOOL(etemp->is_dir); - } else { - char *key; - uint keylen; - ulong unused; - HashTable *manifest; - - efree(arch); - manifest = &(*pphar)->manifest; - 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 (0 != memcmp(key, entry, entry_len)) { - /* entry in directory not found */ - if (SUCCESS != zend_hash_move_forward(manifest)) { - break; - } - continue; - } else { - if (key[entry_len] != '/') { - if (SUCCESS != zend_hash_move_forward(manifest)) { - break; - } - continue; - } - /* found a file in this path */ - efree(entry); - RETURN_TRUE; - } - } - efree(entry); - RETURN_FALSE; - } - } - } - } -skip_phar: - PHAR_G(orig_is_dir)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; -} - -static void phar_fgc(INTERNAL_FUNCTION_PARAMETERS) -{ - char *filename; - int filename_len; - char *contents; - zend_bool use_include_path = 0; - php_stream *stream; - int len, newlen; - long offset = -1; - long maxlen = PHP_STREAM_COPY_ALL; - zval *zcontext = NULL; - - /* Parse arguments */ - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|br!ll", &filename, &filename_len, &use_include_path, &zcontext, &offset, &maxlen) == FAILURE) { - return; - } - if (use_include_path || !IS_ABSOLUTE_PATH(filename, filename_len)) { - char *arch, *entry, *fname; - int arch_len, entry_len, fname_len, free_filename = 0; - php_stream_context *context = NULL; - fname = zend_get_executed_filename(TSRMLS_C); - - if (strncasecmp(fname, "phar://", 7)) { - goto skip_phar; - } - fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { - char *name; - phar_archive_data **pphar; - - efree(entry); - entry = filename; - /* fopen within phar, if :// is not in the url, then prepend phar:/// */ - entry_len = filename_len; - if (strstr(entry, "://")) { - efree(arch); - if (free_filename) { - efree(filename); - } - goto skip_phar; - } - - if (ZEND_NUM_ARGS() == 5 && maxlen < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "length must be greater than or equal to zero"); - RETURN_FALSE; - } - - if (!IS_ABSOLUTE_PATH(filename, filename_len)) { - /* retrieving a file defaults to within the current directory, so use this if possible */ - if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { - name = entry; - entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); - if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { - /* this file is not in the current directory, use the original path */ - efree(entry); - entry = name; - } - } - } - /* auto-convert to phar:// */ - spprintf(&name, 4096, "phar://%s/%s", arch, entry); - if (entry != filename) { - efree(entry); - } - efree(arch); - context = php_stream_context_from_zval(zcontext, 0); - stream = php_stream_open_wrapper_ex(name, "rb", 0 | REPORT_ERRORS, NULL, context); - if (free_filename) { - efree(filename); - } - efree(name); - - - if (!stream) { - RETURN_FALSE; - } - - if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek to position %ld in the stream", offset); - php_stream_close(stream); - RETURN_FALSE; - } - - /* uses mmap if possible */ - if ((len = php_stream_copy_to_mem(stream, &contents, maxlen, 0)) > 0) { - - if (PG(magic_quotes_runtime)) { - contents = php_addslashes(contents, len, &newlen, 1 TSRMLS_CC); /* 1 = free source string */ - len = newlen; - } - - RETVAL_STRINGL(contents, len, 0); - } else if (len == 0) { - RETVAL_EMPTY_STRING(); - } else { - RETVAL_FALSE; - } - - php_stream_close(stream); - return; - } - } -skip_phar: - PHAR_G(orig_fgc)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; -} - -static void phar_file_exists(INTERNAL_FUNCTION_PARAMETERS) -{ - char *filename; - int filename_len; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { - goto skip_phar; - } - if (!IS_ABSOLUTE_PATH(filename, filename_len)) { - char *arch, *entry, *fname; - int arch_len, entry_len, fname_len, free_filename = 0; - fname = zend_get_executed_filename(TSRMLS_C); - - /* we are checking for existence of a file within the relative path. Chances are good that this is - retrieving something from within the phar archive */ - - if (strncasecmp(fname, "phar://", 7)) { - goto skip_phar; - } - fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { - phar_archive_data **pphar; - - efree(entry); - entry = filename; - /* fopen within phar, if :// is not in the url, then prepend phar:/// */ - entry_len = filename_len; - if (strstr(entry, "://")) { - efree(arch); - if (free_filename) { - efree(filename); - } - goto skip_phar; - } - /* retrieving a file within the current directory, so use this if possible */ - if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { - entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); - if (zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { - /* this file is not in the current directory, use the original path */ - efree(entry); - efree(arch); - RETURN_TRUE; - } - } - efree(entry); - efree(arch); - } - } -skip_phar: - PHAR_G(file_exists)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; -} - -static void phar_fopen(INTERNAL_FUNCTION_PARAMETERS) -{ - char *filename, *mode; - int filename_len, mode_len; - zend_bool use_include_path = 0; - zval *zcontext = NULL; - php_stream *stream; - - if (!zend_hash_num_elements(&(PHAR_GLOBALS->phar_fname_map))) { - /* no need to check, include_path not even specified in fopen/ no active phars */ - goto skip_phar; - } - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "ss|br", &filename, &filename_len, &mode, &mode_len, &use_include_path, &zcontext) == FAILURE) { - goto skip_phar; - } - if (use_include_path || !IS_ABSOLUTE_PATH(filename, filename_len)) { - char *arch, *entry, *fname; - int arch_len, entry_len, fname_len, free_filename = 0; - php_stream_context *context = NULL; - fname = zend_get_executed_filename(TSRMLS_C); - - if (strncasecmp(fname, "phar://", 7)) { - goto skip_phar; - } - fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { - char *name; - phar_archive_data **pphar; - - efree(entry); - entry = filename; - /* fopen within phar, if :// is not in the url, then prepend phar:/// */ - entry_len = filename_len; - if (strstr(entry, "://")) { - efree(arch); - if (free_filename) { - efree(filename); - } - goto skip_phar; - } - if (!IS_ABSOLUTE_PATH(filename, filename_len)) { - /* retrieving a file defaults to within the current directory, so use this if possible */ - if (SUCCESS == (zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar))) { - name = entry; - entry = phar_fix_filepath(entry, &entry_len, 1 TSRMLS_CC); - if (!zend_hash_exists(&((*pphar)->manifest), entry, entry_len)) { - /* this file is not in the current directory, use the original path */ - efree(entry); - entry = name; - } - } - } - /* auto-convert to phar:// */ - spprintf(&name, 4096, "phar://%s/%s", arch, entry); - if (entry != filename) { - efree(entry); - } - efree(arch); - context = php_stream_context_from_zval(zcontext, 0); - stream = php_stream_open_wrapper_ex(name, mode, 0 | REPORT_ERRORS, NULL, context); - efree(name); - php_stream_to_zval(stream, return_value); - if (free_filename) { - efree(filename); - } - return; - } - } -skip_phar: - PHAR_G(orig_fopen)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; -} - PHP_MINIT_FUNCTION(phar) /* {{{ */ { ZEND_INIT_MODULE_GLOBALS(phar, php_phar_init_globals_module, NULL); REGISTER_INI_ENTRIES(); - PHAR_G(orig_fopen) = NULL; - PHAR_G(orig_fgc) = NULL; - PHAR_G(file_exists) = NULL; - PHAR_G(orig_is_dir) = NULL; - PHAR_G(cwd) = NULL; - PHAR_G(cwd_len) = 0; phar_has_gnupg = zend_hash_exists(&module_registry, "gnupg", sizeof("gnupg")); phar_has_bz2 = zend_hash_exists(&module_registry, "bz2", sizeof("bz2")); phar_has_zlib = zend_hash_exists(&module_registry, "zlib", sizeof("zlib")); @@ -3578,7 +3251,6 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */ { if (!PHAR_GLOBALS->request_init) { - zend_function *orig; PHAR_GLOBALS->request_init = 1; PHAR_GLOBALS->request_ends = 0; PHAR_GLOBALS->request_done = 0; @@ -3587,26 +3259,9 @@ void phar_request_initialize(TSRMLS_D) /* {{{ */ zend_hash_init(&(PHAR_GLOBALS->phar_plain_map), sizeof(const char *), zend_get_hash_value, NULL, 0); zend_hash_init(&(PHAR_GLOBALS->phar_SERVER_mung_list), sizeof(const char *), zend_get_hash_value, NULL, 0); phar_split_extract_list(TSRMLS_C); - PHAR_G(orig_fopen) = PHAR_G(orig_fgc) = PHAR_G(file_exists) = NULL; - PHAR_G(orig_is_dir) = NULL; PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; - if (SUCCESS == zend_hash_find(CG(function_table), "fopen", 6, (void **)&orig)) { - PHAR_G(orig_fopen) = orig->internal_function.handler; - orig->internal_function.handler = phar_fopen; - } - if (SUCCESS == zend_hash_find(CG(function_table), "file_get_contents", 18, (void **)&orig)) { - PHAR_G(orig_fgc) = orig->internal_function.handler; - orig->internal_function.handler = phar_fgc; - } - if (SUCCESS == zend_hash_find(CG(function_table), "file_exists", 12, (void **)&orig)) { - PHAR_G(file_exists) = orig->internal_function.handler; - orig->internal_function.handler = phar_file_exists; - } - if (SUCCESS == zend_hash_find(CG(function_table), "is_dir", 7, (void **)&orig)) { - PHAR_G(orig_is_dir) = orig->internal_function.handler; - orig->internal_function.handler = phar_is_dir; - } + phar_intercept_functions(TSRMLS_C); } } /* }}} */ @@ -3616,23 +3271,7 @@ PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */ PHAR_GLOBALS->request_ends = 1; if (PHAR_GLOBALS->request_init) { - zend_function *orig; - if (PHAR_G(orig_fopen) && SUCCESS == zend_hash_find(CG(function_table), "fopen", 6, (void **)&orig)) { - orig->internal_function.handler = PHAR_G(orig_fopen); - } - PHAR_G(orig_fopen) = NULL; - if (PHAR_G(file_exists) && SUCCESS == zend_hash_find(CG(function_table), "file_exists", 12, (void **)&orig)) { - orig->internal_function.handler = PHAR_G(file_exists); - } - PHAR_G(file_exists) = NULL; - if (PHAR_G(orig_fgc) && SUCCESS == zend_hash_find(CG(function_table), "file_get_contents", 18, (void **)&orig)) { - orig->internal_function.handler = PHAR_G(orig_fgc); - } - PHAR_G(orig_fgc) = NULL; - if (PHAR_G(orig_is_dir) && SUCCESS == zend_hash_find(CG(function_table), "is_dir", 7, (void **)&orig)) { - orig->internal_function.handler = PHAR_G(orig_is_dir); - } - PHAR_G(orig_is_dir) = NULL; + phar_release_functions(TSRMLS_C); zend_hash_destroy(&(PHAR_GLOBALS->phar_alias_map)); PHAR_GLOBALS->phar_alias_map.arBuckets = NULL; zend_hash_destroy(&(PHAR_GLOBALS->phar_fname_map)); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 16261c5dd7..9d34aff6e3 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -143,9 +143,26 @@ ZEND_BEGIN_MODULE_GLOBALS(phar) int request_done; int request_ends; void (*orig_fopen)(INTERNAL_FUNCTION_PARAMETERS); - void (*orig_fgc)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_file_get_contents)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_is_file)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_is_link)(INTERNAL_FUNCTION_PARAMETERS); void (*orig_is_dir)(INTERNAL_FUNCTION_PARAMETERS); - void (*file_exists)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_opendir)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_file_exists)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_fileperms)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_fileinode)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_filesize)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_fileowner)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_filegroup)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_fileatime)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_filemtime)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_filectime)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_filetype)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_is_writable)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_is_readable)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_is_executable)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_lstat)(INTERNAL_FUNCTION_PARAMETERS); + void (*orig_stat)(INTERNAL_FUNCTION_PARAMETERS); /* used for includes with . in them inside front controller */ char* cwd; int cwd_len; @@ -319,6 +336,7 @@ int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC); int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC); int phar_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC); +char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC); phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, php_stream *fp, char **error, int for_write TSRMLS_DC); int phar_parse_metadata(char **buffer, zval **metadata, int is_zip TSRMLS_DC); @@ -337,7 +355,6 @@ int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_l int phar_zip_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC); #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); extern php_stream_wrapper php_stream_phar_wrapper; #endif diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index a19a019663..50da4ebdee 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -20,6 +20,7 @@ /* $Id$ */ #include "phar_internal.h" +#include "func_interceptors.h" static zend_class_entry *phar_ce_archive; static zend_class_entry *phar_ce_PharException; @@ -705,6 +706,20 @@ PHP_METHOD(Phar, mungServer) } /* }}} */ +/* {{{ proto void Phar::interceptFileFuncs() + * instructs phar to intercept fopen, file_get_contents, opendir, and all of the stat-related functions + * and return stat on files within the phar for relative paths + * + * Once called, this cannot be reversed, and continue until the end of the request. + * + * This allows legacy scripts to be pharred unmodified + */ +PHP_METHOD(Phar, interceptFileFuncs) +{ + phar_intercept_functions(TSRMLS_C); +} +/* }}} */ + /* {{{ proto mixed Phar::mapPhar([string alias, [int dataoffset]]) * Reads the currently executed file (a phar) and registers its manifest */ PHP_METHOD(Phar, mapPhar) @@ -2852,6 +2867,7 @@ zend_function_entry php_archive_methods[] = { PHP_ME(Phar, mapPhar, arginfo_phar_mapPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, webPhar, arginfo_phar_webPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, mungServer, arginfo_phar_mungServer, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, interceptFileFuncs, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, getExtractList, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, getSupportedSignatures,NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, getSupportedCompression,NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 41b51f11f7..ef0bdf0f5b 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -556,7 +556,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, phar_archive_data *phar; phar_entry_info *entry; uint host_len; - int retval; + int retval, internal_file_len; if ((resource = phar_open_url(wrapper, url, "r", flags TSRMLS_CC)) == NULL) { return FAILURE; @@ -611,8 +611,9 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, php_url_free(resource); return SUCCESS; } + internal_file_len = strlen(internal_file); /* 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)) { + if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) { phar_dostat(phar, entry, ssb, 0, phar->alias, phar->alias_len TSRMLS_CC); php_url_free(resource); return SUCCESS; @@ -623,9 +624,9 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags, 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))) { + if (0 == memcmp(internal_file, key, internal_file_len)) { /* directory found, all dirs have the same stat */ - if (key[strlen(internal_file)] == '/') { + if (key[internal_file_len] == '/') { phar_dostat(phar, NULL, ssb, 1, phar->alias, phar->alias_len TSRMLS_CC); php_url_free(resource); return SUCCESS; diff --git a/ext/phar/tar.c b/ext/phar/tar.c index f9afe72df1..cc65a9f472 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -554,7 +554,6 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **er /* deferred flush */ phar->fp = newfile; } else { - phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); if (!phar->fp) { phar->fp = newfile; @@ -573,6 +572,9 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **er filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); zval_dtor(&filterparams); if (!filter) { + /* copy contents uncompressed rather than lose them */ + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + php_stream_close(newfile); if (error) { spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); } -- 2.50.1