320 lines
7.4 KiB
C
320 lines
7.4 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright © The PHP Group and Contributors. |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to the Modified BSD License that is |
|
|
| bundled with this package in the file LICENSE, and is available |
|
|
| through the World Wide Web at <https://www.php.net/license/>. |
|
|
| |
|
|
| SPDX-License-Identifier: BSD-3-Clause |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Marcus Boerger <helly@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "php.h"
|
|
#include "php_streams_int.h"
|
|
|
|
#include "php_glob.h"
|
|
|
|
typedef struct {
|
|
php_glob_t glob;
|
|
size_t index;
|
|
int flags;
|
|
char *path;
|
|
size_t path_len;
|
|
char *pattern;
|
|
size_t pattern_len;
|
|
size_t *open_basedir_indexmap;
|
|
size_t open_basedir_indexmap_size;
|
|
bool open_basedir_used;
|
|
} glob_s_t;
|
|
|
|
PHPAPI char* _php_glob_stream_get_path(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
|
|
{
|
|
glob_s_t *pglob = (glob_s_t *)stream->abstract;
|
|
|
|
if (pglob && pglob->path) {
|
|
if (plen) {
|
|
*plen = pglob->path_len;
|
|
}
|
|
return pglob->path;
|
|
} else {
|
|
if (plen) {
|
|
*plen = 0;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
PHPAPI char* _php_glob_stream_get_pattern(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
|
|
{
|
|
glob_s_t *pglob = (glob_s_t *)stream->abstract;
|
|
|
|
if (pglob && pglob->pattern) {
|
|
if (plen) {
|
|
*plen = pglob->pattern_len;
|
|
}
|
|
return pglob->pattern;
|
|
} else {
|
|
if (plen) {
|
|
*plen = 0;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
static inline int php_glob_stream_get_result_count(glob_s_t *pglob)
|
|
{
|
|
return pglob->open_basedir_used ? (int) pglob->open_basedir_indexmap_size : pglob->glob.gl_pathc;
|
|
}
|
|
|
|
PHPAPI int _php_glob_stream_get_count(php_stream *stream, int *pflags STREAMS_DC) /* {{{ */
|
|
{
|
|
glob_s_t *pglob = (glob_s_t *)stream->abstract;
|
|
|
|
if (pglob) {
|
|
if (pflags) {
|
|
*pflags = pglob->flags;
|
|
}
|
|
return php_glob_stream_get_result_count(pglob);
|
|
} else {
|
|
if (pflags) {
|
|
*pflags = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
static void php_glob_stream_path_split(glob_s_t *pglob, const char *path, int get_path, const char **p_file) /* {{{ */
|
|
{
|
|
const char *pos, *gpath = path;
|
|
|
|
if ((pos = strrchr(path, '/')) != NULL) {
|
|
path = pos+1;
|
|
}
|
|
#ifdef PHP_WIN32
|
|
if ((pos = strrchr(path, '\\')) != NULL) {
|
|
path = pos+1;
|
|
}
|
|
#endif
|
|
|
|
*p_file = path;
|
|
|
|
if (get_path) {
|
|
if (pglob->path) {
|
|
efree(pglob->path);
|
|
}
|
|
if ((path - gpath) > 1) {
|
|
path--;
|
|
}
|
|
pglob->path_len = path - gpath;
|
|
pglob->path = estrndup(gpath, pglob->path_len);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
static ssize_t php_glob_stream_read(php_stream *stream, char *buf, size_t count) /* {{{ */
|
|
{
|
|
glob_s_t *pglob = (glob_s_t *)stream->abstract;
|
|
php_stream_dirent *ent = (php_stream_dirent*)buf;
|
|
const char *path;
|
|
int glob_result_count;
|
|
size_t index;
|
|
|
|
/* avoid problems if someone mis-uses the stream */
|
|
if (count == sizeof(php_stream_dirent) && pglob) {
|
|
glob_result_count = php_glob_stream_get_result_count(pglob);
|
|
if (pglob->index < (size_t) glob_result_count) {
|
|
index = pglob->open_basedir_used && pglob->open_basedir_indexmap ?
|
|
pglob->open_basedir_indexmap[pglob->index] : pglob->index;
|
|
php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[index], pglob->flags & PHP_GLOB_APPEND, &path);
|
|
++pglob->index;
|
|
PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path));
|
|
ent->d_type = DT_UNKNOWN;
|
|
return sizeof(php_stream_dirent);
|
|
}
|
|
pglob->index = glob_result_count;
|
|
if (pglob->path) {
|
|
efree(pglob->path);
|
|
pglob->path = NULL;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
/* }}} */
|
|
|
|
static int php_glob_stream_close(php_stream *stream, int close_handle) /* {{{ */
|
|
{
|
|
glob_s_t *pglob = (glob_s_t *)stream->abstract;
|
|
|
|
if (pglob) {
|
|
pglob->index = 0;
|
|
php_globfree(&pglob->glob);
|
|
if (pglob->path) {
|
|
efree(pglob->path);
|
|
}
|
|
if (pglob->pattern) {
|
|
efree(pglob->pattern);
|
|
}
|
|
if (pglob->open_basedir_indexmap) {
|
|
efree(pglob->open_basedir_indexmap);
|
|
}
|
|
}
|
|
efree(stream->abstract);
|
|
return 0;
|
|
}
|
|
/* {{{ */
|
|
|
|
static int php_glob_stream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) /* {{{ */
|
|
{
|
|
glob_s_t *pglob = (glob_s_t *)stream->abstract;
|
|
|
|
if (pglob) {
|
|
pglob->index = 0;
|
|
if (pglob->path) {
|
|
efree(pglob->path);
|
|
pglob->path = NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
/* }}} */
|
|
|
|
const php_stream_ops php_glob_stream_ops = {
|
|
NULL, php_glob_stream_read,
|
|
php_glob_stream_close, NULL,
|
|
"glob",
|
|
php_glob_stream_rewind,
|
|
NULL, /* cast */
|
|
NULL, /* stat */
|
|
NULL /* set_option */
|
|
};
|
|
|
|
/* {{{ php_glob_stream_opener */
|
|
static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
|
|
int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
|
|
{
|
|
glob_s_t *pglob;
|
|
int ret, i;
|
|
const char *tmp, *pos;
|
|
|
|
if (!strncmp(path, "glob://", sizeof("glob://")-1)) {
|
|
path += sizeof("glob://")-1;
|
|
if (opened_path) {
|
|
*opened_path = zend_string_init(path, strlen(path), 0);
|
|
}
|
|
}
|
|
const char *pattern = path;
|
|
#ifdef ZTS
|
|
char cwd[MAXPATHLEN];
|
|
char work_pattern[MAXPATHLEN];
|
|
char *result;
|
|
size_t cwd_skip = 0;
|
|
if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
|
|
result = VCWD_GETCWD(cwd, MAXPATHLEN);
|
|
if (!result) {
|
|
cwd[0] = '\0';
|
|
}
|
|
# ifdef PHP_WIN32
|
|
if (IS_SLASH(*path)) {
|
|
cwd[2] = '\0';
|
|
}
|
|
# endif
|
|
cwd_skip = strlen(cwd)+1;
|
|
|
|
snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path);
|
|
pattern = work_pattern;
|
|
}
|
|
#endif
|
|
|
|
pglob = ecalloc(1, sizeof(*pglob));
|
|
|
|
if (0 != (ret = php_glob(pattern, pglob->flags & PHP_GLOB_FLAGMASK, NULL, &pglob->glob))) {
|
|
#ifdef PHP_GLOB_NOMATCH
|
|
if (PHP_GLOB_NOMATCH != ret)
|
|
#endif
|
|
{
|
|
efree(pglob);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef ZTS
|
|
if (cwd_skip > 0) {
|
|
/* strip prepended CWD */
|
|
for (i = 0; i < pglob->glob.gl_pathc; i++) {
|
|
char *p = pglob->glob.gl_pathv[i];
|
|
char *q = p + cwd_skip;
|
|
char *e = p + strlen(pglob->glob.gl_pathv[i]) - 1;
|
|
while (q <= e) {
|
|
*p++ = *q++;
|
|
}
|
|
*p = '\0';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* if open_basedir in use, check and filter restricted paths */
|
|
if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) {
|
|
pglob->open_basedir_used = true;
|
|
for (i = 0; i < pglob->glob.gl_pathc; i++) {
|
|
if (!php_check_open_basedir_ex(pglob->glob.gl_pathv[i], 0)) {
|
|
if (!pglob->open_basedir_indexmap) {
|
|
pglob->open_basedir_indexmap = (size_t *) safe_emalloc(
|
|
pglob->glob.gl_pathc, sizeof(size_t), 0);
|
|
}
|
|
pglob->open_basedir_indexmap[pglob->open_basedir_indexmap_size++] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
pos = path;
|
|
if ((tmp = strrchr(pos, '/')) != NULL) {
|
|
pos = tmp+1;
|
|
}
|
|
#ifdef PHP_WIN32
|
|
if ((tmp = strrchr(pos, '\\')) != NULL) {
|
|
pos = tmp+1;
|
|
}
|
|
#endif
|
|
|
|
pglob->pattern_len = strlen(pos);
|
|
pglob->pattern = estrndup(pos, pglob->pattern_len);
|
|
|
|
pglob->flags |= PHP_GLOB_APPEND;
|
|
|
|
if (pglob->glob.gl_pathc) {
|
|
php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[0], 1, &tmp);
|
|
} else {
|
|
php_glob_stream_path_split(pglob, path, 1, &tmp);
|
|
}
|
|
|
|
return php_stream_alloc(&php_glob_stream_ops, pglob, 0, mode);
|
|
}
|
|
/* }}} */
|
|
|
|
static const php_stream_wrapper_ops php_glob_stream_wrapper_ops = {
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
php_glob_stream_opener,
|
|
"glob",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
const php_stream_wrapper php_glob_stream_wrapper = {
|
|
&php_glob_stream_wrapper_ops,
|
|
NULL,
|
|
0
|
|
};
|