fanotify_mark(2)

πŸ’– fanotify_mark(2) πŸ’–

okay so bestie, welcome to the System Calls Manual (section 2, the system calls girlies). Today's episode: fanotify_mark(2). It's long, it's giving saga, so we're doing it in parts β€” buckle up πŸŽ€


🌸 NAME 🌸

fanotify_mark β€” add, remove, or modify an fanotify mark on a filesystem object

πŸ“š LIBRARY πŸ“š

Standard C library (libc, -lc)

✨ SYNOPSIS ✨

#include <sys/fanotify.h>

int fanotify_mark(int fanotify_fd, unsigned int flags,
                  uint64_t mask, int dirfd,
                  const char *_Nullable path);

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so first things first, if you want the whole fanotify lore, go read fanotify(7) β€” that's the overview, this is the details.

fanotify_mark() does exactly what it says: it adds, removes, or modifies an fanotify mark on a filesystem object. And no, you can't just mark whatever you want β€” the caller must have read permission on the filesystem object that is to be marked. so rude but okay 🫢

fanotify_fd is a file descriptor that came back from fanotify_init(2). that's the group you're marking stuff in.

flags is a bit mask that says what modification we're doing. It must include exactly one of these three, no more no less:

  • FAN_MARK_ADD β€” the events in mask get added to the mark mask (or to the ignore mask). mask must be nonempty or you get the error EINVAL. periodt.
  • FAN_MARK_REMOVE β€” the events in the mask argument get removed from the mark mask (or from the ignore mask). mask must be nonempty or, again, EINVAL.
  • FAN_MARK_FLUSH β€” wipe either all marks for filesystems, all marks for mounts, or all marks for directories and files, all from the fanotify group. If flags contains FAN_MARK_MOUNT, all marks for mounts get removed from the group. If flags contains FAN_MARK_FILESYSTEM, all marks for filesystems get removed from the group. Otherwise, all marks for directories and files get removed. And listen: no flag other than, and at most one of, the flags FAN_MARK_MNTNS, FAN_MARK_MOUNT, or FAN_MARK_FILESYSTEM can be used in conjunction with FAN_MARK_FLUSH. mask is ignored, she's not part of this conversation.
and if none of those three values is specified, or more than one is specified, the call fails with the error EINVAL. exactly one bestie, we don't do indecision here πŸ’…

🌈 the "you may OR these in" flags 🌈

