]> granicus.if.org Git - php/commitdiff
implement streams metadata API per RFC
authorStanislav Malyshev <stas@php.net>
Wed, 25 May 2011 21:03:55 +0000 (21:03 +0000)
committerStanislav Malyshev <stas@php.net>
Wed, 25 May 2011 21:03:55 +0000 (21:03 +0000)
ext/standard/filestat.c
ext/standard/tests/file/userstreams_007.phpt [new file with mode: 0644]
main/php_streams.h
main/streams/plain_wrapper.c
main/streams/userspace.c

index 85a6d16ecac9821d519023a75f7a0822da46924f..6ae7cdad3bf349223206cdddfa0af0bf5d9f2376 100644 (file)
@@ -386,21 +386,8 @@ PHP_FUNCTION(disk_free_space)
 /* }}} */
 
 #if !defined(WINDOWS) && !defined(NETWARE)
-static void php_do_chgrp(INTERNAL_FUNCTION_PARAMETERS, int do_lchgrp) /* {{{ */
+PHPAPI int php_get_gid_by_name(const char *name, gid_t *gid TSRMLS_DC)
 {
-       char *filename;
-       int filename_len;
-       zval *group;
-       gid_t gid;
-       int ret;
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/", &filename, &filename_len, &group) == FAILURE) {
-               RETURN_FALSE;
-       }
-
-       if (Z_TYPE_P(group) == IS_LONG) {
-               gid = (gid_t)Z_LVAL_P(group);
-       } else if (Z_TYPE_P(group) == IS_STRING) {
 #if defined(ZTS) && defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
                struct group gr;
                struct group *retgrptr;
@@ -408,26 +395,73 @@ static void php_do_chgrp(INTERNAL_FUNCTION_PARAMETERS, int do_lchgrp) /* {{{ */
                char *grbuf;
 
                if (grbuflen < 1) {
-                       RETURN_FALSE;
+                       return FAILURE;
                }
 
                grbuf = emalloc(grbuflen);
-               if (getgrnam_r(Z_STRVAL_P(group), &gr, grbuf, grbuflen, &retgrptr) != 0 || retgrptr == NULL) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find gid for %s", Z_STRVAL_P(group));
+               if (getgrnam_r(name, &gr, grbuf, grbuflen, &retgrptr) != 0 || retgrptr == NULL) {
                        efree(grbuf);
-                       RETURN_FALSE;
+                       return FAILURE;
                }
                efree(grbuf);
-               gid = gr.gr_gid;
+               *gid = gr.gr_gid;
 #else
-               struct group *gr = getgrnam(Z_STRVAL_P(group));
+               struct group *gr = getgrnam(name);
 
                if (!gr) {
+                       return FAILURE;
+               }
+               *gid = gr->gr_gid;
+#endif
+               return SUCCESS;
+}
+
+static void php_do_chgrp(INTERNAL_FUNCTION_PARAMETERS, int do_lchgrp) /* {{{ */
+{
+       char *filename;
+       int filename_len;
+       zval *group;
+       gid_t gid;
+       int ret;
+       php_stream_wrapper *wrapper;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/", &filename, &filename_len, &group) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       wrapper = php_stream_locate_url_wrapper(filename, NULL, 0 TSRMLS_CC);
+       if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
+               if(wrapper && wrapper->wops->stream_metadata) {
+                       int option;
+                       void *value;
+                       if (Z_TYPE_P(group) == IS_LONG) {
+                               option = PHP_STREAM_META_GROUP;
+                               value = &Z_LVAL_P(group);
+                       } else if (Z_TYPE_P(group) == IS_STRING) {
+                               option = PHP_STREAM_META_GROUP_NAME;
+                               value = Z_STRVAL_P(group);
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "parameter 2 should be string or integer, %s given", zend_zval_type_name(group));
+                               RETURN_FALSE;
+                       }
+                       if(wrapper->wops->stream_metadata(wrapper, filename, option, value, NULL TSRMLS_CC)) {
+                               RETURN_TRUE;
+                       } else {
+                               RETURN_FALSE;
+                       }
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can not call chgrp() for a non-standard stream");
+                       RETURN_FALSE;
+               }
+       }
+
+       if (Z_TYPE_P(group) == IS_LONG) {
+               gid = (gid_t)Z_LVAL_P(group);
+       } else if (Z_TYPE_P(group) == IS_STRING) {
+               if(php_get_gid_by_name(Z_STRVAL_P(group), &gid TSRMLS_CC) != SUCCESS) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find gid for %s", Z_STRVAL_P(group));
                        RETURN_FALSE;
                }
