Hi,

cp has some issues with O_NOFOLLOW meaning O_NOTRANS (per
__hurd_file_name_lookup). One of them is when opening pipes with O_NOFOLLOW (or
when opening them with O_NOTRANS, which is the problem here). Then the
permissions are checked in libdiskfs (if the pipe is in an ext2fs partition for
example), which has the following in libdiskfs/dir-lookup.c:

      if (((type == S_IFSOCK || type == S_IFBLK || type == S_IFCHR ||
           type == S_IFIFO)
           && (flags & (O_READ|O_WRITE|O_EXEC)))
          || (type == S_IFLNK && (flags & (O_WRITE|O_EXEC))))
        error = EACCES;

So opening a named pipe with open(pipe, O_RDONLY | O_NOTRANS, 0) will cause
EACCES. I wonder what is the reason for not allowing access to those kind of
files if they are opened with O_READ, O_WRITE or O_EXEC. Why can't they be
allowed, as long as the opener has the appropriate permissions (which will be
checked later)?

The attached eacces.c program reproduces this issue.


The other problem is that an stat on a named pipe like

fd = open (pipe, O_RDONLY | O_NOFOLLOW, 0);
fstat (fd, &sb);

Doesn't match this:

lstat (pipe, &sb);

In particular, sb.st_dev will be different. The problem is that lstat uses
O_NOLINK internally, and since the named pipe is not a symlink, it will get the
underlying node, and the io_stat call will be handled by the fifo translator,
which will return its PID in st_dev. However the open call has O_NOFOLLOW, which
implies O_NOTRANS and thus it doesn't get the underlying node, returning the
correct st_dev. Would it be possible to make these kind of translators return
the device id of its parent?

The attached stat.c testcase reproduces this issue (you can remove the
O_NOFOLLOW flag to see the devices matching, but being the PID of the fifo
translator).

Regards,
Emilio
#define _GNU_SOURCE
#include <hurd.h>
#include <error.h>
#include <fcntl.h>

int
main (int argc, char **argv)
{
  file_t file;

  if (argc < 2)
    error (1, 0, "Usage: %s <file>", argv[0]);

  file = file_name_lookup (argv[1], O_RDONLY | O_NOFOLLOW, 0);

  if (file == MACH_PORT_NULL)
    error (1, errno, "error when opening %s", argv[1]);

  return 0;
}
#define _GNU_SOURCE
#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>

# define SAME_INODE(Stat_buf_1, Stat_buf_2) \
   ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
    && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)

int
main (int argc, char **argv)
{
  int fd;
  struct stat st1, st2;

  if (argc != 2)
    error (1, 0, "Usage: %s <file>", argv[0]);

  if ((fd = open (argv[1], O_RDONLY | O_NOFOLLOW, 0)) == -1)
    error (1, errno, "error when opening %s", argv[1]);

  if (fstat (fd, &st1) == -1)
    error (1, errno, "error when stating %s", argv[1]);

  if (lstat (argv[1], &st2) == -1)
    error (1, errno, "error when stating %s", argv[1]);

  printf ("devices: %d %d, %d %d\n", st1.st_dev, st2.st_dev);

  if (SAME_INODE(st1, st2))
    printf ("same inode!\n");
  else
    printf ("different inode!\n");

  return 0;
}

Reply via email to