/* +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ | Copyright (c) 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: | | https://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 | +----------------------------------------------------------------------+ */ #include "phar_internal.h" #define PHAR_FUNC(name) \ static PHP_NAMED_FUNCTION(name) PHAR_FUNC(phar_opendir) /* {{{ */ { char *filename; size_t filename_len; zval *zcontext = NULL; if (!PHAR_G(intercepted)) { goto skip_phar; } if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map)))) && !HT_IS_INITIALIZED(&cached_phars)) { goto skip_phar; } if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|r!", &filename, &filename_len, &zcontext) == FAILURE) { RETURN_THROWS(); } if (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://")) { char *arch, *entry; size_t arch_len, entry_len; zend_string *fname = zend_get_executed_filename_ex(); /* 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 (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) { goto skip_phar; } if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) { php_stream_context *context = NULL; php_stream *stream; char *name; efree(entry); entry = estrndup(filename, filename_len); /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file within the current directory, so use this if possible */ entry = phar_fix_filepath(entry, &entry_len, 1); if (entry[0] == '/') { spprintf(&name, 4096, "phar://%s%s", arch, entry); } else { 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); return; } } skip_phar: PHAR_G(orig_opendir)(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } /* }}} */ static zend_string* phar_get_name_for_relative_paths(zend_string *filename, bool using_include_path) { char *arch, *entry; size_t arch_len, entry_len; zend_string *fname = zend_get_executed_filename_ex(); /* 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 (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) { return NULL; } if (FAILURE == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) { return NULL; } efree(entry); entry = NULL; entry_len = 0; /* fopen within phar, if :// is not in the url, then prepend phar:/// */ /* retrieving a file defaults to within the current directory, so use this if possible */ phar_archive_data *phar; if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) { efree(arch); return NULL; } zend_string *name = NULL; if (using_include_path) { if (!(name = phar_find_in_include_path(filename, NULL))) { /* this file is not in the phar, use the original path */ efree(arch); return NULL; } } else { entry_len = ZSTR_LEN(filename); entry = phar_fix_filepath(estrndup(ZSTR_VAL(filename), ZSTR_LEN(filename)), &entry_len, 1); if (entry[0] == '/') { if (!zend_hash_str_exists(&(phar->manifest), entry + 1, entry_len - 1)) { /* this file is not in the phar, use the original path */ notfound: efree(entry); efree(arch); return NULL; } } else { if (!zend_hash_str_exists(&(phar->manifest), entry, entry_len)) { goto notfound; } } /* auto-convert to phar:// */ if (entry[0] == '/') { ZEND_ASSERT(strlen("phar://") + arch_len + entry_len < 4096); name = zend_string_concat3( "phar://", strlen("phar://"), arch, arch_len, entry, entry_len ); } else { name = strpprintf(4096, "phar://%s/%s", arch, entry); } efree(entry); } efree(arch); return name; } PHAR_FUNC(phar_file_get_contents) /* {{{ */ { zend_string *filename; zend_string *contents; bool use_include_path = 0; zend_long offset = -1; zend_long maxlen; bool maxlen_is_null = 1; zval *zcontext = NULL; if (!PHAR_G(intercepted)) { goto skip_phar; } if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map)))) && !HT_IS_INITIALIZED(&cached_phars)) { goto skip_phar; } /* Parse arguments */ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "P|br!ll!", &filename, &use_include_path, &zcontext, &offset, &maxlen, &maxlen_is_null) == FAILURE) { goto skip_phar; } if (maxlen_is_null) { maxlen = (ssize_t) PHP_STREAM_COPY_ALL; } else if (maxlen < 0) { zend_argument_value_error(5, "must be greater than or equal to 0"); RETURN_THROWS(); } if (use_include_path || (!IS_ABSOLUTE_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename)) && !strstr(ZSTR_VAL(filename), "://"))) { zend_string *name = phar_get_name_for_relative_paths(filename, use_include_path); if (!name) { goto skip_phar; } php_stream_context *context = NULL; php_stream *stream; if (zcontext) { context = php_stream_context_from_zval(zcontext, 0); } stream = php_stream_open_wrapper_ex(ZSTR_VAL(name), "rb", 0 | REPORT_ERRORS, NULL, context); zend_string_release_ex(name, false); if (!stream) { RETURN_FALSE; } if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) { php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); php_stream_close(stream); RETURN_FALSE; } /* uses mmap if possible */ contents = php_stream_copy_to_mem(stream, maxlen, 0); if (contents && ZSTR_LEN(contents) > 0) { RETVAL_STR(contents); } else if (contents) { zend_string_release_ex(contents, 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_readfile) /* {{{ */ { zend_string *filename; bool use_include_path = 0; zval *zcontext = NULL; if (!PHAR_G(intercepted)) { goto skip_phar; } if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map)))) && !HT_IS_INITIALIZED(&cached_phars)) { goto skip_phar; } if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "P|br!", &filename, &use_include_path, &zcontext) == FAILURE) { goto skip_phar; } if (use_include_path || (!IS_ABSOLUTE_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename)) && !strstr(ZSTR_VAL(filename), "://"))) { zend_string *name = phar_get_name_for_relative_paths(filename, use_include_path); if (!name) { goto skip_phar; } php_stream *stream; php_stream_context *context = php_stream_context_from_zval(zcontext, 0); stream = php_stream_open_wrapper_ex(ZSTR_VAL(name), "rb", 0 | REPORT_ERRORS, NULL, context); zend_string_release_ex(name, false); if (stream == NULL) { RETURN_FALSE; } ssize_t size = php_stream_passthru(stream); php_stream_close(stream); RETURN_LONG(size); } skip_phar: PHAR_G(orig_readfile)(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } /* }}} */ PHAR_FUNC(phar_fopen) /* {{{ */ { zend_string *filename; char *mode; size_t mode_len; bool use_include_path = 0; zval *zcontext = NULL; if (!PHAR_G(intercepted)) { goto skip_phar; } if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map)))) && !HT_IS_INITIALIZED(&cached_phars)) { /* 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(), "Ps|br!", &filename, &mode, &mode_len, &use_include_path, &zcontext) == FAILURE) { goto skip_phar; } if (use_include_path || (!IS_ABSOLUTE_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename)) && !strstr(ZSTR_VAL(filename), "://"))) { zend_string *name = phar_get_name_for_relative_paths(filename, use_include_path); if (!name) { goto skip_phar; } php_stream *stream; php_stream_context *context = php_stream_context_from_zval(zcontext, 0); stream = php_stream_open_wrapper_ex(ZSTR_VAL(name), mode, 0 | REPORT_ERRORS, NULL, context); zend_string_release_ex(name, false); if (stream == NULL) { RETURN_FALSE; } php_stream_to_zval(stream, return_value); if (zcontext) { Z_ADDREF_P(zcontext); } 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 */ static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value) { 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; 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" }; 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); } } } switch (type) { case FS_PERMS: RETURN_LONG((zend_long)stat_sb->st_mode); case FS_INODE: RETURN_LONG((zend_long)stat_sb->st_ino); case FS_SIZE: RETURN_LONG((zend_long)stat_sb->st_size); case FS_OWNER: RETURN_LONG((zend_long)stat_sb->st_uid); case FS_GROUP: RETURN_LONG((zend_long)stat_sb->st_gid); case FS_ATIME: RETURN_LONG((zend_long)stat_sb->st_atime); case FS_MTIME: RETURN_LONG((zend_long)stat_sb->st_mtime); case FS_CTIME: RETURN_LONG((zend_long)stat_sb->st_ctime); case FS_TYPE: if (S_ISLNK(stat_sb->st_mode)) { RETURN_STRING("link"); } switch(stat_sb->st_mode & S_IFMT) { case S_IFDIR: RETURN_STRING("dir"); case S_IFREG: RETURN_STRING("file"); } php_error_docref(NULL, E_NOTICE, "Unknown file type (%u)", stat_sb->st_mode & S_IFMT); RETURN_STRING("unknown"); 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); ZVAL_LONG(&stat_dev, stat_sb->st_dev); ZVAL_LONG(&stat_ino, stat_sb->st_ino); ZVAL_LONG(&stat_mode, stat_sb->st_mode); ZVAL_LONG(&stat_nlink, stat_sb->st_nlink); ZVAL_LONG(&stat_uid, stat_sb->st_uid); ZVAL_LONG(&stat_gid, stat_sb->st_gid); #ifdef HAVE_STRUCT_STAT_ST_RDEV ZVAL_LONG(&stat_rdev, stat_sb->st_rdev); #else ZVAL_LONG(&stat_rdev, -1); #endif ZVAL_LONG(&stat_size, stat_sb->st_size); ZVAL_LONG(&stat_atime, stat_sb->st_atime); ZVAL_LONG(&stat_mtime, stat_sb->st_mtime); ZVAL_LONG(&stat_ctime, stat_sb->st_ctime); #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE ZVAL_LONG(&stat_blksize, stat_sb->st_blksize); #else ZVAL_LONG(&stat_blksize,-1); #endif #ifdef HAVE_STRUCT_STAT_ST_BLOCKS ZVAL_LONG(&stat_blocks, stat_sb->st_blocks); #else ZVAL_LONG(&stat_blocks,-1); #endif /* Store numeric indexes in proper order */ zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_dev); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ino); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mode); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_nlink); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_uid); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_gid); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_rdev); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_size); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_atime); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mtime); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ctime); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blksize); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks); /* Store string indexes referencing the same zval*/ zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize); zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks); return; } php_error_docref(NULL, E_WARNING, "Didn't understand stat call"); RETURN_FALSE; } /* }}} */ static void phar_file_stat(const char *filename, size_t filename_length, int type, zif_handler orig_stat_func, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ { if (!filename_length) { RETURN_FALSE; } if (!IS_ABSOLUTE_PATH(filename, filename_length) && !strstr(filename, "://")) { char *arch, *entry; size_t arch_len, entry_len; zend_string *fname; zend_stat_t sb = {0}; phar_entry_info *data = NULL; phar_archive_data *phar; fname = zend_get_executed_filename_ex(); /* 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 (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) { goto skip_phar; } if (PHAR_G(last_phar) && ZSTR_LEN(fname) - 7 >= PHAR_G(last_phar_name_len) && !memcmp(ZSTR_VAL(fname) + 7, PHAR_G(last_phar_name), PHAR_G(last_phar_name_len))) { arch = estrndup(PHAR_G(last_phar_name), PHAR_G(last_phar_name_len)); arch_len = PHAR_G(last_phar_name_len); entry = estrndup(filename, filename_length); /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_length; phar = PHAR_G(last_phar); goto splitted; } if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) { efree(entry); entry = estrndup(filename, filename_length); /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_length; if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) { efree(arch); efree(entry); goto skip_phar; } splitted: entry = phar_fix_filepath(entry, &entry_len, 1); if (entry[0] == '/') { if (NULL != (data = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) { efree(entry); goto stat_entry; } goto notfound; } if (NULL != (data = zend_hash_str_find_ptr(&(phar->manifest), entry, entry_len))) { efree(entry); goto stat_entry; } if (zend_hash_str_exists(&(phar->virtual_dirs), entry, entry_len)) { efree(entry); efree(arch); if (IS_EXISTS_CHECK(type)) { RETURN_TRUE; } sb.st_size = 0; sb.st_mode = 0777; sb.st_mode |= S_IFDIR; /* regular directory */ sb.st_mtime = phar->max_timestamp; sb.st_atime = phar->max_timestamp; sb.st_ctime = phar->max_timestamp; goto statme_baby; } else { char *save; size_t save_len; notfound: efree(entry); save = PHAR_G(cwd); save_len = PHAR_G(cwd_len); /* this file is not in the current directory, use the original path */ entry = estrndup(filename, filename_length); entry_len = filename_length; PHAR_G(cwd) = "/"; PHAR_G(cwd_len) = 0; /* clean path without cwd */ entry = phar_fix_filepath(entry, &entry_len, 1); if (NULL != (data = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) { PHAR_G(cwd) = save; PHAR_G(cwd_len) = save_len; efree(entry); if (IS_EXISTS_CHECK(type)) { efree(arch); RETURN_TRUE; } goto stat_entry; } if (zend_hash_str_exists(&(phar->virtual_dirs), entry + 1, entry_len - 1)) { PHAR_G(cwd) = save; PHAR_G(cwd_len) = save_len; efree(entry); efree(arch); if (IS_EXISTS_CHECK(type)) { RETURN_TRUE; } sb.st_size = 0; sb.st_mode = 0777; sb.st_mode |= S_IFDIR; /* regular directory */ sb.st_mtime = phar->max_timestamp; sb.st_atime = phar->max_timestamp; sb.st_ctime = phar->max_timestamp; goto statme_baby; } PHAR_G(cwd) = save; PHAR_G(cwd_len) = save_len; efree(entry); efree(arch); /* Error Occurred */ if (!IS_EXISTS_CHECK(type)) { php_error_docref(NULL, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", filename); } RETURN_FALSE; } stat_entry: efree(arch); if (!data->is_dir) { sb.st_size = data->uncompressed_filesize; sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; if (data->link) { sb.st_mode |= S_IFREG|S_IFLNK; /* regular file */ } else { sb.st_mode |= S_IFREG; /* regular file */ } /* timestamp is just the timestamp when this was added to the phar */ sb.st_mtime = data->timestamp; sb.st_atime = data->timestamp; sb.st_ctime = data->timestamp; } else { sb.st_size = 0; sb.st_mode = data->flags & PHAR_ENT_PERM_MASK; sb.st_mode |= S_IFDIR; /* regular directory */ if (data->link) { sb.st_mode |= S_IFLNK; } /* timestamp is just the timestamp when this was added to the phar */ sb.st_mtime = data->timestamp; sb.st_atime = data->timestamp; sb.st_ctime = data->timestamp; } statme_baby: if (!phar->is_writeable) { sb.st_mode = (sb.st_mode & 0555) | (sb.st_mode & ~0777); } sb.st_nlink = 1; sb.st_rdev = -1; /* this is only for APC, so use /dev/null device - no chance of conflict there! */ sb.st_dev = 0xc; /* generate unique inode number for alias/filename, so no phars will conflict */ if (data) { sb.st_ino = data->inode; } #ifndef PHP_WIN32 sb.st_blksize = -1; sb.st_blocks = -1; #endif phar_fancy_stat(&sb, type, return_value); return; } } skip_phar: orig_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } /* }}} */ #define PharFileFunction(fname, funcnum, orig) \ ZEND_NAMED_FUNCTION(fname) { \ if (!PHAR_G(intercepted)) { \ PHAR_G(orig)(INTERNAL_FUNCTION_PARAM_PASSTHRU); \ } else { \ char *filename; \ size_t filename_len; \ \ if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) { \ RETURN_THROWS(); \ } \ \ phar_file_stat(filename, filename_len, funcnum, PHAR_G(orig), INTERNAL_FUNCTION_PARAM_PASSTHRU); \ } \ } /* }}} */ /* {{{ Get file permissions */ PharFileFunction(phar_fileperms, FS_PERMS, orig_fileperms) /* }}} */ /* {{{ Get file inode */ PharFileFunction(phar_fileinode, FS_INODE, orig_fileinode) /* }}} */ /* {{{ Get file size */ PharFileFunction(phar_filesize, FS_SIZE, orig_filesize) /* }}} */ /* {{{ Get file owner */ PharFileFunction(phar_fileowner, FS_OWNER, orig_fileowner) /* }}} */ /* {{{ Get file group */ PharFileFunction(phar_filegroup, FS_GROUP, orig_filegroup) /* }}} */ /* {{{ Get last access time of file */ PharFileFunction(phar_fileatime, FS_ATIME, orig_fileatime) /* }}} */ /* {{{ Get last modification time of file */ PharFileFunction(phar_filemtime, FS_MTIME, orig_filemtime) /* }}} */ /* {{{ Get inode modification time of file */ PharFileFunction(phar_filectime, FS_CTIME, orig_filectime) /* }}} */ /* {{{ Get file type */ PharFileFunction(phar_filetype, FS_TYPE, orig_filetype) /* }}} */ /* {{{ Returns true if file can be written */ PharFileFunction(phar_is_writable, FS_IS_W, orig_is_writable) /* }}} */ /* {{{ Returns true if file can be read */ PharFileFunction(phar_is_readable, FS_IS_R, orig_is_readable) /* }}} */ /* {{{ Returns true if file is executable */ PharFileFunction(phar_is_executable, FS_IS_X, orig_is_executable) /* }}} */ /* {{{ Returns true if filename exists */ PharFileFunction(phar_file_exists, FS_EXISTS, orig_file_exists) /* }}} */ /* {{{ Returns true if file is directory */ PharFileFunction(phar_is_dir, FS_IS_DIR, orig_is_dir) /* }}} */ PHAR_FUNC(phar_is_file) /* {{{ */ { char *filename; size_t filename_len; if (!PHAR_G(intercepted)) { goto skip_phar; } if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map)))) && !HT_IS_INITIALIZED(&cached_phars)) { goto skip_phar; } if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) { goto skip_phar; } if (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://")) { char *arch, *entry; size_t arch_len, entry_len; zend_string *fname = zend_get_executed_filename_ex(); /* 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 (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) { goto skip_phar; } if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) { phar_archive_data *phar; efree(entry); entry = filename; /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file within the current directory, so use this if possible */ if (SUCCESS == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) { phar_entry_info *etemp; entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1); if (entry[0] == '/') { if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) { /* this file is not in the current directory, use the original path */ found_it: efree(entry); efree(arch); RETURN_BOOL(!etemp->is_dir); } } else { if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry, entry_len))) { goto found_it; } } } if (entry != filename) { 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; size_t filename_len; if (!PHAR_G(intercepted)) { goto skip_phar; } if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map)))) && !HT_IS_INITIALIZED(&cached_phars)) { goto skip_phar; } if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) { goto skip_phar; } if (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://")) { char *arch, *entry; size_t arch_len, entry_len; zend_string *fname = zend_get_executed_filename_ex(); /* 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 (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) { goto skip_phar; } if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) { phar_archive_data *phar; efree(entry); entry = filename; /* fopen within phar, if :// is not in the url, then prepend phar:/// */ entry_len = filename_len; /* retrieving a file within the current directory, so use this if possible */ if (SUCCESS == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) { phar_entry_info *etemp; entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1); if (entry[0] == '/') { if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) { /* this file is not in the current directory, use the original path */ found_it: efree(entry); efree(arch); RETURN_BOOL(etemp->link); } } else { if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry, entry_len))) { goto found_it; } } } efree(entry); efree(arch); RETURN_FALSE; } } skip_phar: PHAR_G(orig_is_link)(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } /* }}} */ /* {{{ Give information about a file or symbolic link */ PharFileFunction(phar_lstat, FS_LSTAT, orig_lstat) /* }}} */ /* {{{ Give information about a file */ PharFileFunction(phar_stat, FS_STAT, orig_stat) /* }}} */ /* {{{ void phar_intercept_functions(void) */ void phar_intercept_functions(void) { if (!PHAR_G(request_init)) { PHAR_G(cwd) = NULL; PHAR_G(cwd_len) = 0; } PHAR_G(intercepted) = 1; } /* }}} */ /* {{{ void phar_release_functions(void) */ void phar_release_functions(void) { PHAR_G(intercepted) = 0; } /* }}} */ /* {{{ void phar_intercept_functions_init(void) */ #define PHAR_INTERCEPT(func) \ PHAR_G(orig_##func) = NULL; \ if (NULL != (orig = zend_hash_str_find_ptr(CG(function_table), #func, sizeof(#func)-1))) { \ PHAR_G(orig_##func) = orig->internal_function.handler; \ orig->internal_function.handler = phar_##func; \ } void phar_intercept_functions_init(void) { zend_function *orig; 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); PHAR_INTERCEPT(readfile); PHAR_G(intercepted) = 0; } /* }}} */ /* {{{ void phar_intercept_functions_shutdown(void) */ #define PHAR_RELEASE(func) \ if (PHAR_G(orig_##func) && NULL != (orig = zend_hash_str_find_ptr(CG(function_table), #func, sizeof(#func)-1))) { \ orig->internal_function.handler = PHAR_G(orig_##func); \ } \ PHAR_G(orig_##func) = NULL; void phar_intercept_functions_shutdown(void) { 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_RELEASE(readfile); PHAR_G(intercepted) = 0; } /* }}} */ static struct _phar_orig_functions { zif_handler orig_fopen; zif_handler orig_file_get_contents; zif_handler orig_is_file; zif_handler orig_is_link; zif_handler orig_is_dir; zif_handler orig_opendir; zif_handler orig_file_exists; zif_handler orig_fileperms; zif_handler orig_fileinode; zif_handler orig_filesize; zif_handler orig_fileowner; zif_handler orig_filegroup; zif_handler orig_fileatime; zif_handler orig_filemtime; zif_handler orig_filectime; zif_handler orig_filetype; zif_handler orig_is_writable; zif_handler orig_is_readable; zif_handler orig_is_executable; zif_handler orig_lstat; zif_handler orig_readfile; zif_handler orig_stat; } phar_orig_functions = {0}; void phar_save_orig_functions(void) /* {{{ */ { phar_orig_functions.orig_fopen = PHAR_G(orig_fopen); phar_orig_functions.orig_file_get_contents = PHAR_G(orig_file_get_contents); phar_orig_functions.orig_is_file = PHAR_G(orig_is_file); phar_orig_functions.orig_is_link = PHAR_G(orig_is_link); phar_orig_functions.orig_is_dir = PHAR_G(orig_is_dir); phar_orig_functions.orig_opendir = PHAR_G(orig_opendir); phar_orig_functions.orig_file_exists = PHAR_G(orig_file_exists); phar_orig_functions.orig_fileperms = PHAR_G(orig_fileperms); phar_orig_functions.orig_fileinode = PHAR_G(orig_fileinode); phar_orig_functions.orig_filesize = PHAR_G(orig_filesize); phar_orig_functions.orig_fileowner = PHAR_G(orig_fileowner); phar_orig_functions.orig_filegroup = PHAR_G(orig_filegroup); phar_orig_functions.orig_fileatime = PHAR_G(orig_fileatime); phar_orig_functions.orig_filemtime = PHAR_G(orig_filemtime); phar_orig_functions.orig_filectime = PHAR_G(orig_filectime); phar_orig_functions.orig_filetype = PHAR_G(orig_filetype); phar_orig_functions.orig_is_writable = PHAR_G(orig_is_writable); phar_orig_functions.orig_is_readable = PHAR_G(orig_is_readable); phar_orig_functions.orig_is_executable = PHAR_G(orig_is_executable); phar_orig_functions.orig_lstat = PHAR_G(orig_lstat); phar_orig_functions.orig_readfile = PHAR_G(orig_readfile); phar_orig_functions.orig_stat = PHAR_G(orig_stat); } /* }}} */ void phar_restore_orig_functions(void) /* {{{ */ { PHAR_G(orig_fopen) = phar_orig_functions.orig_fopen; PHAR_G(orig_file_get_contents) = phar_orig_functions.orig_file_get_contents; PHAR_G(orig_is_file) = phar_orig_functions.orig_is_file; PHAR_G(orig_is_link) = phar_orig_functions.orig_is_link; PHAR_G(orig_is_dir) = phar_orig_functions.orig_is_dir; PHAR_G(orig_opendir) = phar_orig_functions.orig_opendir; PHAR_G(orig_file_exists) = phar_orig_functions.orig_file_exists; PHAR_G(orig_fileperms) = phar_orig_functions.orig_fileperms; PHAR_G(orig_fileinode) = phar_orig_functions.orig_fileinode; PHAR_G(orig_filesize) = phar_orig_functions.orig_filesize; PHAR_G(orig_fileowner) = phar_orig_functions.orig_fileowner; PHAR_G(orig_filegroup) = phar_orig_functions.orig_filegroup; PHAR_G(orig_fileatime) = phar_orig_functions.orig_fileatime; PHAR_G(orig_filemtime) = phar_orig_functions.orig_filemtime; PHAR_G(orig_filectime) = phar_orig_functions.orig_filectime; PHAR_G(orig_filetype) = phar_orig_functions.orig_filetype; PHAR_G(orig_is_writable) = phar_orig_functions.orig_is_writable; PHAR_G(orig_is_readable) = phar_orig_functions.orig_is_readable; PHAR_G(orig_is_executable) = phar_orig_functions.orig_is_executable; PHAR_G(orig_lstat) = phar_orig_functions.orig_lstat; PHAR_G(orig_readfile) = phar_orig_functions.orig_readfile; PHAR_G(orig_stat) = phar_orig_functions.orig_stat; } /* }}} */