-               gid = gr->gr_gid;
-#endif
        } else {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "parameter 2 should be string or integer, %s given", zend_zval_type_name(group));
                RETURN_FALSE;
@@ -483,21 +517,8 @@ PHP_FUNCTION(lchgrp)
 #endif /* !NETWARE */
 
 #if !defined(WINDOWS) && !defined(NETWARE)
-static void php_do_chown(INTERNAL_FUNCTION_PARAMETERS, int do_lchown) /* {{{ */
+PHPAPI uid_t php_get_uid_by_name(const char *name, uid_t *uid TSRMLS_DC)
 {
-       char *filename;
-       int filename_len;
-       zval *user;
-       uid_t uid;
-       int ret;
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/", &filename, &filename_len, &user) == FAILURE) {
-               return;
-       }
-
-       if (Z_TYPE_P(user) == IS_LONG) {
-               uid = (uid_t)Z_LVAL_P(user);
-       } else if (Z_TYPE_P(user) == IS_STRING) {
 #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWNAM_R)
                struct passwd pw;
                struct passwd *retpwptr = NULL;
@@ -509,22 +530,69 @@ static void php_do_chown(INTERNAL_FUNCTION_PARAMETERS, int do_lchown) /* {{{ */
                }
 
                pwbuf = emalloc(pwbuflen);
-               if (getpwnam_r(Z_STRVAL_P(user), &pw, pwbuf, pwbuflen, &retpwptr) != 0 || retpwptr == NULL) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find uid for %s", Z_STRVAL_P(user));
+               if (getpwnam_r(name, &pw, pwbuf, pwbuflen, &retpwptr) != 0 || retpwptr == NULL) {
                        efree(pwbuf);
-                       RETURN_FALSE;
+                       return FAILURE;
                }
                efree(pwbuf);
-               uid = pw.pw_uid;
+               *uid = pw.pw_uid;
 #else
-               struct passwd *pw = getpwnam(Z_STRVAL_P(user));
+               struct passwd *pw = getpwnam(name);
 
                if (!pw) {
+                       return FAILURE;
+               }
+               *uid = pw->pw_uid;
+#endif
+               return SUCCESS;
+}
+
+static void php_do_chown(INTERNAL_FUNCTION_PARAMETERS, int do_lchown) /* {{{ */
+{
+       char *filename;
+       int filename_len;
+       zval *user;
+       uid_t uid;
+       int ret;
+       php_stream_wrapper *wrapper;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/", &filename, &filename_len, &user) == FAILURE) {
+               return;
+       }
+
+       wrapper = php_stream_locate_url_wrapper(filename, NULL, 0 TSRMLS_CC);
+       if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
+               if(wrapper && wrapper->wops->stream_metadata) {
+                       int option;
+                       void *value;
+                       if (Z_TYPE_P(user) == IS_LONG) {
+                               option = PHP_STREAM_META_OWNER;
+                               value = &Z_LVAL_P(user);
+                       } else if (Z_TYPE_P(user) == IS_STRING) {
+                               option = PHP_STREAM_META_OWNER_NAME;
+                               value = Z_STRVAL_P(user);
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "parameter 2 should be string or integer, %s given", zend_zval_type_name(user));
+                               RETURN_FALSE;
+                       }
+                       if(wrapper->wops->stream_metadata(wrapper, filename, option, value, NULL TSRMLS_CC)) {
+                               RETURN_TRUE;
+                       } else {
+                               RETURN_FALSE;
+                       }
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can not call chown() for a non-standard stream");
+                       RETURN_FALSE;
+               }
+       }
+
+       if (Z_TYPE_P(user) == IS_LONG) {
+               uid = (uid_t)Z_LVAL_P(user);
+       } else if (Z_TYPE_P(user) == IS_STRING) {
+               if(php_get_uid_by_name(Z_STRVAL_P(user), &uid TSRMLS_CC) != SUCCESS) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find uid for %s", Z_STRVAL_P(user));
                        RETURN_FALSE;
                }
-               uid = pw->pw_uid;
-#endif
        } else {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "parameter 2 should be string or integer, %s given", zend_zval_type_name(user));
                RETURN_FALSE;
@@ -589,11 +657,28 @@ PHP_FUNCTION(chmod)
        long mode;
        int ret;
        mode_t imode;
+       php_stream_wrapper *wrapper;
 
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &filename, &filename_len, &mode) == FAILURE) {
                return;
        }
 
