diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 7fdf6c9f8781..86aab274977e 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -3254,7 +3254,6 @@ PHP_FUNCTION(exif_imagetype) { zval **arg1; php_stream * stream; - int rsrc_id; int itype = 0; if (ZEND_NUM_ARGS() != 1) @@ -3269,11 +3268,9 @@ PHP_FUNCTION(exif_imagetype) RETURN_FALSE; } - rsrc_id = ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); - itype = itype = php_getimagetype(stream, NULL TSRMLS_CC); - zend_list_delete(rsrc_id); + php_stream_close(stream); if ( itype == IMAGE_FILETYPE_UNKNOWN) { RETURN_FALSE; diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index e82379127e76..6e870c20b0a9 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1478,7 +1478,7 @@ PHP_FUNCTION(pg_trace) php_stream_close(stream); RETURN_FALSE; } - ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); + php_stream_auto_cleanup(stream); PQtrace(pgsql, fp); RETURN_TRUE; } diff --git a/ext/standard/exec.c b/ext/standard/exec.c index f5f337fee596..4b6b75c768dd 100644 --- a/ext/standard/exec.c +++ b/ext/standard/exec.c @@ -49,7 +49,6 @@ int php_Exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC) int buflen = 0; int t, l, output=1; int overflow_limit, lcmd, ldir; - int rsrc_id; char *b, *c, *d=NULL; php_stream *stream = NULL; #if PHP_SIGCHILD @@ -143,8 +142,6 @@ int php_Exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC) */ stream = php_stream_fopen_from_pipe(fp, "rb"); - if (stream) - rsrc_id = ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); if (type != 3) { l=0; @@ -219,8 +216,7 @@ int php_Exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC) } } - /* the zend_list_delete will pclose our popen'ed process */ - zend_list_delete(rsrc_id); + php_stream_close(stream); #if HAVE_SYS_WAIT_H if (WIFEXITED(FG(pclose_ret))) { diff --git a/ext/standard/file.c b/ext/standard/file.c index a58b511902ea..cf76315f2bc8 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -113,7 +113,7 @@ static void _file_stream_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_stream *stream = (php_stream*)rsrc->ptr; /* the stream might be a pipe, so set the return value for pclose */ - FG(pclose_ret) = php_stream_close(stream); + FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR); } PHPAPI int php_file_le_stream(void) @@ -528,7 +528,7 @@ PHP_NAMED_FUNCTION(php_if_tmpfile) stream = php_stream_fopen_tmpfile(); if (stream) { - ZEND_REGISTER_RESOURCE(return_value, stream, le_stream); + php_stream_to_zval(stream, return_value); } else { RETURN_FALSE; @@ -593,7 +593,9 @@ PHP_NAMED_FUNCTION(php_if_fopen) RETURN_FALSE; } FG(fgetss_state) = 0; - ZEND_REGISTER_RESOURCE(return_value, stream, le_stream); + + php_stream_to_zval(stream, return_value); + return; } /* }}} */ @@ -676,9 +678,9 @@ PHP_FUNCTION(popen) if (stream == NULL) { zend_error(E_WARNING, "popen(\"%s\", \"%s\"): %s", Z_STRVAL_PP(arg1), p, strerror(errno)); RETVAL_FALSE; + } else { + php_stream_to_zval(stream, return_value); } - else - ZEND_REGISTER_RESOURCE(return_value, stream, le_stream); efree(p); } diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 84a12eb9fc15..21eb6e8256e3 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -141,7 +141,7 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) (void *) &stream) == SUCCESS) { efree(hashkey); - ZEND_REGISTER_RESOURCE(return_value, stream, php_file_le_stream()); + php_stream_to_zval(stream, return_value); return; } @@ -241,7 +241,7 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) RETURN_FALSE; } - ZEND_REGISTER_RESOURCE(return_value, stream, php_file_le_stream()); + php_stream_to_zval(stream, return_value); } /* }}} */ diff --git a/ext/standard/image.c b/ext/standard/image.c index 9d6896c608e2..f12c8cb39e95 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -684,7 +684,6 @@ PHPAPI int php_getimagetype(php_stream * stream, char *filetype TSRMLS_DC) PHP_FUNCTION(getimagesize) { zval **arg1, **info = NULL; - int rsrc_id; int itype = 0; char temp[64]; struct gfxinfo *result = NULL; @@ -723,8 +722,6 @@ PHP_FUNCTION(getimagesize) RETURN_FALSE; } - rsrc_id = ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); - itype = php_getimagetype(stream, NULL TSRMLS_CC); switch( itype) { case IMAGE_FILETYPE_GIF: @@ -763,7 +760,7 @@ PHP_FUNCTION(getimagesize) break; } - zend_list_delete(rsrc_id); + php_stream_close(stream); if (result) { if (array_init(return_value) == FAILURE) { diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index f4323a8b8be5..9c46953f831f 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -255,15 +255,9 @@ static gzFile php_gzopen_wrapper(char *path, char *mode, int options TSRMLS_DC) stream = php_stream_open_wrapper(path, mode, options | REPORT_ERRORS, NULL); if (stream) { - if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD|PHP_STREAM_CAST_TRY_HARD, (void**)&fd, 1)) + if (SUCCESS == php_stream_cast(stream, PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD|PHP_STREAM_CAST_TRY_HARD, (void**)&fd, 1)) { - gzFile ret = gzdopen(fd, mode); - if (ret) { - /* arrange to clean up the actual stream */ - ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); -/* php_stream_auto_cleanup(stream); */ - return ret; - } + return gzdopen(fd, mode); } php_stream_close(stream); } diff --git a/main/php_streams.h b/main/php_streams.h index 98a62286f8e6..4a63edcd5c39 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -120,10 +120,15 @@ struct _php_stream { int is_persistent; char mode[16]; /* "rwb" etc. ala stdio */ + int rsrc_id; /* used for auto-cleanup */ + int in_free; /* to prevent recursion during free */ /* so we know how to clean it up correctly. This should be set to * PHP_STREAM_FCLOSE_XXX as appropriate */ int fclose_stdiocast; FILE *stdiocast; /* cache this, otherwise we might leak! */ +#if ZEND_DEBUG + int __exposed; /* non-zero if exposed as a zval somewhere */ +#endif }; /* php_stream */ /* state definitions when closing down; these are private to streams.c */ #define PHP_STREAM_FCLOSE_NONE 0 @@ -135,9 +140,24 @@ PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, int persistent, const char *mode STREAMS_DC TSRMLS_DC); #define php_stream_alloc(ops, thisptr, persistent, mode) _php_stream_alloc((ops), (thisptr), (persistent), (mode) STREAMS_CC TSRMLS_CC) +#define php_stream_get_resource_id(stream) (stream)->rsrc_id + +#if ZEND_DEBUG +/* use this to tell the stream that it is OK if we don't explicitly close it */ +# define php_stream_auto_cleanup(stream) { (stream)->__exposed++; } +/* use this to assign the stream to a zval and tell the stream that is + * has been exported to the engine; it will expect to be closed automatically + * when the resources are auto-destructed */ +# define php_stream_to_zval(stream, zval) { ZVAL_RESOURCE(zval, (stream)->rsrc_id); (stream)->__exposed++; } +#else +# define php_stream_auto_cleanup(stream) /* nothing */ +# define php_stream_to_zval(stream, zval) { ZVAL_RESOURCE(zval, (stream)->rsrc_id); } +#endif + #define PHP_STREAM_FREE_CALL_DTOR 1 /* call ops->close */ #define PHP_STREAM_FREE_RELEASE_STREAM 2 /* pefree(stream) */ #define PHP_STREAM_FREE_PRESERVE_HANDLE 4 /* tell ops->close to not close it's underlying handle */ +#define PHP_STREAM_FREE_RSRC_DTOR 8 /* called from the resource list dtor */ #define PHP_STREAM_FREE_CLOSE (PHP_STREAM_FREE_CALL_DTOR | PHP_STREAM_FREE_RELEASE_STREAM) PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC); #define php_stream_free(stream, close_options) _php_stream_free((stream), (close_options) TSRMLS_CC) diff --git a/main/streams.c b/main/streams.c index 687535e4b9ea..419745b9d463 100755 --- a/main/streams.c +++ b/main/streams.c @@ -25,6 +25,7 @@ #include "php_globals.h" #include "php_network.h" #include "php_open_temporary_file.h" +#include "ext/standard/file.h" #ifdef HAVE_SYS_MMAN_H #include @@ -35,6 +36,7 @@ #endif #define CHUNK_SIZE 8192 +#define PHP_STREAM_MAX_MEM 2 * 1024 * 1024 #ifdef PHP_WIN32 #define EWOULDBLOCK WSAEWOULDBLOCK @@ -78,7 +80,7 @@ PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, int pe ret->ops = ops; ret->abstract = abstract; ret->is_persistent = persistent; - + ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, php_file_le_stream()); strlcpy(ret->mode, mode, sizeof(ret->mode)); return ret; @@ -89,6 +91,15 @@ PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* { int ret = 1; + if (stream->in_free) + return 1; + + stream->in_free++; + + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0) { + /* Remove entry from the resource list */ + zend_list_delete(stream->rsrc_id); + } if (close_options & PHP_STREAM_FREE_CALL_DTOR) { if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { /* calling fclose on an fopencookied stream will ultimately @@ -98,6 +109,7 @@ PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* php_stream_free. Lets let the cookie code clean it all up. */ + stream->in_free = 0; return fclose(stream->stdiocast); } @@ -125,6 +137,21 @@ PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* stream->wrapperdata = NULL; } +#if ZEND_DEBUG + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) { + /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it + * as leaked; it will log a warning, but lets help it out and display what kind + * of stream it was. */ + char leakbuf[512]; + snprintf(leakbuf, sizeof(leakbuf), __FILE__ "(%d) : Stream of type '%s' 0x%08X was not closed\n", __LINE__, stream->ops->label, (unsigned int)stream); +# if defined(PHP_WIN32) + OutputDebugString(leakbuf); +# else + fprintf(stderr, leakbuf); +# endif + } + else +#endif pefree(stream, stream->is_persistent); } @@ -619,8 +646,10 @@ php_stream_ops php_stream_stdio_ops = { php_stdiop_gets, php_stdiop_cast, "STDIO" }; +/* }}} */ -PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path STREAMS_DC TSRMLS_DC) /* {{{ */ +/* {{{ php_stream_fopen_with_path */ +PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path STREAMS_DC TSRMLS_DC) { /* code ripped off from fopen_wrappers.c */ char *pathbuf, *ptr, *end; @@ -737,6 +766,7 @@ PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char } /* }}} */ +/* {{{ php_stream_fopen */ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path STREAMS_DC TSRMLS_DC) { FILE *fp; @@ -811,7 +841,8 @@ static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = #endif /* }}} */ -PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC) /* {{{ */ +/* {{{ php_stream_cast */ +PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC) { int flags = castas & PHP_STREAM_CAST_MASK; castas &= ~PHP_STREAM_CAST_MASK; @@ -902,8 +933,10 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show return SUCCESS; -} /* }}} */ +} +/* }}} */ +/* {{{ wrapper init and registration */ int php_init_stream_wrappers(TSRMLS_D) { if (PG(allow_url_fopen)) { @@ -935,7 +968,9 @@ PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC) } return SUCCESS; } +/* }}} */ +/* {{{ php_stream_open_url */ static php_stream *php_stream_open_url(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) { php_stream_wrapper *wrapper; @@ -977,7 +1012,9 @@ static php_stream *php_stream_open_url(char *path, char *mode, int options, char } return NULL; } +/* }}} */ +/* {{{ php_stream_open_wrapper */ PHPAPI php_stream *_php_stream_open_wrapper(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) { php_stream *stream = NULL; @@ -1034,13 +1071,15 @@ PHPAPI php_stream *_php_stream_open_wrapper(char *path, char *mode, int options, } return stream; } +/* }}} */ +/* {{{ php_stream_open_wrapper_as_file */ PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) { FILE *fp = NULL; php_stream *stream = NULL; - stream = php_stream_open_wrapper(path, mode, options, opened_path); + stream = php_stream_open_wrapper_rel(path, mode, options, opened_path); if (stream == NULL) return NULL; @@ -1055,9 +1094,9 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int optio } return fp; } +/* }}} */ -#define PHP_STREAM_MAX_MEM 2 * 1024 * 1024 - +/* {{{ php_stream_make_seekable */ PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC) { assert(newstream != NULL); @@ -1090,6 +1129,7 @@ PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstr return PHP_STREAM_RELEASED; } +/* }}} */ /* * Local variables: diff --git a/main/user_streams.c b/main/user_streams.c index 898ad5e09962..ab90e1cd3b7b 100644 --- a/main/user_streams.c +++ b/main/user_streams.c @@ -115,7 +115,7 @@ static php_stream *user_wrapper_factory(char *filename, char *mode, int options, zval **args[4]; int call_result; php_stream *stream = NULL; - + us = emalloc(sizeof(*us)); us->wrapper = uwrap;