Hopefully fix resource usage so that we have no leaks and don't segfault.

This commit is contained in:
Wez Furlong 2002-03-16 14:39:51 +00:00
parent 6abd7c6f93
commit bed04279c3
5 changed files with 63 additions and 39 deletions

View file

@ -32,7 +32,7 @@ static size_t php_gziop_read(php_stream *stream, char *buf, size_t count)
{ {
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract; struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract;
if (buf == NULL && count == 0) { if (buf == NULL && count == 0) {
if (gzeof(self->gz_file)) if (gzeof(self->gz_file))
return EOF; return EOF;
return 0; return 0;
@ -60,13 +60,14 @@ static int php_gziop_seek(php_stream *stream, off_t offset, int whence)
return gzseek(self->gz_file, offset, whence); return gzseek(self->gz_file, offset, whence);
} }
static int php_gziop_close(php_stream *stream) static int php_gziop_close(php_stream *stream, int close_handle)
{ {
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract; struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract;
int ret; int ret;
ret = gzclose(self->gz_file); if (close_handle)
php_stream_close(self->stream); ret = gzclose(self->gz_file);
php_stream_free(self->stream, close_handle);
efree(self); efree(self);
return ret; return ret;
@ -92,9 +93,9 @@ php_stream *php_stream_gzopen(char *path, char *mode, int options, char **opened
self->stream = php_stream_open_wrapper(path, mode, options, opened_path TSRMLS_CC); self->stream = php_stream_open_wrapper(path, mode, options, opened_path TSRMLS_CC);
if (self->stream) { if (self->stream) {
int fd; int fd;
if (SUCCESS == php_stream_cast(self->stream, PHP_STREAM_AS_FD, (void**)&fd, REPORT_ERRORS)) { if (SUCCESS == php_stream_cast(self->stream, PHP_STREAM_AS_FD, (void**)&fd, REPORT_ERRORS)) {
self->gz_file = gzdopen(fd, mode); self->gz_file = gzdopen(fd, mode);
if (self->gz_file) { if (self->gz_file) {
stream = php_stream_alloc(&php_stream_gzio_ops, self, 0, mode); stream = php_stream_alloc(&php_stream_gzio_ops, self, 0, mode);

View file

@ -572,11 +572,10 @@ static FILE *php_fopen_wrapper_for_zend(const char *filename, char **opened_path
/* no need for us to check the stream type here */ /* no need for us to check the stream type here */
php_stream_sock_set_chunk_size(stream, 1); php_stream_sock_set_chunk_size(stream, 1);
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void**)&retval, 1) == SUCCESS) { /* when this succeeds, stream either has or will be freed automatically */
/* The leak here prevents a segfault */ if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE,
/* ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); */ (void**)&retval, REPORT_ERRORS) == FAILURE)
} {
else {
php_stream_close(stream); php_stream_close(stream);
if (opened_path && *opened_path) if (opened_path && *opened_path)
efree(*opened_path); efree(*opened_path);

View file

@ -675,22 +675,24 @@ static size_t php_sockop_read(php_stream *stream, char *buf, size_t count)
return ret; return ret;
} }
static int php_sockop_close(php_stream *stream) static int php_sockop_close(php_stream *stream, int close_handle)
{ {
php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
if (close_handle) {
#if HAVE_OPENSSL_EXT #if HAVE_OPENSSL_EXT
if (sock->ssl_active) { if (sock->ssl_active) {
SSL_shutdown(sock->ssl_handle); SSL_shutdown(sock->ssl_handle);
sock->ssl_active = 0; sock->ssl_active = 0;
SSL_free(sock->ssl_handle); SSL_free(sock->ssl_handle);
sock->ssl_handle = NULL; sock->ssl_handle = NULL;
} }
#endif #endif
shutdown(sock->socket, 0);
closesocket(sock->socket);
shutdown(sock->socket, 0);
closesocket(sock->socket);
}
if (sock->readbuf) if (sock->readbuf)
pefree(sock->readbuf, php_stream_is_persistent(stream)); pefree(sock->readbuf, php_stream_is_persistent(stream));

View file

@ -43,7 +43,7 @@ typedef struct _php_stream_ops {
/* stdio like functions - these are mandatory! */ /* stdio like functions - these are mandatory! */
size_t (*write)(php_stream *stream, const char *buf, size_t count); size_t (*write)(php_stream *stream, const char *buf, size_t count);
size_t (*read)(php_stream *stream, char *buf, size_t count); size_t (*read)(php_stream *stream, char *buf, size_t count);
int (*close)(php_stream *stream); int (*close)(php_stream *stream, int close_handle);
int (*flush)(php_stream *stream); int (*flush)(php_stream *stream);
/* these are optional */ /* these are optional */
int (*seek)(php_stream *stream, off_t offset, int whence); int (*seek)(php_stream *stream, off_t offset, int whence);
@ -132,7 +132,8 @@ PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir, const char *
/* try really, really hard to make sure the cast happens (socketpair) */ /* try really, really hard to make sure the cast happens (socketpair) */
#define PHP_STREAM_CAST_TRY_HARD 0x80000000 #define PHP_STREAM_CAST_TRY_HARD 0x80000000
#define PHP_STREAM_CAST_RELEASE 0x40000000 /* stream becomes invalid on success */
#define PHP_STREAM_CAST_MASK (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE)
PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_err); PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_err);
/* use this to check if a stream can be cast into another form */ /* use this to check if a stream can be cast into another form */
#define php_stream_can_cast(stream, as) php_stream_cast(stream, as, NULL, 0) #define php_stream_can_cast(stream, as) php_stream_cast(stream, as, NULL, 0)

View file

@ -69,6 +69,7 @@ PHPAPI int php_stream_free(php_stream *stream, int call_dtor) /* {{{ */
if (stream->wrapper && stream->wrapper->destroy) { if (stream->wrapper && stream->wrapper->destroy) {
stream->wrapper->destroy(stream); stream->wrapper->destroy(stream);
stream->wrapper = NULL;
} }
if (call_dtor) { if (call_dtor) {
@ -84,9 +85,12 @@ PHPAPI int php_stream_free(php_stream *stream, int call_dtor) /* {{{ */
} }
php_stream_flush(stream); php_stream_flush(stream);
ret = stream->ops->close(stream); }
stream->abstract = NULL;
ret = stream->ops->close(stream, call_dtor);
stream->abstract = NULL;
if (call_dtor) {
/* tidy up any FILE* that might have been fdopened */ /* tidy up any FILE* that might have been fdopened */
if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) { if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) {
fclose(stream->stdiocast); fclose(stream->stdiocast);
@ -96,6 +100,7 @@ PHPAPI int php_stream_free(php_stream *stream, int call_dtor) /* {{{ */
if (stream->wrapperdata) { if (stream->wrapperdata) {
FREE_ZVAL(stream->wrapperdata); FREE_ZVAL(stream->wrapperdata);
stream->wrapperdata = NULL;
} }
pefree(stream, stream->is_persistent); pefree(stream, stream->is_persistent);
@ -494,18 +499,22 @@ static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
return fread(buf, 1, count, data->file); return fread(buf, 1, count, data->file);
} }
static int php_stdiop_close(php_stream *stream) static int php_stdiop_close(php_stream *stream, int close_handle)
{ {
int ret; int ret;
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
assert(data != NULL); assert(data != NULL);
if (data->is_pipe) { if (close_handle) {
ret = pclose(data->file); if (data->is_pipe) {
} else { ret = pclose(data->file);
ret = fclose(data->file); } else {
ret = fclose(data->file);
}
} }
else
ret = 0;
efree(data); efree(data);
@ -759,16 +768,15 @@ static COOKIE_IO_FUNCTIONS_T stream_cookie_functions =
PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) /* {{{ */ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) /* {{{ */
{ {
int flags = castas & PHP_STREAM_CAST_MASK;
castas &= ~PHP_STREAM_CAST_MASK;
/* trying hard is not yet implemented */ if (castas == PHP_STREAM_AS_STDIO) {
castas &= ~PHP_STREAM_CAST_TRY_HARD; if (stream->stdiocast) {
if (castas == PHP_STREAM_AS_STDIO) {
if (stream->stdiocast) {
if (ret) { if (ret) {
*ret = stream->stdiocast; *ret = stream->stdiocast;
} }
return SUCCESS; goto exit_success;
} }
if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS) if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS)
@ -782,8 +790,8 @@ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_
*ret = fopencookie(stream, stream->mode, stream_cookie_functions); *ret = fopencookie(stream, stream->mode, stream_cookie_functions);
if (*ret != NULL) { if (*ret != NULL) {
stream->fclose_stdiocast = 1; stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE;
goto exit_success; goto exit_success;
} }
@ -807,7 +815,7 @@ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_
exit_fail: exit_fail:
if (show_err) { if (show_err) {
/* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */ /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */
static const char *cast_names[3] = { static const char *cast_names[3] = {
"STDIO FILE*", "File Descriptor", "Socket Descriptor" "STDIO FILE*", "File Descriptor", "Socket Descriptor"
@ -827,6 +835,19 @@ exit_success:
if (castas == PHP_STREAM_AS_STDIO && ret) if (castas == PHP_STREAM_AS_STDIO && ret)
stream->stdiocast = *ret; stream->stdiocast = *ret;
if (flags & PHP_STREAM_CAST_RELEASE) {
/* Something other than php_stream_close will be closing
* the underlying handle, so we should free the stream handle/data
* here now. The stream may not be freed immediately (in the case
* of fopencookie), but the caller should still not touch their
* original stream pointer in any case. */
if (stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE) {
/* ask the implementation to release resources other than
* the underlying handle */
php_stream_free(stream, 0);
}
}
return SUCCESS; return SUCCESS;
} /* }}} */ } /* }}} */