FANOTIFY - Linux手册页

时间:2019-08-20 18:01:53  来源:igfitidea点击:

Linux程序员手册 第7部分
更新日期: 2020-06-09

名称

fanotify-监视文件系统事件

说明

fanotify API提供文件系统事件的通知和拦截。用例包括病毒扫描和分层存储管理。当前,仅支持有限的事件集。特别是,不支持创建,删除和移动事件。 (有关通知那些事件的API的详细信息,请参见inotify(7)。)

inotify(7)API相比,其他功能包括监视已挂载文件系统中所有对象的能力,做出访问许可决策的能力以及在被其他应用程序访问之前读取或修改文件的可能性。

此API使用以下系统调用:fanotify_init(2),fanotify_mark(2),read(2),write(2)和close(2)。

fanotify_init(), fanotify_mark(), and notification groups

fanotify_init(2)系统调用创建并初始化fanotify通知组,并返回引用该组的文件描述符。

fanotify通知组是内核内部的对象,其中包含应为其创建事件的文件,目录,文件系统和安装点的列表。

对于fanotify通知组中的每个条目,都有两个位掩码:标记掩码和忽略掩码。标记掩码定义了应为其创建事件的文件活动。忽略掩码定义不为其生成任何事件的活动。具有这两种类型的掩码允许标记文件系统,安装点或目录以接收事件,同时忽略安装点或目录下特定对象的事件。

fanotify_mark(2)系统调用将文件,目录,文件系统或安装点添加到通知组,并指定应报告(或忽略)哪些事件,或删除或修改此类条目。

忽略掩码的可能用法是用于文件缓存。文件缓存感兴趣的事件是文件的修改和关闭。因此,缓存的目录或挂载点将被标记为接收这些事件。收到第一个通知已修改文件的事件后,相应的缓存条目将失效。在关闭文件之前,不会对此文件进行进一步的修改事件。因此,可以将Modify事件添加到忽略掩码。收到关闭事件后,可以从忽略掩码中删除Modify事件,并且可以更新文件缓存项。

fanotify通知组中的条目通过其inode编号引用文件和目录,并通过其安装ID引用安装。如果文件或目录在同一装载中被重命名或移动,则相应的条目将保留。如果文件或目录被删除或移动到另一个挂载,或者文件系统或挂载被卸载,则相应的条目将被删除。

The event queue

当事件在通知组监视的文件系统对象上发生时,fanotify系统会生成在队列中收集的事件。然后可以从fanotify_init(2)返回的fanotify文件描述符中读取(使用read(2)或类似的事件)。

生成两种类型的事件:通知事件和权限事件。通知事件仅是提供信息的消息,不需要接收应用程序采取任何措施,只是必须关闭通用事件中提供的文件描述符。每个事件的文件描述符关闭仅适用于已初始化fanotify且未使用FAN_REPORT_FID的应用程序(请参见下文)。权限事件是对接收应用程序的请求,以决定是否应授予文件访问权限。对于这些事件,接收者必须写一个响应来决定是否授予访问权限。

读取事件后,将从fanotify组的事件队列中删除事件。已读取的权限事件将保留在fanotify组的内部列表中,直到通过写入fanotify文件描述符做出权限决定或关闭fanotify文件描述符为止。

Reading fanotify events

调用read(2)来获取fanotify_init(2)块返回的文件描述符(如果在对fanotify_init(2)的调用中未指定标志FAN_NONBLOCK),直到发生文件事件或调用被信号中断(请参见信号) (7))。

fanotify_init(2)中使用FAN_REPORT_FID标志会影响每个事件返回到事件侦听器的数据结构。成功执行read(2)之后,读取缓冲区包含以下一种或多种结构:

struct fanotify_event_metadata {
    __u32 event_len;
    __u8 vers;
    __u8 reserved;
    __u16 metadata_len;
    __aligned_u64 mask;
    __s32 fd;
    __s32 pid;
};

在将FAN_REPORT_FID作为标志之一提供给fanotify_init(2)的情况下,您还应该期望在读取缓冲区内的常规fanotify_event_metadata结构之后收到下面详述的结构:

struct fanotify_event_info_fid {
    struct fanotify_event_info_header hdr;
    __kernel_fsid_t fsid;
    unsigned char file_handle[0];
};

出于性能原因,建议使用较大的缓冲区大小(例如4096字节),以便可以通过单个read(2)检索多个事件。

read(2)的返回值是放置在缓冲区中的字节数,如果发生错误则返回-1(但请参阅BUGS)。

