Web lists-archives.com

/dev/fd/N not synonymous with file descriptor N; it is on Linux




L.S.,

/dev/fd/N not synonymous with file descriptor N; it is on Linux

64-@@ cat /dev/fd/0 <<\EOF
Hi
EOF
cat: /dev/fd/0: No such file or directory

fails on Cygwin; not on Linux.

Also see:

    https://cygwin.com/ml/cygwin/2018-12/msg00028.html
    ( Bash heredoc on FD 3 )

Based on the output of strace on Linux, I composed an STC, that duplicates
the steps taken by bash (and cat).

This STC succeeds on Linux, but fails on Cygwin.

What does the STC do:

 - it creates a (temporary) file in the same way that bash does
- the file is written to, the file descriptor is closed and the file unlinked - however, before the file is unlinked, it is opened a second time, like bash
   would do

Next
- the file /dev/fd/N is opened, where N is the file descriptor that has been
   left open; this is what the "cat command" would do.

The "cat command" on Linux succeeds; it fails on Cygwin.

Regards,
Henri

STC attached (hopefully)

=====
// gcc -Wall -o stc stc.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> // strerror()
#include <errno.h>
#include <fcntl.h>

/*
 LPI, 5.11 The /dev/fd Directory (Linux Programming Interface, Michael Kerrisk)
 "For each process, the kernel provides the special virtual directory /dev/fd.
  This directory contains filenames of the form /dev/fd/n, where n is a number
  corresponding to one of the open file descriptors for the process."
 "... Opening one of the files in the /dev/fd directory is equivalent to duplicating
  the corresponding file descriptor. ..."
 "... The files in the /dev/fd directory are rarely used within programs. Their most
  common use is in the shell. Many user-level commands take filename arguments, and
  sometimes we would like to put them in a pipeline and have one of the arguments be
  standard input or output instead. ..."
 */

void
errExit(const char *str)
{
    printf("id = %s, errno = %d, errstr = %s\n", str, errno, strerror(errno));
    fflush(stdout);
    exit(EXIT_FAILURE);
}

int
main()
{
    int fd1, fd2, fd3;

    errno = 0;
    // create a tmpfile in the same way that bash would do ...
    fd1 = open("/tmp/stc.txt", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
    if (fd1 == -1)
        errExit("openfd1");

    errno = 0;
    if (write(fd1, "Hello, world!\n", 14) == -1) errExit("writefd1");

    errno = 0;
    // also open this tmpfile for reading like bash would do ...
    fd2 = open("/tmp/stc.txt", O_RDONLY);
    if (fd2 == -1)
        errExit("openfd2");

    errno = 0;
    // close fd 1 like bash would do ...
    if (close(fd1) == -1) errExit("closefd1");

    errno = 0;
    // delete the tmpfile like bash would do ...
    if (unlink("/tmp/stc.txt") == -1) errExit("unlink");

    // kludge: compose a string (using fd2) representing "the device file"
    // in /dev/fd (a symlnk to /proc/self/fd) for the file descriptor fd 2
    // that is still open ...
    char devfile[12] = "/dev/fd/"; devfile[8] = fd2 + 0x30; devfile[9] = '\0';

    printf("devfile = %s\n", devfile);

    errno = 0;
    // open this device file; it succeeds on Linux, but fails on Cygwin ...
    fd3 = open(devfile, O_RDONLY);

    const char *id = "openfd3";
    if (fd3 == -1) {
#if 0
        errExit(id);
#else
	printf("%s: Cannot open!, id = %s, errno = %d, errmsg = %s\n", devfile,
            id, errno, strerror(errno));
        char buf[16] = { 0 };
        // however the file to which the symlnk refers, is still present ...
        // Q: does Cygwin attempt to read the /tmp directory? (an attempt that
        //    will fail, because the file has been unlinked)
        // it appears that a readlink of a file in /dev/fd must be diverted to
        // the open file descriptor of the process ...
        errno = 0;
        if (read(fd2, buf, sizeof(buf) ) == -1) errExit("readfd2");
        printf("buf = %s", buf);
        if (close(fd2) == -1) printf("closefd2 failed\n");
        exit(EXIT_FAILURE);
#endif
    }

    char buf[16] = { 0 };
    errno = 0;
    if (read(fd3, buf, sizeof(buf) ) == -1) errExit("readfd3");
    printf("buf = %s", buf);

    if (close(fd3) == -1) printf("closefd3 failed\n");
    if (close(fd2) == -1) printf("closefd2 failed\n");
}

//=====
--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple