fanotify(7) Miscellaneous Information Manual fanotify(7) fanotify - fanotify . . fanotify . . 5.1. ( inotify(7) 5.1.) inotify(7) . : fanotify_init(2) fanotify_mark(2) read(2) write(2) close(2). fanotify_init() fanotify_mark() fanotify_init(2) fanotify . fanotify . fanotify : mark ignore. (mark) . (ignore) . . fanotify_mark(2) ( ) . . . . . . . . fanotify inode (mount ID). . . fanotify . ( read(2) ) fanotify fanotify_init(2). : . : . . . fanotify . fanotify fanotify fanotify. fanotify read(2) fanotify_init(2) ( FAN_NONBLOCK fanotify_init(2)) ( signal(7)). read(2) : struct fanotify_event_metadata { __u32 event_len; __u8 vers; __u8 reserved; __u16 metadata_len; __aligned_u64 mask; __s32 fd; __s32 pid; }; fanotify_event_metadata . flags fanotify_init(2) . FAN_REPORT_FID FAN_REPORT_DIR_FID fanotify_event_info_fid fanotify_event_metadata . FAN_REPORT_* . . FAN_REPORT_TARGET_FID FAN_REPORT_PIDFD fanotify_event_info_fid fanotify_event_info_pidfd . fanotify . fanotify_event_info_header. info_type . fanotify fanotify_event_metadata : struct fanotify_event_info_fid { struct fanotify_event_info_header hdr; __kernel_fsid_t fsid; unsigned char handle[]; }; fanotify FAN_REPORT_PIDFD fanotify_event_metadata : struct fanotify_event_info_pidfd { struct fanotify_event_info_header hdr; __s32 pidfd; }; fanotify FAN_REPORT_MNT fanotify_event_metadata . : struct fanotify_event_info_mnt { struct fanotify_event_info_header hdr; __u64 mnt_id; }; FAN_FS_ERROR fanotify_event_metadata . : struct fanotify_event_info_error { struct fanotify_event_info_header hdr; __s32 error; __u32 error_count; }; FAN_PRE_ACCESS fanotify_event_metadata . : struct fanotify_event_info_range { struct fanotify_event_info_header hdr; __u32 pad; __u64 offset; __u64 count; }; fanotify_event_info_header. fanotify_event_metadata . : struct fanotify_event_info_header { __u8 info_type; __u8 pad; __u16 len; }; ( 4096 ) read(2) . read(2) -1 ( BUGS). fanotify_event_metadata : event_len . event_len FAN_EVENT_METADATA_LEN. event_len . vers . FANOTIFY_METADATA_VERSION . fanotify. reserved . metadata_len . . . mask ( ). fd FAN_NOFD . fanotify FAN_NOFD . . . fanotify_init(2) ( event_f_flags) . FMODE_NONOTIFY ( ) . fanotify. fanotify . fanotify FAN_REPORT_FD_ERROR -EBADF. pid FAN_REPORT_TID fanotify_init(2) (TID) . (PID) . fanotify (PID) getpid(2) . mask . . . mask : FAN_ACCESS () ( BUGS). FAN_OPEN . FAN_OPEN_EXEC . NOTES fanotify_mark(2) . FAN_ATTRIB . FAN_CREATE . FAN_DELETE . FAN_DELETE_SELF . FAN_RENAME . FAN_MOVED_FROM . FAN_MOVED_TO . FAN_MOVE_SELF . FAN_MODIFY . FAN_CLOSE_WRITE (O_WRONLY O_RDWR). FAN_CLOSE_NOWRITE (O_RDONLY). FAN_MNT_ATTACH . FAN_MNT_DETACH . FAN_FS_ERROR . FAN_Q_OVERFLOW . FAN_UNLIMITED_QUEUE fanotify_init(2). FAN_ACCESS_PERM read(2) readdir(2). ( ) . FAN_OPEN_PERM . . FAN_OPEN_EXEC_PERM . . (NOTES) fanotify_mark(2) . : FAN_CLOSE . : FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE : FAN_MOVE . : FAN_MOVED_FROM | FAN_MOVED_TO mask : FAN_ONDIR mask . . fanotify_mark(2) . FAN_ONDIR fanotify . fanotify_event_metadata fanotify_event_info_header. fanotify_event_info_header : info_type . . FAN_EVENT_INFO_TYPE_FID FAN_EVENT_INFO_TYPE_DFID FAN_EVENT_INFO_TYPE_DFID_NAME FAN_EVENT_INFO_TYPE_PIDFD FAN_EVENT_INFO_TYPE_ERROR FAN_EVENT_INFO_TYPE_RANGE FAN_EVENT_INFO_TYPE_MNT fanotify_init(2). info_type. pad . len len fanotify_event_info_header. (event_len - metadata_len). fanotify_event_info_fid : hdr fanotify_event_info_header. fanotify FAN_REPORT_FID info_type FAN_EVENT_INFO_TYPE_FID. fanotify FAN_REPORT_FID FAN_REPORT_DIR_FID : info_type FAN_EVENT_INFO_TYPE_DFID info_type FAN_EVENT_INFO_TYPE_FID . FAN_CREATE FAN_DELETE FAN_MOVE FAN_RENAME // fanotify FAN_REPORT_TARGET_FID. fsid . __kernel_fsid_t f_fsid statfs(2). ( fuse(4)) fsid . fsid fsid fanotify . handle struct file_handle. name_to_handle_at(2). open_by_handle_at(2). info_type FAN_EVENT_INFO_TYPE_DFID_NAME //. FAN_OPEN FAN_ATTRIB FAN_DELETE_SELF FAN_MOVE_SELF info_type FAN_EVENT_INFO_TYPE_FID handle . info_type FAN_EVENT_INFO_TYPE_DFID handle . info_type FAN_EVENT_INFO_TYPE_DFID_NAME handle FAN_EVENT_INFO_TYPE_DFID '.' . fanotify_event_info_pidfd : hdr fanotify_event_info_header. fanotify FAN_REPORT_PIDFD info_type fanotify_event_info_header FAN_EVENT_INFO_TYPE_PIDFD. pidfd . pidfd_open(2) fanotify_event_metadata.pid. pidfd pidfd . FAN_NOPIDFD. pidfd . pidfd FAN_EPIDFD. pidfd pidfd close(2). fanotify FAN_REPORT_FD_ERROR pidfd -ESRCH. fanotify_event_info_mnt : .hdr fanotify_event_info_header. .info_type FAN_EVENT_INFO_TYPE_MNT. .mnt_id . 64- statx(2) STATX_MNT_ID_UNIQUE. fanotify_event_info_error : hdr fanotify_event_info_header. info_type FAN_EVENT_INFO_TYPE_ERROR. error . error_count . fanotify_event_info_range : hdr fanotify_event_info_header. info_type FAN_EVENT_INFO_TYPE_RANGE. count . offset . fanotify read(2) fanotify: FAN_EVENT_OK(meta, size) size meta event_len . FAN_EVENT_NEXT(meta, size) event_len meta meta. size . meta size ( meta->event_len size). : FAN_EVENT_METADATA_LEN () fanotify_event_metadata. ( ) . fanotify fanotify fanotify epoll(7) poll(2) select(2). write(2) fanotify: struct fanotify_response { __s32 fd; __u32 response; }; : fd fanotify_event_metadata. response . FAN_ALLOW FAN_DENY . EPERM. 6.13 FAN_CLASS_PRE_CONTENT EPERM. EIO FAN_DENY_ERRNO(EIO). FAN_DENY_ERRNO(e): EPERM EIO EBUSY ETXTBSY EAGAIN ENOSPC EDQUOT. FAN_ENABLE_AUDIT FAN_AUDIT response. . 6.3 FAN_INFO .response. fanotify_response. : struct fanotify_response_info_header { __u8 type; __u8 pad; __u16 len; }; .type . FAN_RESPONSE_INFO_AUDIT_RULE : struct fanotify_response_info_audit_rule { struct fanotify_response_info_header hdr; __u32 rule_number; __u32 subj_trust; __u32 obj_trust; }; FAN_FS_ERROR . error_count FAN_FS_ERROR . FAN_FS_ERROR errno . ( ) handle . handle_type handle FILEID_INVALID 0. fanotify fanotify fanotify kernel . close(2) . /proc /proc/pid/fdinfo/fd fanotify fd pid. proc(5) . 5.13 ( 5.10.220) kernel fanotify: /proc/sys/fs/fanotify/max_queued_events fanotify_init(2) fanotify . FAN_Q_OVERFLOW. 5.13 16384 . /proc/sys/fs/fanotify/max_user_group fanotify . 5.13 128 . /proc/sys/fs/fanotify/max_user_marks fanotify . 5.13 8192 ( ). read(2) fanotify: EINVAL . EMFILE . RLIMIT_NOFILE getrlimit(2). ENFILE . /proc/sys/fs/file-max proc(5). ETXTBSY read(2) O_RDWR O_WRONLY event_f_flags fanotify_init(2) . write(2) fanotify: EINVAL Fanotify response . ENOENT fd . . . fanotify 2.6.36 2.6.37. fdinfo 3.8. fanotify CONFIG_FANOTIFY. fanotify CONFIG_FANOTIFY_ACCESS_PERMISSIONS. fanotify . . fanotify mmap(2) msync(2) munmap(2). . . fanotify : . FAN_CREATE . . (racy) . . . . . 3.19 fallocate(2) fanotify. 3.19 fallocate(2) FAN_MODIFY. 3.17 : o --bind mount(8). . . o . CAP_SYS_ADMIN . o read(2) fanotify . -1 errno. . fanotify. : fanotify_example.c fanotify . FAN_OPEN_PERM FAN_CLOSE_WRITE. FAN_ALLOW. . /home/user/temp/notes. FAN_OPEN_PERM. FAN_CLOSE_WRITE. ENTER. # ./fanotify_example /home; ENTER . . FAN_OPEN_PERM: File /home/user/temp/notes FAN_CLOSE_WRITE: File /home/user/temp/notes . : fanotify_example.c #define _GNU_SOURCE /* O_LARGEFILE */ #include #include #include #include #include #include #include #include /* fanotify 'fd'. */ static void handle_events(int fd) { const struct fanotify_event_metadata *metadata; struct fanotify_event_metadata buf[200]; ssize_t size; char path[PATH_MAX]; ssize_t path_len; char procfd_path[PATH_MAX]; struct fanotify_response response; /* fanotify. */ for (;;) { /* . */ size = read(fd, buf, sizeof(buf)); if (size == -1 && errno != EAGAIN) { perror("read"); exit(EXIT_FAILURE); } /* . */ if (size <= 0) break; /* . */ metadata = buf; /* . */ while (FAN_EVENT_OK(metadata, size)) { /* . */ if (metadata->vers != FANOTIFY_METADATA_VERSION) { fprintf(stderr, "Mismatch of fanotify metadata version.\n"); exit(EXIT_FAILURE); } /* metadata->fd FAN_NOFD ( ). . */ if (metadata->fd >= 0) { /* . */ if (metadata->mask & FAN_OPEN_PERM) { printf("FAN_OPEN_PERM: "); /* . */ response.fd = metadata->fd; response.response = FAN_ALLOW; write(fd, &response, sizeof(response)); } /* . */ if (metadata->mask & FAN_CLOSE_WRITE) printf("FAN_CLOSE_WRITE: "); /* . */ 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] = '\0'; printf("File %s\n", path); /* . */ close(metadata->fd); } /* . */ metadata = FAN_EVENT_NEXT(metadata, size); } } } int main(int argc, char *argv[]) { char buf; int fd, poll_num; nfds_t nfds; struct pollfd fds[2]; /* . */ if (argc != 2) { fprintf(stderr, "Usage: %s MOUNT\n", argv[0]); exit(EXIT_FAILURE); } printf("Press enter key to terminate.\n"); /* fanotify. */ fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK, O_RDONLY | O_LARGEFILE); if (fd == -1) { perror("fanotify_init"); exit(EXIT_FAILURE); } /* : - - . */ 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); } /* (polling). */ nfds = 2; fds[0].fd = STDIN_FILENO; /* */ fds[0].events = POLLIN; fds[1].fd = fd; /* Fanotify */ fds[1].events = POLLIN; /* . */ printf("Listening for events.\n"); for (;;) { poll_num = poll(fds, nfds, -1); if (poll_num == -1) { if (errno == EINTR) /* */ continue; /* poll() */ perror("poll"); /* */ exit(EXIT_FAILURE); } if (poll_num > 0) { if (fds[0].revents & POLLIN) { /* : stdin . */ while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n') continue; break; } if (fds[1].revents & POLLIN) { /* Fanotify . */ handle_events(fd); } } } printf("Listening for events stopped.\n"); exit(EXIT_SUCCESS); } : fanotify_fid.c fanotify . FAN_CREATE. -- -- . . . /home/user. /home/user/testfile.txt. FAN_CREATE . . # ./fanotify_fid /home/user; . FAN_CREATE ( ): /home/user. 'testfile.txt' . . . $ touch /home/user/testfile.txt; # /home/user. /home/user/testdir. FAN_CREATE FAN_ONDIR . # ./fanotify_fid /home/user; . FAN_CREATE | FAN_ONDIR ( ): /home/user. 'testdir' . . . $ mkdir -p /home/user/testdir; # : fanotify_fid.c #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define BUF_SIZE 256 int main(int argc, char *argv[]) { int fd, ret, event_fd, mount_fd; ssize_t size, 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; const char *file_name; struct stat sb; 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); } /* fanotify FAN_REPORT_DFID_NAME fid . */ fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME, 0); if (fd == -1) { perror("fanotify_init"); exit(EXIT_FAILURE); } /* 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"); /* . */ size = read(fd, events_buf, sizeof(events_buf)); if (size == -1 && errno != EAGAIN) { perror("read"); exit(EXIT_FAILURE); } /* . */ for (metadata = (struct fanotify_event_metadata *) events_buf; FAN_EVENT_OK(metadata, size); metadata = FAN_EVENT_NEXT(metadata, size)) { fid = (struct fanotify_event_info_fid *) (metadata + 1); file_handle = (struct file_handle *) fid->handle; /* . */ if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_FID || fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID) { file_name = NULL; } else if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) { file_name = file_handle->f_handle + file_handle->handle_bytes; } else { 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 FAN_NOFD . struct file_handle fanotify_event_info_fid open_by_handle_at(2). ESTALE . */ 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); /* dentry . */ path_len = readlink(procfd_path, path, sizeof(path) - 1); if (path_len == -1) { perror("readlink"); exit(EXIT_FAILURE); } path[path_len] = '\0'; printf("\tDirectory '%s' has been modified.\n", path); if (file_name) { ret = fstatat(event_fd, file_name, &sb, 0); if (ret == -1) { if (errno != ENOENT) { perror("fstatat"); exit(EXIT_FAILURE); } printf("\tEntry '%s' does not exist.\n", file_name); } else if ((sb.st_mode & S_IFMT) == S_IFDIR) { printf("\tEntry '%s' is a subdirectory.\n", file_name); } else { printf("\tEntry '%s' is not a subdirectory.\n", file_name); } } /* . */ close(event_fd); } printf("All events processed successfully. Program exiting.\n"); exit(EXIT_SUCCESS); } fanotify_init(2) fanotify_mark(2) inotify(7) 3 . . : . 6.18 14 2026 fanotify(7)