Skip to content

Commit

Permalink
Fix pipe detection and stream position handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Sep 5, 2019
1 parent 4ecdff2 commit 9ec61e4
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 31 deletions.
52 changes: 21 additions & 31 deletions main/streams/plain_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,35 +242,38 @@ 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) {
#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);
}
#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_pipe = file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR;
}
#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);

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

detect_is_pipe(self);
if (self->is_pipe) {
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;
}
Expand All @@ -288,23 +291,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

detect_is_pipe(self);
if (self->is_pipe) {
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
stream->position = -1;
} else {
stream->position = zend_ftell(file);
}
Expand Down
31 changes: 31 additions & 0 deletions sapi/cli/tests/std_streams.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Testing ftell() on std streams
--SKIPIF--
<?php
if (getenv("SKIP_IO_CAPTURE_TESTS")) {
die("skip I/O capture test");
}
?>
--CAPTURE_STDIO--
STDOUT
--FILE--
<?php

// These have proc_open pipes attached
var_dump(ftell(STDIN));
var_dump(ftell(STDERR));
var_dump(ftell(fopen("php://stdin", "r")));
var_dump(ftell(fopen("php://stderr", "w")));

// These have a tty attached
var_dump(ftell(STDOUT));
var_dump(ftell(fopen("php://stdout", "w")));

?>
--EXPECT--
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)

0 comments on commit 9ec61e4

Please sign in to comment.