fanotify_event_metadata结构的字段如下:

event_len
这是当前事件的数据长度,也是缓冲区中下一个事件的偏移量。如果没有FAN_REPORT_FID,则event_len的值始终为FAN_EVENT_METADATA_LEN。对于FAN_REPORT_FID,event_len还包括可变长度文件标识符。
vers
该字段保存该结构的版本号。必须将其与FANOTIFY_METADATA_VERSION进行比较,以验证在运行时返回的结构与在编译时定义的结构是否匹配。如果不匹配,则应用程序应放弃尝试使用fanotify文件描述符。
reserved
未使用此字段。
metadata_len
这是结构的长度。引入该字段是为了促进每种事件类型的可选标头的实现。当前实现中不存在此类可选标头。
mask
这是一个描述事件的位掩码(请参见下文)。
fd
这是被访问对象的打开文件描述符,如果发生队列溢出,则为FAN_NOFD。如果fanotify文件描述符已使用FAN_REPORT_FID进行了初始化,则应用程序应期望对于收到的每个事件将此值设置为FAN_NOFD。文件描述符可用于访问受监视文件或目录的内容。阅读应用程序负责关闭此文件描述符。
当调用fanotify_init(2)时,调用者可以(通过event_f_flags参数)指定各种文件状态标记,这些标记将在与该文件描述符相对应的打开文件描述中设置。此外,在打开的文件描述中设置了(内核内部)FMODE_NONOTIFY文件状态标志。该标志禁止产生幻想事件。因此,当fanotify事件的接收者使用此文件描述符访问通知的文件或目录时,将不会创建其他事件。
pid
如果在fanotify_init(2)中设置了标志FAN_REPORT_TID,则这是引起事件的线程的TID。否则,这是导致事件的进程的PID。

侦听fanotify事件的程序可以将此PID与getpid(2)返回的PID进行比较,以确定该事件是由侦听器本身引起的,还是由另一个进程的文件访问引起的。

mask中的位掩码指示单个文件系统对象发生了哪些事件。如果被监视的文件系统对象发生多个事件,则可以在此掩码中设置多个位。特别地,可以将用于同一文件系统对象且源自同一进程的连续事件合并为一个事件,但两个许可事件从不合并为一个队列条目。

掩码中可能出现的位如下:

FAN_ACCESS
已访问(读取)文件或目录(但请参见BUGS)。
FAN_OPEN
文件或目录已打开。
FAN_OPEN_EXEC
已打开文件,意在执行该文件。有关其他详细信息,请参见fanotify_mark(2)中的NOTES。
FAN_ATTRIB
文件或目录元数据已更改。
FAN_CREATE
子文件或目录是在受监视的父文件中创建的。
FAN_DELETE
子文件或目录已在受监视的父文件中删除。
FAN_DELETE_SELF
监视的文件或目录已删除。
FAN_MOVED_FROM
文件或目录已从受监视的父目录中移出。
FAN_MOVED_TO
文件或目录已移动到监视的父目录。
FAN_MOVE_SELF
监视的文件或目录已移动。
FAN_MODIFY
文件已被修改。
FAN_CLOSE_WRITE
已打开用于写入的文件(O_WRONLY或O_RDWR)已关闭。
FAN_CLOSE_NOWRITE
已关闭以只读(O_RDONLY)打开的文件或目录。
FAN_Q_OVERFLOW
事件队列超过了16384个条目的限制。在调用fanotify_init(2)时,可以通过指定FAN_UNLIMITED_QUEUE标志来覆盖此限制。
FAN_ACCESS_PERM
应用程序想要读取文件或目录,例如使用read(2)或readdir(2)。读者必须编写一个响应(如下所述),以确定是否应授予访问文件系统对象的权限。
FAN_OPEN_PERM
应用程序要打开文件或目录。读者必须编写一个响应,以确定是否应授予打开文件系统对象的权限。
FAN_OPEN_EXEC_PERM
应用程序要打开一个文件以执行。读者必须编写一个响应,该响应确定是否应授予打开文件系统对象以执行的权限。有关其他详细信息,请参见fanotify_mark(2)中的NOTES。

要检查任何关闭事件,可以使用以下位掩码:

FAN_CLOSE
文件已关闭。这是以下内容的同义词:
FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE

要检查任何移动事件,可以使用以下位掩码:

FAN_MOVE
文件或目录已移动。这是以下内容的同义词:
FAN_MOVED_FROM FAN_MOVED_TO

