]> granicus.if.org Git - php/commitdiff
Allow user streams/wrappers to implement fstat(), opendir() and stat().
authorWez Furlong <wez@php.net>
Sat, 28 Sep 2002 13:05:47 +0000 (13:05 +0000)
committerWez Furlong <wez@php.net>
Sat, 28 Sep 2002 13:05:47 +0000 (13:05 +0000)
main/php_streams.h
main/streams.c
main/user_streams.c

index ff7861a452a51bbf673697d384f838d09e6ceca7..fb260a713064a5b55ed5c531d476c6a5b9a99c39 100755 (executable)
@@ -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);
index 432f27fad192540517c6490bc1cd99a0cc164f3f..68928548e4efaf5bfe3aef356a51872e2a941a37 100755 (executable)
@@ -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);
index 5c4ef63b921f03295ce2a119eee1547a9f066b52..b74715ce9219a39ec888862e2419089070e3fd9c 100644 (file)
@@ -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 */
 };