+       wrapper = php_stream_locate_url_wrapper(filename, NULL, 0 TSRMLS_CC);
+       if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
+               if(wrapper && wrapper->wops->stream_metadata) {
+                       int option;
+                       void *value;
+                       if(wrapper->wops->stream_metadata(wrapper, filename, PHP_STREAM_META_ACCESS, &mode, NULL TSRMLS_CC)) {
+                               RETURN_TRUE;
+                       } else {
+                               RETURN_FALSE;
+                       }
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can not call chmod() for a non-standard stream");
+                       RETURN_FALSE;
+               }
+       }
+
        /* Check the basedir */
        if (php_check_open_basedir(filename TSRMLS_CC)) {
                RETURN_FALSE;
@@ -622,11 +707,16 @@ PHP_FUNCTION(touch)
        FILE *file;
        struct utimbuf newtimebuf;
        struct utimbuf *newtime = &newtimebuf;
+       php_stream_wrapper *wrapper;
 
        if (zend_parse_parameters(argc TSRMLS_CC, "s|ll", &filename, &filename_len, &filetime, &fileatime) == FAILURE) {
                return;
        }
 
+       if (!filename_len) {
+               RETURN_FALSE;
+       }
+
        switch (argc) {
                case 1:
 #ifdef HAVE_UTIME_NULL
@@ -647,6 +737,30 @@ PHP_FUNCTION(touch)
                        WRONG_PARAM_COUNT;
        }
 
+       wrapper = php_stream_locate_url_wrapper(filename, NULL, 0 TSRMLS_CC);
+       if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
+               if(wrapper && wrapper->wops->stream_metadata) {
+                       if(wrapper->wops->stream_metadata(wrapper, filename, PHP_STREAM_META_TOUCH, newtime, NULL TSRMLS_CC)) {
+                               RETURN_TRUE;
+                       } else {
+                               RETURN_FALSE;
+                       }
+               } else {
+                       php_stream *stream;
+                       if(argc > 1) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can not call touch() for a non-standard stream");
+                               RETURN_FALSE;
+                       }
+                       stream = php_stream_open_wrapper_ex(filename, "c", REPORT_ERRORS, NULL, NULL);
+                       if(stream != NULL) {
+                               php_stream_pclose(stream);
+                               RETURN_TRUE;
+                       } else {
+                               RETURN_FALSE;
+                       }
+               }
+       }
+
        /* Check the basedir */
        if (php_check_open_basedir(filename TSRMLS_CC)) {
                RETURN_FALSE;
diff --git a/ext/standard/tests/file/userstreams_007.phpt b/ext/standard/tests/file/userstreams_007.phpt
new file mode 100644 (file)
index 0000000..3f66f18
--- /dev/null
@@ -0,0 +1,49 @@
+--TEST--
+User-space streams: test metadata option
+--FILE--
+<?php
+class test_wrapper {
+       function stream_open($path, $mode, $openedpath) {
+               return true;
+       }
+    public function stream_metadata($path, $option, $var) {
+               echo "metadata: $path, $option\n";
+               if(is_array($var)) {
+                       echo join(",", $var);
+               } else {
+                       echo $var;
+               }
+               echo "\n";
+               return false;
+       }
+}
+
+var_dump(stream_wrapper_register('test', 'test_wrapper'));
+
+$fd = fopen("test://foo","r");
+touch("test://testdir/touch");
+touch("test://testdir/touch", 1);
+touch("test://testdir/touch", 1, 2);
+chown("test://testdir/chown", "test");
+chown("test://testdir/chown", 42);
+chgrp("test://testdir/chgrp", "test");
+chgrp("test://testdir/chgrp", 42);
+chmod("test://testdir/chmod", 0755);
+--EXPECT--
+bool(true)
+metadata: test://testdir/touch, 1
+
+metadata: test://testdir/touch, 1
+1,1
+metadata: test://testdir/touch, 1
+1,2
+metadata: test://testdir/chown, 2
+test
+metadata: test://testdir/chown, 3
+42
+metadata: test://testdir/chgrp, 4
+test
+metadata: test://testdir/chgrp, 5
+42
+metadata: test://testdir/chmod, 6
+493
\ No newline at end of file
index 444c2a038e2578e002cd63b32e306109a010b42e..b87cf1b86ef925f1b95bab16d31ca72330aa607f 100755 (executable)
@@ -154,6 +154,8 @@ typedef struct _php_stream_wrapper_ops {
        /* Create/Remove directory */
        int (*stream_mkdir)(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
        int (*stream_rmdir)(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
+       /* Metadata handling */
+       int (*stream_metadata)(php_stream_wrapper *wrapper, char *url, int options, void *value, php_stream_context *context TSRMLS_DC);
 } php_stream_wrapper_ops;
 
 struct _php_stream_wrapper     {
@@ -590,6 +592,15 @@ END_EXTERN_C()
 
 /* Definitions for user streams */
 #define PHP_STREAM_IS_URL              1
+
+/* Stream metadata definitions */
+/* Create if referred resource does not exist */
+#define PHP_STREAM_META_TOUCH          1
+#define PHP_STREAM_META_OWNER_NAME     2
+#define PHP_STREAM_META_OWNER          3
+#define PHP_STREAM_META_GROUP_NAME     4
+#define PHP_STREAM_META_GROUP          5
+#define PHP_STREAM_META_ACCESS         6
 /*
  * Local variables:
  * tab-width: 4
index 62ae48e4ddd820c5425d10727a336bc696eb5d63..041a0e34e2e650fe3c1c047d2e0a64c69ba3a46f 100644 (file)
 #define php_stream_fopen_from_file_int(file, mode)     _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC TSRMLS_CC)
 #define php_stream_fopen_from_file_int_rel(file, mode)  _php_stream_fopen_from_file_int((file), (mode) STREAMS_REL_CC TSRMLS_CC)
 
+#if !defined(WINDOWS) && !defined(NETWARE)
+extern int php_get_uid_by_name(const char *name, uid_t *uid TSRMLS_DC);
+extern int php_get_gid_by_name(const char *name, gid_t *gid TSRMLS_DC);
+#endif
+
 /* parse standard "fopen" modes into open() flags */
 PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags)
 {
@@ -1249,6 +1254,92 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, char *url, int opt
        return 1;
 }
 
+static int php_plain_files_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC)
+{
+       struct utimbuf *newtime;
+       char *p;
+#if !defined(WINDOWS) && !defined(NETWARE)
+       uid_t uid;
+       gid_t gid;
+#endif
+       mode_t mode;
+       int ret = 0;
+#if PHP_WIN32
+       int url_len = strlen(url);
+#endif
+
+#if PHP_WIN32
+       if (!php_win32_check_trailing_space(url, url_len)) {
+               php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "%s", strerror(ENOENT));
+               return 0;
+       }
+#endif
+
+       if ((p = strstr(url, "://")) != NULL) {
+               url = p + 3;
+       }
+
+       if (php_check_open_basedir(url TSRMLS_CC)) {
+               return 0;
+       }
+
+       switch(option) {
+               case PHP_STREAM_META_TOUCH:
+                       newtime = (struct utimbuf *)value;
+                       if (VCWD_ACCESS(url, F_OK) != 0) {
+                               FILE *file = VCWD_FOPEN(url, "w");
+                               if (file == NULL) {
+                                       php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno));
+                                       return 0;
+                               }
+                               fclose(file);
+                       }
+
+                       ret = VCWD_UTIME(url, newtime);
+                       break;
+#if !defined(WINDOWS) && !defined(NETWARE)
+               case PHP_STREAM_META_OWNER_NAME:
+               case PHP_STREAM_META_OWNER:
+                       if(option == PHP_STREAM_META_OWNER_NAME) {
+                               if(php_get_uid_by_name((char *)value, &uid TSRMLS_CC) != SUCCESS) {
+                                       php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unable to find uid for %s", (char *)value);
+                                       return 0;
+                               }
+                       } else {
+                               uid = (uid_t)*(long *)value;
+                       }
+                       ret = VCWD_CHOWN(url, uid, -1);
+                       break;
+               case PHP_STREAM_META_GROUP:
+               case PHP_STREAM_META_GROUP_NAME:
+                       if(option == PHP_STREAM_META_OWNER_NAME) {
+                               if(php_get_gid_by_name((char *)value, &gid TSRMLS_CC) != SUCCESS) {
+                                       php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unable to find gid for %s", (char *)value);
+                                       return 0;
+                               }
+                       } else {
+                               gid = (gid_t)*(long *)value;
+                       }
+                       ret = VCWD_CHOWN(url, -1, gid);
+                       break;
+#endif
+               case PHP_STREAM_META_ACCESS:
+                       mode = (mode_t)*(long *)value;
+                       ret = VCWD_CHMOD(url, mode);
+                       break;
+               default:
+                       php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unknown option %d for stream_metadata", option);
+                       return 0;
+       }
+       if (ret == -1) {
+               php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Operation failed: %s", strerror(errno));
+               return 0;
+       }
+       php_clear_stat_cache(0, NULL, 0 TSRMLS_CC);
+       return 1;
+}
+
+
 static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
        php_plain_files_stream_opener,
        NULL,
