Skip to content

Fix pipe detection and stream position handling #4684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)