fanotify.7, fanotify_init.2, fanotify_mark.2: Document FAN_REPORT_FID and directory modification events

Details relating to the new initialization flag FAN_REPORT_FID has been
added. As part of the FAN_REPORT_FID feature, a new set of event masks are
available and have been documented accordingly.

A simple example program has been added to also support the understanding
and use of FAN_REPORT_FID and directory modification events.

Signed-off-by: Matthew Bobrowski <mbobrowski@mbobrowski.org>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
This commit is contained in:
Matthew Bobrowski 2019-06-06 19:48:02 +10:00 committed by Michael Kerrisk
parent 59a4d9b398
commit 0a4db6dc74
3 changed files with 448 additions and 15 deletions

View File

@ -40,8 +40,8 @@ queue associated with the group.
.PP
The file descriptor is used in calls to
.BR fanotify_mark (2)
to specify the files, directories, and mounts for which fanotify events
shall be created.
to specify the files, directories, mounts or filesystems for which fanotify
events shall be created.
These events are received by reading from the file descriptor.
Some events are only informative, indicating that a file has been accessed.
Other events can be used to determine whether
@ -94,6 +94,36 @@ already contain their final content.
This notification class might be used by malware detection programs, for
example.
.TP
.BR FAN_REPORT_FID " (since Linux 5.1)"
.\" commit a8b13aa20afb69161b5123b4f1acc7ea0a03d360
This value allows the receipt of events which contain additional information
about the underlying object correlated to an event.
An additional structure encapsulates the information about the object and is
included alongside the generic event metadata structure.
The file descriptor that is used to represent the object correlated to an
event is instead substituted with a file handle.
It is intended for applications that may find the use of a file handle to
identify an object more suitable than a file descriptor.
Additionally, it may be used for applications that are interested in
directory entry events, such as
.BR FAN_CREATE ,
.BR FAN_ATTRIB ,
.BR FAN_MOVE
and
.BR FAN_DELETE
for example.
Note that the use of directory modification events are not supported when
monitoring a mount point.
The use of
.BR FAN_CLASS_CONTENT
or
.BR FAN_CLASS_PRE_CONTENT
is not permitted with this flag and will result in the error
.BR EINVAL .
See
.BR fanotify (7)
for additional information.
.TP
.B FAN_CLASS_NOTIF
This is the default value.
It does not need to be specified.
@ -224,6 +254,8 @@ An invalid value was passed in
or
.IR event_f_flags .
.B FAN_ALL_INIT_FLAGS
(deprecated since Linux kernel version 4.20)
.\" commit 23c9deeb3285d34fd243abb3d6b9f07db60c3cf4
defines all allowable bits for
.IR flags .
.TP

View File

@ -126,6 +126,15 @@ is not itself a mount point, the mount point containing
will be marked.
All directories, subdirectories, and the contained files of the mount point
will be monitored.
This value cannot be used if the
.I fanotify_fd
file descriptor has been initialized with the flag
.BR FAN_REPORT_FID
or if any of the new directory modification events are provided as a
.IR mask .
Attempting to do so will result in the error
.B EINVAL
being returned.
.TP
.BR FAN_MARK_FILESYSTEM " (since Linux 4.20)"
.\" commit d54f4fba889b205e9cd8239182ca5d27d0ac3bc2
@ -171,6 +180,28 @@ Create an event when a file or directory is opened.
Create an event when a file is opened with the intent to be executed.
See NOTES for additional details.
.TP
.B FAN_ATTRIB
Create an event when the metadata for a file or directory has changed.
.TP
.B FAN_CREATE
Create an event when a file or directory has been created in a marked
parent directory.
.TP
.B FAN_DELETE
Create an event when a file or directory has been deleted in a marked
parent directory.
.TP
.B FAN_DELETE_SELF
Create an event when a marked file or directory itself is deleted.
.TP
.B FAN_MOVED_FROM
Create an event when a file or directory has been moved from a marked
parent directory.
.TP
.B FAN_MOVED_TO
Create an event when a file or directory has been moved to a marked parent
directory.
.TP
.B FAN_Q_OVERFLOW
Create an event when an overflow of the event queue occurs.
The size of the event queue is limited to 16384 entries if
@ -205,13 +236,33 @@ or
is required.
.TP
.B FAN_ONDIR
Create events for directories\(emfor example, when
Create events for directories \(em for example, when
.BR opendir (3),
.BR readdir (3)
(but see BUGS), and
.BR closedir (3)
are called.
Without this flag, only events for files are created.
The
.BR FAN_ONDIR
flag is reported in an event mask only if the
.I fanotify_fd
file descriptor has been initialized with the flag
.BR FAN_REPORT_FID .
In the context of directory entry events, such as
.BR FAN_CREATE ,
.BR FAN_DELETE ,
.BR FAN_MOVED_FROM
and
.BR FAN_MOVED_TO
for example, specifying the flag
.BR FAN_ONDIR
is required in order to create events when subdirectory entries are
modified (i.e. mkdir/rmdir).
Subdirectory entry modification events will never be merged with non
subdirectory entry modification events.
This flag is never reported individually within an event and is always
supplied in conjunction with another event type.
.TP
.B FAN_EVENT_ON_CHILD
Events for the immediate children of marked directories shall be created.
@ -221,11 +272,15 @@ of marked directories.
To monitor complete directory trees it is necessary to mark the relevant
mount.
.PP
The following composed value is defined:
The following composed values are defined:
.TP
.B FAN_CLOSE
A file is closed
.RB ( FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE ).
.TP
.B FAN_MOVE
A file or directory has been moved
.RB ( FAN_MOVED_FROM | FAN_MOVED_TO ).
.PP
The filesystem object to be marked is determined by the file descriptor
.I dirfd
@ -297,6 +352,8 @@ was not an fanotify file descriptor.
.B EINVAL
The fanotify file descriptor was opened with
.B FAN_CLASS_NOTIF
or
.B FAN_REPORT_FID
and mask contains a flag for permission events
.RB ( FAN_OPEN_PERM
or
@ -335,6 +392,41 @@ and
and
.I pathname
do not specify a directory.
.TP
.B EXDEV
The object indicated by
.I pathname
resides within a filesystem subvolume (e.g. btrfs) which uses a different
.I fsid
than its root superblock.
This error can only be returned when an fanotify file descriptor returned
by
.BR fanotify_init (2)
has been created with
.BR FAN_REPORT_FID .
.TP
.B ENODEV
The object indicated by
.I pathname
is not associated with a filesystem that supports
.I fsid
(e.g. tmpfs).
This error can only be returned when an fanotify file descriptor returned
by
.BR fanotify_init (2)
has been created with
.BR FAN_REPORT_FID .
.TP
.B EOPNOTSUPP
The object indicated by
.I pathname
is associated with a filesystem that does not support the encoding of file
handles.
This error can only be returned when an fanotify file descriptor returned
by
.BR fanotify_init (2)
has been created with
.BR FAN_REPORT_FID .
.SH VERSIONS
.BR fanotify_mark ()
was introduced in version 2.6.36 of the Linux kernel and enabled in version

View File

@ -112,8 +112,12 @@ events and
events.
Notification events are merely informative
and require no action to be taken by
the receiving application except for closing the file descriptor passed
in the event (see below).
the receiving application with the exception being that the file
descriptor provided within a generic event must be closed.
The closing of file descriptors for each event only applies to
applications that have initialized fanotify without using
.BR FAN_REPORT_FID
(see below).
Permission events are requests to the receiving application to decide
whether permission for a file access shall be granted.
For these events, the recipient must write a response which decides whether
@ -138,6 +142,12 @@ until either a file event occurs or the call is interrupted by a signal
(see
.BR signal (7)).
.PP
Depending on whether
.BR FAN_REPORT_FID
is supplied as one of the flags when calling
.BR fanotify_init (2)
determines what structure(s) are returned for an event within the read
buffer.
After a successful
.BR read (2),
the read buffer contains one or more of the following structures:
@ -156,6 +166,25 @@ struct fanotify_event_metadata {
.EE
.in
.PP
In the instance that
.BR FAN_REPORT_FID
is supplied as one of the flags to
.BR fanotify_init (2),
you should also expect to receive the structure detailed below following
the generic
.I fanotify_event_metadata
structure within the read buffer:
.PP
.in +4n
.EX
struct fanotify_event_info_fid {
struct fanotify_event_info_header hdr;
__kernel_fsid_t fsid;
unsigned char file_handle[0];
};
.EE
.in
.PP
For performance reasons, it is recommended to use a large
buffer size (for example, 4096 bytes),
so that multiple events can be retrieved by a single
@ -173,12 +202,16 @@ structure are as follows:
.I event_len
This is the length of the data for the current event and the offset
to the next event in the buffer.
In the current implementation, the value of
Without
.BR FAN_REPORT_FID ,
the value of
.I event_len
is always
.BR FAN_EVENT_METADATA_LEN .
However, the API is designed to allow
variable-length structures to be returned in the future.
With
.BR FAN_REPORT_FID ,
.I event_len
also includes the variable length file identifier.
.TP
.I vers
This field holds a version number for the structure.
@ -205,6 +238,11 @@ This is a bit mask describing the event (see below).
This is an open file descriptor for the object being accessed, or
.B FAN_NOFD
if a queue overflow occurred.
If the fanotify file descriptor has been initialized using
.BR FAN_REPORT_FID ,
applications should expect this value to be set to
.B FAN_NOFD
for each event that is received.
The file descriptor can be used to access the contents
of the monitored file or directory.
The reading application is responsible for closing this file descriptor.
@ -262,6 +300,27 @@ See NOTES in
.BR fanotify_mark (2)
for additional details.
.TP
.B FAN_ATTRIB
A file or directory metadata was changed.
.TP
.B FAN_CREATE
A child file or directory was created in a watched parent.
.TP
.B FAN_DELETE
A child file or directory was deleted in a watched parent.
.TP
.B FAN_DELETE_SELF
A watched file or directory was deleted.
.TP
.B FAN_MOVED_FROM
A file or directory has been moved from a watched parent directory.
.TP
.B FAN_MOVED_TO
A file or directory has been moved to a watched parent directory.
.TP
.B FAN_MOVE_SELF
A watched file or directory was moved.
.TP
.B FAN_MODIFY
A file was modified.
.TP
@ -314,6 +373,76 @@ This is a synonym for:
.IP
FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE
.PP
To check for any move event, the following bit mask may be used:
.TP
.B FAN_MOVE
A file or directory was moved.
This is a synonym for:
.IP
FAN_MOVED_FROM | FAN_MOVED_TO
.PP
The fields of the
.I fanotify_event_info_fid
structure are as follows:
.TP
.I hdr
This is a structure of type
.IR fanotify_event_info_header .
It is a generic header that contains information used to describe
additional information attached to the event.
For example, when an fanotify file descriptor is created using
.B FAN_REPORT_FID
the
.I info_type
field of this header is set to
.BR FAN_EVENT_INFO_TYPE_FID .
Event listeners can use this field to check that the additional
information received for an event is of the correct type.
Additionally, the
.I fanotify_event_info_header
also contains a
.I len
field.
In the current implementation, the value of
.I len
is always (event_len - FAN_EVENT_METADATA_LEN).
.TP
.I fsid
This is a unique identifier of the filesystem containing the object
associated with the event.
It is a structure of type
.I __kernel_fsid_t
and contains the same value as
.I f_fsid
when calling
.BR statfs (2).
.TP
.I file_handle
This is a variable length structure of type
.IR file_handle .
It is an opaque handle that corresponds to a specified object on a
filesystem as returned by
.BR name_to_handle_at (2) .
It can be used to uniquely identify a file on a filesystem and can be
passed as an argument to
.BR open_by_handle_at (2) .
Note that for directory entry events, such as
.BR FAN_CREATE ,
.BR FAN_DELETE ,
.BR FAN_MOVE
the
.IR file_handle
describes the modified directory and not the created/deleted/moved child
object.
The events
.BR FAN_ATTRIB ,
.BR FAN_DELETE_SELF
and
.BR FAN_MOVE_SELF
will carry the
.IR file_handle
information for the child object if the child object is being watched.
.PP
The following macros are provided to iterate over a buffer containing
fanotify event metadata returned by a
.BR read (2)
@ -549,9 +678,12 @@ The return value will not be \-1, and
will not be set.
Thus, the reading application has no way to detect the error.
.SH EXAMPLE
The following program demonstrates the usage of the fanotify API.
It marks the mount point passed as a command-line argument
and waits for events of type
The two example programs below demonstrate the usage of the fanotify API.
The first program (fanotify_example.c) is an example of fanotify being
used with its event object information passed in the form of a file
descriptor.
It marks the mount point passed as a command-line argument and waits for
events of type
.B FAN_OPEN_PERM
and
.BR FAN_CLOSE_WRITE .
@ -559,7 +691,21 @@ When a permission event occurs, a
.B FAN_ALLOW
response is given.
.PP
The following output was recorded while editing the file
The second program (fanotify_fid.c) is an example of fanotify being used
with
.B FAN_REPORT_FID
enabled.
It attempts to mark the object that is passed as a command-line argument
and waits until an event of type
.B FAN_CREATE
has occurred.
Depending on whether a file or directory is created depends on what mask
is returned in the event mask.
Once all events have been read from the buffer and processed accordingly,
the program simply terminates.
.PP
The first example program output was captured from fanotify_example.
This session involved editing the file
.IR /home/user/temp/notes .
Before the file was opened, a
.B FAN_OPEN_PERM
@ -568,7 +714,34 @@ After the file was closed, a
.B FAN_CLOSE_WRITE
event occurred.
Execution of the program ends when the user presses the ENTER key.
.SS Example output
.PP
The second example program output was captured from fanotify_fid.
There are two discrete invocations to this program with each invocation
accommodating a different action performed on a watched object.
This first session shows a mark being placed on
.IR /home/user .
This is followed by a subsequent regular file
.IR /home/user/testfile.txt
being created.
This results in a
.B FAN_CREATE
event being created and reported against the file's parent watched
directory object.
Program execution ends once all events captured within the buffer have
been processed.
The second session shows a mark being placed on
.IR /home/user .
This is followed by a directory
.IR /home/user/testdir
being created.
This specific action results in the program producing a
.B FAN_CREATE
and
.B FAN_ONDIR
event.
Program execution ends once all events captured within the buffer are
processed.
.SS Example output (fanotify_example.c)
.in +4n
.EX
# ./fanotify_example /home
@ -579,8 +752,27 @@ FAN_CLOSE_WRITE: File /home/user/temp/notes
Listening for events stopped.
.EE
.SS Example output (fanotify_fid.c)
.in +4n
.EX
# ./fanotify_fid /home/user
Listening for events.
FAN_CREATE (file created): Directory /home/user has been modified.
All events processed successfully. Program exiting.
$ touch /home/user/testing
---
# ./fanotify_fid /home/user
Listening for events.
FAN_CREATE | FAN_ONDIR (subdirectory created): Directory /home/user has been modified.
All events processed successfully. Program exiting.
$ mkdir -p /home/user/testing
.EE
.in
.SS Program source
.SS Program source: fanotify_example.c
\&
.EX
#define _GNU_SOURCE /* Needed to get O_LARGEFILE definition */
@ -778,6 +970,123 @@ main(int argc, char *argv[])
exit(EXIT_SUCCESS);
}
.EE
.in
.SS Program source: fanotify_fid.c
\&
.EX
#define _GNU_SOURCE
#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 BUF_SIZE 256
int main(int argc, char **argv)
{
int fd, ret, event_fd;
ssize_t len, path_len;
char path[PATH_MAX];
char procfd_path[PATH_MAX];
char events_buf[BUF_SIZE];
struct file_handle *file_handle;
struct fanotify_event_metadata *metadata;
struct fanotify_event_info_fid *fid;
if (argc != 2) {
fprintf(stderr, "Invalid number of command line arguments.\\n");
exit(EXIT_FAILURE);
}
/* Create an fanotify file descriptor with FAN_REPORT_FID as a flag
* so that program can receive fid events.
*/
fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, 0);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* Place a mark on the filesystem object supplied in argv[1]. */
ret = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_ONLYDIR,
FAN_CREATE | FAN_ONDIR,
AT_FDCWD, argv[1]);
if (ret == -1) {
perror("fanotify_mark");
exit(EXIT_FAILURE);
}
printf("Listening for events.\\n");
/* Read events from the event queue into a buffer */
len = read(fd, (void *) &events_buf, sizeof(events_buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* Process all events within the buffer */
for (metadata = (struct fanotify_event_metadata *) events_buf;
FAN_EVENT_OK(metadata, len);
metadata = FAN_EVENT_NEXT(metadata, len)) {
fid = (struct fanotify_event_info_fid *) (metadata + 1);
file_handle = (struct file_handle *) fid->handle;
/* Ensure that the event info is of the correct type */
if (fid->hdr.info_type != FAN_EVENT_INFO_TYPE_FID) {
fprintf(stderr, "Received unexpected event info type.\\n");
exit(EXIT_FAILURE);
}
if (metadata->mask == FAN_CREATE)
printf("FAN_CREATE (file created): ");
if (metadata->mask == FAN_CREATE | FAN_ONDIR)
printf("FAN_CREATE | FAN_ONDIR (subdirectory created): ");
/* metadata->fd is set to FAN_NOFD when FAN_REPORT_FID is enabled.
* To obtain a file descriptor for the file object corresponding to
* an event you can use the struct file_handle that's provided
* within the fanotify_event_info_fid in conjunction with the
* open_by_handle_at(2) system call. A check for -ESTALE is done
* to accommodate for the situation where the file handle was
* deleted for the object prior to this system call.
*/
event_fd = open_by_handle_at(AT_FDCWD, file_handle, O_RDONLY);
if (ret == -1 && errno == ESTALE) {
printf("File handle is no longer valid. File has been deleted\\n");
continue;
} else if (ret == -1) {
perror("open_by_handle_at");
exit(EXIT_FAILURE);
}
snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d", event_fd);
/* Retreive and print the path of the modified dentry */
path_len = readlink(procfd_path, path, sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = '\\0';
printf("Directory '%s' has been modified.\\n", path);
/* Close associated file descriptor for this event */
close(event_fd);
}
printf("All events processed successfully. Program exiting.\\n");
exit(EXIT_SUCCESS);
}
.EE
.SH SEE ALSO
.ad l
.BR fanotify_init (2),