diff options
Diffstat (limited to 'main')
| -rw-r--r-- | main/php.h | 9 | ||||
| -rwxr-xr-x | main/php_streams.h | 31 | ||||
| -rwxr-xr-x | main/streams.c | 269 |
3 files changed, 249 insertions, 60 deletions
diff --git a/main/php.h b/main/php.h index 6784e06df9..026e5713e8 100644 --- a/main/php.h +++ b/main/php.h @@ -171,10 +171,6 @@ typedef unsigned int socklen_t; char *strerror(int); #endif -#include "php_streams.h" -#include "php_memory_streams.h" -#include "fopen_wrappers.h" - #if (REGEX == 1 || REGEX == 0) && !defined(NO_REGEX_EXTRA_H) #include "regex/regex_extra.h" #endif @@ -221,6 +217,11 @@ char *strerror(int); # endif #endif +#include "php_streams.h" +#include "php_memory_streams.h" +#include "fopen_wrappers.h" + + /* global variables */ extern pval *data; #if !defined(PHP_WIN32) diff --git a/main/php_streams.h b/main/php_streams.h index aaf7f01e17..af1bcefc23 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -104,6 +104,10 @@ typedef struct _php_stream_statbuf { /* extended info to go here some day */ } php_stream_statbuf; +typedef struct _php_stream_dirent { + char d_name[MAXPATHLEN]; +} php_stream_dirent; + #define PHP_STREAM_NOTIFIER_PROGRESS 1 typedef struct _php_stream_notifier { @@ -128,6 +132,7 @@ typedef struct _php_stream_wrapper_options { * to go here some day */ } php_stream_wrapper_options; +/* operations on streams that are file-handles */ typedef struct _php_stream_ops { /* stdio like functions - these are mandatory! */ size_t (*write)(php_stream *stream, const char *buf, size_t count TSRMLS_DC); @@ -146,22 +151,26 @@ typedef struct _php_stream_ops { typedef struct _php_stream_wrapper_ops { /* open/create a wrapped stream */ - php_stream *(*opener)(php_stream_wrapper *wrapper, char *filename, char *mode, + php_stream *(*stream_opener)(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); /* close/destroy a wrapped stream */ - int (*closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC); + int (*stream_closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC); /* stat a wrapped stream */ int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); /* stat a URL */ - int (*url_stat)(php_stream_wrapper *wrapper, php_stream_statbuf *ssb TSRMLS_DC); + int (*url_stat)(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC); + /* open a "directory" stream */ + php_stream *(*dir_opener)(php_stream_wrapper *wrapper, char *filename, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); + } php_stream_wrapper_ops; struct _php_stream_wrapper { php_stream_wrapper_ops *wops; /* operations the wrapper can perform */ void *abstract; /* context for the wrapper */ + int is_url; /* so that PG(allow_url_fopen) can be respected */ }; - struct _php_stream { php_stream_ops *ops; void *abstract; /* convenience pointer for abstraction */ @@ -255,12 +264,24 @@ PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC); PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); #define php_stream_stat(stream, ssb) _php_stream_stat((stream), (ssb) TSRMLS_CC) +PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC); +#define php_stream_stat_path(path, ssb) _php_stream_stat((path), (ssb) TSRMLS_CC) + +PHPAPI php_stream *_php_stream_opendir(char *path, int options, php_stream_context *context STREAMS_DC TSRMLS_DC); +#define php_stream_opendir(path, options, context) _php_stream_opendir((path), (options), (context) STREAMS_CC TSRMLS_CC) +PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC); +#define php_stream_readdir(dirstream, dirent) _php_stream_readdir((dirstream), (dirent) TSRMLS_CC) +#define php_stream_closedir(dirstream) php_stream_close((dirstream)) +#define php_stream_rewinddir(dirstream) php_stream_rewind((dirstream)) + + /* copy up to maxlen bytes from src to dest. If maxlen is PHP_STREAM_COPY_ALL, copy until eof(src). * Uses mmap if the src is a plain file and at offset 0 */ #define PHP_STREAM_COPY_ALL -1 PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC); #define php_stream_copy_to_stream(src, dest, maxlen) _php_stream_copy_to_stream((src), (dest), (maxlen) STREAMS_CC TSRMLS_CC) + /* read all data from stream and put into a buffer. Caller must free buffer when done. * The copy will use mmap if available. */ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, @@ -364,7 +385,7 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char * path, char * mode, int opt #define php_stream_open_wrapper_as_file(path, mode, options, opened_path) _php_stream_open_wrapper_as_file((path), (mode), (options), (opened_path) STREAMS_CC TSRMLS_CC) /* for user-space streams */ -extern php_stream_ops php_stream_userspace_ops; +PHPAPI extern php_stream_ops php_stream_userspace_ops; #define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops PHPAPI void php_stream_context_free(php_stream_context *context); diff --git a/main/streams.c b/main/streams.c index 84809d2b57..a6cfccddb8 100755 --- a/main/streams.c +++ b/main/streams.c @@ -45,6 +45,8 @@ #define STREAM_DEBUG 0 +#define STREAM_WRAPPER_PLAIN_FILES ((php_stream_wrapper*)-1) + /* {{{ some macros to help track leaks */ #if ZEND_DEBUG #define emalloc_rel_orig(size) \ @@ -134,8 +136,8 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label, if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) { - if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->closer) { - stream->wrapper->wops->closer(stream->wrapper, stream TSRMLS_CC); + if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) { + stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC); stream->wrapper = NULL; } @@ -169,7 +171,7 @@ fprintf(stderr, "stream_free: %s:%p in_free=%d opts=%08x\n", stream->ops->label, /* {{{ generic stream operations */ PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC) { - return stream->ops->read(stream, buf, size TSRMLS_CC); + return stream->ops->read == NULL ? 0 : stream->ops->read(stream, buf, size TSRMLS_CC); } PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC) @@ -177,7 +179,7 @@ PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC) /* we define our stream reading function so that it must return EOF when an EOF condition occurs, when working in unbuffered mode and called with these args */ - return stream->ops->read(stream, NULL, 0 TSRMLS_CC) == EOF ? 1 : 0; + return stream->ops->read == NULL ? -1 : stream->ops->read(stream, NULL, 0 TSRMLS_CC) == EOF ? 1 : 0; } PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC) @@ -242,6 +244,8 @@ PHPAPI char *_php_stream_gets(php_stream *stream, char *buf, size_t maxlen TSRML if (stream->ops->gets) { return stream->ops->gets(stream, buf, maxlen TSRMLS_CC); + } else if (stream->ops->read == NULL) { + return NULL; } else { /* unbuffered fgets - poor performance ! */ size_t n = 1; @@ -273,7 +277,7 @@ PHPAPI int _php_stream_flush(php_stream *stream TSRMLS_DC) PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) { assert(stream); - if (buf == NULL || count == 0) + if (buf == NULL || count == 0 || stream->ops->write == NULL) return 0; return stream->ops->write(stream, buf, count TSRMLS_CC); } @@ -1037,45 +1041,145 @@ exit_success: /* {{{ wrapper init and registration */ int php_init_stream_wrappers(TSRMLS_D) { - if (PG(allow_url_fopen)) { - return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1); - } - return SUCCESS; + return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1); } int php_shutdown_stream_wrappers(TSRMLS_D) { - if (PG(allow_url_fopen)) { - zend_hash_destroy(&url_stream_wrappers_hash); - } + zend_hash_destroy(&url_stream_wrappers_hash); return SUCCESS; } PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC) { - if (PG(allow_url_fopen)) { - return zend_hash_add(&url_stream_wrappers_hash, protocol, strlen(protocol), wrapper, sizeof(*wrapper), NULL); - } - return FAILURE; + if (!PG(allow_url_fopen) && wrapper->is_url) + return FAILURE; + + return zend_hash_add(&url_stream_wrappers_hash, protocol, strlen(protocol), wrapper, sizeof(*wrapper), NULL); } PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC) { - if (PG(allow_url_fopen)) { - return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol)); - } - return SUCCESS; + return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol)); } /* }}} */ -/* {{{ php_stream_open_url */ -static php_stream *php_stream_open_url(char *path, char *mode, int options, - char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) + +static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) { - php_stream_wrapper *wrapper; + DIR *dir = (DIR*)stream->abstract; + /* avoid libc5 readdir problems */ + char entry[sizeof(struct dirent)+MAXPATHLEN]; + struct dirent *result = (struct dirent *)&entry; + php_stream_dirent *ent = (php_stream_dirent*)buf; + + /* avoid problems if someone mis-uses the stream */ + if (count != sizeof(php_stream_dirent)) + return 0; + + if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) { + PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); + return sizeof(php_stream_dirent); + } + return 0; +} + +static int php_plain_files_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + return closedir((DIR *)stream->abstract); +} + +static int php_plain_files_dirstream_rewind(php_stream *stream, off_t offset, int whence TSRMLS_DC) +{ + rewinddir((DIR *)stream->abstract); + return 0; +} + +static php_stream_ops php_plain_files_dirstream_ops = { + NULL, php_plain_files_dirstream_read, + php_plain_files_dirstream_close, NULL, + "dir", + php_plain_files_dirstream_rewind, + NULL, NULL, + NULL +}; + +static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char *path, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + DIR *dir = NULL; + php_stream *stream = NULL; + + if (php_check_open_basedir(path TSRMLS_CC)) { + return NULL; + } + + if (PG(safe_mode) &&(!php_checkuid(path, NULL, CHECKUID_ALLOW_ONLY_FILE))) { + return NULL; + } + + dir = VCWD_OPENDIR(path); + +#ifdef PHP_WIN32 + if (dir && dir->finished) { + closedir(dir); + dir = NULL; + } +#endif + if (dir) { + stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode); + if (stream == NULL) + closedir(dir); + } + + return stream; +} + + + +static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + if ((options & USE_PATH) && PG(include_path) != NULL) { + return php_stream_fopen_with_path_rel(path, mode, PG(include_path), opened_path); + } + + if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) + return NULL; + + return php_stream_fopen_rel(path, mode, opened_path); +} + +static int php_plain_files_url_stater(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC) +{ + return VCWD_STAT(url, &ssb->sb); +} + +static php_stream_wrapper_ops php_plain_files_wrapper_ops = { + php_plain_files_stream_opener, + NULL, + NULL, + php_plain_files_url_stater, + php_plain_files_dir_opener +}; + +static php_stream_wrapper php_plain_files_wrapper = { + &php_plain_files_wrapper_ops, + NULL, + 0 +}; + +static php_stream_wrapper *locate_url_wrapper(char *path, char **path_for_open, int options TSRMLS_DC) +{ + php_stream_wrapper *wrapper = NULL; const char *p, *protocol = NULL; int n = 0; + *path_for_open = path; + + if (options & IGNORE_URL) + return &php_plain_files_wrapper; + for (p = path; isalnum((int)*p); p++) { n++; } @@ -1103,57 +1207,113 @@ static php_stream *php_stream_open_url(char *path, char *mode, int options, wrapper = NULL; protocol = NULL; } - if (wrapper) { - php_stream *stream = wrapper->wops->opener(wrapper, path, mode, options, opened_path, context STREAMS_REL_CC TSRMLS_CC); - if (stream) - stream->wrapper = wrapper; - return stream; - } } - if (!protocol || !strncasecmp(protocol, "file", n)) { if (protocol && path[n+1] == '/' && path[n+2] == '/') { zend_error(E_WARNING, "remote host file access not supported, %s", path); return NULL; } if (protocol) - path += n + 1; - + *path_for_open = path + n + 1; + /* fall back on regular file access */ - return php_stream_open_wrapper_rel(path, mode, (options & ~REPORT_ERRORS) | IGNORE_URL, - opened_path); + return &php_plain_files_wrapper; } - return NULL; + + if (wrapper && wrapper->is_url && !PG(allow_url_fopen)) { + zend_error(E_WARNING, "URL file-access is disabled in the server configuration"); + return NULL; + } + + return wrapper; +} + +PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC) +{ + php_stream_wrapper *wrapper = NULL; + char *path_to_open = path; + + wrapper = locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC); + if (wrapper && wrapper->wops->url_stat) { + return wrapper->wops->url_stat(wrapper, path_to_open, ssb TSRMLS_CC); + } + return -1; +} + +/* {{{ php_stream_opendir */ +PHPAPI php_stream *_php_stream_opendir(char *path, int options, + php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = NULL; + php_stream_wrapper *wrapper = NULL; + char *path_to_open; + + if (!path || !*path) + return NULL; + + path_to_open = path; + + wrapper = locate_url_wrapper(path, &path_to_open, options TSRMLS_CC); + + if (wrapper && wrapper->wops->dir_opener) { + stream = wrapper->wops->dir_opener(wrapper, + path_to_open, "r", options, NULL, + context STREAMS_REL_CC TSRMLS_CC); + + if (stream) + stream->wrapper = wrapper; + } + + if (stream == NULL && (options & REPORT_ERRORS)) { + char *tmp = estrdup(path); + char *msg; + + if (wrapper) + msg = strerror(errno); + else + msg = "no suitable wrapper could be found"; + + php_strip_url_passwd(tmp); + zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, msg); + efree(tmp); + } + return stream; } /* }}} */ +PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC) +{ + if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent))) + return ent; + + return NULL; +} + /* {{{ php_stream_open_wrapper_ex */ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) { php_stream *stream = NULL; - + php_stream_wrapper *wrapper = NULL; + char *path_to_open; + if (opened_path) *opened_path = NULL; if (!path || !*path) return NULL; - if (PG(allow_url_fopen) && !(options & IGNORE_URL)) { - stream = php_stream_open_url(path, mode, options, opened_path, context STREAMS_REL_CC TSRMLS_CC); - goto out; - } + path_to_open = path; - if ((options & USE_PATH) && PG(include_path) != NULL) { - stream = php_stream_fopen_with_path_rel(path, mode, PG(include_path), opened_path); - goto out; - } + wrapper = locate_url_wrapper(path, &path_to_open, options TSRMLS_CC); - if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) - return NULL; - - stream = php_stream_fopen_rel(path, mode, opened_path); -out: + if (wrapper) { + stream = wrapper->wops->stream_opener(wrapper, + path_to_open, mode, options, + opened_path, context STREAMS_REL_CC TSRMLS_CC); + if (stream) + stream->wrapper = wrapper; + } if (stream != NULL && (options & STREAM_MUST_SEEK)) { php_stream *newstream; @@ -1179,8 +1339,15 @@ out: } if (stream == NULL && (options & REPORT_ERRORS)) { char *tmp = estrdup(path); + char *msg; + + if (wrapper) + msg = strerror(errno); + else + msg = "no suitable wrapper could be found"; + php_strip_url_passwd(tmp); - zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, strerror(errno)); + zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, msg); efree(tmp); } return stream; |