以下位只能与其他事件类型位一起出现在掩码中:

FAN_ONDIR
掩码中描述的事件已发生在目录对象上。报告目录事件需要在标记掩码中设置此标志。有关更多详细信息,请参见fanotify_mark(2)。仅当fanotify组已用FAN_REPORT_FID标志初始化时,才会在事件掩码中报告FAN_ONDIR标志。

fanotify_event_info_fid结构的字段如下:

hdr
这是fanotify_event_info_header类型的结构。它是一个通用标头,其中包含用于描述附加到事件的其他信息的信息。例如,当使用FAN_REPORT_FID创建fanotify文件描述符时,此标头的info_type字段设置为FAN_EVENT_INFO_TYPE_FID。事件侦听器可以使用此字段来检查为事件接收的其他信息的类型正确。此外,fanotify_event_info_header还包含一个len字段。在当前实现中,len的值始终为(event_len-FAN_EVENT_METADATA_LEN)。
fsid
这是包含与事件关联的对象的文件系统的唯一标识符。它是__kernel_fsid_t类型的结构,在调用statfs(2)时包含与f_fsid相同的值。
file_handle
这是file_handle类型的可变长度结构。它是一个不透明的句柄,它对应于name_to_handle_at(2)返回的文件系统上的指定对象。它可以用来唯一地标识文件系统上的文件,并且可以作为参数传递给open_by_handle_at(2)。请注意,对于诸如FAN_CREATE,FAN_DELETE和FAN_MOVE之类的目录条目事件,file_handle描述的是修改后的目录,而不是创建/删除/移动的子对象。如果正在监视子对象,则事件FAN_ATTRIB,FAN_DELETE_SELF和FAN_MOVE_SELF将携带该子对象的file_handle信息。

提供以下宏来循环访问缓冲区,该缓冲区包含从fanotify文件描述符中的read(2)返回的fanotify事件元数据:

FAN_EVENT_OK(meta, len)
该宏将元数据结构的长度和缓冲区中第一元数据结构的event_len字段与缓冲区元数据的剩余长度len进行检查。
FAN_EVENT_NEXT(meta, len)
该宏使用meta指向的元数据结构的event_len字段中指示的长度来计算meta之后的下一个元数据结构的地址。 len是当前保留在缓冲区中的元数据的字节数。巨集会传回指向meta后面的下一个中继资料结构的指标,并以len减去len所跳过的中继资料结构中的位元组数目(即从len减去meta->event_len)。

另外,还有:

FAN_EVENT_METADATA_LEN
该宏返回fanotify_event_metadata结构的大小(以字节为单位)。这是任何事件元数据的最小大小(当前是唯一的大小)。

Monitoring an fanotify file descriptor for events

当发生fanotify事件时,fanotify文件描述符在传递给epoll(7),poll(2)或select(2)时指示为可读。

Dealing with permission events

对于权限事件,应用程序必须将以下形式的结构写入(2)到fanotify文件描述符中:

struct fanotify_response {
    __s32 fd;
    __u32 response;
};

此结构的字段如下:

fd
这是来自fanotify_event_metadata结构的文件描述符。
response
该字段指示是否要授予许可。它的值必须是FAN_ALLOW以允许文件操作,或者是FAN_DENY拒绝文件操作。

如果访问被拒绝,则请求的应用程序调用将收到EPERM错误。

Closing the fanotify file descriptor

当所有引用fanotify通知组的文件描述符都关闭时,将释放fanotify组,并释放其资源以供内核重用。在close(2)时,未决权限事件将设置为允许。

/proc/[pid]/fdinfo

文件/ proc / [pid] / fdinfo / [fd]包含有关进程pid的文件描述符fd的有效标记的信息。有关详细信息,请参见proc(5)。

错误说明

除了read(2)的常见错误外,从fanotify文件描述符读取时还可能发生以下错误:

EINVAL
缓冲区太小,无法容纳事件。
EMFILE
已达到打开文件数的每个进程限制。请参阅getrlimit(2)中对RLIMIT_NOFILE的描述。
ENFILE
已达到系统范围内打开文件总数的限制。请参阅proc(5)中的/ proc / sys / fs / file-max。
ETXTBSY
如果在调用fanotify_init(2)时在event_f_flags参数中指定了O_RDWR或O_WRONLY,并且由当前正在执行的受监视文件发生了事件,则read(2)将返回此错误。

除了write(2)的常见错误外,在写入fanotify文件描述符时还会发生以下错误:

EINVAL
内核配置中未启用fanotify访问权限,或者响应结构中的response值无效。
ENOENT
响应结构中的文件描述符fd无效。当已经写入对权限事件的响应时,可能会发生这种情况。

版本

fanotify API在Linux内核的2.6.36版本中引入,并在2.6.37版本中启用。在3.8版中增加了对Fdinfo的支持。

遵循规范

fanotify API是特定于Linux的。

备注

仅当内核是在启用CONFIG_FANOTIFY配置选项的情况下构建的,fanotify API才可用。此外,仅在启用CONFIG_FANOTIFY_ACCESS_PERMISSIONS配置选项时,fanotify权限处理才可用。

Limitations and caveats

Fanotify仅报告用户空间程序通过文件系统API触发的事件。结果,它不会捕获网络文件系统上发生的远程事件。

fanotify API不报告由于mmap(2),msync(2)和munmap(2)可能发生的文件访问和修改。

仅在打开,读取和关闭目录本身时,才会创建目录事件。添加,删除或更改已标记目录的子级不会为受监视目录本身创建事件。

Fanotify监视目录不是递归的:要监视目录下的子目录,必须创建其他标记。 (但是请注意,fanotify API无法提供检测标记目录下何时创建子目录的方法,这使得递归监视变得很困难。)监视装载提供了监视整个目录树的功能。监视文件系统提供了监视文件系统实例的任何装入所做的更改的功能。

事件队列可能溢出。在这种情况下,事件将丢失。

BUGS

在Linux 3.19之前,fallocate(2)不会生成fanotify事件。从Linux 3.19开始,调用fallocate(2)会生成FAN_MODIFY事件。

从Linux 3.17开始,存在以下错误:

*
在Linux上,可以通过多个路径访问文件系统对象,例如,可以使用mount(8)的--bind选项重新安装文件系统的一部分。标记为挂载的侦听器仅会通知使用同一挂载为文件系统对象触发的事件。任何其他事件都将被忽略。
*
生成事件时,在传递该文件的文件描述符之前,不会检查接收进程的用户ID是否具有读取或写入文件的权限。当为非特权用户执行的程序设置CAP_SYS_ADMIN功能时,会带来安全风险。
*
如果对read(2)的调用处理了来自fanotify队列的多个事件并且发生错误,则返回值将是错误发生之前成功复制到用户空间缓冲区的事件的总长度。返回值将不会是-1,并且不会设置errno。因此,读取应用程序无法检测到错误。

示例

下面的两个示例程序演示了fanotify API的用法。

Example program: fanotify_example.c

第一个程序是fanotify的一个示例,它的事件对象信息以文件描述符的形式传递。程序将挂载点标记为命令行参数,并等待FAN_OPEN_PERM和FAN_CLOSE_WRITE类型的事件。发生权限事件时,将给出FAN_ALLOW响应。

以下Shell会话显示了运行该程序的示例。该会话涉及编辑文件/ home / user / temp / notes。在打开文件之前,发生了FAN_OPEN_PERM事件。关闭文件后,发生FAN_CLOSE_WRITE事件。当用户按下ENTER键时,程序的执行结束。

# ./fanotify_example /home
Press enter key to terminate.
Listening for events.
FAN_OPEN_PERM: File /home/user/temp/notes
FAN_CLOSE_WRITE: File /home/user/temp/notes

Listening for events stopped.

Program source: fanotify_example.c

#define _GNU_SOURCE     /* Needed to get O_LARGEFILE 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 aqfdaq */