@@ -1259,7 +1350,8 @@ static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
        php_plain_files_unlink,
        php_plain_files_rename,
        php_plain_files_mkdir,
-       php_plain_files_rmdir
+       php_plain_files_rmdir,
+       php_plain_files_metadata
 };
 
 php_stream_wrapper php_plain_files_wrapper = {
index 44ee715264ec435114da6418192d4bb265a02ce9..e10c63ce547a8af852a2836b5a0f7fe32ff3b59c 100644 (file)
 #endif
 #include <stddef.h>
 
+#if HAVE_UTIME
+# ifdef PHP_WIN32
+#  include <sys/utime.h>
+# else
+#  include <utime.h>
+# endif
+#endif
+
 static int le_protocols;
 
 struct php_user_stream_wrapper {
@@ -43,6 +51,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio
 static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC);
 static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
 static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
+static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context 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);
 
@@ -56,7 +65,8 @@ static php_stream_wrapper_ops user_stream_wops = {
        user_wrapper_unlink,
        user_wrapper_rename,
        user_wrapper_mkdir,
-       user_wrapper_rmdir
+       user_wrapper_rmdir,
+       user_wrapper_metadata
 };
 
 
@@ -99,6 +109,12 @@ PHP_MINIT_FUNCTION(user_streams)
        REGISTER_LONG_CONSTANT("STREAM_CAST_AS_STREAM",         PHP_STREAM_AS_STDIO,                    CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("STREAM_CAST_FOR_SELECT",        PHP_STREAM_AS_FD_FOR_SELECT,            CONST_CS|CONST_PERSISTENT);
 
+       REGISTER_LONG_CONSTANT("STREAM_META_TOUCH",                     PHP_STREAM_META_TOUCH,                  CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_META_OWNER",                     PHP_STREAM_META_OWNER,                  CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_META_OWNER_NAME",        PHP_STREAM_META_OWNER_NAME,             CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_META_GROUP",                     PHP_STREAM_META_GROUP,                  CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_META_GROUP_NAME",        PHP_STREAM_META_GROUP_NAME,             CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("STREAM_META_ACCESS",            PHP_STREAM_META_ACCESS,                 CONST_CS|CONST_PERSISTENT);
        return SUCCESS;
 }
 
@@ -131,35 +147,36 @@ typedef struct _php_userstream_data php_userstream_data_t;
 #define USERSTREAM_CAST                "stream_cast"
 #define USERSTREAM_SET_OPTION  "stream_set_option"
 #define USERSTREAM_TRUNCATE    "stream_truncate"
+#define USERSTREAM_METADATA    "stream_metadata"
 
 /* {{{ class should have methods like these:
+
        function stream_open($path, $mode, $options, &$opened_path)
        {
                return true/false;
        }
-       
+
        function stream_read($count)
        {
                return false on error;
                else return string;
        }
-       
+
        function stream_write($data)
        {
                return false on error;
                else return count written;
        }
-       
+
        function stream_close()
        {
        }
-       
+
        function stream_flush()
        {
                return true/false;
        }
-       
+
        function stream_seek($offset, $whence)
        {
                return true/false;
@@ -261,7 +278,7 @@ typedef struct _php_userstream_data php_userstream_data_t;
        {
                return true / false;
        }
-  
+
        }}} **/
 
 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)
