$RodHat_
Syscall reference · Events

fanotify_init(2)

Create an fanotify group: VFS-level file access events with live fds and blockable opens.

Since
Linux 2.6.37check your kernel →
glibc
unknown
System
Linux
Man page
fanotify_init(2)

Synopsis

#include <fcntl.h>            /* Definition of O_* constants */
#include <sys/fanotify.h>

int fanotify_init(unsigned int flags, unsigned int event_f_flags);

Rod's take

inotify tells you what happened after it happened, which is useless when you want to say no. fanotify hands you a live fd to the actual file instead of a name that may already be wrong, and with a content-class group it parks the opener in the kernel until your daemon writes FAN_ALLOW or FAN_DENY. That's what the AV and EDR crowd actually hook. The bill: CAP_SYS_ADMIN for anything useful, every event fd must be closed or you bleed descriptors, and a handler that opens files on its own marked filesystem has built itself a deadlock. Sharp tool. Respect it.

Flags and arguments that matter

NameWhat it does
FAN_CLASS_NOTIFDefault class. Notification only; no permission decisions possible.
FAN_CLASS_CONTENTNotification plus permission events, for listeners that need files with their final content (e.g. malware scanners). CAP_SYS_ADMIN.
FAN_CLASS_PRE_CONTENTNotification plus permission events, for listeners that may write data before it's accessed (e.g. HSMs). CAP_SYS_ADMIN.
FAN_CLOEXECSet close-on-exec on the group fd.
FAN_NONBLOCKMake the group fd nonblocking; read(2) returns EAGAIN when the queue is empty.
FAN_UNLIMITED_QUEUERemove the event queue limit. Requires CAP_SYS_ADMIN.
FAN_REPORT_TIDLinux 4.20Report the thread ID instead of the process ID in the event metadata pid field.
FAN_REPORT_FIDLinux 5.1Identify objects by file handle instead of an open fd; not allowed with the CONTENT/PRE_CONTENT classes.
FAN_REPORT_DFID_NAMESynonym for FAN_REPORT_DIR_FID|FAN_REPORT_NAME (both since Linux 5.9): report parent dir handle plus entry name.
FAN_REPORT_PIDFDLinux 5.15 and 5.10.220Attach a pidfd for the process that caused the event, so PID reuse can't fool you.

Errors you will actually hit

errnoMeaning
EPERMCaller lacks a required capability (CAP_SYS_ADMIN for the content classes and unlimited flags).
EINVALInvalid value in flags or event_f_flags.
EMFILEPer-user fanotify group limit exceeded, or the per-process fd limit was hit.
ENOSYSKernel built without CONFIG_FANOTIFY.
ENOMEMAllocating the notification group failed.

Example

int fan = fanotify_init(FAN_CLASS_CONTENT | FAN_CLOEXEC,
                        O_RDONLY | O_LARGEFILE);
if (fan < 0) err(1, "fanotify_init");
if (fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
                  FAN_OPEN_PERM, AT_FDCWD, "/sensitive") < 0)
    err(1, "fanotify_mark");

char buf[4096];
ssize_t n = read(fan, buf, sizeof buf);
struct fanotify_event_metadata *ev = (void *)buf;
for (; FAN_EVENT_OK(ev, n); ev = FAN_EVENT_NEXT(ev, n)) {
    if (ev->fd == FAN_NOFD) continue;   /* queue overflow */
    struct fanotify_response r = { .fd = ev->fd, .response = FAN_ALLOW };
    if (ev->mask & FAN_OPEN_PERM) write(fan, &r, sizeof r);
    close(ev->fd);                      /* mandatory */
}

Trimmed from the full walkthrough in fanotify(7): watch and block file opens at the VFS layer.

BSD equivalent

FreeBSD and OpenBSD: no equivalent. kqueue(2) EVFILT_VNODE gives after-the-fact vnode notifications only, with no permission decisions.

The long version, from the console

Sources

  1. fanotify_init(2), man7.org
  2. fanotify(7), man7.org
  3. fanotify_mark(2), man7.org

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.