static void
handle_events(int fd)
{
    const struct fanotify_event_metadata *metadata;
    struct fanotify_event_metadata buf[200];
    ssize_t len;
    char path[PATH_MAX];
    ssize_t path_len;
    char procfd_path[PATH_MAX];
    struct fanotify_response response;

    /* Loop while events can be read from fanotify file descriptor */

    for (;;) {

        /* Read some events */

        len = read(fd, (void *) &buf, sizeof(buf));
        if (len == -1 && errno != EAGAIN) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        /* 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 (FAN_EVENT_OK(metadata, len)) {

            /* Check that run-time and compile-time structures match */

            if (metadata->vers != FANOTIFY_METADATA_VERSION) {
                fprintf(stderr,
                        "Mismatch of fanotify metadata version.\n");
                exit(EXIT_FAILURE);
            }

            /* metadata->fd contains either FAN_NOFD, 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 & FAN_OPEN_PERM) {
                    printf("FAN_OPEN_PERM: ");

                    /* Allow file to be opened */

                    response.fd = metadata->fd;
                    response.response = FAN_ALLOW;
                    write(fd, &response,
                          sizeof(struct fanotify_response));
                }

                /* Handle closing of writable file event */

                if (metadata->mask & FAN_CLOSE_WRITE)
                    printf("FAN_CLOSE_WRITE: ");

                /* Retrieve and print pathname of the accessed file */

                snprintf(procfd_path, sizeof(procfd_path),
                         "/proc/self/fd/%d", metadata->fd);
                path_len = readlink(procfd_path, path,
                                    sizeof(path) - 1);
                if (path_len == -1) {
                    perror("readlink");
                    exit(EXIT_FAILURE);
                }

                path[path_len] = aq
# ./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/testfile.txt              # In another terminal
aq; printf("File %s\n", path); /* Close the file descriptor of the event */ close(metadata->fd); } /* Advance to next event */ metadata = FAN_EVENT_NEXT(metadata, len); } } } int main(int argc, char *argv[]) { char buf; int fd, poll_num; nfds_t nfds; struct pollfd fds[2]; /* Check mount point is supplied */ if (argc != 2) { fprintf(stderr, "Usage: %s MOUNT\n", argv[0]); exit(EXIT_FAILURE); } printf("Press enter key to terminate.\n"); /* Create the file descriptor for accessing the fanotify API */ fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK, O_RDONLY | O_LARGEFILE); if (fd == -1) { perror("fanotify_init"); exit(EXIT_FAILURE); } /* Mark the mount for: - permission events before opening files - notification events after closing a write-enabled file descriptor */ if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD, argv[1]) == -1) { perror("fanotify_mark"); exit(EXIT_FAILURE); } /* Prepare for polling */ nfds = 2; /* Console input */ fds[0].fd = STDIN_FILENO; 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) { poll_num = poll(fds, nfds, -1); if (poll_num == -1) { if (errno == EINTR) /* Interrupted by a signal */ continue; /* Restart poll() */ perror("poll"); /* Unexpected error */ exit(EXIT_FAILURE); } if (poll_num > 0) { if (fds[0].revents & POLLIN) { /* Console input is available: empty stdin and quit */ while (read(STDIN_FILENO, &buf, 1) > 0 && buf != aq\naq) continue; break; } if (fds[1].revents & POLLIN) { /* Fanotify events are available */ handle_events(fd); } } } printf("Listening for events stopped.\n"); exit(EXIT_SUCCESS); }

Example program: fanotify_fid.c

第二个程序是在启用FAN_REPORT_FID的情况下使用fanotify的示例。该程序标记作为命令行参数传递的文件系统对象,并等待直到发生FAN_CREATE类型的事件。事件掩码指示创建了哪种类型的文件系统对象(文件或目录)。一旦从缓冲区中读取了所有事件并进行了相应的处理,程序便会终止。

以下shell会话显示了此程序的两种不同调用,并对被监视的对象执行了不同的操作。

第一个会话显示在/ home / user上放置的标记。接下来是创建常规文件/home/user/testfile.txt。这将导致生成FAN_CREATE事件,并针对该文件的父监视目录对象进行报告。处理完缓冲区中捕获的所有事件后,程序执行结束。

# ./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/testdir          # In another terminal

第二个会话显示在/ home / user上放置的标记。接下来是创建目录/ home / user / testdir。此特定操作导致生成FAN_CREATE事件,并在设置FAN_ONDIR标志的情况下进行报告。

#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, mount_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);
    }

    mount_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
    if (mount_fd == -1) {
        perror(argv[1]);
        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):\n");

        if (metadata->mask == (FAN_CREATE | FAN_ONDIR))
            printf("FAN_CREATE | FAN_ONDIR (subdirectory created):\n");

        /* 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
           thataqs 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 for the object was deleted prior to
           this system call. */

        event_fd = open_by_handle_at(mount_fd, file_handle, O_RDONLY);
        if (event_fd == -1) {
            if (errno == ESTALE) {
                printf("File handle is no longer valid. "
                        "File has been deleted\n");
                continue;
            } else {
                perror("open_by_handle_at");
                exit(EXIT_FAILURE);
            }
        }

        snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
                event_fd);

        /* Retrieve 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] = aq##代码##aq;
        printf("\tDirectory aq%saq 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);
}

Program source: fanotify_fid.c

##代码##

另外参见

fanotify_init(2),fanotify_mark(2),inotify(7)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/