From: Christoph M. Becker Date: Mon, 2 Dec 2019 14:14:57 +0000 (+0100) Subject: Fix #78883: fgets(STDIN) fails on Windows X-Git-Tag: php-7.4.7RC1~475 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=996f217aa5487ead468fd8edc3ef278fb0686c32;p=php Fix #78883: fgets(STDIN) fails on Windows We add the `is_seekable` member to `php_stdio_stream_data`, and prefer that over `is_pipe`, since the latter is simply a misnomer. We keep `is_pipe` for now for Windows only, though, because we need special support for pipes there. We also fix the misaligned bitfield which formerly took 33 bit. --- diff --git a/NEWS b/NEWS index 46ba2741ca..1c7775c45f 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ PHP NEWS . Fixed bug #78868 (Calling __autoload() with incorrect EG(fake_scope) value). (Antony Dovgal, Dmitry) . Fixed bug #78296 (is_file fails to detect file). (cmb) + . Fixed bug #78883 (fgets(STDIN) fails on Windows). (cmb) - GD: . Fixed bug #78849 (GD build broken with -D SIGNED_COMPARE_SLOW). (cmb) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 61b19e48ad..8f7774a30e 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -125,11 +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 no_forced_fstat:1; /* Use fstat cache even if forced */ - unsigned _reserved:28; + 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 @@ -173,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; @@ -192,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; @@ -242,10 +245,11 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC) return php_stream_fopen_temporary_file(NULL, "php", NULL); } -static void detect_is_pipe(php_stdio_stream_data *self) { +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_pipe = S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode); + 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); @@ -253,7 +257,8 @@ static void detect_is_pipe(php_stdio_stream_data *self) { if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) { DWORD file_type = GetFileType((HANDLE)handle); - self->is_pipe = file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR; + self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR); + self->is_pipe = file_type == FILE_TYPE_PIPE; } #endif } @@ -265,8 +270,8 @@ 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; - detect_is_pipe(self); - if (self->is_pipe) { + detect_is_seekable(self); + if (!self->is_seekable) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; stream->position = -1; } else { @@ -275,7 +280,7 @@ PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const cha /* FIXME: Is this code still needed? */ if (stream->position == (zend_off_t)-1 && errno == ESPIPE) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; - self->is_pipe = 1; + self->is_seekable = 0; } #endif } @@ -291,8 +296,8 @@ 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; - detect_is_pipe(self); - if (self->is_pipe) { + detect_is_seekable(self); + if (!self->is_seekable) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; stream->position = -1; } else { @@ -311,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; @@ -355,7 +361,7 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun } 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'; @@ -430,7 +436,7 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) } 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 @@ -531,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; }