Browse Source

Allow user streams/wrappers to implement fstat(), opendir() and stat().

migration/unlabaled-1.1.2
Wez Furlong 24 years ago
parent
commit
84e0df3dfe
  1. 2
      main/php_streams.h
  2. 143
      main/streams.c
  3. 361
      main/user_streams.c

2
main/php_streams.h

@ -514,7 +514,9 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char * path, char * mode, int opt
/* for user-space streams */
PHPAPI extern php_stream_ops php_stream_userspace_ops;
PHPAPI extern php_stream_ops php_stream_userspace_dir_ops;
#define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops
#define PHP_STREAM_IS_USERSPACE_DIR &php_stream_userspace_dir_ops
PHPAPI void php_stream_context_free(php_stream_context *context);
PHPAPI php_stream_context *php_stream_context_alloc(void);

143
main/streams.c

@ -133,6 +133,72 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream *
return PHP_STREAM_PERSISTENT_NOT_EXIST;
}
static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
{
char *tmp = estrdup(path);
char *msg;
int free_msg = 0;
if (wrapper) {
if (wrapper->err_count > 0) {
int i;
size_t l;
int brlen;
char *br;
if (PG(html_errors)) {
brlen = 7;
br = "<br />\n";
} else {
brlen = 1;
br = "\n";
}
for (i = 0, l = 0; i < wrapper->err_count; i++) {
l += strlen(wrapper->err_stack[i]);
if (i < wrapper->err_count - 1)
l += brlen;
}
msg = emalloc(l + 1);
msg[0] = '\0';
for (i = 0; i < wrapper->err_count; i++) {
strcat(msg, wrapper->err_stack[i]);
if (i < wrapper->err_count - 1)
strcat(msg, br);
}
free_msg = 1;
} else {
msg = strerror(errno);
}
} else {
msg = "no suitable wrapper could be found";
}
php_strip_url_passwd(tmp);
php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg);
efree(tmp);
if (free_msg)
efree(msg);
}
static void tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
{
if (wrapper) {
/* tidy up the error stack */
int i;
for (i = 0; i < wrapper->err_count; i++)
efree(wrapper->err_stack[i]);
if (wrapper->err_stack)
efree(wrapper->err_stack);
wrapper->err_stack = NULL;
wrapper->err_count = 0;
}
}
/* allocate a new stream for a particular ops */
PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */
{
@ -1858,28 +1924,21 @@ PHPAPI php_stream *_php_stream_opendir(char *path, int options,
if (wrapper && wrapper->wops->dir_opener) {
stream = wrapper->wops->dir_opener(wrapper,
path_to_open, "r", options, NULL,
path_to_open, "r", options ^ REPORT_ERRORS, NULL,
context STREAMS_REL_CC TSRMLS_CC);
if (stream) {
stream->wrapper = wrapper;
stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
}
} else if (wrapper) {
php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
}
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);
php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s", msg);
efree(tmp);
display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
}
tidy_wrapper_error_log(wrapper TSRMLS_CC);
return stream;
}
/* }}} */
@ -1983,63 +2042,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio
}
}
if (stream == NULL && (options & REPORT_ERRORS)) {
char *tmp = estrdup(path);
char *msg;
int free_msg = 0;
if (wrapper) {
if (wrapper->err_count > 0) {
int i;
size_t l;
int brlen;
char *br;
if (PG(html_errors)) {
brlen = 7;
br = "<br />\n";
} else {
brlen = 1;
br = "\n";
}
for (i = 0, l = 0; i < wrapper->err_count; i++) {
l += strlen(wrapper->err_stack[i]);
if (i < wrapper->err_count - 1)
l += brlen;
}
msg = emalloc(l + 1);
msg[0] = '\0';
for (i = 0; i < wrapper->err_count; i++) {
strcat(msg, wrapper->err_stack[i]);
if (i < wrapper->err_count - 1)
strcat(msg, br);
}
free_msg = 1;
} else {
msg = strerror(errno);
}
} else {
msg = "no suitable wrapper could be found";
}
php_strip_url_passwd(tmp);
php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "failed to create stream: %s", msg);
efree(tmp);
if (free_msg)
efree(msg);
}
if (wrapper) {
/* tidy up the error stack */
int i;
for (i = 0; i < wrapper->err_count; i++)
efree(wrapper->err_stack[i]);
if (wrapper->err_stack)
efree(wrapper->err_stack);
wrapper->err_stack = NULL;
wrapper->err_count = 0;
display_wrapper_errors(wrapper, path, "failed to create stream" TSRMLS_CC);
}
tidy_wrapper_error_log(wrapper TSRMLS_CC);
#if ZEND_DEBUG
if (stream == NULL && copy_of_path != NULL)
efree(copy_of_path);

361
main/user_streams.c

