php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #53465 Cannot open file descriptor streams
Submitted: 2010-12-03 18:24 UTC Modified: 2010-12-11 02:53 UTC
From: dchurch at sciencelogic dot com Assigned: cataphract (profile)
Status: Duplicate Package: Filesystem function related
PHP Version: 5.3.3 OS: Linux
Private report: No CVE-ID: None
 [2010-12-03 18:24 UTC] dchurch at sciencelogic dot com
Description:
------------
PHP cannot open, read from, or write to arbitrary file descriptors, a feature that is necessary for communications with certain utilities that expect input or output on certain file descriptors.  When programming in C, this functionality is provided by fdopen().  A feature request was filed over seven years ago for PHP to have a similar capability through the php:// stream wrapper (#26158), but the bug was marked as bogus for no adequately defined reason.

The alternative to being able to use a fdopen()-like function is to open the file descriptor as a named file through /dev/fd/ or /proc/self/fd/.  This works in C using fopen("/dev/fd/<descriptor>", "w"), but it does not work in PHP because PHP incorrectly attempts to dereference the pseudo-symlink before opening it.

I have a C program and a PHP script that both attempt to do the same thing: open file descriptor 3 for writing using /dev/fd/3, read until EOF from standard in and write to the file descriptor.  Both should be run as:

(executable) 3>&1 | cat

Test script:
---------------
PHP script:
<?php
$file = fopen("/dev/fd/3", "w");

if (!$file) exit(1);

$input = stream_get_contents(STDIN);
fwrite($file, $input);
fclose($file);

C program:
#include <stdio.h>

int main() {
  char buf[8192];
  int count;
  FILE *file;

  file = fopen("/dev/fd/3", "w");

  if (file == NULL) {
    perror("Opening /dev/fd/3");
    return 1;
  }

  while ((count = fread(buf, 1, sizeof(buf), stdin)) > 0) {
    fwrite(buf, count, 1, file);
    if (feof(stdin)) break;
  }
  return 0;
}

Expected result:
----------------
Input should be mirrored to file descriptor 3; when called using the command line above, it should be mirrored to standard output.

Actual result:
--------------
I get the following warning:

PHP Warning:  fopen(/dev/fd/3): failed to open stream: No such file or directory in test.php on line 2

An strace of the PHP process shows the incorrect behavior:

lstat("/dev/fd/3", {st_mode=S_IFLNK|0300, st_size=64, ...}) = 0
readlink("/dev/fd/3", "pipe:[637257]", 4096) = 13
lstat("/dev/fd/pipe:[637257]", 0x7fff76ae8140) = -1 ENOENT (No such file or directory)
lstat("/dev/fd", {st_mode=S_IFLNK|0777, st_size=13, ...}) = 0
readlink("/dev/fd", "/proc/self/fd"..., 4096) = 13
lstat("/proc/self/fd", {st_mode=S_IFDIR|0500, st_size=0, ...}) = 0
lstat("/proc/self", {st_mode=S_IFLNK|0777, st_size=64, ...}) = 0
readlink("/proc/self", "28234"..., 4096) = 5
lstat("/proc/28234", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
lstat("/proc", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/28234/fd/pipe:[637257]", O_WRONLY|O_CREAT|O_TRUNC, 0666) = -1 ENOENT (No such file or directory)


Patches

php_fd_open.diff (last revision 2010-12-06 22:24 UTC by cataphract@php.net)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-12-06 23:24 UTC] cataphract@php.net
The following patch has been added/updated:

Patch Name: php_fd_open.diff
Revision:   1291674246
URL:        http://bugs.php.net/patch-display.php?bug=53465&patch=php_fd_open.diff&revision=1291674246
 [2010-12-06 23:29 UTC] cataphract@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: cataphract
 [2010-12-06 23:29 UTC] cataphract@php.net
I've attached a patch (against trunk) that allows duping+open arbitrary file descriptor. /dev/fd doesn't seem like a good option because it's not portable.

Example:
<?php
$f = fopen("php://fd/1", "w");
fwrite($f, "test");

Can you see if this is enough?

Thanks.
 [2010-12-08 21:17 UTC] dchurch at sciencelogic dot com
This works like a charm.  I much prefer the php://fd/ syntax to fixing the /dev/fd symlink trickery, anyway.  The patch also applies cleanly against the 5.3 branch.  Will this be in 5.3.4?
 [2010-12-09 01:31 UTC] cataphract@php.net
5.3.4 is in the final stages of the release process, so it will not be possible. Maybe 5.3.5, if the release manager agrees with the inclusion.
 [2010-12-11 02:52 UTC] cataphract@php.net
Automatic comment from SVN on behalf of cataphract
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=306215
Log: - Implemented request #26158/bug #53465 (open arbitrary file descriptor with fopen)
 [2010-12-11 02:53 UTC] cataphract@php.net
-Status: Assigned +Status: Duplicate
 [2010-12-11 02:53 UTC] cataphract@php.net
Committed to 5.3 and trunk.

I'm marking this as a duplicate and request #26158 as closed.

Thank you for your report.
 [2013-07-04 06:46 UTC] jakub dot lopuszanski at nasza-klasa dot pl
The problem with "php://fd/3" instead of "/dev/fd/3" is that when I use 
anonymous fifos in bash like this:

php -r 'var_dump($argv);' <(echo "hello")

then the output is as follows:

array(2) {
  [0]=>
  string(1) "-"
  [1]=>
  string(10) "/dev/fd/63"
}

which means that if you have an existing PHP program which accepts filenames, 
you have to add extra "if" to it, with special case just to handle /dev/fd/63 -> 
php://fd/3 conversion. I think that this adds extra complexity for the developer  
in a place where Linux tried hard to eliminate it.
 [2014-12-04 22:51 UTC] dave at lyte dot id dot au
It's a bit of a crazy work around, but if you're stuck with 5.3.3 for now (because it's the supported version in RHEL6 still) and you really really really want to do this:

$pid = getmypid();
$writeFd = popen("cat - > /proc/$pid/fd/3", 'w');
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Nov 23 21:01:28 2024 UTC