@@ -269,7 +286,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
        php_userstream_data_t *us;
        zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname;
-       zval **args[4]; 
+       zval **args[4];
        int call_result;
        php_stream *stream = NULL;
        zend_bool old_in_user_include;
@@ -280,32 +297,32 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
                return NULL;
        }
        FG(user_stream_current_filename) = filename;
-       
+
        /* if the user stream was registered as local and we are in include context,
                we add allow_url_include restrictions to allow_url_fopen ones */
        /* we need only is_url == 0 here since if is_url == 1 and remote wrappers
                were restricted we wouldn't get here */
        old_in_user_include = PG(in_user_include);
-       if(uwrap->wrapper.is_url == 0 && 
+       if(uwrap->wrapper.is_url == 0 &&
                (options & STREAM_OPEN_FOR_INCLUDE) &&
                !PG(allow_url_include)) {
                PG(in_user_include) = 1;
        }
 
        us = emalloc(sizeof(*us));
-       us->wrapper = uwrap;    
+       us->wrapper = uwrap;
 
        /* create an instance of our class */
        ALLOC_ZVAL(us->object);
        object_init_ex(us->object, uwrap->ce);
        Z_SET_REFCOUNT_P(us->object, 1);
        Z_SET_ISREF_P(us->object);
-       
+
        if (uwrap->ce->constructor) {
                zend_fcall_info fci;
                zend_fcall_info_cache fcc;
                zval *retval_ptr;
-               
+
                fci.size = sizeof(fci);
                fci.function_table = &uwrap->ce->function_table;
                fci.function_name = NULL;
@@ -315,7 +332,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
                fci.param_count = 0;
                fci.params = NULL;
                fci.no_separation = 1;
-               
+
                fcc.initialized = 1;
                fcc.function_handler = uwrap->ce->constructor;
                fcc.calling_scope = EG(scope);
@@ -343,7 +360,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        } else {
                add_property_null(us->object, "context");
        }
-       
+
        /* call it's stream_open method - set up params first */
        MAKE_STD_ZVAL(zfilename);
        ZVAL_STRING(zfilename, filename, 1);
@@ -365,14 +382,14 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
 
        MAKE_STD_ZVAL(zfuncname);
        ZVAL_STRING(zfuncname, USERSTREAM_OPEN, 1);
-       
+
        call_result = call_user_function_ex(NULL,
                        &us->object,
                        zfuncname,
                        &zretval,
                        4, 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_ops, us, 0, mode);
@@ -389,7 +406,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_OPEN "\" call failed",
                        us->wrapper->classname);
        }