In addition, zero or more of the following values may be ORed into flags:

  • FAN_MARK_DONT_FOLLOW β€” if path is a symbolic link, mark the link itself instead of the file it points at. (by default, fanotify_mark() dereferences path if it's a symbolic link β€” so this flag is you saying "no, the link, I said what I said".)
  • FAN_MARK_ONLYDIR β€” if the filesystem object to be marked is not a directory, the error ENOTDIR shall be raised. directories only, gatekept ✨
  • FAN_MARK_MNTNS (since Linux 6.14) β€” mark the mount namespace specified by path. If path doesn't represent a mount namespace (e.g. /proc/pid/ns/mnt), the call fails with the error EINVAL. You also need an fanotify group that was initialized with the flag FAN_REPORT_MNT. no group, no mntns, sorry.
  • FAN_MARK_MOUNT β€” mark the mount specified by path. If path is not itself a mount point, the mount containing path gets marked. all directories, subdirectories, and the contained files of the mount get monitored πŸ‘‘ The events which require that filesystem objects are identified by file handles β€” like FAN_CREATE, FAN_ATTRIB, FAN_MOVE, and FAN_DELETE_SELF β€” cannot be provided as a mask when flags contains FAN_MARK_MOUNT. Trying it anyway returns the error EINVAL. Also, using this flag requires the CAP_SYS_ADMIN capability. big girl privileges fr fr.
  • FAN_MARK_FILESYSTEM (since Linux 4.20) β€” mark the filesystem specified by path. The filesystem containing path gets marked. all the contained files and directories of the filesystem, from any mount point, get monitored. Using this flag requires the CAP_SYS_ADMIN capability.
  • FAN_MARK_IGNORED_MASK β€” the events in mask shall be added to or removed from the ignore mask. ⚠️ note that the flags FAN_ONDIR and FAN_EVENT_ON_CHILD have no effect when provided with this flag. And the effect of setting FAN_ONDIR and FAN_EVENT_ON_CHILD in the mark mask on the events set in the ignore mask is undefined and depends on the Linux kernel version β€” so like, genuinely don't rely on it 😭 Specifically: prior to Linux 5.9, setting a mark mask on a file and a mark with ignore mask on its parent directory would not result in ignoring events on the file, regardless of the FAN_EVENT_ON_CHILD flag in the parent directory's mark mask. Also, when the ignore mask is updated with the FAN_MARK_IGNORED_MASK flag on a mark that was previously updated with the FAN_MARK_IGNORE flag, the update fails with EEXIST error.
  • FAN_MARK_IGNORE (since Linux 6.0, 5.15.154, and 5.10.220) β€” similar vibe to setting the FAN_MARK_IGNORED_MASK flag: the events in mask shall be added to or removed from the ignore mask. BUT, unlike FAN_MARK_IGNORED_MASK, this flag also means the FAN_ONDIR and FAN_EVENT_ON_CHILD flags actually take effect on the ignore mask. Concretely: unless the FAN_ONDIR flag is set with FAN_MARK_IGNORE, events on directories will not be ignored. And if the flag FAN_EVENT_ON_CHILD is set with FAN_MARK_IGNORE, events on children will be ignored. Example time, bestie: a mark on a directory with a combination of a mask with the FAN_CREATE event and the FAN_ONDIR flag, plus an ignore mask with the FAN_CREATE event and without FAN_ONDIR, results in getting only the events for creation of sub-directories. Also, when you use the FAN_MARK_IGNORE flag to add to an ignore mask of a mount, filesystem, or directory inode mark, the FAN_MARK_IGNORED_SURV_MODIFY flag must be specified β€” fail to do that and you get EINVAL or EISDIR error.
  • FAN_MARK_IGNORED_SURV_MODIFY β€” the ignore mask shall survive modify events. If this flag is not set, the ignore mask is cleared whenever a modify event occurs on the marked object. omitting this flag is typically used to suppress events (e.g., FAN_OPEN) for one specific file, until that specific file's content has been modified. It's far less useful to suppress events on an entire filesystem, or mount, or on all files inside a directory, until some file's content has been modified β€” like, imagine. For this reason, the FAN_MARK_IGNORE flag requires the FAN_MARK_IGNORED_SURV_MODIFY flag on a mount, filesystem, or directory inode mark. And heads up: this flag cannot be removed from a mark once set. When the ignore mask is updated without this flag on a mark that was previously updated with the FAN_MARK_IGNORE and FAN_MARK_IGNORED_SURV_MODIFY flags, the update fails with EEXIST error.
  • FAN_MARK_IGNORE_SURV β€” a synonym for (FAN_MARK_IGNORE|FAN_MARK_IGNORED_SURV_MODIFY). cute lil shorthand, we love a combo deal πŸ’–
  • FAN_MARK_EVICTABLE (since Linux 5.19, 5.15.154, and 5.10.220) β€” when an inode mark is created with this flag, the inode object will not be pinned to the inode cache, therefore allowing the inode object to be evicted from the inode cache when the memory pressure on the system is high. the catch? the eviction of the inode object results in the evictable mark also being lost. When the mask of an evictable inode mark is updated without using the FAN_MARK_EVICATBLE flag, the marked inode is pinned to inode cache and the mark is no longer evictable. And when the mask of a non-evictable inode mark is updated with the FAN_MARK_EVICTABLE flag, the inode mark remains non-evictable and the update fails with EEXIST error. Mounts and filesystems are not evictable objects, so an attempt to create a mount mark or a filesystem mark with the FAN_MARK_EVICTABLE flag results in the error EINVAL. For example, inode marks can be used in combination with mount marks to reduce the amount of events from noninteresting paths: the event listener reads events, checks if the path reported in the event is of interest, and if it is not, the listener sets a mark with an ignore mask on the directory. Evictable inode marks let you use this method for a large number of directories without the concern of pinning all inodes and exhausting the system's memory. memory-safe queen πŸ™ˆ

πŸŽ€ what mask actually means πŸŽ€

mask defines which events shall be listened for (or which shall be ignored). It's a bit mask composed of the following values:

  • FAN_ACCESS β€” create an event when a file or directory (but see BUGS) is accessed (read).
  • FAN_MODIFY β€” create an event when a file is modified (write).
  • FAN_CLOSE_WRITE β€” create an event when a writable file is closed.
  • FAN_CLOSE_NOWRITE β€” create an event when a read-only file or directory is closed.
  • FAN_OPEN β€” create an event when a file or directory is opened.
  • FAN_OPEN_EXEC (since Linux 5.0) β€” create an event when a file is opened with the intent to be executed. see NOTES for additional details.
  • FAN_ATTRIB (since Linux 5.1) β€” create an event when the metadata for a file or directory has changed. requires an fanotify group that identifies filesystem objects by file handles.
  • FAN_CREATE (since Linux 5.1) β€” create an event when a file or directory has been created in a marked parent directory. requires an fanotify group that identifies filesystem objects by file handles.
  • FAN_DELETE (since Linux 5.1) β€” create an event when a file or directory has been deleted in a marked parent directory. requires an fanotify group that identifies filesystem objects by file handles.
  • FAN_DELETE_SELF (since Linux 5.1) β€” create an event when a marked file or directory itself is deleted. requires an fanotify group that identifies filesystem objects by file handles.
  • FAN_MOVED_FROM (since Linux 5.1) β€” create an event when a file or directory has been moved from a marked parent directory. requires an fanotify group that identifies filesystem objects by file handles.
  • FAN_MOVED_TO (since Linux 5.1) β€” create an event when a file or directory has been moved to a marked parent directory. requires an fanotify group that identifies filesystem objects by file handles.
  • FAN_RENAME (since Linux 5.17, 5.15.154, and 5.10.220) β€” this event contains the same information provided by the events FAN_MOVED_FROM and FAN_MOVED_TO, however it's represented by a single event with up to two information records. requires an fanotify group that identifies filesystem objects by file handles. And if the filesystem object to be marked is not a directory, the error ENOTDIR shall be raised. one event, two records, main character energy ⭐
  • FAN_MOVE_SELF (since Linux 5.1) β€” bestie, this one fires when a marked file or directory itself gets moved. That's the whole object sliding somewhere else. You need an fanotify group that identifies filesystem objects by file handles, no exceptions, periodt.
  • FAN_MNT_ATTACH, FAN_MNT_DETACH (both since Linux 6.14) β€” one event for when a mount gets attached to a marked mount namespace, one for when it gets detached πŸ’…. If you try to set either of these on an inode, mount, or filesystem mark? Boom, EINVAL. You need an fanotify group initialized with the flag FAN_REPORT_MNT plus the mark flag FAN_MARK_MNTNS. Oh and you get an extra info record of type FAN_EVENT_INFO_TYPE_MNT riding along with the event. More deets in fanotify(7), iykyk.
  • FAN_FS_ERROR (since Linux 5.16, 5.15.154, and 5.10.220) β€” fires when a filesystem error that leads to inconsistent filesystem metadata gets detected 😭. Every event in the read buffer gets an extra information record of type FAN_EVENT_INFO_TYPE_ERROR. Needs an fanotify group that identifies filesystem objects by file handles. Also β€” and this is the tea β€” these events depend on the underlying filesystem actually supporting them. At the time of writing, ext4 is literally the only one reporting FAN_FS_ERROR events. See fanotify(7) for more.
  • FAN_OPEN_PERM β€” fires when permission to open a file or directory is requested. You need an fanotify file descriptor created with FAN_CLASS_PRE_CONTENT or FAN_CLASS_CONTENT. πŸ‘‘
  • FAN_OPEN_EXEC_PERM (since Linux 5.0) β€” fires when permission to open a file for execution is requested πŸƒ. Same deal: fanotify file descriptor created with FAN_CLASS_PRE_CONTENT or FAN_CLASS_CONTENT. Peep the NOTES section for the extra lore.
  • FAN_ACCESS_PERM β€” fires when permission to read a file or directory is requested. Yep, you guessed it: fanotify file descriptor created with FAN_CLASS_PRE_CONTENT or FAN_CLASS_CONTENT is required.
  • FAN_PRE_ACCESS (since Linux 6.14) β€” this one is so iconic tbh. It fires before read or write access to a file range, which gives the event listener a chance to modify the file content before anything gets accessed in that specified range ✨. You get an extra info record of type FAN_EVENT_INFO_TYPE_RANGE for each event in the read buffer. Needs an fanotify file descriptor created with FAN_CLASS_PRE_CONTENT.
  • FAN_ONDIR β€” create events for directories too πŸŽ€ β€” like when opendir(3), readdir(3) (but check BUGS), and closedir(3) get called. Without this flag you only get events for files, rip. And for directory entry events like FAN_CREATE, FAN_DELETE, FAN_MOVED_FROM, and FAN_MOVED_TO? Specifying FAN_ONDIR is required if you want events when subdirectory entries get modified (that's mkdir(2)/rmdir(2) bestie).
  • FAN_EVENT_ON_CHILD β€” events for the immediate children of marked directories get created. Lowkey important: this flag does absolutely nothing when you're marking mounts and filesystems. Also, events are not generated for children of the subdirectories of marked directories. More specifically, the directory entry modification events FAN_CREATE, FAN_DELETE, FAN_MOVED_FROM, and FAN_MOVED_TO are not generated for any entry modifications performed inside subdirectories of marked directories. And heads up β€” FAN_DELETE_SELF and FAN_MOVE_SELF are not generated for children of marked directories either. So if you wanna monitor whole-ass directory trees, you have to mark the relevant mount or filesystem. No shortcuts bestie πŸ’€.

The following composed values are defined:

  • FAN_CLOSE β€” a file is closed (FAN_CLOSE_WRITE|FAN_CLOSE_NOWRITE).
  • FAN_MOVE β€” a file or directory has been moved (FAN_MOVED_FROM|FAN_MOVED_TO).

So, which filesystem object are we actually marking here? That's decided by the file descriptor dirfd and the pathname in path 🌸:

  • If path is NULL, then dirfd defines the filesystem object to be marked.
  • If path is absolute, it defines the filesystem object to be marked, and dirfd gets ignored (sorry dirfd, not your moment).
  • If path is relative, and dirfd does not have the value AT_FDCWD, then the filesystem object to be marked is determined by interpreting path relative to the directory referred to by dirfd.
  • If path is relative, and dirfd does have the value AT_FDCWD, then the filesystem object to be marked is determined by interpreting path relative to the current working directory. (Peep openat(2) if you're wondering why the dirfd argument is even useful β€” it's a whole thing.)

✨ RETURN VALUE ✨

Slayed successfully? fanotify_mark() returns 0 πŸ’–. If it goes wrong, -1 comes back and errno gets set to tell you what happened.

πŸ’€ ERRORS πŸ’€

  • EBADF β€” an invalid file descriptor was passed in fanotify_fd.
  • EBADF β€” path is relative but dirfd is neither AT_FDCWD nor a valid file descriptor.
  • EEXIST β€” the filesystem object indicated by dirfd and path has a mark that was updated without the FAN_MARK_EVICTABLE flag, and you tried to update the mark with the FAN_MARK_EVICTABLE flag. Pick a lane bestie 😭.
  • EEXIST β€” the filesystem object indicated by dirfd and path has a mark that was updated with the FAN_MARK_IGNORE flag, and you tried to update the mark with the FAN_MARK_IGNORED_MASK flag.
  • EEXIST β€” the filesystem object indicated by dirfd and path has a mark that was updated with the FAN_MARK_IGNORE and FAN_MARK_IGNORED_SURV_MODIFY flags, and you tried to update the mark only with the FAN_MARK_IGNORE flag.
  • EINVAL β€” an invalid value was passed in flags or mask, or fanotify_fd was not an fanotify file descriptor.
  • EINVAL β€” the fanotify file descriptor was opened with FAN_CLASS_NOTIF, or the fanotify group identifies filesystem objects by file handles and mask contains a flag for permission events (FAN_OPEN_PERM or FAN_ACCESS_PERM).
  • EINVAL β€” the group was initialized without FAN_REPORT_FID but one or more event types specified in the mask require it.
  • EINVAL β€” flags contains FAN_MARK_IGNORE, and either FAN_MARK_MOUNT or FAN_MARK_FILESYSTEM, but does not contain FAN_MARK_IGNORED_SURV_MODIFY.
  • EISDIR β€” flags contains FAN_MARK_IGNORE, but does not contain FAN_MARK_IGNORED_SURV_MODIFY, and dirfd and path specify a directory.
  • ENODEV β€” the filesystem object indicated by dirfd and path is associated with a filesystem that reports zero fsid (e.g., fuse(4)). This error can be returned only with an fanotify group that identifies filesystem objects by file handles. Since Linux 6.8, this error can also be returned when trying to add a mount or filesystem mark.
  • ENOENT β€” the filesystem object indicated by dirfd and path does not exist. This error also happens when you try to remove a mark from an object that isn't marked. Awkward πŸ™ˆ.
  • ENOMEM β€” the necessary memory could not be allocated.
  • ENOSPC β€” the number of marks for this user exceeds the limit and the FAN_UNLIMITED_MARKS flag was not specified when the fanotify file descriptor was created with fanotify_init(2). See fanotify(7) for details about this limit.
  • ENOSYS β€” this kernel does not implement fanotify_mark(). The fanotify API is available only if the kernel was configured with CONFIG_FANOTIFY.
  • ENOTDIR β€” flags contains FAN_MARK_ONLYDIR, and dirfd and path do not specify a directory.
  • ENOTDIR β€” mask contains FAN_RENAME, and dirfd and path do not specify a directory.
  • ENOTDIR β€” flags contains FAN_MARK_IGNORE, or the fanotify group was initialized with flag FAN_REPORT_TARGET_FID, and mask contains directory entry modification events (e.g., FAN_CREATE, FAN_DELETE), or directory event flags (e.g., FAN_ONDIR, FAN_EVENT_ON_CHILD), and dirfd and path do not specify a directory.
  • EOPNOTSUPP β€” the object indicated by path is associated with a filesystem that does not support the encoding of file handles. This error can be returned only with an fanotify group that identifies filesystem objects by file handles. Calling name_to_handle_at(2) with the flag AT_HANDLE_FID (since Linux 6.5) can be used as a test to check if a filesystem supports reporting events with file handles.
  • EPERM β€” the operation is not permitted because the caller lacks a required capability.
  • EXDEV β€” the filesystem object indicated by path resides within a filesystem subvolume (e.g., btrfs(5)) which uses a different fsid than its root superblock. This error can be returned only with an fanotify group that identifies filesystem objects by file handles. Since Linux 6.8, this error will be returned when trying to add a mount or filesystem mark on a subvolume, when trying to add inode marks in different subvolumes, or when trying to add inode marks in a btrfs(5) subvolume and in another filesystem. Since Linux 6.8, this error will also be returned when trying to add marks in different filesystems, where one of the filesystems reports zero fsid (e.g., fuse(4)).

🌈 STANDARDS 🌈

Linux. That's it, that's the standard πŸ’….

πŸ“œ HISTORY πŸ“œ

Linux 2.6.37.

πŸ‘‘ NOTES πŸ‘‘

FAN_OPEN_EXEC and FAN_OPEN_EXEC_PERM

When you use either FAN_OPEN_EXEC or FAN_OPEN_EXEC_PERM inside the mask, events of these types only get returned when a program is executed directly πŸ’«. More specifically? That means these events get generated for files opened using execve(2), execveat(2), or uselib(2). They will not be raised when an interpreter is passed (or reads) a file for interpretation. So no sneak-attack exec events via the interpreter bestie 😌.

Also, if a mark has also been placed on the Linux dynamic linker, expect to receive an event for it too when an ELF object has been successfully opened using execve(2) or execveat(2).

For example, if the following ELF binary were to be invoked and a FAN_OPEN_EXEC mark has been placed on /:

$ /bin/echo foo

The listening application in this case would receive FAN_OPEN_EXEC events for both the ELF binary and interpreter, respectively:

/bin/echo
/lib64/ld-linux-x86-64.so.2

πŸ› BUGS πŸ›

The following bugs were present in before Linux 3.16 β€” yeah, old-school, but we're keeping the receipts:

  • If flags contains FAN_MARK_FLUSH, dirfd and path must specify a valid filesystem object, even though this object is not used.
  • readdir(2) does not generate a FAN_ACCESS event.
  • If fanotify_mark() is called with FAN_MARK_FLUSH, flags is not checked for invalid values.

✨ SEE ALSO ✨

okay bestie, you're not done yet β€” these two are literally the other halves of this whole situation, go read them πŸ’–

  • fanotify_init(2) β€” the one that makes the fanotify group in the first place. you literally cannot fanotify_mark() nothing, so start here bb 🌱
  • fanotify(7) β€” the big-picture overview page. the lore. the worldbuilding. the whole fanotify cinematic universe 🎬

and that's the whole page, bestie!! you made it to the end, i'm so proud of you πŸ₯ΉπŸ«Ά

this lil masterpiece comes to you from Linux man-pages 6.20, dated 2026-06-04 β€” so yeah, freshly dated, no dusty vibes πŸŽ€

now go forth, mark some files, and remember: reading the whole manpage before you ship is main character energy πŸ‘‘βœ¨ no cap.

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ“œ the page's license: Linux-man-pages-copyleft
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms