Web lists-archives.com

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




On 2019-01-22 09:50, Houder wrote:
On Sun, 6 Jan 2019 21:19:50, Corinna Vinschen  wrote:

On Dec 16 17:31, Houder wrote:
> 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 should work in the latest developer snapshot uploaded to
https://cygwin.com/snapshots/  Please give it a try.

Thanks,
Corinna

Nice!

This solves: <program> -i /dev/fd/N N<<EOF (i.e. Steven Penny's problem)

Howver ...
(and I sure that I am not telling you anything new)

it still not the same as Linux ...

So, for the record only:

and as another example, this STC succeeds on Linux ..., but fails on Cygwin.

64-@@ ./stca /dev/fd/0 <<EOF
bla
EOF
fd1 = 0
argv[1] = /dev/fd/0
fd2 = 3
id = writefd2, errno = 13, errstr = Permission denied
64-@@

=====

// gcc -Wall -o stca stca.c

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

/*
 A file that has been deleted, but is still "held open" by an entry in the open file table
 (i.e. the file is referred by an open file descriptor), can be opened again read-write in
 Linux, but not in Cygwin ...
 */

// NOTE: my text uses "fd 2" for a file descriptor that equals 2; and fd2 for variable fd2

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

static void
Usage(const char *const arg)
{
    fprintf(stderr, "Usage: %s /dev/fd/N\n", arg);
    exit(EXIT_FAILURE);
}

#if 0
#define DISPLAYSTATINFO
static void displayStatInfo(const struct stat *sb);
#endif

// invoke as follows: ./stca /dev/fd/0 <<EOF

int
main(int argc, char *argv[])
{
    if (argc < 2) {
        Usage(argv[0]);
        exit(EXIT_SUCCESS);
    }

    // @@ ./stca /dev/fd/8 8<<EOF 0<&- # fd 8 is connected to tmpfile (bash!)
    // using an argument, the stc can be told to read from fd 8 (fd 0 can be closed)
    // @@ ./stca /dev/fd/5 8<<EOF 5<&8- 0<&- # ... here fd 0 and fd 8 are closed

    // kludge
    if (strlen(argv[1]) < 9) Usage(argv[0]);
    if (strncmp(argv[1], "/dev/fd/", 8) != 0) Usage(argv[0]);
    if ( (strlen(argv[1]) > 9) ||
         ( (argv[1][8] - 0x30 < 0) || (argv[1][8] - 0x30 > 9) ) ) {
        fprintf(stderr, "N must be a single digit, please!\n");
        Usage(argv[0]);
    }
    // end kludge

    int fd1;
    fd1 = argv[1][8] - 0x30;
    printf("fd1 = %d\n", fd1);

    // using bash, /dev/fd/N refers to a file which been unlink'ed
    printf("argv[1] = %s\n", argv[1]);

    errno = 0;

    int fd2;
#if 0
    if ( (fd2 = open(argv[1], O_RDONLY)) == -1) errExit("openfd2"); // what 'cat' does
#else
#define WRITE
    if ( (fd2 = open(argv[1], O_RDWR)) == -1) errExit("openfd2");
    //if ( (fd2 = open(argv[1], O_RDWR | O_APPEND)) == -1) errExit("openfd2");
#endif
    printf("fd2 = %d\n", fd2);

#if defined(DISPLAYSTATINFO)
    struct stat sb;
    if (fstat(fd2, &sb) == -1) errExit("fstatfd2");
    displayStatInfo(&sb);
#endif

    /* if file has been opened read-write, write() will succeed in Linux,
       but fail in Cygwin
     */

#if defined(WRITE)
    if (write(fd2, "Hello, world!\n", 14) == -1) errExit("writefd2");
#endif

    // how about an lseek() first? (write() modifies the file offset)
    if (lseek(fd2, 0, SEEK_SET) == -1) errExit("lseekfd2");

    char buf[80] = { 0 }; // another kludge
    if (read(fd2, buf, sizeof(buf) ) == -1) errExit("readfd2");
    printf("buf = \\\n%s", buf);

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

#if defined(DISPLAYSTATINFO)
  #if 1
static void
displayStatInfo(const struct stat *sb)
{
   printf("Link count: %ld\n", (long)sb->st_nlink);
}

  #else
static char *filePermStr(mode_t perm, int flags);

static void
displayStatInfo(const struct stat *sb)
{
   printf("File type:                ");

   // deprecated by S_ISREG(m) and c.s. ...? /usr/include/linux/stat.h
   switch (sb->st_mode & S_IFMT) {
   case S_IFBLK:  printf("block device\n");            break;
   case S_IFCHR:  printf("character device\n");        break;
   case S_IFDIR:  printf("directory\n");               break;
   case S_IFIFO:  printf("FIFO/pipe\n");               break;
   case S_IFLNK:  printf("symlink\n");                 break;
   case S_IFREG:  printf("regular file\n");            break;
   case S_IFSOCK: printf("socket\n");                  break;
   default:       printf("unknown?\n");                break;
   }

   printf("I-node number:            %ld\n", (long)sb->st_ino);

#if 0
   printf("Mode:                     %lo (octal)\n", (unsigned long)sb->st_mode);
#else
   printf("Mode:                     %lo (octal), ls(1)-style: %s\n",
                                     (unsigned long)sb->st_mode, filePermStr(sb->st_mode, 0));
#endif

   printf("Link count:               %ld\n", (long)sb->st_nlink);
   printf("Ownership:                UID=%ld   GID=%ld\n", (long)sb->st_uid, (long)sb->st_gid);

   printf("Preferred I/O block size: %ld bytes\n", (long)sb->st_blksize);
   printf("File size:                %lld bytes\n", (long long)sb->st_size);
   printf("Blocks allocated:         %lld\n", (long long)sb->st_blocks);

   printf("Last status change:       %s", ctime(&sb->st_ctime));
   printf("Last file access:         %s", ctime(&sb->st_atime));
   printf("Last file modification:   %s", ctime(&sb->st_mtime));
}

/* Return ls(1)-style string for file permissions mask */

/* Include set-user-ID, set-group-ID, and sticky bit information in
   returned string */
#define FP_SPECIAL 1

#define STR_SIZE sizeof("rwxrwxrwx")

static char *
filePermStr(mode_t perm, int flags)
{
    static char str[STR_SIZE];

    /* If FP_SPECIAL was specified, we emulate the trickery of ls(1) in
       returning set-user-ID, set-group-ID, and sticky bit information in
       the user/group/other execute fields. This is made more complex by
       the fact that the case of the character displayed for this bits
       depends on whether the corresponding execute bit is on or off. */

    snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
        (perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
        (perm & S_IXUSR) ?
            (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
            (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
        (perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-',
        (perm & S_IXGRP) ?
            (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
            (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
        (perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
        (perm & S_IXOTH) ?
            (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 't' : 'x') :
            (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 'T' : '-'));

    return str;
}
  #endif
#endif

//=====
--
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