-       
+
        /* destroy everything else */
        if (stream == NULL) {
                zval_ptr_dtor(&us->object);
@@ -397,7 +414,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        }
        if (zretval)
                zval_ptr_dtor(&zretval);
-       
+
        zval_ptr_dtor(&zfuncname);
        zval_ptr_dtor(&zopened);
        zval_ptr_dtor(&zoptions);
@@ -405,7 +422,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        zval_ptr_dtor(&zfilename);
 
        FG(user_stream_current_filename) = NULL;
-               
+
        PG(in_user_include) = old_in_user_include;
        return stream;
 }
@@ -416,7 +433,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen
        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]; 
+       zval **args[2];
        int call_result;
        php_stream *stream = NULL;
 
@@ -426,9 +443,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen
                return NULL;
        }
        FG(user_stream_current_filename) = filename;
-       
+
        us = emalloc(sizeof(*us));
-       us->wrapper = uwrap;    
+       us->wrapper = uwrap;
 
        /* create an instance of our class */
        ALLOC_ZVAL(us->object);
@@ -442,7 +459,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen
        } else {
                add_property_null(us->object, "context");
        }
-       
+
        /* call it's dir_open method - set up params first */
        MAKE_STD_ZVAL(zfilename);
        ZVAL_STRING(zfilename, filename, 1);
@@ -454,14 +471,14 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen
 
        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);
@@ -473,7 +490,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen
                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);
@@ -481,13 +498,13 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen
        }
        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;
 }
 
@@ -501,11 +518,11 @@ PHP_FUNCTION(stream_wrapper_register)
        struct php_user_stream_wrapper * uwrap;
        int rsrc_id;
        long flags = 0;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &protocol, &protocol_len, &classname, &classname_len, &flags) == FAILURE) {
                RETURN_FALSE;
        }
-       
+
        uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap));
        uwrap->protoname = estrndup(protocol, protocol_len);
        uwrap->classname = estrndup(classname, classname_len);
@@ -591,7 +608,7 @@ PHP_FUNCTION(stream_wrapper_restore)
        if (php_register_url_stream_wrapper_volatile(protocol, wrapper TSRMLS_CC) == FAILURE) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to restore original %s:// wrapper", protocol);
                RETURN_FALSE;
-       }       
+       }
 
        RETURN_TRUE;
 }
@@ -639,10 +656,10 @@ static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t
                                (long)(didwrite - count), (long)didwrite, (long)count);
                didwrite = count;
        }
-       
+
        if (retval)
                zval_ptr_dtor(&retval);
-       
+
        return didwrite;
 }
 
@@ -727,9 +744,9 @@ static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC
        php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
 
        assert(us != NULL);
-       
+
        ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0);
-       
+
        call_user_function_ex(NULL,
                        &us->object,
                        &func_name,
@@ -738,11 +755,11 @@ static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC
 
        if (retval)
                zval_ptr_dtor(&retval);
-       
+
        zval_ptr_dtor(&us->object);
 
        efree(us);
-       
+
        return 0;
 }
 
@@ -756,7 +773,7 @@ static int php_userstreamop_flush(php_stream *stream TSRMLS_DC)
        assert(us != NULL);
 
        ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0);
-       
+
        call_result = call_user_function_ex(NULL,
                        &us->object,
                        &func_name,
@@ -767,10 +784,10 @@ static int php_userstreamop_flush(php_stream *stream TSRMLS_DC)
                call_result = 0;
        else
                call_result = -1;
-       
+
        if (retval)
                zval_ptr_dtor(&retval);
-       
+
        return call_result;
 }
 
@@ -809,10 +826,10 @@ static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, o
                /* stream_seek is not implemented, so disable seeks for this stream */
                stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
                /* there should be no retval to clean up */
-               
-               if (retval) 
+
+               if (retval)
                        zval_ptr_dtor(&retval);
-               
+
                return -1;
        } else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
                ret = 0;
@@ -895,8 +912,8 @@ static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC)
        STAT_PROP_ENTRY(blocks);
 #endif
 
-#undef STAT_PROP_ENTRY 
-#undef STAT_PROP_ENTRY_EX      
+#undef STAT_PROP_ENTRY
+#undef STAT_PROP_ENTRY_EX
        return SUCCESS;
 }
 
@@ -926,9 +943,9 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSR
                }
        }
 
-       if (retval) 
+       if (retval)
                zval_ptr_dtor(&retval);
-       
+
        return ret;
 }
 
@@ -976,34 +993,34 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
                }
 
                args[0] = &zvalue;
-               
+
                /* TODO wouldblock */
                ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1, 0);
-               
+
                call_result = call_user_function_ex(NULL,
                                                                                        &us->object,
                                                                                        &func_name,
                                                                                        &retval,
                                                                                        1, args, 0, NULL TSRMLS_CC);
-               
+
                if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) {
                        ret = !Z_LVAL_P(retval);
                } else if (call_result == FAILURE) {
-                       if (value == 0) { 
+                       if (value == 0) {
                                /* lock support test (TODO: more check) */
                                ret = PHP_STREAM_OPTION_RETURN_OK;
                        } else {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!", 
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
                                                                 us->wrapper->classname);
                                ret = PHP_STREAM_OPTION_RETURN_ERR;
                        }
                }
 
                break;
-               
+
        case PHP_STREAM_OPTION_TRUNCATE_API:
                ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1, 0);
-               
+
                switch (value) {
                case PHP_STREAM_TRUNCATE_SUPPORTED:
                        if (zend_is_callable_ex(&func_name, us->object, IS_CALLABLE_CHECK_SILENT,
@@ -1012,7 +1029,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
                        else
                                ret = PHP_STREAM_OPTION_RETURN_ERR;
                        break;
-                       
+
                case PHP_STREAM_TRUNCATE_SET_SIZE: {
                        ptrdiff_t new_size = *(ptrdiff_t*) ptrparam;
                        if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) {
@@ -1045,14 +1062,14 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
                }
                }
                break;
-       
+
        case PHP_STREAM_OPTION_READ_BUFFER:
        case PHP_STREAM_OPTION_WRITE_BUFFER:
        case PHP_STREAM_OPTION_READ_TIMEOUT:
        case PHP_STREAM_OPTION_BLOCKING: {
                zval *zoption = NULL;
                zval *zptrparam = NULL;
-               
+
                ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1, 0);
 
                ALLOC_INIT_ZVAL(zoption);
@@ -1093,7 +1110,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
                        &func_name,
                        &retval,
                        3, args, 0, NULL TSRMLS_CC);
-       
+
                if (call_result == FAILURE) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!",
                                        us->wrapper->classname);
@@ -1119,7 +1136,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value
        if (retval) {
                zval_ptr_dtor(&retval);
        }
-  
+
 
        if (zvalue) {
                zval_ptr_dtor(&zvalue);
@@ -1158,7 +1175,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio
 
        MAKE_STD_ZVAL(zfuncname);
        ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1);
-       
+
        call_result = call_user_function_ex(NULL,
                        &object,
                        zfuncname,
@@ -1176,7 +1193,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio
        zval_ptr_dtor(&object);
        if (zretval)
                zval_ptr_dtor(&zretval);
-       
+
        zval_ptr_dtor(&zfuncname);
        zval_ptr_dtor(&zfilename);
 
@@ -1216,7 +1233,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
 
        MAKE_STD_ZVAL(zfuncname);
        ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1);
-       
+
        call_result = call_user_function_ex(NULL,
                        &object,
                        zfuncname,
@@ -1234,7 +1251,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
        zval_ptr_dtor(&object);
        if (zretval)
                zval_ptr_dtor(&zretval);
-       
+
        zval_ptr_dtor(&zfuncname);
        zval_ptr_dtor(&zold_name);
        zval_ptr_dtor(&znew_name);
@@ -1279,7 +1296,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode,
 
        MAKE_STD_ZVAL(zfuncname);
        ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 1);
-       
+
        call_result = call_user_function_ex(NULL,
                        &object,
                        zfuncname,
@@ -1298,7 +1315,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode,
        if (zretval) {
                zval_ptr_dtor(&zretval);
        }
-       
+
        zval_ptr_dtor(&zfuncname);
        zval_ptr_dtor(&zfilename);
        zval_ptr_dtor(&zmode);
@@ -1340,7 +1357,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option
 
        MAKE_STD_ZVAL(zfuncname);
        ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1);
