summaryrefslogtreecommitdiff
path: root/main/streams/plain_wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/streams/plain_wrapper.c')
-rw-r--r--main/streams/plain_wrapper.c153
1 files changed, 87 insertions, 66 deletions
diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c
index 131f77c421..8bd20969f9 100644
--- a/main/streams/plain_wrapper.c
+++ b/main/streams/plain_wrapper.c
@@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2018 The PHP Group |
+ | Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -125,10 +125,12 @@ typedef struct {
FILE *file;
int fd; /* underlying file descriptor */
unsigned is_process_pipe:1; /* use pclose instead of fclose */
- unsigned is_pipe:1; /* don't try and seek */
+ unsigned is_pipe:1; /* stream is an actual pipe, currently Windows only*/
unsigned cached_fstat:1; /* sb is valid */
unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */
- unsigned _reserved:28;
+ unsigned no_forced_fstat:1; /* Use fstat cache even if forced */
+ unsigned is_seekable:1; /* don't try and seek, if not set */
+ unsigned _reserved:26;
int lock_flag; /* stores the lock state */
zend_string *temp_name; /* if non-null, this is the path to a temporary file that
@@ -152,7 +154,7 @@ typedef struct {
static int do_fstat(php_stdio_stream_data *d, int force)
{
- if (!d->cached_fstat || force) {
+ if (!d->cached_fstat || (force && !d->no_forced_fstat)) {
int fd;
int r;
@@ -172,6 +174,7 @@ static php_stream *_php_stream_fopen_from_fd_int(int fd, const char *mode, const
self = pemalloc_rel_orig(sizeof(*self), persistent_id);
memset(self, 0, sizeof(*self));
self->file = NULL;
+ self->is_seekable = 1;
self->is_pipe = 0;
self->lock_flag = LOCK_UN;
self->is_process_pipe = 0;
@@ -191,6 +194,7 @@ static php_stream *_php_stream_fopen_from_file_int(FILE *file, const char *mode
self = emalloc_rel_orig(sizeof(*self));
memset(self, 0, sizeof(*self));
self->file = file;
+ self->is_seekable = 1;
self->is_pipe = 0;
self->lock_flag = LOCK_UN;
self->is_process_pipe = 0;
@@ -241,6 +245,24 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
return php_stream_fopen_temporary_file(NULL, "php", NULL);
}
+static void detect_is_seekable(php_stdio_stream_data *self) {
+#if defined(S_ISFIFO) && defined(S_ISCHR)
+ if (self->fd >= 0 && do_fstat(self, 0) == 0) {
+ self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode));
+ self->is_pipe = S_ISFIFO(self->sb.st_mode);
+ }
+#elif defined(PHP_WIN32)
+ zend_uintptr_t handle = _get_osfhandle(self->fd);
+
+ if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
+ DWORD file_type = GetFileType((HANDLE)handle);
+
+ self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR);
+ self->is_pipe = file_type == FILE_TYPE_PIPE;
+ }
+#endif
+}
+
PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC)
{
php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
@@ -248,30 +270,17 @@ PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const cha
if (stream) {
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
-#ifdef S_ISFIFO
- /* detect if this is a pipe */
- if (self->fd >= 0) {
- self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
- }
-#elif defined(PHP_WIN32)
- {
- zend_uintptr_t handle = _get_osfhandle(self->fd);
-
- if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
- self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
- }
- }
-#endif
-
- if (self->is_pipe) {
+ detect_is_seekable(self);
+ if (!self->is_seekable) {
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
+ stream->position = -1;
} else {
stream->position = zend_lseek(self->fd, 0, SEEK_CUR);
#ifdef ESPIPE
+ /* FIXME: Is this code still needed? */
if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {
- stream->position = 0;
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
- self->is_pipe = 1;
+ self->is_seekable = 0;
}
#endif
}
@@ -287,23 +296,10 @@ PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STRE
if (stream) {
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
-#ifdef S_ISFIFO
- /* detect if this is a pipe */
- if (self->fd >= 0) {
- self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
- }
-#elif defined(PHP_WIN32)
- {
- zend_uintptr_t handle = _get_osfhandle(self->fd);
-
- if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
- self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
- }
- }
-#endif
-
- if (self->is_pipe) {
+ detect_is_seekable(self);
+ if (!self->is_seekable) {
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
+ stream->position = -1;
} else {
stream->position = zend_ftell(file);
}
@@ -320,6 +316,7 @@ PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STRE
self = emalloc_rel_orig(sizeof(*self));
memset(self, 0, sizeof(*self));
self->file = file;
+ self->is_seekable = 0;
self->is_pipe = 1;
self->lock_flag = LOCK_UN;
self->is_process_pipe = 1;
@@ -334,7 +331,7 @@ PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STRE
return stream;
}
-static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count)
+static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t count)
{
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
@@ -342,33 +339,42 @@ static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count
if (data->fd >= 0) {
#ifdef PHP_WIN32
- int bytes_written;
+ ssize_t bytes_written;
if (ZEND_SIZE_T_UINT_OVFL(count)) {
count = UINT_MAX;
}
bytes_written = _write(data->fd, buf, (unsigned int)count);
#else
- int bytes_written = write(data->fd, buf, count);
+ ssize_t bytes_written = write(data->fd, buf, count);
#endif
- if (bytes_written < 0) return 0;
- return (size_t) bytes_written;
+ if (bytes_written < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ return 0;
+ }
+ if (errno == EINTR) {
+ /* TODO: Should this be treated as a proper error or not? */
+ return bytes_written;
+ }
+ php_error_docref(NULL, E_NOTICE, "write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
+ }
+ return bytes_written;
} else {
#if HAVE_FLUSHIO
- if (!data->is_pipe && data->last_op == 'r') {
+ if (data->is_seekable && data->last_op == 'r') {
zend_fseek(data->file, 0, SEEK_CUR);
}
data->last_op = 'w';
#endif
- return fwrite(buf, 1, count, data->file);
+ return (ssize_t) fwrite(buf, 1, count, data->file);
}
}
-static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
+static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
{
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
- size_t ret;
+ ssize_t ret;
assert(data != NULL);
@@ -410,11 +416,27 @@ static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));
}
- stream->eof = (ret == 0 || (ret == (size_t)-1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF));
+ if (ret < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ /* Not an error. */
+ ret = 0;
+ } else if (errno == EINTR) {
+ /* TODO: Should this be treated as a proper error or not? */
+ } else {
+ php_error_docref(NULL, E_NOTICE, "read of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
+
+ /* TODO: Remove this special-case? */
+ if (errno != EBADF) {
+ stream->eof = 1;
+ }
+ }
+ } else if (ret == 0) {
+ stream->eof = 1;
+ }
} else {
#if HAVE_FLUSHIO
- if (!data->is_pipe && data->last_op == 'w')
+ if (data->is_seekable && data->last_op == 'w')
zend_fseek(data->file, 0, SEEK_CUR);
data->last_op = 'r';
#endif
@@ -515,8 +537,8 @@ static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, ze
assert(data != NULL);
- if (data->is_pipe) {
- php_error_docref(NULL, E_WARNING, "cannot seek on a pipe");
+ if (!data->is_seekable) {
+ php_error_docref(NULL, E_WARNING, "cannot seek on this stream");
return -1;
}
@@ -820,6 +842,11 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
delta = (DWORD)range->offset - loffs;
}
+ /* MapViewOfFile()ing zero bytes would map to the end of the file; match *nix behavior instead */
+ if (range->length + delta == 0) {
+ return PHP_STREAM_OPTION_RETURN_ERR;
+ }
+
data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta);
if (data->last_mapped_addr) {
@@ -932,19 +959,18 @@ PHPAPI php_stream_ops php_stream_stdio_ops = {
/* }}} */
/* {{{ plain files opendir/readdir implementation */
-static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count)
+static ssize_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count)
{
DIR *dir = (DIR*)stream->abstract;
- /* avoid libc5 readdir problems */
- char entry[sizeof(struct dirent)+MAXPATHLEN];
- struct dirent *result = (struct dirent *)&entry;
+ struct dirent *result;
php_stream_dirent *ent = (php_stream_dirent*)buf;
/* avoid problems if someone mis-uses the stream */
if (count != sizeof(php_stream_dirent))
- return 0;
+ return -1;
- if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) {
+ result = readdir(dir);
+ if (result) {
PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));
return sizeof(php_stream_dirent);
}
@@ -1087,6 +1113,10 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen
php_stream_close(ret);
return NULL;
}
+
+ /* Make sure the fstat result is reused when we later try to get the
+ * file size. */
+ self->no_forced_fstat = 1;
}
if (options & STREAM_USE_BLOCKING_PIPE) {
@@ -1624,12 +1654,3 @@ stream_skip:
}
/* }}} */
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: noet sw=4 ts=4 fdm=marker
- * vim<600: noet sw=4 ts=4
- */