Skip to content

Commit 9ec61e4

Browse files
committed
Fix pipe detection and stream position handling
There are two related changes here: 1. Also check for S_ISCHR/FILE_TYPE_CHAR when checking for pipes, so that we detect ttys as well, which are also not seekable. 2. Always set position=-1 (i.e. ftell will return false) when a pipe is detected. Previously position=0 was sometimes used, depending on whether we're on Windows/Linux and whether the FD or FILE codepath was used.
1 parent 4ecdff2 commit 9ec61e4

File tree

2 files changed

+52
-31
lines changed

2 files changed

+52
-31
lines changed

main/streams/plain_wrapper.c

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -242,35 +242,38 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
242242
return php_stream_fopen_temporary_file(NULL, "php", NULL);
243243
}
244244

245+
static void detect_is_pipe(php_stdio_stream_data *self) {
246+
#if defined(S_ISFIFO) && defined(S_ISCHR)
247+
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);
249+
}
250+
#elif defined(PHP_WIN32)
251+
zend_uintptr_t handle = _get_osfhandle(self->fd);
252+
253+
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
254+
DWORD file_type = GetFileType((HANDLE)handle);
255+
256+
self->is_pipe = file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR;
257+
}
258+
#endif
259+
}
260+
245261
PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC)
246262
{
247263
php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
248264

249265
if (stream) {
250266
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
251267

252-
#ifdef S_ISFIFO
253-
/* detect if this is a pipe */
254-
if (self->fd >= 0) {
255-
self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
256-
}
257-
#elif defined(PHP_WIN32)
258-
{
259-
zend_uintptr_t handle = _get_osfhandle(self->fd);
260-
261-
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
262-
self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
263-
}
264-
}
265-
#endif
266-
268+
detect_is_pipe(self);
267269
if (self->is_pipe) {
268270
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
271+
stream->position = -1;
269272
} else {
270273
stream->position = zend_lseek(self->fd, 0, SEEK_CUR);
271274
#ifdef ESPIPE
275+
/* FIXME: Is this code still needed? */
272276
if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {
273-
stream->position = 0;
274277
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
275278
self->is_pipe = 1;
276279
}
@@ -288,23 +291,10 @@ PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STRE
288291
if (stream) {
289292
php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
290293

291-
#ifdef S_ISFIFO
292-
/* detect if this is a pipe */
293-
if (self->fd >= 0) {
294-
self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
295-
}
296-
#elif defined(PHP_WIN32)
297-
{
298-
zend_uintptr_t handle = _get_osfhandle(self->fd);
299-
300-
if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
301-
self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
302-
}
303-
}
304-
#endif
305-
294+
detect_is_pipe(self);
306295
if (self->is_pipe) {
307296
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
297+
stream->position = -1;
308298
} else {
309299
stream->position = zend_ftell(file);
310300
}

sapi/cli/tests/std_streams.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Testing ftell() on std streams
3+
--SKIPIF--
4+
<?php
5+
if (getenv("SKIP_IO_CAPTURE_TESTS")) {
6+
die("skip I/O capture test");
7+
}
8+
?>
9+
--CAPTURE_STDIO--
10+
STDOUT
11+
--FILE--
12+
<?php
13+
14+
// These have proc_open pipes attached
15+
var_dump(ftell(STDIN));
16+
var_dump(ftell(STDERR));
17+
var_dump(ftell(fopen("php://stdin", "r")));
18+
var_dump(ftell(fopen("php://stderr", "w")));
19+
20+
// These have a tty attached
21+
var_dump(ftell(STDOUT));
22+
var_dump(ftell(fopen("php://stdout", "w")));
23+
24+
?>
25+
--EXPECT--
26+
bool(false)
27+
bool(false)
28+
bool(false)
29+
bool(false)
30+
bool(false)
31+
bool(false)

0 commit comments

Comments
 (0)