{
    "content": [
        {
            "type": "text",
            "text": "# fanotify(7) (man)\n\n**Summary:** fanotify - monitoring filesystem events\n\n## Examples\n\n- `The two example programs below demonstrate the usage of the fanotify API.`\n- `Example program: fanotifyexample.c`\n- `The  first  program  is  an  example of fanotify being used with its event object information`\n- `passed in the form of a file descriptor.  The program marks the mount point passed as a  com‐`\n- `mand-line  argument  and  waits for events of type FANOPENPERM and FANCLOSEWRITE.  When a`\n- `permission event occurs, a FANALLOW response is given.`\n- `The following shell session shows an example of running this program.  This session  involved`\n- `editing  the  file  /home/user/temp/notes.  Before the file was opened, a FANOPENPERM event`\n- `occurred.  After the file was closed, a FANCLOSEWRITE event  occurred.   Execution  of  the`\n- `program ends when the user presses the ENTER key.`\n- `# ./fanotifyexample /home`\n- `Press enter key to terminate.`\n- `Listening for events.`\n- `FANOPENPERM: File /home/user/temp/notes`\n- `FANCLOSEWRITE: File /home/user/temp/notes`\n- `Listening for events stopped.`\n- `Program source: fanotifyexample.c`\n- `#define GNUSOURCE     /* Needed to get OLARGEFILE definition */`\n- `#include <errno.h>`\n- `#include <fcntl.h>`\n- `#include <limits.h>`\n- `#include <poll.h>`\n- `#include <stdio.h>`\n- `#include <stdlib.h>`\n- `#include <sys/fanotify.h>`\n- `#include <unistd.h>`\n- `/* Read all available fanotify events from the file descriptor 'fd' */`\n- `static void`\n- `handleevents(int fd)`\n- `const struct fanotifyeventmetadata *metadata;`\n- `struct fanotifyeventmetadata buf[200];`\n- `ssizet len;`\n- `char path[PATHMAX];`\n- `ssizet pathlen;`\n- `char procfdpath[PATHMAX];`\n- `struct fanotifyresponse response;`\n- `/* Loop while events can be read from fanotify file descriptor */`\n- `for (;;) {`\n- `/* Read some events */`\n- `len = read(fd, buf, sizeof(buf));`\n- `if (len == -1 && errno != EAGAIN) {`\n- `perror(\"read\");`\n- `exit(EXITFAILURE);`\n- `/* Check if end of available data reached */`\n- `if (len <= 0)`\n- `break;`\n- `/* Point to the first event in the buffer */`\n- `metadata = buf;`\n- `/* Loop over all events in the buffer */`\n- `while (FANEVENTOK(metadata, len)) {`\n- `/* Check that run-time and compile-time structures match */`\n- `if (metadata->vers != FANOTIFYMETADATAVERSION) {`\n- `fprintf(stderr,`\n- `\"Mismatch of fanotify metadata version.\\n\");`\n- `exit(EXITFAILURE);`\n- `/* metadata->fd contains either FANNOFD, indicating a`\n- `queue overflow, or a file descriptor (a nonnegative`\n- `integer). Here, we simply ignore queue overflow. */`\n- `if (metadata->fd >= 0) {`\n- `/* Handle open permission event */`\n- `if (metadata->mask & FANOPENPERM) {`\n- `printf(\"FANOPENPERM: \");`\n- `/* Allow file to be opened */`\n- `response.fd = metadata->fd;`\n- `response.response = FANALLOW;`\n- `write(fd, &response, sizeof(response));`\n- `/* Handle closing of writable file event */`\n- `if (metadata->mask & FANCLOSEWRITE)`\n- `printf(\"FANCLOSEWRITE: \");`\n- `/* Retrieve and print pathname of the accessed file */`\n- `snprintf(procfdpath, sizeof(procfdpath),`\n- `\"/proc/self/fd/%d\", metadata->fd);`\n- `pathlen = readlink(procfdpath, path,`\n- `sizeof(path) - 1);`\n- `if (pathlen == -1) {`\n- `perror(\"readlink\");`\n- `exit(EXITFAILURE);`\n- `path[pathlen] = '\\0';`\n- `printf(\"File %s\\n\", path);`\n- `/* Close the file descriptor of the event */`\n- `close(metadata->fd);`\n- `/* Advance to next event */`\n- `metadata = FANEVENTNEXT(metadata, len);`\n- `int`\n- `main(int argc, char *argv[])`\n- `char buf;`\n- `int fd, pollnum;`\n- `nfdst nfds;`\n- `struct pollfd fds[2];`\n- `/* Check mount point is supplied */`\n- `if (argc != 2) {`\n- `fprintf(stderr, \"Usage: %s MOUNT\\n\", argv[0]);`\n- `exit(EXITFAILURE);`\n- `printf(\"Press enter key to terminate.\\n\");`\n- `/* Create the file descriptor for accessing the fanotify API */`\n- `fd = fanotifyinit(FANCLOEXEC | FANCLASSCONTENT | FANNONBLOCK,`\n- `ORDONLY | OLARGEFILE);`\n- `if (fd == -1) {`\n- `perror(\"fanotifyinit\");`\n- `exit(EXITFAILURE);`\n- `/* Mark the mount for:`\n- `- permission events before opening files`\n- `- notification events after closing a write-enabled`\n- `file descriptor */`\n- `if (fanotifymark(fd, FANMARKADD | FANMARKMOUNT,`\n- `FANOPENPERM | FANCLOSEWRITE, ATFDCWD,`\n- `argv[1]) == -1) {`\n- `perror(\"fanotifymark\");`\n- `exit(EXITFAILURE);`\n- `/* Prepare for polling */`\n- `nfds = 2;`\n- `/* Console input */`\n- `fds[0].fd = STDINFILENO;`\n- `fds[0].events = POLLIN;`\n- `/* Fanotify input */`\n- `fds[1].fd = fd;`\n- `fds[1].events = POLLIN;`\n- `/* This is the loop to wait for incoming events */`\n- `printf(\"Listening for events.\\n\");`\n- `while (1) {`\n- `pollnum = poll(fds, nfds, -1);`\n- `if (pollnum == -1) {`\n- `if (errno == EINTR)     /* Interrupted by a signal */`\n- `continue;           /* Restart poll() */`\n- `perror(\"poll\");         /* Unexpected error */`\n- `exit(EXITFAILURE);`\n- `if (pollnum > 0) {`\n- `if (fds[0].revents & POLLIN) {`\n- `/* Console input is available: empty stdin and quit */`\n- `while (read(STDINFILENO, &buf, 1) > 0 && buf != '\\n')`\n- `continue;`\n- `break;`\n- `if (fds[1].revents & POLLIN) {`\n- `/* Fanotify events are available */`\n- `handleevents(fd);`\n- `printf(\"Listening for events stopped.\\n\");`\n- `exit(EXITSUCCESS);`\n- `Example program: fanotifyfid.c`\n- `The  second program is an example of fanotify being used with a group that identifies objects`\n- `by file handles.  The program marks the filesystem object that is passed  as  a  command-line`\n- `argument  and waits until an event of type FANCREATE has occurred.  The event mask indicates`\n- `which type of filesystem object—either a file or a directory—was created.   Once  all  events`\n- `have been read from the buffer and processed accordingly, the program simply terminates.`\n- `The  following  shell sessions show two different invocations of this program, with different`\n- `actions performed on a watched object.`\n- `The first session shows a mark being placed on /home/user.  This is followed by the  creation`\n- `of  a regular file, /home/user/testfile.txt.  This results in a FANCREATE event being gener‐`\n- `ated and reported against the file's parent watched directory object  and  with  the  created`\n- `file  name.  Program execution ends once all events captured within the buffer have been pro‐`\n- `cessed.`\n- `# ./fanotifyfid /home/user`\n- `Listening for events.`\n- `FANCREATE (file created):`\n- `Directory /home/user has been modified.`\n- `Entry 'testfile.txt' is not a subdirectory.`\n- `All events processed successfully. Program exiting.`\n- `$ touch /home/user/testfile.txt              # In another terminal`\n- `The second session shows a mark being placed on /home/user.  This is followed by the creation`\n- `of a directory, /home/user/testdir.  This specific action results in a FANCREATE event being`\n- `generated and is reported with the FANONDIR flag set and with the created directory name.`\n- `# ./fanotifyfid /home/user`\n- `Listening for events.`\n- `FANCREATE | FANONDIR (subdirectory created):`\n- `Directory /home/user has been modified.`\n- `Entry 'testdir' is a subdirectory.`\n- `All events processed successfully. Program exiting.`\n- `$ mkdir -p /home/user/testdir          # In another terminal`\n- `Program source: fanotifyfid.c`\n- `#define GNUSOURCE`\n- `#include <errno.h>`\n- `#include <fcntl.h>`\n- `#include <limits.h>`\n- `#include <stdio.h>`\n- `#include <stdlib.h>`\n- `#include <sys/types.h>`\n- `#include <sys/stat.h>`\n- `#include <sys/fanotify.h>`\n- `#include <unistd.h>`\n- `#define BUFSIZE 256`\n- `int`\n- `main(int argc, char argv)`\n- `int fd, ret, eventfd, mountfd;`\n- `ssizet len, pathlen;`\n- `char path[PATHMAX];`\n- `char procfdpath[PATHMAX];`\n- `char eventsbuf[BUFSIZE];`\n- `struct filehandle *filehandle;`\n- `struct fanotifyeventmetadata *metadata;`\n- `struct fanotifyeventinfofid *fid;`\n- `const char *filename;`\n- `struct stat sb;`\n- `if (argc != 2) {`\n- `fprintf(stderr, \"Invalid number of command line arguments.\\n\");`\n- `exit(EXITFAILURE);`\n- `mountfd = open(argv[1], ODIRECTORY | ORDONLY);`\n- `if (mountfd == -1) {`\n- `perror(argv[1]);`\n- `exit(EXITFAILURE);`\n- `/* Create an fanotify file descriptor with FANREPORTDFIDNAME as`\n- `a flag so that program can receive fid events with directory`\n- `entry name. */`\n- `fd = fanotifyinit(FANCLASSNOTIF | FANREPORTDFIDNAME, 0);`\n- `if (fd == -1) {`\n- `perror(\"fanotifyinit\");`\n- `exit(EXITFAILURE);`\n- `/* Place a mark on the filesystem object supplied in argv[1]. */`\n- `ret = fanotifymark(fd, FANMARKADD | FANMARKONLYDIR,`\n- `FANCREATE | FANONDIR,`\n- `ATFDCWD, argv[1]);`\n- `if (ret == -1) {`\n- `perror(\"fanotifymark\");`\n- `exit(EXITFAILURE);`\n- `printf(\"Listening for events.\\n\");`\n- `/* Read events from the event queue into a buffer */`\n- `len = read(fd, eventsbuf, sizeof(eventsbuf));`\n- `if (len == -1 && errno != EAGAIN) {`\n- `perror(\"read\");`\n- `exit(EXITFAILURE);`\n- `/* Process all events within the buffer */`\n- `for (metadata = (struct fanotifyeventmetadata *) eventsbuf;`\n- `FANEVENTOK(metadata, len);`\n- `metadata = FANEVENTNEXT(metadata, len)) {`\n- `fid = (struct fanotifyeventinfofid *) (metadata + 1);`\n- `filehandle = (struct filehandle *) fid->handle;`\n- `/* Ensure that the event info is of the correct type */`\n- `if (fid->hdr.infotype == FANEVENTINFOTYPEFID ||`\n- `fid->hdr.infotype == FANEVENTINFOTYPEDFID) {`\n- `filename = NULL;`\n- `} else if (fid->hdr.infotype == FANEVENTINFOTYPEDFIDNAME) {`\n- `filename = filehandle->fhandle +`\n- `filehandle->handlebytes;`\n- `} else {`\n- `fprintf(stderr, \"Received unexpected event info type.\\n\");`\n- `exit(EXITFAILURE);`\n- `if (metadata->mask == FANCREATE)`\n- `printf(\"FANCREATE (file created):\\n\");`\n- `if (metadata->mask == (FANCREATE | FANONDIR))`\n- `printf(\"FANCREATE | FANONDIR (subdirectory created):\\n\");`\n- `/* metadata->fd is set to FANNOFD when the group identifies`\n- `objects by file handles.  To obtain a file descriptor for`\n- `the file object corresponding to an event you can use the`\n- `struct filehandle that's provided within the`\n- `fanotifyeventinfofid in conjunction with the`\n- `openbyhandleat(2) system call.  A check for ESTALE is`\n- `done to accommodate for the situation where the file handle`\n- `for the object was deleted prior to this system call. */`\n- `eventfd = openbyhandleat(mountfd, filehandle, ORDONLY);`\n- `if (eventfd == -1) {`\n- `if (errno == ESTALE) {`\n- `printf(\"File handle is no longer valid. \"`\n- `\"File has been deleted\\n\");`\n- `continue;`\n- `} else {`\n- `perror(\"openbyhandleat\");`\n- `exit(EXITFAILURE);`\n- `snprintf(procfdpath, sizeof(procfdpath), \"/proc/self/fd/%d\",`\n- `eventfd);`\n- `/* Retrieve and print the path of the modified dentry */`\n- `pathlen = readlink(procfdpath, path, sizeof(path) - 1);`\n- `if (pathlen == -1) {`\n- `perror(\"readlink\");`\n- `exit(EXITFAILURE);`\n- `path[pathlen] = '\\0';`\n- `printf(\"\\tDirectory '%s' has been modified.\\n\", path);`\n- `if (filename) {`\n- `ret = fstatat(eventfd, filename, &sb, 0);`\n- `if (ret == -1) {`\n- `if (errno != ENOENT) {`\n- `perror(\"fstatat\");`\n- `exit(EXITFAILURE);`\n- `printf(\"\\tEntry '%s' does not exist.\\n\", filename);`\n- `} else if ((sb.stmode & SIFMT) == SIFDIR) {`\n- `printf(\"\\tEntry '%s' is a subdirectory.\\n\", filename);`\n- `} else {`\n- `printf(\"\\tEntry '%s' is not a subdirectory.\\n\",`\n- `filename);`\n- `/* Close associated file descriptor for this event */`\n- `close(eventfd);`\n- `printf(\"All events processed successfully. Program exiting.\\n\");`\n- `exit(EXITSUCCESS);`\n\n## See Also\n\n- fanotifyinit(2)\n- fanotifymark(2)\n- inotify(7)\n\n## Section Outline\n\n- **NAME** (2 lines)\n- **DESCRIPTION** (44 lines) — 6 subsections\n  - The event queue (16 lines)\n  - Reading fanotify events (240 lines)\n  - Monitoring an fanotify file descriptor for events (3 lines)\n  - Dealing with permission events (19 lines)\n  - Closing the fanotify file descriptor (4 lines)\n  - /proc/[pid]/fdinfo (3 lines)\n- **ERRORS** (25 lines)\n- **VERSIONS** (3 lines)\n- **CONFORMING TO** (2 lines)\n- **NOTES** (4 lines) — 1 subsections\n  - Limitations and caveats (21 lines)\n- **BUGS** (20 lines)\n- **EXAMPLES** (413 lines)\n- **SEE ALSO** (2 lines)\n- **COLOPHON** (7 lines)\n\n## Full Content\n\n### NAME\n\nfanotify - monitoring filesystem events\n\n### DESCRIPTION\n\nThe  fanotify API provides notification and interception of filesystem events.  Use cases in‐\nclude virus scanning and hierarchical storage management.  In the original fanotify API, only\na  limited  set  of  events  was  supported.  In particular, there was no support for create,\ndelete, and move events.  The support for those events was added in  Linux  5.1.   (See  ino‐‐\ntify(7) for details of an API that did notify those events pre Linux 5.1.)\n\nAdditional  capabilities compared to the inotify(7) API include the ability to monitor all of\nthe objects in a mounted filesystem, the ability to make access permission decisions, and the\npossibility to read or modify files before access by other applications.\n\nThe  following  system  calls  are  used  with  this API: fanotifyinit(2), fanotifymark(2),\nread(2), write(2), and close(2).\n\nfanotifyinit(), fanotifymark(), and notification groups\nThe fanotifyinit(2) system call creates and initializes an fanotify notification  group  and\nreturns a file descriptor referring to it.\n\nAn fanotify notification group is a kernel-internal object that holds a list of files, direc‐\ntories, filesystems, and mount points for which events shall be created.\n\nFor each entry in an fanotify notification group, two bit masks exist: the mark mask and  the\nignore mask.  The mark mask defines file activities for which an event shall be created.  The\nignore mask defines activities for which no event shall be generated.  Having these two types\nof  masks  permits a filesystem, mount point, or directory to be marked for receiving events,\nwhile at the same time ignoring events for specific objects under a mount point or directory.\n\nThe fanotifymark(2) system call adds a file, directory, filesystem or mount point to a noti‐\nfication group and specifies which events shall be reported (or ignored), or removes or modi‐\nfies such an entry.\n\nA possible usage of the ignore mask is for a file cache.  Events of interest for a file cache\nare  modification  of  a  file and closing of the same.  Hence, the cached directory or mount\npoint is to be marked to receive these events.  After receiving  the  first  event  informing\nthat a file has been modified, the corresponding cache entry will be invalidated.  No further\nmodification events for this file are of interest until the file is closed.  Hence, the  mod‐\nify  event can be added to the ignore mask.  Upon receiving the close event, the modify event\ncan be removed from the ignore mask and the file cache entry can be updated.\n\nThe entries in the fanotify notification groups refer to files and directories via their  in‐\node  number  and  to mounts via their mount ID.  If files or directories are renamed or moved\nwithin the same mount, the respective entries survive.  If files or directories  are  deleted\nor  moved  to  another mount or if filesystems or mounts are unmounted, the corresponding en‐\ntries are deleted.\n\n#### The event queue\n\nAs events occur on the filesystem objects monitored by a  notification  group,  the  fanotify\nsystem  generates events that are collected in a queue.  These events can then be read (using\nread(2) or similar) from the fanotify file descriptor returned by fanotifyinit(2).\n\nTwo types of events are generated: notification events and permission  events.   Notification\nevents  are merely informative and require no action to be taken by the receiving application\nwith one exception: if a valid file descriptor is provided within a generic event,  the  file\ndescriptor  must  be  closed.  Permission events are requests to the receiving application to\ndecide whether permission for a file access shall be granted.  For these events, the  recipi‐\nent must write a response which decides whether access is granted or not.\n\nAn  event  is removed from the event queue of the fanotify group when it has been read.  Per‐\nmission events that have been read are kept in an internal list of the fanotify  group  until\neither a permission decision has been taken by writing to the fanotify file descriptor or the\nfanotify file descriptor is closed.\n\n#### Reading fanotify events\n\nCalling read(2) for the file descriptor returned by  fanotifyinit(2)  blocks  (if  the  flag\nFANNONBLOCK  is not specified in the call to fanotifyinit(2)) until either a file event oc‐\ncurs or the call is interrupted by a signal (see signal(7)).\n\nThe use of one of the flags FANREPORTFID, FANREPORTDIRFID in fanotifyinit(2) influences\nwhat data structures are returned to the event listener for each event.  Events reported to a\ngroup initialized with one of these flags will use file handles to  identify  filesystem  ob‐\njects instead of file descriptors.\n\nAfter a successful\nread(2), the read buffer contains one or more of the following structures:\n\nstruct fanotifyeventmetadata {\nu32 eventlen;\nu8 vers;\nu8 reserved;\nu16 metadatalen;\nalignedu64 mask;\ns32 fd;\ns32 pid;\n};\n\nIn  case  of an fanotify group that identifies filesystem objects by file handles, you should\nalso expect to receive one or more additional information records of the  structure  detailed\nbelow following the generic fanotifyeventmetadata structure within the read buffer:\n\nstruct fanotifyeventinfoheader {\nu8 infotype;\nu8 pad;\nu16 len;\n};\n\nstruct fanotifyeventinfofid {\nstruct fanotifyeventinfoheader hdr;\nkernelfsidt fsid;\nunsigned char filehandle[0];\n};\n\nFor  performance  reasons,  it  is  recommended to use a large buffer size (for example, 4096\nbytes), so that multiple events can be retrieved by a single read(2).\n\nThe return value of read(2) is the number of bytes placed in the buffer, or -1 in case of  an\nerror (but see BUGS).\n\nThe fields of the fanotifyeventmetadata structure are as follows:\n\neventlen\nThis  is the length of the data for the current event and the offset to the next event\nin the buffer.  Unless the group identifies filesystem objects by  file  handles,  the\nvalue  of  eventlen  is  always  FANEVENTMETADATALEN.  For a group that identifies\nfilesystem objects by file handles, eventlen also includes the variable  length  file\nidentifier records.\n\nvers   This  field  holds  a  version  number for the structure.  It must be compared to FAN‐‐\nOTIFYMETADATAVERSION to verify that the structures returned at run  time  match  the\nstructures  defined  at  compile  time.  In case of a mismatch, the application should\nabandon trying to use the fanotify file descriptor.\n\nreserved\nThis field is not used.\n\nmetadatalen\nThis is the length of the structure.  The field was introduced to facilitate  the  im‐\nplementation  of  optional  headers per event type.  No such optional headers exist in\nthe current implementation.\n\nmask   This is a bit mask describing the event (see below).\n\nfd     This is an open file descriptor for the object being accessed, or FANNOFD if a  queue\noverflow  occurred.  With an fanotify group that identifies filesystem objects by file\nhandles, applications should expect this value to be set to FANNOFD  for  each  event\nthat is received.  The file descriptor can be used to access the contents of the moni‐\ntored file or directory.  The reading application is responsible for closing this file\ndescriptor.\n\nWhen calling fanotifyinit(2), the caller may specify (via the eventfflags argument)\nvarious file status flags that are to be set on the open file description that  corre‐\nsponds  to  this  file  descriptor.  In addition, the (kernel-internal) FMODENONOTIFY\nfile status flag is set on the open file description.  This flag  suppresses  fanotify\nevent  generation.   Hence, when the receiver of the fanotify event accesses the noti‐\nfied file or directory using this file descriptor, no additional events will  be  cre‐\nated.\n\npid    If flag FANREPORTTID was set in fanotifyinit(2), this is the TID of the thread that\ncaused the event.  Otherwise, this the PID of the process that caused the event.\n\nA program listening to fanotify events can compare this PID to the PID returned by getpid(2),\nto  determine  whether the event is caused by the listener itself, or is due to a file access\nby another process.\n\nThe bit mask in mask indicates which events have occurred for  a  single  filesystem  object.\nMultiple  bits  may  be  set  in this mask, if more than one event occurred for the monitored\nfilesystem object.  In particular, consecutive events for  the  same  filesystem  object  and\noriginating  from the same process may be merged into a single event, with the exception that\ntwo permission events are never merged into one queue entry.\n\nThe bits that may appear in mask are as follows:\n\nFANACCESS\nA file or a directory (but see BUGS) was accessed (read).\n\nFANOPEN\nA file or a directory was opened.\n\nFANOPENEXEC\nA file was opened with the intent to be executed.  See NOTES in  fanotifymark(2)  for\nadditional details.\n\nFANATTRIB\nA file or directory metadata was changed.\n\nFANCREATE\nA child file or directory was created in a watched parent.\n\nFANDELETE\nA child file or directory was deleted in a watched parent.\n\nFANDELETESELF\nA watched file or directory was deleted.\n\nFANMOVEDFROM\nA file or directory has been moved from a watched parent directory.\n\nFANMOVEDTO\nA file or directory has been moved to a watched parent directory.\n\nFANMOVESELF\nA watched file or directory was moved.\n\nFANMODIFY\nA file was modified.\n\nFANCLOSEWRITE\nA file that was opened for writing (OWRONLY or ORDWR) was closed.\n\nFANCLOSENOWRITE\nA file or directory that was opened read-only (ORDONLY) was closed.\n\nFANQOVERFLOW\nThe  event queue exceeded the limit of 16384 entries.  This limit can be overridden by\nspecifying the FANUNLIMITEDQUEUE flag when calling fanotifyinit(2).\n\nFANACCESSPERM\nAn application wants to read a file or directory, for example using read(2)  or  read‐‐\ndir(2).  The reader must write a response (as described below) that determines whether\nthe permission to access the filesystem object shall be granted.\n\nFANOPENPERM\nAn application wants to open a file or directory.  The reader must  write  a  response\nthat determines whether the permission to open the filesystem object shall be granted.\n\nFANOPENEXECPERM\nAn  application  wants to open a file for execution.  The reader must write a response\nthat determines whether the permission to open the  filesystem  object  for  execution\nshall be granted.  See NOTES in fanotifymark(2) for additional details.\n\nTo check for any close event, the following bit mask may be used:\n\nFANCLOSE\nA file was closed.  This is a synonym for:\n\nFANCLOSEWRITE | FANCLOSENOWRITE\n\nTo check for any move event, the following bit mask may be used:\n\nFANMOVE\nA file or directory was moved.  This is a synonym for:\n\nFANMOVEDFROM | FANMOVEDTO\n\nThe following bits may appear in mask only in conjunction with other event type bits:\n\nFANONDIR\nThe  events  described  in  the  mask  have occurred on a directory object.  Reporting\nevents on directories  requires  setting  this  flag  in  the  mark  mask.   See  fan‐‐\notifymark(2) for additional details.  The FANONDIR flag is reported in an event mask\nonly if the fanotify group identifies filesystem objects by file handles.\n\nThe fields of the fanotifyeventinfofid structure are as follows:\n\nhdr    This is a structure of type fanotifyeventinfoheader.  It is a generic  header  that\ncontains information used to describe an additional information record attached to the\nevent.  For example, when an fanotify file descriptor is created using FANREPORTFID,\na  single  information  record  is expected to be attached to the event with infotype\nfield value of FANEVENTINFOTYPEFID.  When an fanotify file descriptor  is  created\nusing  the  combination of FANREPORTFID and FANREPORTDIRFID, there may be two in‐\nformation  records  attached  to  the  event:  one  with  infotype  field  value   of\nFANEVENTINFOTYPEDFID,   identifying  a  parent  directory  object,  and  one  with\ninfotype field value of FANEVENTINFOTYPEFID, identifying a non-directory  object.\nThe  fanotifyeventinfoheader contains a len field.  The value of len is the size of\nthe additional information record  including  the  fanotifyeventinfoheader  itself.\nThe total size of all additional information records is not expected to be bigger than\n( eventlen - metadatalen ).\n\nfsid   This is a unique identifier of the filesystem containing the  object  associated  with\nthe  event.   It is a structure of type kernelfsidt and contains the same value as\nffsid when calling statfs(2).\n\nfilehandle\nThis is a variable length structure of type struct filehandle.  It is an opaque  han‐\ndle that corresponds to a specified object on a filesystem as returned by nametohan‐‐\ndleat(2).  It can be used to uniquely identify a file on  a  filesystem  and  can  be\npassed as an argument to openbyhandleat(2).  Note that for the directory entry mod‐\nification events FANCREATE, FANDELETE, and FANMOVE, the filehandle identifies  the\nmodified  directory  and  not the created/deleted/moved child object.  If the value of\ninfotype field is FANEVENTINFOTYPEDFIDNAME, the file handle  is  followed  by  a\nnull terminated string that identifies the created/deleted/moved directory entry name.\nFor other events such as FANOPEN, FANATTRIB, FANDELETESELF, and FANMOVESELF,  if\nthe  value  of  infotype field is FANEVENTINFOTYPEFID, the filehandle identifies\nthe  object  correlated  to  the  event.   If  the  value  of   infotype   field   is\nFANEVENTINFOTYPEDFID,  the  filehandle identifies the directory object correlated\nto the event or the parent directory of  a  non-directory  object  correlated  to  the\nevent.   If  the  value  of  infotype  field  is  FANEVENTINFOTYPEDFIDNAME,  the\nfilehandle  identifies  the  same  directory  object  that  would  be  reported  with\nFANEVENTINFOTYPEDFID  and  the file handle is followed by a null terminated string\nthat identifies the name of a directory entry in that directory, or  '.'  to  identify\nthe directory object itself.\n\nThe following macros are provided to iterate over a buffer containing fanotify event metadata\nreturned by a read(2) from an fanotify file descriptor:\n\nFANEVENTOK(meta, len)\nThis macro checks the remaining length len of the buffer meta against  the  length  of\nthe  metadata structure and the eventlen field of the first metadata structure in the\nbuffer.\n\nFANEVENTNEXT(meta, len)\nThis macro uses the length indicated in the eventlen field of the metadata  structure\npointed  to  by meta to calculate the address of the next metadata structure that fol‐\nlows meta.  len is the number of bytes of metadata that currently remain in  the  buf‐\nfer.   The  macro  returns a pointer to the next metadata structure that follows meta,\nand reduces len by the number of bytes in the metadata structure that has been skipped\nover (i.e., it subtracts meta->eventlen from len).\n\nIn addition, there is:\n\nFANEVENTMETADATALEN\nThis macro returns the size (in bytes) of the structure fanotifyeventmetadata.  This\nis the minimum size (and currently the only size) of any event metadata.\n\n#### Monitoring an fanotify file descriptor for events\n\nWhen an fanotify event occurs, the fanotify file descriptor indicates as readable when passed\nto epoll(7), poll(2), or select(2).\n\n#### Dealing with permission events\n\nFor permission events, the application must write(2) a structure of the following form to the\nfanotify file descriptor:\n\nstruct fanotifyresponse {\ns32 fd;\nu32 response;\n};\n\nThe fields of this structure are as follows:\n\nfd     This is the file descriptor from the structure fanotifyeventmetadata.\n\nresponse\nThis field indicates whether or not the permission is to be granted.  Its  value  must\nbe  either  FANALLOW  to allow the file operation or FANDENY to deny the file opera‐\ntion.\n\nIf access is denied, the requesting application call will receive an EPERM error.\n\n#### Closing the fanotify file descriptor\n\nWhen all file descriptors referring to the fanotify notification group are closed,  the  fan‐\notify  group is released and its resources are freed for reuse by the kernel.  Upon close(2),\noutstanding permission events will be set to allowed.\n\n#### /proc/[pid]/fdinfo\n\nThe file /proc/[pid]/fdinfo/[fd] contains information about fanotify marks for file  descrip‐\ntor fd of process pid.  See proc(5) for details.\n\n### ERRORS\n\nIn addition to the usual errors for read(2), the following errors can occur when reading from\nthe fanotify file descriptor:\n\nEINVAL The buffer is too small to hold the event.\n\nEMFILE The per-process limit on the number of open files has been reached.  See the  descrip‐\ntion of RLIMITNOFILE in getrlimit(2).\n\nENFILE The  system-wide  limit  on  the  total  number  of  open files has been reached.  See\n/proc/sys/fs/file-max in proc(5).\n\nETXTBSY\nThis error is returned  by  read(2)  if  ORDWR  or  OWRONLY  was  specified  in  the\neventfflags argument when calling fanotifyinit(2) and an event occurred for a moni‐\ntored file that is currently being executed.\n\nIn addition to the usual errors for write(2), the following errors can occur when writing  to\nthe fanotify file descriptor:\n\nEINVAL Fanotify  access  permissions are not enabled in the kernel configuration or the value\nof response in the response structure is not valid.\n\nENOENT The file descriptor fd in the response structure is not valid.  This may occur when  a\nresponse for the permission event has already been written.\n\n### VERSIONS\n\nThe  fanotify API was introduced in version 2.6.36 of the Linux kernel and enabled in version\n2.6.37.  Fdinfo support was added in version 3.8.\n\n### CONFORMING TO\n\nThe fanotify API is Linux-specific.\n\n### NOTES\n\nThe fanotify API is available only if the kernel was built with the CONFIGFANOTIFY  configu‐\nration  option  enabled.   In addition, fanotify permission handling is available only if the\nCONFIGFANOTIFYACCESSPERMISSIONS configuration option is enabled.\n\n#### Limitations and caveats\n\nFanotify reports only events that a user-space program triggers through the  filesystem  API.\nAs a result, it does not catch remote events that occur on network filesystems.\n\nThe  fanotify  API  does not report file accesses and modifications that may occur because of\nmmap(2), msync(2), and munmap(2).\n\nEvents for directories are created only if the directory itself is opened, read, and  closed.\nAdding,  removing,  or changing children of a marked directory does not create events for the\nmonitored directory itself.\n\nFanotify monitoring of directories is not recursive: to monitor subdirectories under a direc‐\ntory,  additional marks must be created.  The FANCREATE event can be used for detecting when\na subdirectory has been created under a marked directory.  An additional mark  must  then  be\nset  on  the  newly  created subdirectory.  This approach is racy, because it can lose events\nthat occurred inside the newly created subdirectory, before a mark is added on that subdirec‐\ntory.   Monitoring  mounts offers the capability to monitor a whole directory tree in a race-\nfree manner.  Monitoring filesystems offers the capability to monitor changes made  from  any\nmount of a filesystem instance in a race-free manner.\n\nThe event queue can overflow.  In this case, events are lost.\n\n### BUGS\n\nBefore Linux 3.19, fallocate(2) did not generate fanotify events.  Since Linux 3.19, calls to\nfallocate(2) generate FANMODIFY events.\n\nAs of Linux 3.17, the following bugs exist:\n\n*  On Linux, a filesystem object may be accessible through multiple  paths,  for  example,  a\npart  of  a  filesystem  may be remounted using the --bind option of mount(8).  A listener\nthat marked a mount will be notified only of events that were triggered for  a  filesystem\nobject using the same mount.  Any other event will pass unnoticed.\n\n*  When  an  event is generated, no check is made to see whether the user ID of the receiving\nprocess has authorization to read or write the file before passing a file  descriptor  for\nthat  file.  This poses a security risk, when the CAPSYSADMIN capability is set for pro‐\ngrams executed by unprivileged users.\n\n*  If a call to read(2) processes multiple events from the fanotify queue and  an  error  oc‐\ncurs,  the  return value will be the total length of the events successfully copied to the\nuser-space buffer before the error occurred.  The return value will not be -1,  and  errno\nwill not be set.  Thus, the reading application has no way to detect the error.\n\n### EXAMPLES\n\nThe two example programs below demonstrate the usage of the fanotify API.\n\nExample program: fanotifyexample.c\nThe  first  program  is  an  example of fanotify being used with its event object information\npassed in the form of a file descriptor.  The program marks the mount point passed as a  com‐\nmand-line  argument  and  waits for events of type FANOPENPERM and FANCLOSEWRITE.  When a\npermission event occurs, a FANALLOW response is given.\n\nThe following shell session shows an example of running this program.  This session  involved\nediting  the  file  /home/user/temp/notes.  Before the file was opened, a FANOPENPERM event\noccurred.  After the file was closed, a FANCLOSEWRITE event  occurred.   Execution  of  the\nprogram ends when the user presses the ENTER key.\n\n# ./fanotifyexample /home\nPress enter key to terminate.\nListening for events.\nFANOPENPERM: File /home/user/temp/notes\nFANCLOSEWRITE: File /home/user/temp/notes\n\nListening for events stopped.\n\nProgram source: fanotifyexample.c\n\n#define GNUSOURCE     /* Needed to get OLARGEFILE definition */\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <poll.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/fanotify.h>\n#include <unistd.h>\n\n/* Read all available fanotify events from the file descriptor 'fd' */\n\nstatic void\nhandleevents(int fd)\n{\nconst struct fanotifyeventmetadata *metadata;\nstruct fanotifyeventmetadata buf[200];\nssizet len;\nchar path[PATHMAX];\nssizet pathlen;\nchar procfdpath[PATHMAX];\nstruct fanotifyresponse response;\n\n/* Loop while events can be read from fanotify file descriptor */\n\nfor (;;) {\n\n/* Read some events */\n\nlen = read(fd, buf, sizeof(buf));\nif (len == -1 && errno != EAGAIN) {\nperror(\"read\");\nexit(EXITFAILURE);\n}\n\n/* Check if end of available data reached */\n\nif (len <= 0)\nbreak;\n\n/* Point to the first event in the buffer */\n\nmetadata = buf;\n\n/* Loop over all events in the buffer */\n\nwhile (FANEVENTOK(metadata, len)) {\n\n/* Check that run-time and compile-time structures match */\n\nif (metadata->vers != FANOTIFYMETADATAVERSION) {\nfprintf(stderr,\n\"Mismatch of fanotify metadata version.\\n\");\nexit(EXITFAILURE);\n}\n\n/* metadata->fd contains either FANNOFD, indicating a\nqueue overflow, or a file descriptor (a nonnegative\ninteger). Here, we simply ignore queue overflow. */\n\nif (metadata->fd >= 0) {\n\n/* Handle open permission event */\n\nif (metadata->mask & FANOPENPERM) {\nprintf(\"FANOPENPERM: \");\n\n/* Allow file to be opened */\n\nresponse.fd = metadata->fd;\nresponse.response = FANALLOW;\nwrite(fd, &response, sizeof(response));\n}\n\n/* Handle closing of writable file event */\n\nif (metadata->mask & FANCLOSEWRITE)\nprintf(\"FANCLOSEWRITE: \");\n\n/* Retrieve and print pathname of the accessed file */\n\nsnprintf(procfdpath, sizeof(procfdpath),\n\"/proc/self/fd/%d\", metadata->fd);\npathlen = readlink(procfdpath, path,\nsizeof(path) - 1);\nif (pathlen == -1) {\nperror(\"readlink\");\nexit(EXITFAILURE);\n}\n\npath[pathlen] = '\\0';\nprintf(\"File %s\\n\", path);\n\n/* Close the file descriptor of the event */\n\nclose(metadata->fd);\n}\n\n/* Advance to next event */\n\nmetadata = FANEVENTNEXT(metadata, len);\n}\n}\n}\n\nint\nmain(int argc, char *argv[])\n{\nchar buf;\nint fd, pollnum;\nnfdst nfds;\nstruct pollfd fds[2];\n\n/* Check mount point is supplied */\n\nif (argc != 2) {\nfprintf(stderr, \"Usage: %s MOUNT\\n\", argv[0]);\nexit(EXITFAILURE);\n}\n\nprintf(\"Press enter key to terminate.\\n\");\n\n/* Create the file descriptor for accessing the fanotify API */\n\nfd = fanotifyinit(FANCLOEXEC | FANCLASSCONTENT | FANNONBLOCK,\nORDONLY | OLARGEFILE);\nif (fd == -1) {\nperror(\"fanotifyinit\");\nexit(EXITFAILURE);\n}\n\n/* Mark the mount for:\n- permission events before opening files\n- notification events after closing a write-enabled\nfile descriptor */\n\nif (fanotifymark(fd, FANMARKADD | FANMARKMOUNT,\nFANOPENPERM | FANCLOSEWRITE, ATFDCWD,\nargv[1]) == -1) {\nperror(\"fanotifymark\");\nexit(EXITFAILURE);\n}\n\n/* Prepare for polling */\n\nnfds = 2;\n\n/* Console input */\n\nfds[0].fd = STDINFILENO;\nfds[0].events = POLLIN;\n\n/* Fanotify input */\n\nfds[1].fd = fd;\nfds[1].events = POLLIN;\n\n/* This is the loop to wait for incoming events */\n\nprintf(\"Listening for events.\\n\");\n\nwhile (1) {\npollnum = poll(fds, nfds, -1);\nif (pollnum == -1) {\nif (errno == EINTR)     /* Interrupted by a signal */\ncontinue;           /* Restart poll() */\n\nperror(\"poll\");         /* Unexpected error */\nexit(EXITFAILURE);\n}\n\nif (pollnum > 0) {\nif (fds[0].revents & POLLIN) {\n\n/* Console input is available: empty stdin and quit */\n\nwhile (read(STDINFILENO, &buf, 1) > 0 && buf != '\\n')\ncontinue;\nbreak;\n}\n\nif (fds[1].revents & POLLIN) {\n\n/* Fanotify events are available */\n\nhandleevents(fd);\n}\n}\n}\n\nprintf(\"Listening for events stopped.\\n\");\nexit(EXITSUCCESS);\n}\n\nExample program: fanotifyfid.c\nThe  second program is an example of fanotify being used with a group that identifies objects\nby file handles.  The program marks the filesystem object that is passed  as  a  command-line\nargument  and waits until an event of type FANCREATE has occurred.  The event mask indicates\nwhich type of filesystem object—either a file or a directory—was created.   Once  all  events\nhave been read from the buffer and processed accordingly, the program simply terminates.\n\nThe  following  shell sessions show two different invocations of this program, with different\nactions performed on a watched object.\n\nThe first session shows a mark being placed on /home/user.  This is followed by the  creation\nof  a regular file, /home/user/testfile.txt.  This results in a FANCREATE event being gener‐\nated and reported against the file's parent watched directory object  and  with  the  created\nfile  name.  Program execution ends once all events captured within the buffer have been pro‐\ncessed.\n\n# ./fanotifyfid /home/user\nListening for events.\nFANCREATE (file created):\nDirectory /home/user has been modified.\nEntry 'testfile.txt' is not a subdirectory.\nAll events processed successfully. Program exiting.\n\n$ touch /home/user/testfile.txt              # In another terminal\n\nThe second session shows a mark being placed on /home/user.  This is followed by the creation\nof a directory, /home/user/testdir.  This specific action results in a FANCREATE event being\ngenerated and is reported with the FANONDIR flag set and with the created directory name.\n\n# ./fanotifyfid /home/user\nListening for events.\nFANCREATE | FANONDIR (subdirectory created):\nDirectory /home/user has been modified.\nEntry 'testdir' is a subdirectory.\nAll events processed successfully. Program exiting.\n\n$ mkdir -p /home/user/testdir          # In another terminal\n\nProgram source: fanotifyfid.c\n\n#define GNUSOURCE\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/fanotify.h>\n#include <unistd.h>\n\n#define BUFSIZE 256\n\nint\nmain(int argc, char argv)\n{\nint fd, ret, eventfd, mountfd;\nssizet len, pathlen;\nchar path[PATHMAX];\nchar procfdpath[PATHMAX];\nchar eventsbuf[BUFSIZE];\nstruct filehandle *filehandle;\nstruct fanotifyeventmetadata *metadata;\nstruct fanotifyeventinfofid *fid;\nconst char *filename;\nstruct stat sb;\n\nif (argc != 2) {\nfprintf(stderr, \"Invalid number of command line arguments.\\n\");\nexit(EXITFAILURE);\n}\n\nmountfd = open(argv[1], ODIRECTORY | ORDONLY);\nif (mountfd == -1) {\nperror(argv[1]);\nexit(EXITFAILURE);\n}\n\n\n/* Create an fanotify file descriptor with FANREPORTDFIDNAME as\na flag so that program can receive fid events with directory\nentry name. */\n\nfd = fanotifyinit(FANCLASSNOTIF | FANREPORTDFIDNAME, 0);\nif (fd == -1) {\nperror(\"fanotifyinit\");\nexit(EXITFAILURE);\n}\n\n/* Place a mark on the filesystem object supplied in argv[1]. */\n\nret = fanotifymark(fd, FANMARKADD | FANMARKONLYDIR,\nFANCREATE | FANONDIR,\nATFDCWD, argv[1]);\nif (ret == -1) {\nperror(\"fanotifymark\");\nexit(EXITFAILURE);\n}\n\nprintf(\"Listening for events.\\n\");\n\n/* Read events from the event queue into a buffer */\n\nlen = read(fd, eventsbuf, sizeof(eventsbuf));\nif (len == -1 && errno != EAGAIN) {\nperror(\"read\");\nexit(EXITFAILURE);\n}\n\n/* Process all events within the buffer */\n\nfor (metadata = (struct fanotifyeventmetadata *) eventsbuf;\nFANEVENTOK(metadata, len);\nmetadata = FANEVENTNEXT(metadata, len)) {\nfid = (struct fanotifyeventinfofid *) (metadata + 1);\nfilehandle = (struct filehandle *) fid->handle;\n\n/* Ensure that the event info is of the correct type */\n\nif (fid->hdr.infotype == FANEVENTINFOTYPEFID ||\nfid->hdr.infotype == FANEVENTINFOTYPEDFID) {\nfilename = NULL;\n} else if (fid->hdr.infotype == FANEVENTINFOTYPEDFIDNAME) {\nfilename = filehandle->fhandle +\nfilehandle->handlebytes;\n} else {\nfprintf(stderr, \"Received unexpected event info type.\\n\");\nexit(EXITFAILURE);\n}\n\nif (metadata->mask == FANCREATE)\nprintf(\"FANCREATE (file created):\\n\");\n\nif (metadata->mask == (FANCREATE | FANONDIR))\nprintf(\"FANCREATE | FANONDIR (subdirectory created):\\n\");\n\n/* metadata->fd is set to FANNOFD when the group identifies\nobjects by file handles.  To obtain a file descriptor for\nthe file object corresponding to an event you can use the\nstruct filehandle that's provided within the\nfanotifyeventinfofid in conjunction with the\nopenbyhandleat(2) system call.  A check for ESTALE is\ndone to accommodate for the situation where the file handle\nfor the object was deleted prior to this system call. */\n\neventfd = openbyhandleat(mountfd, filehandle, ORDONLY);\nif (eventfd == -1) {\nif (errno == ESTALE) {\nprintf(\"File handle is no longer valid. \"\n\"File has been deleted\\n\");\ncontinue;\n} else {\nperror(\"openbyhandleat\");\nexit(EXITFAILURE);\n}\n}\n\nsnprintf(procfdpath, sizeof(procfdpath), \"/proc/self/fd/%d\",\neventfd);\n\n/* Retrieve and print the path of the modified dentry */\n\npathlen = readlink(procfdpath, path, sizeof(path) - 1);\nif (pathlen == -1) {\nperror(\"readlink\");\nexit(EXITFAILURE);\n}\n\npath[pathlen] = '\\0';\nprintf(\"\\tDirectory '%s' has been modified.\\n\", path);\n\nif (filename) {\nret = fstatat(eventfd, filename, &sb, 0);\nif (ret == -1) {\nif (errno != ENOENT) {\nperror(\"fstatat\");\nexit(EXITFAILURE);\n}\nprintf(\"\\tEntry '%s' does not exist.\\n\", filename);\n} else if ((sb.stmode & SIFMT) == SIFDIR) {\nprintf(\"\\tEntry '%s' is a subdirectory.\\n\", filename);\n} else {\nprintf(\"\\tEntry '%s' is not a subdirectory.\\n\",\nfilename);\n}\n}\n\n/* Close associated file descriptor for this event */\n\nclose(eventfd);\n}\n\nprintf(\"All events processed successfully. Program exiting.\\n\");\nexit(EXITSUCCESS);\n}\n\n### SEE ALSO\n\nfanotifyinit(2), fanotifymark(2), inotify(7)\n\n### COLOPHON\n\nThis page is part of release 5.10 of the Linux man-pages project.  A description of the\nproject, information about reporting bugs, and the latest version of this page, can be found\nat https://www.kernel.org/doc/man-pages/.\n\n\n\nLinux                                        2020-11-01                                  FANOTIFY(7)\n\n"
        }
    ],
    "structuredContent": {
        "command": "fanotify",
        "section": "7",
        "mode": "man",
        "summary": "fanotify - monitoring filesystem events",
        "synopsis": null,
        "flags": [],
        "examples": [
            "The two example programs below demonstrate the usage of the fanotify API.",
            "Example program: fanotifyexample.c",
            "The  first  program  is  an  example of fanotify being used with its event object information",
            "passed in the form of a file descriptor.  The program marks the mount point passed as a  com‐",
            "mand-line  argument  and  waits for events of type FANOPENPERM and FANCLOSEWRITE.  When a",
            "permission event occurs, a FANALLOW response is given.",
            "The following shell session shows an example of running this program.  This session  involved",
            "editing  the  file  /home/user/temp/notes.  Before the file was opened, a FANOPENPERM event",
            "occurred.  After the file was closed, a FANCLOSEWRITE event  occurred.   Execution  of  the",
            "program ends when the user presses the ENTER key.",
            "# ./fanotifyexample /home",
            "Press enter key to terminate.",
            "Listening for events.",
            "FANOPENPERM: File /home/user/temp/notes",
            "FANCLOSEWRITE: File /home/user/temp/notes",
            "Listening for events stopped.",
            "Program source: fanotifyexample.c",
            "#define GNUSOURCE     /* Needed to get OLARGEFILE definition */",
            "#include <errno.h>",
            "#include <fcntl.h>",
            "#include <limits.h>",
            "#include <poll.h>",
            "#include <stdio.h>",
            "#include <stdlib.h>",
            "#include <sys/fanotify.h>",
            "#include <unistd.h>",
            "/* Read all available fanotify events from the file descriptor 'fd' */",
            "static void",
            "handleevents(int fd)",
            "const struct fanotifyeventmetadata *metadata;",
            "struct fanotifyeventmetadata buf[200];",
            "ssizet len;",
            "char path[PATHMAX];",
            "ssizet pathlen;",
            "char procfdpath[PATHMAX];",
            "struct fanotifyresponse response;",
            "/* Loop while events can be read from fanotify file descriptor */",
            "for (;;) {",
            "/* Read some events */",
            "len = read(fd, buf, sizeof(buf));",
            "if (len == -1 && errno != EAGAIN) {",
            "perror(\"read\");",
            "exit(EXITFAILURE);",
            "/* Check if end of available data reached */",
            "if (len <= 0)",
            "break;",
            "/* Point to the first event in the buffer */",
            "metadata = buf;",
            "/* Loop over all events in the buffer */",
            "while (FANEVENTOK(metadata, len)) {",
            "/* Check that run-time and compile-time structures match */",
            "if (metadata->vers != FANOTIFYMETADATAVERSION) {",
            "fprintf(stderr,",
            "\"Mismatch of fanotify metadata version.\\n\");",
            "exit(EXITFAILURE);",
            "/* metadata->fd contains either FANNOFD, indicating a",
            "queue overflow, or a file descriptor (a nonnegative",
            "integer). Here, we simply ignore queue overflow. */",
            "if (metadata->fd >= 0) {",
            "/* Handle open permission event */",
            "if (metadata->mask & FANOPENPERM) {",
            "printf(\"FANOPENPERM: \");",
            "/* Allow file to be opened */",
            "response.fd = metadata->fd;",
            "response.response = FANALLOW;",
            "write(fd, &response, sizeof(response));",
            "/* Handle closing of writable file event */",
            "if (metadata->mask & FANCLOSEWRITE)",
            "printf(\"FANCLOSEWRITE: \");",
            "/* Retrieve and print pathname of the accessed file */",
            "snprintf(procfdpath, sizeof(procfdpath),",
            "\"/proc/self/fd/%d\", metadata->fd);",
            "pathlen = readlink(procfdpath, path,",
            "sizeof(path) - 1);",
            "if (pathlen == -1) {",
            "perror(\"readlink\");",
            "exit(EXITFAILURE);",
            "path[pathlen] = '\\0';",
            "printf(\"File %s\\n\", path);",
            "/* Close the file descriptor of the event */",
            "close(metadata->fd);",
            "/* Advance to next event */",
            "metadata = FANEVENTNEXT(metadata, len);",
            "int",
            "main(int argc, char *argv[])",
            "char buf;",
            "int fd, pollnum;",
            "nfdst nfds;",
            "struct pollfd fds[2];",
            "/* Check mount point is supplied */",
            "if (argc != 2) {",
            "fprintf(stderr, \"Usage: %s MOUNT\\n\", argv[0]);",
            "exit(EXITFAILURE);",
            "printf(\"Press enter key to terminate.\\n\");",
            "/* Create the file descriptor for accessing the fanotify API */",
            "fd = fanotifyinit(FANCLOEXEC | FANCLASSCONTENT | FANNONBLOCK,",
            "ORDONLY | OLARGEFILE);",
            "if (fd == -1) {",
            "perror(\"fanotifyinit\");",
            "exit(EXITFAILURE);",
            "/* Mark the mount for:",
            "- permission events before opening files",
            "- notification events after closing a write-enabled",
            "file descriptor */",
            "if (fanotifymark(fd, FANMARKADD | FANMARKMOUNT,",
            "FANOPENPERM | FANCLOSEWRITE, ATFDCWD,",
            "argv[1]) == -1) {",
            "perror(\"fanotifymark\");",
            "exit(EXITFAILURE);",
            "/* Prepare for polling */",
            "nfds = 2;",
            "/* Console input */",
            "fds[0].fd = STDINFILENO;",
            "fds[0].events = POLLIN;",
            "/* Fanotify input */",
            "fds[1].fd = fd;",
            "fds[1].events = POLLIN;",
            "/* This is the loop to wait for incoming events */",
            "printf(\"Listening for events.\\n\");",
            "while (1) {",
            "pollnum = poll(fds, nfds, -1);",
            "if (pollnum == -1) {",
            "if (errno == EINTR)     /* Interrupted by a signal */",
            "continue;           /* Restart poll() */",
            "perror(\"poll\");         /* Unexpected error */",
            "exit(EXITFAILURE);",
            "if (pollnum > 0) {",
            "if (fds[0].revents & POLLIN) {",
            "/* Console input is available: empty stdin and quit */",
            "while (read(STDINFILENO, &buf, 1) > 0 && buf != '\\n')",
            "continue;",
            "break;",
            "if (fds[1].revents & POLLIN) {",
            "/* Fanotify events are available */",
            "handleevents(fd);",
            "printf(\"Listening for events stopped.\\n\");",
            "exit(EXITSUCCESS);",
            "Example program: fanotifyfid.c",
            "The  second program is an example of fanotify being used with a group that identifies objects",
            "by file handles.  The program marks the filesystem object that is passed  as  a  command-line",
            "argument  and waits until an event of type FANCREATE has occurred.  The event mask indicates",
            "which type of filesystem object—either a file or a directory—was created.   Once  all  events",
            "have been read from the buffer and processed accordingly, the program simply terminates.",
            "The  following  shell sessions show two different invocations of this program, with different",
            "actions performed on a watched object.",
            "The first session shows a mark being placed on /home/user.  This is followed by the  creation",
            "of  a regular file, /home/user/testfile.txt.  This results in a FANCREATE event being gener‐",
            "ated and reported against the file's parent watched directory object  and  with  the  created",
            "file  name.  Program execution ends once all events captured within the buffer have been pro‐",
            "cessed.",
            "# ./fanotifyfid /home/user",
            "Listening for events.",
            "FANCREATE (file created):",
            "Directory /home/user has been modified.",
            "Entry 'testfile.txt' is not a subdirectory.",
            "All events processed successfully. Program exiting.",
            "$ touch /home/user/testfile.txt              # In another terminal",
            "The second session shows a mark being placed on /home/user.  This is followed by the creation",
            "of a directory, /home/user/testdir.  This specific action results in a FANCREATE event being",
            "generated and is reported with the FANONDIR flag set and with the created directory name.",
            "# ./fanotifyfid /home/user",
            "Listening for events.",
            "FANCREATE | FANONDIR (subdirectory created):",
            "Directory /home/user has been modified.",
            "Entry 'testdir' is a subdirectory.",
            "All events processed successfully. Program exiting.",
            "$ mkdir -p /home/user/testdir          # In another terminal",
            "Program source: fanotifyfid.c",
            "#define GNUSOURCE",
            "#include <errno.h>",
            "#include <fcntl.h>",
            "#include <limits.h>",
            "#include <stdio.h>",
            "#include <stdlib.h>",
            "#include <sys/types.h>",
            "#include <sys/stat.h>",
            "#include <sys/fanotify.h>",
            "#include <unistd.h>",
            "#define BUFSIZE 256",
            "int",
            "main(int argc, char argv)",
            "int fd, ret, eventfd, mountfd;",
            "ssizet len, pathlen;",
            "char path[PATHMAX];",
            "char procfdpath[PATHMAX];",
            "char eventsbuf[BUFSIZE];",
            "struct filehandle *filehandle;",
            "struct fanotifyeventmetadata *metadata;",
            "struct fanotifyeventinfofid *fid;",
            "const char *filename;",
            "struct stat sb;",
            "if (argc != 2) {",
            "fprintf(stderr, \"Invalid number of command line arguments.\\n\");",
            "exit(EXITFAILURE);",
            "mountfd = open(argv[1], ODIRECTORY | ORDONLY);",
            "if (mountfd == -1) {",
            "perror(argv[1]);",
            "exit(EXITFAILURE);",
            "/* Create an fanotify file descriptor with FANREPORTDFIDNAME as",
            "a flag so that program can receive fid events with directory",
            "entry name. */",
            "fd = fanotifyinit(FANCLASSNOTIF | FANREPORTDFIDNAME, 0);",
            "if (fd == -1) {",
            "perror(\"fanotifyinit\");",
            "exit(EXITFAILURE);",
            "/* Place a mark on the filesystem object supplied in argv[1]. */",
            "ret = fanotifymark(fd, FANMARKADD | FANMARKONLYDIR,",
            "FANCREATE | FANONDIR,",
            "ATFDCWD, argv[1]);",
            "if (ret == -1) {",
            "perror(\"fanotifymark\");",
            "exit(EXITFAILURE);",
            "printf(\"Listening for events.\\n\");",
            "/* Read events from the event queue into a buffer */",
            "len = read(fd, eventsbuf, sizeof(eventsbuf));",
            "if (len == -1 && errno != EAGAIN) {",
            "perror(\"read\");",
            "exit(EXITFAILURE);",
            "/* Process all events within the buffer */",
            "for (metadata = (struct fanotifyeventmetadata *) eventsbuf;",
            "FANEVENTOK(metadata, len);",
            "metadata = FANEVENTNEXT(metadata, len)) {",
            "fid = (struct fanotifyeventinfofid *) (metadata + 1);",
            "filehandle = (struct filehandle *) fid->handle;",
            "/* Ensure that the event info is of the correct type */",
            "if (fid->hdr.infotype == FANEVENTINFOTYPEFID ||",
            "fid->hdr.infotype == FANEVENTINFOTYPEDFID) {",
            "filename = NULL;",
            "} else if (fid->hdr.infotype == FANEVENTINFOTYPEDFIDNAME) {",
            "filename = filehandle->fhandle +",
            "filehandle->handlebytes;",
            "} else {",
            "fprintf(stderr, \"Received unexpected event info type.\\n\");",
            "exit(EXITFAILURE);",
            "if (metadata->mask == FANCREATE)",
            "printf(\"FANCREATE (file created):\\n\");",
            "if (metadata->mask == (FANCREATE | FANONDIR))",
            "printf(\"FANCREATE | FANONDIR (subdirectory created):\\n\");",
            "/* metadata->fd is set to FANNOFD when the group identifies",
            "objects by file handles.  To obtain a file descriptor for",
            "the file object corresponding to an event you can use the",
            "struct filehandle that's provided within the",
            "fanotifyeventinfofid in conjunction with the",
            "openbyhandleat(2) system call.  A check for ESTALE is",
            "done to accommodate for the situation where the file handle",
            "for the object was deleted prior to this system call. */",
            "eventfd = openbyhandleat(mountfd, filehandle, ORDONLY);",
            "if (eventfd == -1) {",
            "if (errno == ESTALE) {",
            "printf(\"File handle is no longer valid. \"",
            "\"File has been deleted\\n\");",
            "continue;",
            "} else {",
            "perror(\"openbyhandleat\");",
            "exit(EXITFAILURE);",
            "snprintf(procfdpath, sizeof(procfdpath), \"/proc/self/fd/%d\",",
            "eventfd);",
            "/* Retrieve and print the path of the modified dentry */",
            "pathlen = readlink(procfdpath, path, sizeof(path) - 1);",
            "if (pathlen == -1) {",
            "perror(\"readlink\");",
            "exit(EXITFAILURE);",
            "path[pathlen] = '\\0';",
            "printf(\"\\tDirectory '%s' has been modified.\\n\", path);",
            "if (filename) {",
            "ret = fstatat(eventfd, filename, &sb, 0);",
            "if (ret == -1) {",
            "if (errno != ENOENT) {",
            "perror(\"fstatat\");",
            "exit(EXITFAILURE);",
            "printf(\"\\tEntry '%s' does not exist.\\n\", filename);",
            "} else if ((sb.stmode & SIFMT) == SIFDIR) {",
            "printf(\"\\tEntry '%s' is a subdirectory.\\n\", filename);",
            "} else {",
            "printf(\"\\tEntry '%s' is not a subdirectory.\\n\",",
            "filename);",
            "/* Close associated file descriptor for this event */",
            "close(eventfd);",
            "printf(\"All events processed successfully. Program exiting.\\n\");",
            "exit(EXITSUCCESS);"
        ],
        "see_also": [
            {
                "name": "fanotifyinit",
                "section": "2",
                "url": "https://www.chedong.com/phpMan.php/man/fanotifyinit/2/json"
            },
            {
                "name": "fanotifymark",
                "section": "2",
                "url": "https://www.chedong.com/phpMan.php/man/fanotifymark/2/json"
            },
            {
                "name": "inotify",
                "section": "7",
                "url": "https://www.chedong.com/phpMan.php/man/inotify/7/json"
            }
        ],
        "section_outline": [
            {
                "name": "NAME",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "DESCRIPTION",
                "lines": 44,
                "subsections": [
                    {
                        "name": "The event queue",
                        "lines": 16
                    },
                    {
                        "name": "Reading fanotify events",
                        "lines": 240
                    },
                    {
                        "name": "Monitoring an fanotify file descriptor for events",
                        "lines": 3
                    },
                    {
                        "name": "Dealing with permission events",
                        "lines": 19
                    },
                    {
                        "name": "Closing the fanotify file descriptor",
                        "lines": 4
                    },
                    {
                        "name": "/proc/[pid]/fdinfo",
                        "lines": 3
                    }
                ]
            },
            {
                "name": "ERRORS",
                "lines": 25,
                "subsections": []
            },
            {
                "name": "VERSIONS",
                "lines": 3,
                "subsections": []
            },
            {
                "name": "CONFORMING TO",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "NOTES",
                "lines": 4,
                "subsections": [
                    {
                        "name": "Limitations and caveats",
                        "lines": 21
                    }
                ]
            },
            {
                "name": "BUGS",
                "lines": 20,
                "subsections": []
            },
            {
                "name": "EXAMPLES",
                "lines": 413,
                "subsections": []
            },
            {
                "name": "SEE ALSO",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "COLOPHON",
                "lines": 7,
                "subsections": []
            }
        ]
    }
}