On 1/23/2020 11:31 AM, Ken Brown wrote:
> Commit aa55d22c, "Cygwin: honor the O_PATH flag when opening a FIFO",
> fixed a hang but otherwise didn't accomplish the purpose of the O_PATH
> flag as stated in the Linux man page for open(2):
> 
>      Obtain a file descriptor that can be used for two purposes: to
>      indicate a location in the filesystem tree and to perform
>      operations that act purely at the file descriptor level.  The
>      file itself is not opened, and other file operations (e.g.,
>      read(2), write(2), fchmod(2), fchown(2), fgetxattr(2),
>      ioctl(2), mmap(2)) fail with the error EBADF.
> 
>      [The man page goes on to describe operations that *can* be
>      performed: close(2), fchdir(2), fstat(2),....]
> 
>      Opening a file or directory with the O_PATH flag requires no
>      permissions on the object itself (but does require execute
>      permission on the directories in the path prefix).
> 
> The first problem in the current implementation is that if open(2) is
> called on a FIFO, fhandler_base::device_access_denied is called and
> tries to open the FIFO with read access, which isn't supposed to be
> required.  This is fixed by the first patch in this series.
> 
> The second patch makes fhandler_fifo::open call fhandler_base::open_fs
> if O_PATH is set, so that we actually obtain a handle that can be used
> for the purposes stated above.
> 
> The third page tweaks fhandler_fifo::fcntl and fhandler_fifo::dup so
> that they work with O_PATH.
> 
> In a followup email I'll provide the program I used to test this
> implementation.

Test program attached.

$ gcc -o o_path_fifo_test o_path_fifo_test.c

$ ./o_path_fifo_test.exe
The following calls should fail with EBADF:
read: OK
write: OK
fchmod: OK
fchown: OK
ioctl: OK
fgetxattr: OK
mmap: OK

The following calls should succeed:
fstat: OK
fstatfs: OK
fcntl_dup: OK
fcntl_getfl: OK
fcntl_setfl: OK
fcntl_getfd: OK
fcntl_setfd: OK
close: OK
#define _GNU_SOURCE
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/vfs.h>
#include <sys/xattr.h>
#include <sys/mman.h>

#define FIFO_PATH "/tmp/myfifo"

int
main ()
{
  struct stat s;
  int fd;

  if (mkfifo (FIFO_PATH, S_IRUSR | S_IWUSR | S_IWGRP) < 0
      && errno != EEXIST)
    {
      perror ("mkfifo");
      exit (1);
    }

  fd = open (FIFO_PATH, O_PATH);
  if (fd < 0)
    {
      perror ("open");
      exit (1);
    }
  printf ("The following calls should fail with EBADF:\n");

  errno = 0;
  if (read (fd, NULL, 0) < 0 && errno == EBADF)
    printf ("read: OK\n");
  else
    perror ("read");

  errno = 0;
  if (write (fd, NULL, 0) < 0 && errno == EBADF)
    printf ("write: OK\n");
  else
    perror ("write");

  errno = 0;
  if (fchmod (fd, 0) < 0 && errno == EBADF)
    printf ("fchmod: OK\n");
  else
    perror ("fchmod");

  errno = 0;
  if (fchown (fd, -1, -1) < 0 && errno == EBADF)
    printf ("fchown: OK\n");
  else
    perror ("fchown");

  errno = 0;
  if (ioctl (fd, FIONBIO, NULL) < 0 && errno == EBADF)
    printf ("ioctl: OK\n");
  else
    perror ("ioctl");

  errno = 0;
  if (fgetxattr (fd, "", NULL, 0) < 0 && errno == EBADF)
    printf ("fgetxattr: OK\n");
  else
    perror ("fgetxattr");

  errno = 0;
  if (mmap (NULL, 1, 0, MAP_SHARED, fd, 0) == MAP_FAILED && errno == EBADF)
    printf ("mmap: OK\n");
  else
    perror ("mmap");

  printf ("\nThe following calls should succeed:\n");

  if (fstat (fd, &s) < 0)
    perror ("fstat");
  else
    printf ("fstat: OK\n");

  struct statfs st;
  if (fstatfs (fd, &st) < 0)
    perror ("fstatfs");
  else
    printf ("fstatfs: OK\n");

  if (fcntl (fd, F_DUPFD, 0) < 0)
    perror ("fcntl_dup");
  else
    printf ("fcntl_dup: OK\n");

  int flags = fcntl (fd, F_GETFL);
  if (flags < 0)
    perror ("fcntl_getfl");
  else
    printf ("fcntl_getfl: OK\n");

  if (flags >= 0)
    {
      if (fcntl (fd, F_SETFL, flags) < 0)
        perror ("fcntl_setfl");
      else
        printf ("fcntl_setfl: OK\n");
    }

  flags = fcntl (fd, F_GETFD);
  if (flags < 0)
    perror ("fcntl_getfd");
  else
    printf ("fcntl_getfd: OK\n");

  if (flags >= 0)
    {
      if (fcntl (fd, F_SETFD, flags) < 0)
        perror ("fcntl_setfd");
      else
        printf ("fcntl_setfd: OK\n");
    }

  if (close (fd) < 0)
    perror ("close");
  else
    printf ("close: OK\n");
}

Reply via email to