What does epoll do with a file descriptor that refers to a directory?
Asked Answered
B

1

13

Just like the title says, it I register a file descriptor that is a directory with epoll, what does it do?

Blinker answered 6/8, 2012 at 17:36 Comment(1)
If you want to monitor file system events on Linux, use inotify.Riga
O
18

Nothing -- the call to register the fd will (at least for common Linux filesystems) fail with EPERM.

I tested this using the following demo program:

#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void) {
    int ep = epoll_create1(0);
    int fd = open("/tmp", O_RDONLY|O_DIRECTORY);
    struct epoll_event evt = {
        .events = EPOLLIN
    };

    if (ep < 0 || fd < 0) {
        printf("Error opening fds.\n");
        return -1;
    }

    if (epoll_ctl(ep, EPOLL_CTL_ADD, fd, &evt) < 0) {
        perror("epoll_ctl");
        return -1;
    }
    return 0;
}

With the following result:

[nelhage@hectique:/tmp]$ make epoll
cc     epoll.c   -o epoll
[nelhage@hectique:/tmp]$ ./epoll
epoll_ctl: Operation not permitted

To figure out what was going on here, I went to the source. I happen to know that most of the behavior of epoll is determined by the ->poll function on the struct file_operations corresponding to the target file, which depends on the file system in question. I picked ext4 as a typical example, and looked at fs/ext4/dir.c, which defines ext4_dir_operations as follows:

const struct file_operations ext4_dir_operations = {
    .llseek     = ext4_dir_llseek,
    .read       = generic_read_dir,
    .readdir    = ext4_readdir,
    .unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = ext4_compat_ioctl,
#endif
    .fsync      = ext4_sync_file,
    .release    = ext4_release_dir,
};

Note the lack of a .poll definition, meaning it will be initialized to NULL. So, swinging back to epoll, which is defined in fs/eventpoll.c, we look for checks for poll being NULL, and we find one early on in the epoll_ctl syscall definition:

/* The target file descriptor must support poll */
error = -EPERM;
if (!tfile->f_op || !tfile->f_op->poll)
    goto error_tgt_fput;

As our test indicated, if the target file doesn't support poll, the insert attempt will just fail out with EPERM.

It's possible that other filesystems define .poll methods on their directory file objects, but I doubt that many do.

Oscoumbrian answered 6/8, 2012 at 18:4 Comment(2)
Is dirfd(opendir("/tmp")) preferred over open(path, O_RDONLY|O_DIRECTORY);? Just a style question. Using opendir doesn't magically make the fs support poll.Teyde
dirfd(opendir("...")) is more portable, so is probably preferred in general. I'm a Linux kernel hacker, so I personally tend to default to using the system-call interface even when it's not the most appropriate, because I know it better. Obviously here it doesn't really matter, since epoll is also Linux specific.Oscoumbrian

© 2022 - 2024 — McMap. All rights reserved.