-       
+
        call_result = call_user_function_ex(NULL,
                        &object,
                        zfuncname,
@@ -1359,7 +1376,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option
        if (zretval) {
                zval_ptr_dtor(&zretval);
        }
-       
+
        zval_ptr_dtor(&zfuncname);
        zval_ptr_dtor(&zfilename);
        zval_ptr_dtor(&zoptions);
@@ -1367,11 +1384,100 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option
        return ret;
 }
 
+static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC)
+{
+       struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
+       zval *zfilename, *zoption, *zvalue, *zfuncname, *zretval;
+       zval **args[3];
+       int call_result;
+       zval *object;
+       int ret = 0;
+
+       MAKE_STD_ZVAL(zvalue);
+       switch(option) {
+               case PHP_STREAM_META_TOUCH:
+                       array_init(zvalue);
+                       if(value) {
+                               struct utimbuf *newtime = (struct utimbuf *)value;
+                               add_index_long(zvalue, 0, newtime->modtime);
+                               add_index_long(zvalue, 1, newtime->actime);
+                       }
+                       break;
+               case PHP_STREAM_META_GROUP:
+               case PHP_STREAM_META_OWNER:
+               case PHP_STREAM_META_ACCESS:
+                       ZVAL_LONG(zvalue, *(long *)value);
+                       break;
+               case PHP_STREAM_META_GROUP_NAME:
+               case PHP_STREAM_META_OWNER_NAME:
+                       ZVAL_STRING(zvalue, value, 1);
+                       break;
+               default:
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option);
+                       zval_ptr_dtor(&zvalue);
+                       return ret;
+       }
+
+       /* create an instance of our class */
+       ALLOC_ZVAL(object);
+       object_init_ex(object, uwrap->ce);
+       Z_SET_REFCOUNT_P(object, 1);
+       Z_SET_ISREF_P(object);
+
+       if (context) {
+               add_property_resource(object, "context", context->rsrc_id);
+               zend_list_addref(context->rsrc_id);
+       } else {
+               add_property_null(object, "context");
+       }
+
+       /* call the mkdir method */
+       MAKE_STD_ZVAL(zfilename);
+       ZVAL_STRING(zfilename, url, 1);
+       args[0] = &zfilename;
+
+       MAKE_STD_ZVAL(zoption);
+       ZVAL_LONG(zoption, option);
+       args[1] = &zoption;
+
+       args[2] = &zvalue;
+
+       MAKE_STD_ZVAL(zfuncname);
+       ZVAL_STRING(zfuncname, USERSTREAM_METADATA, 1);
+
+       call_result = call_user_function_ex(NULL,
+                       &object,
+                       zfuncname,
+                       &zretval,
+                       3, args,
+                       0, NULL TSRMLS_CC);
+
+       if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
+               ret = Z_LVAL_P(zretval);
+       } else if (call_result == FAILURE) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_METADATA " 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);
+       zval_ptr_dtor(&zoption);
+       zval_ptr_dtor(&zvalue);
+
+       return ret;
+}
+
+
 static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
 {
        struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
        zval *zfilename, *zfuncname, *zretval, *zflags;
-       zval **args[2]; 
+       zval **args[2];
        int call_result;
        zval *object;
        int ret = -1;
@@ -1400,14 +1506,14 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int fla
 
        MAKE_STD_ZVAL(zfuncname);
        ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1);
-       
+
        call_result = call_user_function_ex(NULL,
                        &object,
                        zfuncname,
                        &zretval,
                        2, 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))
@@ -1418,16 +1524,16 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int fla
                                        uwrap->classname);
                }
        }
-       
+
        /* clean up */
        zval_ptr_dtor(&object);
        if (zretval)
                zval_ptr_dtor(&zretval);
-       
+
        zval_ptr_dtor(&zfuncname);
        zval_ptr_dtor(&zfilename);
        zval_ptr_dtor(&zflags);
-               
+
        return ret;
 
 }
@@ -1477,9 +1583,9 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS
        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,
@@ -1488,11 +1594,11 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS
 
        if (retval)
                zval_ptr_dtor(&retval);
-       
+
        zval_ptr_dtor(&us->object);
 
        efree(us);
-       
+
        return 0;
 }
 
@@ -1503,7 +1609,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int when
        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,
@@ -1512,7 +1618,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int when
 
        if (retval)
                zval_ptr_dtor(&retval);
-       
+
        return 0;
 
 }
@@ -1587,7 +1693,7 @@ php_stream_ops php_stream_userspace_ops = {
        "user-space",
        php_userstreamop_seek,
        php_userstreamop_cast,
-       php_userstreamop_stat, 
+       php_userstreamop_stat,
        php_userstreamop_set_option,
 };