Skip to content

Commit 996f217

Browse files
committed
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.
1 parent 3356dd0 commit 996f217

File tree

2 files changed

+21
-14
lines changed

2 files changed

+21
-14
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ PHP NEWS
99
. Fixed bug #78868 (Calling __autoload() with incorrect EG(fake_scope) value).
1010
(Antony Dovgal, Dmitry)
1111
. Fixed bug #78296 (is_file fails to detect file). (cmb)
12+
. Fixed bug #78883 (fgets(STDIN) fails on Windows). (cmb)
1213

1314
- GD:
1415
. Fixed bug #78849 (GD build broken with -D SIGNED_COMPARE_SLOW). (cmb)

main/streams/plain_wrapper.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,12 @@ typedef struct {
125125
FILE *file;
126126
int fd; /* underlying file descriptor */
127127
unsigned is_process_pipe:1; /* use pclose instead of fclose */
128-
unsigned is_pipe:1; /* don't try and seek */
128+
unsigned is_pipe:1; /* stream is an actual pipe, currently Windows only*/
129129
unsigned cached_fstat:1; /* sb is valid */
130130
unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */
131131
unsigned no_forced_fstat:1; /* Use fstat cache even if forced */
132-
unsigned _reserved:28;
132+
unsigned is_seekable:1; /* don't try and seek, if not set */
133+
unsigned _reserved:26;
133134

134135
int lock_flag; /* stores the lock state */
135136
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
173174
self = pemalloc_rel_orig(sizeof(*self), persistent_id);
174175
memset(self, 0, sizeof(*self));
175176
self->file = NULL;
177+
self->is_seekable = 1;
176178
self->is_pipe = 0;
177179
self->lock_flag = LOCK_UN;
178180
self->is_process_pipe = 0;
@@ -192,6 +194,7 @@ static php_stream *_php_stream_fopen_from_file_int(FILE *file, const char *mode
192194
self = emalloc_rel_orig(sizeof(*self));
193195
memset(self, 0, sizeof(*self));
194196
self->file = file;
197+
self->is_seekable = 1;
195198
self->is_pipe = 0;
196199
self->lock_flag = LOCK_UN;
197200
self->is_process_pipe = 0;
@@ -242,18 +245,20 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
242245
return php_stream_fopen_temporary_file(NULL, "php", NULL);
243246
}
244247

245-
static void detect_is_pipe(php_stdio_stream_data *self) {
248+
static void detect_is_seekable(php_stdio_stream_data *self) {
246249
#if defined(S_ISFIFO) && defined(S_ISCHR)
247250
if (self->fd >= 0 && do_fstat(self, 0) == 0) {
248-
self->is_pipe = S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode);
251+
self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode));
252+
self->is_pipe = S_ISFIFO(self->sb.st_mode);
249253
}
250254
#elif defined(PHP_WIN32)
251255
zend_uintptr_t handle = _get_osfhandle(self->fd);
252256

253257
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
254258
DWORD file_type = GetFileType((HANDLE)handle);
255259

256-
self->is_pipe = file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR;
260+
self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR);
261+
self->is_pipe = file_type == FILE_TYPE_PIPE;
257262
}
258263
#endif
259264
}
@@ -265,8 +270,8 @@ PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const cha
265270
if (stream) {
266271
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
267272

268-
detect_is_pipe(self);
269-
if (self->is_pipe) {
273+
detect_is_seekable(self);
274+
if (!self->is_seekable) {
270275
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
271276
stream->position = -1;
272277
} else {
@@ -275,7 +280,7 @@ PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const cha
275280
/* FIXME: Is this code still needed? */
276281
if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {
277282
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
278-
self->is_pipe = 1;
283+
self->is_seekable = 0;
279284
}
280285
#endif
281286
}
@@ -291,8 +296,8 @@ PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STRE
291296
if (stream) {
292297
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
293298

294-
detect_is_pipe(self);
295-
if (self->is_pipe) {
299+
detect_is_seekable(self);
300+
if (!self->is_seekable) {
296301
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
297302
stream->position = -1;
298303
} else {
@@ -311,6 +316,7 @@ PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STRE
311316
self = emalloc_rel_orig(sizeof(*self));
312317
memset(self, 0, sizeof(*self));
313318
self->file = file;
319+
self->is_seekable = 0;
314320
self->is_pipe = 1;
315321
self->lock_flag = LOCK_UN;
316322
self->is_process_pipe = 1;
@@ -355,7 +361,7 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun
355361
} else {
356362

357363
#if HAVE_FLUSHIO
358-
if (!data->is_pipe && data->last_op == 'r') {
364+
if (data->is_seekable && data->last_op == 'r') {
359365
zend_fseek(data->file, 0, SEEK_CUR);
360366
}
361367
data->last_op = 'w';
@@ -430,7 +436,7 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
430436

431437
} else {
432438
#if HAVE_FLUSHIO
433-
if (!data->is_pipe && data->last_op == 'w')
439+
if (data->is_seekable && data->last_op == 'w')
434440
zend_fseek(data->file, 0, SEEK_CUR);
435441
data->last_op = 'r';
436442
#endif
@@ -531,8 +537,8 @@ static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, ze
531537

532538
assert(data != NULL);
533539

534-
if (data->is_pipe) {
535-
php_error_docref(NULL, E_WARNING, "cannot seek on a pipe");
540+
if (!data->is_seekable) {
541+
php_error_docref(NULL, E_WARNING, "cannot seek on this stream");
536542
return -1;
537543
}
538544

0 commit comments

Comments
 (0)