@ -33,12 +33,17 @@ struct php_user_stream_wrapper {
};
static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC);
static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
static php_stream_wrapper_ops user_stream_wops = {
user_wrapper_opener,
NULL,
NULL,
NULL
NULL, /* close - the streams themselves know how */
NULL, /* stat - the streams themselves know how */
user_wrapper_stat_url,
user_wrapper_opendir,
"user-space"
};
@ -83,8 +88,14 @@ typedef struct _php_userstream_data php_userstream_data_t;
#define USERSTREAM_SEEK "stream_seek"
#define USERSTREAM_TELL "stream_tell"
#define USERSTREAM_EOF "stream_eof"
/* class should have methods like these:
#define USERSTREAM_STAT "stream_stat"
#define USERSTREAM_STATURL "url_stat"
#define USERSTREAM_DIR_OPEN "dir_opendir"
#define USERSTREAM_DIR_READ "dir_readdir"
#define USERSTREAM_DIR_REWIND "dir_rewinddir"
#define USERSTREAM_DIR_CLOSE "dir_closedir"
/* {{{ class should have methods like these:
function stream_open($path, $mode, $options, &$opened_path)
{
@ -126,8 +137,38 @@ typedef struct _php_userstream_data php_userstream_data_t;
{
return true/false;
}
function stream_stat()
{
return array( just like that returned by fstat() );
}
function url_stat(string $url)
{
return array( just like that returned by stat() );
}
function dir_opendir(string $url, int $options)
{
return true / false;
}
function dir_readdir()
{
return string next filename in dir ;
}
function dir_closedir()
{
release dir related resources;
}
function dir_rewinddir()
{
reset to start of dir list;
}
**/
}}} **/
static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
{
@ -138,7 +179,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
int call_result;
php_stream *stream = NULL;
/* Try to catch bad usage without prevent flexibility */
/* Try to catch bad usage without preventing flexibility */
if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
return NULL;
@ -219,6 +260,81 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
return stream;
}
static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
{
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
php_userstream_data_t *us;
zval *zfilename, *zoptions, *zretval = NULL, *zfuncname;
zval **args[2];
int call_result;
php_stream *stream = NULL;
/* Try to catch bad usage without preventing flexibility */
if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
return NULL;
}
FG(user_stream_current_filename) = filename;
us = emalloc(sizeof(*us));
us->wrapper = uwrap;
/* create an instance of our class */
ALLOC_ZVAL(us->object);
object_init_ex(us->object, uwrap->ce);
ZVAL_REFCOUNT(us->object) = 1;
PZVAL_IS_REF(us->object) = 1;
/* call it's dir_open method - set up params first */
MAKE_STD_ZVAL(zfilename);
ZVAL_STRING(zfilename, filename, 1);
args[0] = &zfilename;
MAKE_STD_ZVAL(zoptions);
ZVAL_LONG(zoptions, options);
args[1] = &zoptions;
MAKE_STD_ZVAL(zfuncname);
ZVAL_STRING(zfuncname, USERSTREAM_DIR_OPEN, 1);
call_result = call_user_function_ex(NULL,
&us->object,
zfuncname,
&zretval,
2, args,
0, NULL TSRMLS_CC);
if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) {
/* the stream is now open! */
stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
/* set wrapper data to be a reference to our object */
stream->wrapperdata = us->object;
zval_add_ref(&stream->wrapperdata);
} else {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
us->wrapper->classname);
}
/* destroy everything else */
if (stream == NULL) {
zval_ptr_dtor(&us->object);
efree(us);
}
if (zretval)
zval_ptr_dtor(&zretval);
zval_ptr_dtor(&zfuncname);
zval_ptr_dtor(&zoptions);
zval_ptr_dtor(&zfilename);
FG(user_stream_current_filename) = NULL;
return stream;
}
/* {{{ proto bool file_register_wrapper(string protocol, string classname)
Registers a custom URL protocol handler class */
PHP_FUNCTION(file_register_wrapper)
@ -286,16 +402,16 @@ static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t
didwrite = 0;
if (call_result == SUCCESS && retval != NULL) {
convert_to_long_ex(&retval);
convert_to_long(retval);
didwrite = Z_LVAL_P(retval);
} else if (call_result == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " - is not implemented!",
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
us->wrapper->classname);
}
/* don't allow strange buffer overruns due to bogus return */
if (didwrite > count) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " - wrote %d bytes more data than requested (%d written, %d max)",
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " wrote %d bytes more data than requested (%d written, %d max)",
us->wrapper->classname,
didwrite - count, didwrite, count);
didwrite = count;
@ -331,7 +447,7 @@ static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
didread = 0;
else {
if (call_result == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_EOF " - is not implemented! Assuming EOF",
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
us->wrapper->classname);
}
@ -355,7 +471,7 @@ static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
0, NULL TSRMLS_CC);
if (call_result == SUCCESS && retval != NULL) {
convert_to_string_ex(&retval);
convert_to_string(retval);
didread = Z_STRLEN_P(retval);
if (didread > count) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - read %d bytes more data than requested (%d read, %d max) - excess data will be lost",
@ -365,7 +481,7 @@ static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
if (didread > 0)
memcpy(buf, Z_STRVAL_P(retval), didread);
} else if (call_result == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - is not implemented!",
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
us->wrapper->classname);
}
zval_ptr_dtor(&zcount);
@ -488,21 +604,238 @@ static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, o
if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG)
*newoffs = Z_LVAL_P(retval);
else
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " - is not implemented!",
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!",
us->wrapper->classname);
if (retval)
zval_ptr_dtor(&retval);
return 0;
}
/* parse the return value from one of the stat functions and store the
* relevant fields into the statbuf provided */
static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC)
{
zval *elem;
#define STAT_PROP_ENTRY(name) \
if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), (void**)&elem)) { \
convert_to_long(elem); \
ssb->sb.st_##name = Z_LVAL_P(elem); \
}
STAT_PROP_ENTRY(dev);
STAT_PROP_ENTRY(ino);
STAT_PROP_ENTRY(mode);
STAT_PROP_ENTRY(nlink);
STAT_PROP_ENTRY(uid);
STAT_PROP_ENTRY(gid);
#if HAVE_ST_RDEV
STAT_PROP_ENTRY(rdev);
#endif
STAT_PROP_ENTRY(size);
STAT_PROP_ENTRY(atime);
STAT_PROP_ENTRY(mtime);
STAT_PROP_ENTRY(ctime);
#ifdef HAVE_ST_BLKSIZE
STAT_PROP_ENTRY(blksize);
#endif
#ifdef HAVE_ST_BLOCKS
STAT_PROP_ENTRY(blocks);
#endif
#undef STAT_PROP_ENTRY
return SUCCESS;
}
static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
{
zval func_name;
zval *retval = NULL;
int call_result;
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
int ret = -1;
ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1, 0);
call_result = call_user_function_ex(NULL,
&us->object,
&func_name,
&retval,
0, NULL, 0, NULL TSRMLS_CC);
if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_ARRAY) {
if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC))
ret = 0;
} else {
if (call_result == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
us->wrapper->classname);
}
}
return ret;
}
static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC)
{
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
zval *zfilename, *zfuncname, *zretval;
zval **args[1];
int call_result;
zval *object;
int ret = -1;
/* create an instance of our class */
ALLOC_ZVAL(object);
object_init_ex(object, uwrap->ce);
ZVAL_REFCOUNT(object) = 1;
PZVAL_IS_REF(object) = 1;
/* call the stat_url method */
/* call it's stream_open method - set up params first */
MAKE_STD_ZVAL(zfilename);
ZVAL_STRING(zfilename, url, 1);
args[0] = &zfilename;
MAKE_STD_ZVAL(zfuncname);
ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1);
call_result = call_user_function_ex(NULL,
&object,
zfuncname,
&zretval,
1, args,
0, NULL TSRMLS_CC);
if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == IS_ARRAY) {
/* We got the info we needed */
if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC))
ret = 0;
} else {
if (call_result == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
uwrap->classname);
}
}
/* clean up */
zval_ptr_dtor(&object);
if (zretval)
zval_ptr_dtor(&zretval);
zval_ptr_dtor(&zfuncname);
zval_ptr_dtor(&zfilename);
return ret;
}
static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
zval func_name;
zval *retval = NULL;
int call_result;
size_t didread = 0;
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
php_stream_dirent *ent = (php_stream_dirent*)buf;
/* avoid problems if someone mis-uses the stream */
if (count != sizeof(php_stream_dirent))
return 0;
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1, 0);
call_result = call_user_function_ex(NULL,
&us->object,
&func_name,
&retval,
0, NULL,
0, NULL TSRMLS_CC);
if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) != IS_BOOL) {
convert_to_string(retval);
PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), Z_STRLEN_P(retval));
didread = sizeof(php_stream_dirent);
} else if (call_result == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
us->wrapper->classname);
}
if (retval)
zval_ptr_dtor(&retval);
return didread;
}
static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC)
{
zval func_name;
zval *retval = NULL;
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
assert(us != NULL);
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, 0);
call_user_function_ex(NULL,
&us->object,
&func_name,
&retval,
0, NULL, 0, NULL TSRMLS_CC);
if (retval)
zval_ptr_dtor(&retval);
zval_ptr_dtor(&us->object);
efree(us);
return 0;
}
static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
{
zval func_name;
zval *retval = NULL;
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1, 0);
call_user_function_ex(NULL,
&us->object,
&func_name,
&retval,
0, NULL, 0, NULL TSRMLS_CC);
if (retval)
zval_ptr_dtor(&retval);
return 0;
}
php_stream_ops php_stream_userspace_ops = {
php_userstreamop_write, php_userstreamop_read,
php_userstreamop_close, php_userstreamop_flush,
"user-space",
php_userstreamop_seek,
NULL, /* cast */
php_userstreamop_stat, /* stat */
NULL /* set_option */
};
php_stream_ops php_stream_userspace_dir_ops = {
NULL, /* write */
php_userstreamop_readdir,
php_userstreamop_closedir,
NULL, /* flush */
"user-space-dir",
php_userstreamop_rewinddir,
NULL, /* cast */
NULL, /* stat */
NULL /* set_option */
};

Loading…
Cancel
Save