static void check_mark(char *file, unsigned long long flag, char *flagstr, int expect, void (*test_event)(char *)) { if (fanotify_mark(fd_notify, FAN_MARK_ADD | flag, FAN_OPEN, AT_FDCWD, file) != expect) { tst_res(TFAIL, "fanotify_mark (%d, FAN_MARK_ADD | %s, FAN_OPEN, AT_FDCWD, " "'%s') %s", fd_notify, flagstr, file, expect_str_fail(expect)); } else { tst_res(TPASS, "fanotify_mark (%d, FAN_MARK_ADD | %s, FAN_OPEN, AT_FDCWD, " "'%s') %s", fd_notify, flagstr, file, expect_str_pass(expect)); /* If we expected failure there's nothing to clean up */ if (expect == -1) return; if (test_event) test_event(file); if (fanotify_mark(fd_notify, FAN_MARK_REMOVE | flag, FAN_OPEN, AT_FDCWD, file) < 0) { tst_brk(TBROK | TERRNO, "fanotify_mark (%d, FAN_MARK_REMOVE | %s, " "FAN_OPEN, AT_FDCWD, '%s') failed", fd_notify, flagstr, file); } } }
int main(int ac, char **av) { int lc; const char *msg; if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { /* Check ONLYDIR on a directory */ CHECK_MARK(".", FAN_MARK_ONLYDIR, 0, NULL); /* Check ONLYDIR without a directory */ CHECK_MARK(fname, FAN_MARK_ONLYDIR, -1, NULL); /* Check DONT_FOLLOW for a symlink */ CHECK_MARK(sname, FAN_MARK_DONT_FOLLOW, 0, test_open_symlink); /* Check without DONT_FOLLOW for a symlink */ CHECK_MARK(sname, 0, 0, test_open_file); /* Verify FAN_MARK_FLUSH destroys all inode marks */ if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, fname) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN, " "AT_FDCWD, '%s') failed", fd_notify, fname); } if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_OPEN | FAN_ONDIR, AT_FDCWD, dir) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN | " "FAN_ONDIR, AT_FDCWD, '%s') failed", fd_notify, dir); } open_file(fname); verify_event(S_IFREG); open_dir(dir); verify_event(S_IFDIR); if (fanotify_mark(fd_notify, FAN_MARK_FLUSH, 0, AT_FDCWD, ".") < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_FLUSH, 0, " "AT_FDCWD, '.') failed", fd_notify); } open_dir(dir); verify_no_event(); } cleanup(); tst_exit(); }
static void setup(void) { tst_sig(NOFORK, DEF_HANDLER, cleanup); TEST_PAUSE; tst_tmpdir(); fd_notify = fanotify_init(FAN_CLASS_NOTIF | FAN_NONBLOCK, O_RDONLY); if (fd_notify < 0) { if (errno == ENOSYS) { tst_brkm(TCONF, cleanup, "fanotify is not configured in this kernel."); } else { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_init failed"); } } if (fanotify_mark(fd_notify, FAN_MARK_MOUNT | FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, ".") < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_MOUNT | FAN_MARK_ADD, " "FAN_OPEN, AT_FDCWD, \".\") failed", fd_notify); } }
static int do_test (void) { int fd, ret; fd = fanotify_init (0, 0); if (fd < 0) { switch (errno) { case ENOSYS: puts ("SKIP: missing support for fanotify (check CONFIG_FANOTIFY=y)"); return 0; case EPERM: puts ("SKIP: missing proper permissions for runtime test"); return 0; } perror ("fanotify_init (0, 0) failed"); return 1; } ret = fanotify_mark (fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_ACCESS | FAN_MODIFY | FAN_OPEN | FAN_CLOSE | FAN_ONDIR | FAN_EVENT_ON_CHILD, AT_FDCWD, "."); if (ret) { perror ("fanotify_mark (...) failed"); return 1; } puts ("All OK"); return 0; }
int main(int argc, char** argv) { int fan_fd, len; char buf[sizeof(struct fanotify_event_metadata)*1024]; struct fanotify_event_metadata *metadata; if (argc != 3) { printf("Usage: %s <root-dir> <procfs>\n", argv[0]); return 1; } procfs = argv[2]; fan_fd = fanotify_init(0, 0); if (fan_fd == -1) { perror("fanotify_init"); return 1; } fanotify_mark(fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_ACCESS, AT_FDCWD, argv[1]); if (fan_fd == -1) { perror("fanotify_mark"); return 1; } while (1) { len = read(fan_fd, buf, sizeof(buf)); metadata = (struct fanotify_event_metadata*)&buf; while (FAN_EVENT_OK(metadata, len)) { print_filename(metadata->fd); close(metadata->fd); metadata = FAN_EVENT_NEXT(metadata, len); } } }
static int onas_ddd_watch_hierarchy(const char* pathname, size_t len, int fd, uint64_t mask, uint32_t type) { if (!pathname || fd <= 0 || !type) return CL_ENULLARG; if (type == (ONAS_IN | ONAS_FAN)) return CL_EARG; struct onas_hnode *hnode = NULL; struct onas_element *elem = NULL; int wd = 0; if(onas_ht_get(ddd_ht, pathname, len, &elem) != CL_SUCCESS) return CL_EARG; hnode = elem->data; if (type & ONAS_IN) { wd = inotify_add_watch(fd, pathname, (uint32_t) mask); if (wd < 0) return CL_EARG; if (wd >= wdlt_len) { onas_ddd_grow_wdlt(); } /* Link the hash node to the watch descriptor lookup table */ hnode->wd = wd; wdlt[wd] = hnode->pathname; hnode->watched |= ONAS_INWATCH; } else if (type & ONAS_FAN) { if(fanotify_mark(fd, FAN_MARK_ADD, mask, AT_FDCWD, hnode->pathname) < 0) return CL_EARG; hnode->watched |= ONAS_FANWATCH; } else { return CL_EARG; } struct onas_lnode *curr = hnode->childhead; while (curr->next != hnode->childtail) { curr = curr->next; size_t size = len + strlen(curr->dirname) + 2; char *child_path = (char *) cli_malloc(size); if (child_path == NULL) return CL_EMEM; if (hnode->pathname[len-1] == '/') snprintf(child_path, --size, "%s%s", hnode->pathname, curr->dirname); else snprintf(child_path, size, "%s/%s", hnode->pathname, curr->dirname); if(onas_ddd_watch_hierarchy(child_path, strlen(child_path), fd, mask, type)) { return CL_EARG; } free(child_path); } return CL_SUCCESS; }
static void create_fanotify_groups(unsigned int ondir) { unsigned int i, onchild; int ret; for (i = 0; i < NUM_GROUPS; i++) { fd_notify[i] = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_NONBLOCK, O_RDONLY); /* Add mount mark for each group without MODIFY event */ onchild = (i == 0) ? FAN_EVENT_ON_CHILD | ondir : 0; ret = fanotify_mark(fd_notify[i], FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_CLOSE_NOWRITE | onchild, AT_FDCWD, "."); if (ret < 0) { tst_brk(TBROK | TERRNO, "fanotify_mark(%d, FAN_MARK_ADD | " "FAN_MARK_MOUNT, FAN_MODIFY%s, AT_FDCWD," " '.') failed", fd_notify[i], ondir ? " | FAN_ONDIR" : ""); } /* * Add inode mark on parent for each group with MODIFY * event, but only one group requests events on child. * The one mark with FAN_EVENT_ON_CHILD is needed for * setting the DCACHE_FSNOTIFY_PARENT_WATCHED dentry * flag. */ ret = fanotify_mark(fd_notify[i], FAN_MARK_ADD, FAN_MODIFY | ondir | onchild, AT_FDCWD, "."); if (ret < 0) { tst_brk(TBROK | TERRNO, "fanotify_mark(%d, FAN_MARK_ADD, " "FAN_MODIFY%s%s, AT_FDCWD, '.') failed", fd_notify[i], ondir ? " | FAN_ONDIR" : "", onchild ? " | FAN_EVENT_ON_CHILD" : ""); } } }
void test01(void) { /* Check ONLYDIR on a directory */ CHECK_MARK(".", FAN_MARK_ONLYDIR, 0, NULL); /* Check ONLYDIR without a directory */ CHECK_MARK(fname, FAN_MARK_ONLYDIR, -1, NULL); /* Check DONT_FOLLOW for a symlink */ CHECK_MARK(sname, FAN_MARK_DONT_FOLLOW, 0, test_open_symlink); /* Check without DONT_FOLLOW for a symlink */ CHECK_MARK(sname, 0, 0, test_open_file); /* Verify FAN_MARK_FLUSH destroys all inode marks */ if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, fname) < 0) { tst_brk(TBROK | TERRNO, "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN, " "AT_FDCWD, '%s') failed", fd_notify, fname); } if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_OPEN | FAN_ONDIR, AT_FDCWD, dir) < 0) { tst_brk(TBROK | TERRNO, "fanotify_mark (%d, FAN_MARK_ADD, FAN_OPEN | " "FAN_ONDIR, AT_FDCWD, '%s') failed", fd_notify, dir); } open_file(fname); verify_event(S_IFREG); open_dir(dir); verify_event(S_IFDIR); if (fanotify_mark(fd_notify, FAN_MARK_FLUSH, 0, AT_FDCWD, ".") < 0) { tst_brk(TBROK | TERRNO, "fanotify_mark (%d, FAN_MARK_FLUSH, 0, " "AT_FDCWD, '.') failed", fd_notify); } open_dir(dir); verify_no_event(); }
void facron_conf_entry_apply (const FacronConfEntry *entry, int fanotify_fd, int flag, bool notice) { if (notice) fprintf (stderr, "Notice: tracking \"%s\"\n", entry->path); for (int i = 0; i < MAX_MASK_LEN && entry->mask[i]; ++i) fanotify_mark (fanotify_fd, flag, entry->mask[i], AT_FDCWD, entry->path); }
static int onas_ddd_unwatch_hierarchy(const char* pathname, size_t len, int fd, uint32_t type) { if (!pathname || fd <= 0 || !type) return CL_ENULLARG; if (type == (ONAS_IN | ONAS_FAN)) return CL_EARG; struct onas_hnode *hnode = NULL; struct onas_element *elem = NULL; int wd = 0; if(onas_ht_get(ddd_ht, pathname, len, &elem)) return CL_EARG; hnode = elem->data; if (type & ONAS_IN) { wd = hnode->wd; if(!inotify_rm_watch(fd, wd)) return CL_EARG; /* Unlink the hash node from the watch descriptor lookup table */ hnode->wd = 0; wdlt[wd] = NULL; hnode->watched = ONAS_STOPWATCH; } else if (type & ONAS_FAN) { if(fanotify_mark(fd, FAN_MARK_REMOVE, 0, AT_FDCWD, hnode->pathname) < 0) return CL_EARG; hnode->watched = ONAS_STOPWATCH; } else { return CL_EARG; } struct onas_lnode *curr = hnode->childhead; while (curr->next != hnode->childtail) { curr = curr->next; size_t size = len + strlen(curr->dirname) + 2; char *child_path = (char *) cli_malloc(size); if (child_path == NULL) return CL_EMEM; if (hnode->pathname[len-1] == '/') snprintf(child_path, --size, "%s%s", hnode->pathname, curr->dirname); else snprintf(child_path, size, "%s/%s", hnode->pathname, curr->dirname); onas_ddd_unwatch_hierarchy(child_path, strlen(child_path), fd, type); free(child_path); } return CL_SUCCESS; }
static void changeMarkMount(int fanotifyfd, const char* mountpoint, const unsigned int flags) { const uint64_t mask = FAN_OPEN_PERM | FAN_CLOSE_WRITE; int dfd = openMountPoint(mountpoint); errno = 0; int ret = fanotify_mark(fanotifyfd, flags, mask, dfd, NULL); int error = errno; close(dfd); if (ret < 0) { PRINT(" error: " << ret<<" errno "<<error<<" strerror "<<strerror(error) << " on "<<fanotifyfd); exit(11); } }
void GonkDiskSpaceWatcher::DoStop() { NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(), "Not on the correct message loop"); if (mFd != -1) { mReadWatcher.StopWatchingFileDescriptor(); fanotify_mark(mFd, FAN_MARK_FLUSH, 0, 0, kWatchedPath); close(mFd); mFd = -1; } // Dispatch the cleanup to the main thread. nsCOMPtr<nsIRunnable> runnable = new DiskSpaceCleaner(); NS_DispatchToMainThread(runnable); }
int main(void) { struct fanotify_event_metadata* event; int file_fd; int fd = fanotify_init(0, O_RDONLY); if (fd == -1 && errno == EPERM) { atomic_puts("fanotify requires CAP_SYS_ADMIN (in the root namespace) but " "we don't have those privileges; skipping tests"); atomic_puts("EXIT-SUCCESS"); return 0; } if (fd == -1 && errno == ENOSYS) { atomic_puts("fanotify is not available in this kernel; skipping tests"); atomic_puts("EXIT-SUCCESS"); return 0; } test_assert(fd >= 0); file_fd = open("foo", O_WRONLY | O_CREAT, 0777); test_assert(file_fd >= 0); test_assert(0 == close(file_fd)); test_assert(0 == fanotify_mark(fd, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, "foo")); file_fd = open("foo", O_WRONLY | O_CREAT, 0777); test_assert(file_fd >= 0); test_assert(0 == close(file_fd)); ALLOCATE_GUARD(event, 'x'); test_assert(sizeof(*event) == read(fd, event, sizeof(*event))); VERIFY_GUARD(event); test_assert(event->event_len == sizeof(*event)); test_assert(event->vers == FANOTIFY_METADATA_VERSION); test_assert(event->mask == FAN_OPEN); test_assert(event->fd >= 0); test_assert(event->pid == getpid()); test_assert(0 == unlink("foo")); atomic_puts("EXIT-SUCCESS"); return 0; }
/** * Inits and starts fanotify notifications * @param opt : a filled options_t * structure that contains all options * by default, read into the file or selected in the command line. */ gint start_fanotify(options_t *opt) { gint fanotify_fd = -1; GSList *head = NULL; /** Leaving only FAN_CLOSE_WRITE for some tests */ /* Setup fanotify notifications (FAN) mask. All these defined in linux/fanotify.h. */ static uint64_t event_mask = (FAN_CLOSE_WRITE | /* Writtable file closed */ FAN_ONDIR | /* We want to be reported of events in the directory */ FAN_EVENT_ON_CHILD); /* We want to be reported of events in files of the directory */ unsigned int mark_flags = FAN_MARK_ADD | FAN_MARK_MOUNT; if (opt != NULL) { /* Create new fanotify device */ if ((fanotify_fd = fanotify_init(FAN_CLOEXEC, O_RDONLY | O_CLOEXEC | O_LARGEFILE)) < 0) { print_error(__FILE__, __LINE__, _("Couldn't setup new fanotify device: %s\n"), strerror(errno)); } else { head = opt->dirname_list; while (head != NULL) { if (fanotify_mark(fanotify_fd, mark_flags, event_mask, AT_FDCWD, head->data) < 0) { print_error(__FILE__, __LINE__, _("Couldn't add monitor in directory %s: %s\n"), head->data , strerror(errno)); } else { print_debug(_("Started monitoring directory %s\n"), head->data); } head = g_slist_next(head); } } } return fanotify_fd; }
int FileNotifyInit(const char *Path, int Flags) { char *Token=NULL, *ptr; int fd; fd=fanotify_init(Flags, O_RDWR); if (fd==-1) return(fd); ptr=GetToken(Path,":",&Token,0); while (ptr) { fanotify_mark(fd, FAN_MARK_ADD, FAN_CLOSE_WRITE, -1, NULL); ptr=GetToken(ptr,":",&Token,0); } DestroyString(Token); return(fd); }
void GonkDiskSpaceWatcher::DoStart() { NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(), "Not on the correct message loop"); mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC | O_LARGEFILE); if (mFd == -1) { if (errno == ENOSYS) { // Don't change these printf_stderr since we need these logs even // in opt builds. printf_stderr("Warning: No fanotify support in this device's kernel.\n"); #if ANDROID_VERSION >= 19 MOZ_CRASH("Fanotify support must be enabled in the kernel."); #endif } else { printf_stderr("Error calling fanotify_init()"); } return; } if (fanotify_mark(mFd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_CLOSE, 0, kWatchedPath) < 0) { NS_WARNING("Error calling fanotify_mark"); close(mFd); mFd = -1; return; } if (!MessageLoopForIO::current()->WatchFileDescriptor( mFd, /* persistent = */ true, MessageLoopForIO::WATCH_READ, &mReadWatcher, gHalDiskSpaceWatcher)) { NS_WARNING("Unable to watch fanotify fd."); close(mFd); mFd = -1; } }
static void setup(void) { int fd; tst_sig(FORK, DEF_HANDLER, cleanup); TEST_PAUSE; tst_tmpdir(); sprintf(fname, "fname_%d", getpid()); fd = SAFE_OPEN(cleanup, fname, O_CREAT | O_RDWR, 0644); SAFE_WRITE(cleanup, 1, fd, fname, 1); SAFE_CLOSE(cleanup, fd); if ((fd_notify = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY)) < 0) { if (errno == ENOSYS) { tst_brkm(TCONF, cleanup, "fanotify is not configured in this kernel."); } else { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_init failed"); } } if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS_PERM | FAN_OPEN_PERM, AT_FDCWD, fname) < 0) { if (errno == EINVAL) { tst_brkm(TCONF | TERRNO, cleanup, "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not " "configured in kernel?"); } else { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM | " "FAN_OPEN_PERM, AT_FDCWD, %s) failed.", fd_notify, fname); } } }
/** * Stops fanotify notifications * @param opt is the options of the program * @param fanotify_fd is the file descriptor of the file which is * concerned by the event. */ void stop_fanotify(options_t *opt, int fanotify_fd) { GSList *head = NULL; /* Setup fanotify notifications (FAN) mask. All these defined in linux/fanotify.h. */ static uint64_t event_mask = (FAN_CLOSE_WRITE | /* Writtable file closed */ FAN_ONDIR | /* We want to be reported of events in the directory */ FAN_EVENT_ON_CHILD); /* We want to be reported of events in files of the directory */ if (opt != NULL) { head = opt->dirname_list; while (head != NULL) { fanotify_mark(fanotify_fd, FAN_MARK_REMOVE, event_mask, AT_FDCWD, head->data); head = g_slist_next(head); } } close(fanotify_fd); }
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 API にアクセスするためのファイルディスクリプターを作成する */ 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); } /* ポーリングの準備(コンソール入力とfanotifyからの入力音2つ */ nfds = 2; /* コンソールの入力 */ fds[0].fd = STDIN_FILENO; fds[0].events = POLLIN; /* fanotify の入力 */ fds[1].fd = fd; fds[1].events = POLLIN; /* イベントの発生を待つループ */ printf("Listening for events.\n"); while (1) { poll_num = poll(fds, nfds, -1); if (poll_num == -1) { if (errno == EINTR) /* シグナルに割り込まれた場合 */ continue; /* poll() を再開する */ perror("poll"); /* 予期しないエラー */ exit(EXIT_FAILURE); } if (poll_num > 0) { // コンソール上から改行入力があればwhileループから抜けて処理を終了する。 if (fds[0].revents & POLLIN) { /* コンソールからの入力がある場合: 空の標準入力であれば終了 */ 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); }
int main(int ac, char **av) { int lc; tst_parse_opts(ac, av, NULL, NULL); setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { int ret, len, i = 0, test_num = 0; tst_count = 0; if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, fname) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | " "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) " "failed", fd_notify, fname); } /* * generate sequence of events */ fd = SAFE_OPEN(cleanup, fname, O_RDONLY); event_set[tst_count] = FAN_OPEN; tst_count++; SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); event_set[tst_count] = FAN_ACCESS; tst_count++; SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_NOWRITE; tst_count++; /* * Get list of events so far. We get events here to avoid * merging of following events with the previous ones. */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf, EVENT_BUF_LEN); len = ret; fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); event_set[tst_count] = FAN_OPEN; tst_count++; SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname)); event_set[tst_count] = FAN_MODIFY; tst_count++; SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_WRITE; tst_count++; /* * get another list of events */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; /* * Ignore mask testing */ /* Ignore access events */ if (fanotify_mark(fd_notify, FAN_MARK_ADD | FAN_MARK_IGNORED_MASK, FAN_ACCESS, AT_FDCWD, fname) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD | " "FAN_MARK_IGNORED_MASK, FAN_ACCESS, " "AT_FDCWD, %s) failed", fd_notify, fname); } fd = SAFE_OPEN(cleanup, fname, O_RDWR); event_set[tst_count] = FAN_OPEN; tst_count++; /* This event should be ignored */ SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); /* * get another list of events to verify the last one got ignored */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; lseek(fd, 0, SEEK_SET); /* Generate modify event to clear ignore mask */ SAFE_WRITE(cleanup, 1, fd, fname, 1); event_set[tst_count] = FAN_MODIFY; tst_count++; /* * This event shouldn't be ignored because previous modification * should have removed the ignore mask */ SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); event_set[tst_count] = FAN_ACCESS; tst_count++; SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_WRITE; tst_count++; /* Read events to verify previous access was properly generated */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; /* * Now ignore open & close events regardless of file * modifications */ if (fanotify_mark(fd_notify, FAN_MARK_ADD | FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY, FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD | " "FAN_MARK_IGNORED_MASK | " "FAN_MARK_IGNORED_SURV_MODIFY, FAN_OPEN | " "FAN_CLOSE, AT_FDCWD, %s) failed", fd_notify, fname); } /* This event should be ignored */ fd = SAFE_OPEN(cleanup, fname, O_RDWR); SAFE_WRITE(cleanup, 1, fd, fname, 1); event_set[tst_count] = FAN_MODIFY; tst_count++; /* This event should be still ignored */ SAFE_CLOSE(cleanup, fd); /* This event should still be ignored */ fd = SAFE_OPEN(cleanup, fname, O_RDWR); /* Read events to verify open & close were ignored */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; /* Now remove open and close from ignored mask */ if (fanotify_mark(fd_notify, FAN_MARK_REMOVE | FAN_MARK_IGNORED_MASK, FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_REMOVE | " "FAN_MARK_IGNORED_MASK, FAN_OPEN | " "FAN_CLOSE, AT_FDCWD, %s) failed", fd_notify, fname); } SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_WRITE; tst_count++; /* Read events to verify close was generated */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; if (TST_TOTAL != tst_count) { tst_brkm(TBROK, cleanup, "TST_TOTAL (%d) and tst_count (%d) are not " "equal", TST_TOTAL, tst_count); } tst_count = 0; /* * check events */ while (i < len) { struct fanotify_event_metadata *event; event = (struct fanotify_event_metadata *)&event_buf[i]; if (test_num >= TST_TOTAL) { tst_resm(TFAIL, "get unnecessary event: mask=%llx " "pid=%u fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, event->fd); } else if (!(event->mask & event_set[test_num])) { tst_resm(TFAIL, "get event: mask=%llx (expected %llx) " "pid=%u fd=%u", (unsigned long long)event->mask, event_set[test_num], (unsigned)event->pid, event->fd); } else if (event->pid != getpid()) { tst_resm(TFAIL, "get event: mask=%llx pid=%u " "(expected %u) fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, (unsigned)getpid(), event->fd); } else { if (event->fd == -2) goto pass; ret = read(event->fd, buf, BUF_SIZE); if (ret != strlen(fname)) { tst_resm(TFAIL, "cannot read from returned fd " "of event: mask=%llx pid=%u " "fd=%u ret=%d (errno=%d)", (unsigned long long)event->mask, (unsigned)event->pid, event->fd, ret, errno); } else if (memcmp(buf, fname, strlen(fname))) { tst_resm(TFAIL, "wrong data read from returned fd " "of event: mask=%llx pid=%u " "fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, event->fd); } else { pass: tst_resm(TPASS, "get event: mask=%llx pid=%u fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, event->fd); } } /* * We have verified the data now so close fd and * invalidate it so that we don't check it again * unnecessarily */ close(event->fd); event->fd = -2; event->mask &= ~event_set[test_num]; /* No events left in current mask? Go for next event */ if (event->mask == 0) { i += event->event_len; } test_num++; } for (; test_num < TST_TOTAL; test_num++) { tst_resm(TFAIL, "didn't get event: mask=%llx", event_set[test_num]); } /* Remove mark to clear FAN_MARK_IGNORED_SURV_MODIFY */ if (fanotify_mark(fd_notify, FAN_MARK_REMOVE, FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, fname) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_REMOVE, FAN_ACCESS | " "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) " "failed", fd_notify, fname); } } cleanup(); tst_exit(); }
void *fan_th(void *arg) { struct thrarg *tharg = (struct thrarg *) arg; sigset_t sigset; struct sigaction act; const struct optstruct *pt; short int scan; int sizelimit = 0, extinfo; STATBUF sb; uint64_t fan_mask = FAN_ACCESS | FAN_EVENT_ON_CHILD; int fan_fd; fd_set rfds; char buf[4096]; ssize_t bread; struct fanotify_event_metadata *fmd; char fname[1024]; int ret, len; char err[128]; /* ignore all signals except SIGUSR1 */ sigfillset(&sigset); sigdelset(&sigset, SIGUSR1); /* The behavior of a process is undefined after it ignores a * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ sigdelset(&sigset, SIGFPE); sigdelset(&sigset, SIGILL); sigdelset(&sigset, SIGSEGV); #ifdef SIGBUS sigdelset(&sigset, SIGBUS); #endif pthread_sigmask(SIG_SETMASK, &sigset, NULL); memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = fan_exit; sigfillset(&(act.sa_mask)); sigaction(SIGUSR1, &act, NULL); sigaction(SIGSEGV, &act, NULL); fan_fd = fanotify_init(0, O_RDONLY); if(fan_fd < 0) { logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err))); if(errno == EPERM) logg("ScanOnAccess: clamd must be started by root\n"); return NULL; } if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) { while(pt) { if(fanotify_mark(fan_fd, FAN_MARK_ADD, fan_mask, fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else { logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n"); return NULL; } if((pt = optget(tharg->opts, "OnAccessExcludePath"))->enabled) { while(pt) { if(fanotify_mark(fan_fd, FAN_MARK_REMOVE, fan_mask, fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't exclude path %s\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Excluded path %s\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg; if(sizelimit) logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit); else logg("ScanOnAccess: File size limit disabled\n"); extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled; FD_ZERO(&rfds); FD_SET(fan_fd, &rfds); do { ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL); } while(ret == -1 && errno == EINTR); while((bread = read(fan_fd, buf, sizeof(buf))) > 0) { fmd = (struct fanotify_event_metadata *) buf; while(FAN_EVENT_OK(fmd, bread)) { scan = 1; if(fmd->fd >= 0) { sprintf(fname, "/proc/self/fd/%d", fmd->fd); len = readlink(fname, fname, sizeof(fname) - 1); if(len == -1) { close(fmd->fd); logg("!ScanOnAccess: Internal error (readlink() failed)\n"); return NULL; } fname[len] = 0; if(fan_checkowner(fmd->pid, tharg->opts)) { scan = 0; logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname); } if(sizelimit) { if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) { scan = 0; /* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */ } } if(fan_scanfile(fan_fd, fname, fmd, scan, extinfo, tharg) == -1) { close(fmd->fd); return NULL; } if(close(fmd->fd) == -1) { printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd); close(fmd->fd); return NULL; } } fmd = FAN_EVENT_NEXT(fmd, bread); } do { ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL); } while(ret == -1 && errno == EINTR); } if(bread < 0) logg("!ScanOnAccess: Internal error (failed to read data)\n"); return NULL; }
void ScannerThread::handleFanotifyEvent() { char buf[(FAN_EVENT_METADATA_LEN / 2) * 3]; ssize_t len; int fanotifyfd = m_fanotifyfd; setState(ScannerThread::BEFORE_READ); len = ::read(fanotifyfd, buf, sizeof(buf)); setState(ScannerThread::AFTER_READ); if (len <= 0) { // nothing actually there - maybe another thread got it if (errno != EINTR && errno != EAGAIN) { PRINT(m_thread<<" no event or error: " << len << " (" << errno <<" "<<strerror(errno)<< ")"); } return; } struct fanotify_event_metadata* metadata = reinterpret_cast<struct fanotify_event_metadata*>(buf); for (; FAN_EVENT_OK(metadata, len); metadata = FAN_EVENT_NEXT(metadata, len)) { if (metadata->vers < 2) { PRINT("fanotify kernel version too old"); throw "FIXME"; // TODO: Throw proper exception } if (metadata->vers != FANOTIFY_METADATA_VERSION) { // TODO? PRINT("fanotify wrong protocol version " << metadata->vers); throw "FIXME2"; // TODO: Throw proper exception } if ((metadata->mask & FAN_ALL_PERM_EVENTS) == 0) { close(metadata->fd); continue; } std::string path = getPath(metadata->fd); //~ { //~ struct stat fstatbuf; //~ int ret = ::fstat(metadata->fd, &fstatbuf); //~ if (ret < 0) //~ { //~ PRINT("Failed to fstat for "<<metadata->pid <<" "<<path); //~ } //~ } { struct fanotify_response response_struct; ssize_t ret; response_struct.fd = metadata->fd; response_struct.response = FAN_ALLOW; PRINT(m_thread<< " Responding to fanotify event for "<<metadata->pid << " "<<path); setState(ScannerThread::BEFORE_WRITE); ret = ::write(fanotifyfd, &response_struct, sizeof(response_struct)); setState(ScannerThread::AFTER_WRITE); if (ret != sizeof(response_struct)) { PRINT(m_thread<<" response error " << ret << " (" << errno <<" "<<strerror(errno)<< ")"); } } { unsigned int flags = FAN_MARK_ADD | FAN_MARK_IGNORED_MASK; int ret = fanotify_mark(fanotifyfd,flags, FAN_OPEN_PERM, metadata->fd, NULL); if (ret < 0) { PRINT(m_thread<<" adding cache mark failed: " << ret); } } if (metadata->fd >= 0) { ::close(metadata->fd); } } }
void *onas_fan_th(void *arg) { struct thrarg *tharg = (struct thrarg *) arg; sigset_t sigset; struct sigaction act; const struct optstruct *pt; short int scan; int sizelimit = 0, extinfo; STATBUF sb; uint64_t fan_mask = FAN_EVENT_ON_CHILD | FAN_CLOSE; fd_set rfds; char buf[4096]; ssize_t bread; struct fanotify_event_metadata *fmd; char fname[1024]; int ret, len; char err[128]; pthread_attr_t ddd_attr; struct ddd_thrarg *ddd_tharg = NULL; ddd_pid = 0; /* ignore all signals except SIGUSR1 */ sigfillset(&sigset); sigdelset(&sigset, SIGUSR1); /* The behavior of a process is undefined after it ignores a * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ sigdelset(&sigset, SIGFPE); sigdelset(&sigset, SIGILL); sigdelset(&sigset, SIGSEGV); #ifdef SIGBUS sigdelset(&sigset, SIGBUS); #endif pthread_sigmask(SIG_SETMASK, &sigset, NULL); memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = onas_fan_exit; sigfillset(&(act.sa_mask)); sigaction(SIGUSR1, &act, NULL); sigaction(SIGSEGV, &act, NULL); /* Initialize fanotify */ onas_fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS, O_LARGEFILE | O_RDONLY); if(onas_fan_fd < 0) { logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err))); if(errno == EPERM) logg("ScanOnAccess: clamd must be started by root\n"); return NULL; } if (!tharg) { logg("!Unable to start on-access scanner. Bad thread args.\n"); return NULL; } if (optget(tharg->opts, "OnAccessPrevention")->enabled && !optget(tharg->opts, "OnAccessMountPath")->enabled) { logg("ScanOnAccess: preventing access attempts on malicious files.\n"); fan_mask |= FAN_ACCESS_PERM | FAN_OPEN_PERM; } else { logg("ScanOnAccess: notifying only for access attempts.\n"); fan_mask |= FAN_ACCESS | FAN_OPEN; } if ((pt = optget(tharg->opts, "OnAccessMountPath"))->enabled) { while(pt) { if(fanotify_mark(onas_fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, fan_mask, onas_fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't include mountpoint '%s'\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Protecting '%s' and rest of mount.\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else if (!optget(tharg->opts, "OnAccessDisableDDD")->enabled) { do { if(pthread_attr_init(&ddd_attr)) break; pthread_attr_setdetachstate(&ddd_attr, PTHREAD_CREATE_JOINABLE); if(!(ddd_tharg = (struct ddd_thrarg *) malloc(sizeof(struct ddd_thrarg)))) break; ddd_tharg->fan_fd = onas_fan_fd; ddd_tharg->fan_mask = fan_mask; ddd_tharg->opts = tharg->opts; ddd_tharg->engine = tharg->engine; ddd_tharg->options = tharg->options; if(!pthread_create(&ddd_pid, &ddd_attr, onas_ddd_th, ddd_tharg)) break; free(ddd_tharg); ddd_tharg=NULL; } while(0); if (!ddd_tharg) logg("!Unable to start dynamic directory determination.\n"); } else { if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) { while(pt) { if(fanotify_mark(onas_fan_fd, FAN_MARK_ADD, fan_mask, onas_fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else { logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n"); return NULL; } } /* Load other options. */ sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg; if(sizelimit) logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit); else logg("ScanOnAccess: File size limit disabled\n"); extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled; FD_ZERO(&rfds); FD_SET(onas_fan_fd, &rfds); do { if (reload) sleep(1); ret = select(onas_fan_fd + 1, &rfds, NULL, NULL, NULL); } while((ret == -1 && errno == EINTR) || reload); time_t start = time(NULL) - 30; while(((bread = read(onas_fan_fd, buf, sizeof(buf))) > 0) || errno == EOVERFLOW) { if (errno == EOVERFLOW) { if (time(NULL) - start >= 30) { logg("!ScanOnAccess: Internal error (failed to read data) ... %s\n", strerror(errno)); logg("!ScanOnAccess: File too large for fanotify ... recovering and continuing scans...\n"); start = time(NULL); } errno = 0; continue; } fmd = (struct fanotify_event_metadata *) buf; while(FAN_EVENT_OK(fmd, bread)) { scan = 1; if(fmd->fd >= 0) { sprintf(fname, "/proc/self/fd/%d", fmd->fd); len = readlink(fname, fname, sizeof(fname) - 1); if(len == -1) { close(fmd->fd); logg("!ScanOnAccess: Internal error (readlink() failed)\n"); return NULL; } fname[len] = 0; if(onas_fan_checkowner(fmd->pid, tharg->opts)) { scan = 0; logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname); } if(sizelimit) { if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) { scan = 0; /* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */ } } if(onas_fan_scanfile(onas_fan_fd, fname, fmd, scan, extinfo, tharg) == -1) { close(fmd->fd); return NULL; } if(close(fmd->fd) == -1) { printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd); close(fmd->fd); return NULL; } } fmd = FAN_EVENT_NEXT(fmd, bread); } do { if (reload) sleep(1); ret = select(onas_fan_fd + 1, &rfds, NULL, NULL, NULL); } while((ret == -1 && errno == EINTR) || reload); } if(bread < 0) logg("!ScanOnAccess: Internal error (failed to read data) ... %s\n", strerror(errno)); return NULL; }
int main() { fd = creat(fname, S_IREAD|S_IWRITE); close(fd); /* Note that the fanotify calls require root access or the * CAP_SYS_ADMIN capability. */ fd_notify = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY); //staptest// fanotify_init (FAN_CLASS_NOTIF, O_RDONLY) = NNNN fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS|FAN_MODIFY|FAN_CLOSE|FAN_OPEN, AT_FDCWD, fname); //staptest// fanotify_mark (NNNN, FAN_MARK_ADD, FAN_ACCESS|FAN_MODIFY|FAN_CLOSE_WRITE|FAN_CLOSE_NOWRITE|FAN_OPEN, AT_FDCWD, "testfile") = NNNN // Now, modify the test file. fd = open(fname, O_WRONLY); //staptest// [[[[open (!!!!openat (AT_FDCWD, ]]]]"testfile", O_WRONLY) = NNNN write(fd, fname, strlen(fname) + 1); //staptest// write (NNNN, "testfile", NNNN) = NNNN close(fd); //staptest// close (NNNN) = 0 // Read list of events. read(fd_notify, event_buf, EVENT_BUF_LEN); //staptest// read (NNNN, XXXX, NNNN) = NNNN // A real program would process the list of events. We're not // going to bother. close(fd_notify); //staptest// close (NNNN) = NNNN /* Limit testing. */ fanotify_init(-1, O_RDONLY); //staptest// fanotify_init (XXXX|FAN_[^ ]+|XXXX, O_RDONLY) = -NNNN // Here's we're passing an invalid flags value (we hope) to make // sure this fails. fanotify_init(0x80000000, -1); //staptest// fanotify_init (FAN_CLASS_NOTIF|0x80000000, O_[^ ]+|XXXX) = -NNNN fanotify_mark(-1, FAN_MARK_REMOVE, FAN_ACCESS, AT_FDCWD, fname); //staptest// fanotify_mark (-1, FAN_MARK_REMOVE, FAN_ACCESS, AT_FDCWD, "testfile") = -NNNN fanotify_mark(-1, -1, FAN_MODIFY, AT_FDCWD, fname); //staptest// fanotify_mark (-1, FAN_[^ ]+|XXXX, FAN_MODIFY, AT_FDCWD, "testfile") = -NNNN fanotify_mark(-1, FAN_MARK_FLUSH, -1, AT_FDCWD, fname); //staptest// fanotify_mark (-1, FAN_MARK_FLUSH, FAN_[^ ]+|XXXX, AT_FDCWD, "testfile") = -NNNN fanotify_mark(-1, FAN_MARK_ADD|FAN_MARK_DONT_FOLLOW, FAN_CLOSE_WRITE, -1LL, fname); //staptest// fanotify_mark (-1, FAN_MARK_ADD|FAN_MARK_DONT_FOLLOW, FAN_CLOSE_WRITE, -1, "testfile") = -NNNN fanotify_mark(-1, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_CLOSE_NOWRITE, AT_FDCWD, (char *)-1); #ifdef __s390__ //staptest// fanotify_mark (-1, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_CLOSE_NOWRITE, AT_FDCWD, [7]?[f]+) = -NNNN #else //staptest// fanotify_mark (-1, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_CLOSE_NOWRITE, AT_FDCWD, [f]+) = -NNNN #endif return 0; }
int main(int ac, char **av) { int lc; tst_parse_opts(ac, av, NULL, NULL); setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { int ret, len, i = 0, test_num = 0; tst_count = 0; if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN | FAN_EVENT_ON_CHILD | FAN_ONDIR, AT_FDCWD, ".") < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | " "FAN_MODIFY | FAN_CLOSE | FAN_OPEN | " "FAN_EVENT_ON_CHILD | FAN_ONDIR, AT_FDCWD, '.') " "failed", fd_notify); } /* * generate sequence of events */ fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); event_set[tst_count] = FAN_OPEN; tst_count++; SAFE_WRITE(cleanup, 1, fd, fname, strlen(fname)); event_set[tst_count] = FAN_MODIFY; tst_count++; SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_WRITE; tst_count++; /* * Get list of events so far. We get events here to avoid * merging of following events with the previous ones. */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf, EVENT_BUF_LEN); len = ret; fd = SAFE_OPEN(cleanup, fname, O_RDONLY); event_set[tst_count] = FAN_OPEN; tst_count++; SAFE_READ(cleanup, 0, fd, buf, BUF_SIZE); event_set[tst_count] = FAN_ACCESS; tst_count++; SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_NOWRITE; tst_count++; /* * get next events */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; /* * now remove child mark */ if (fanotify_mark(fd_notify, FAN_MARK_REMOVE, FAN_EVENT_ON_CHILD, AT_FDCWD, ".") < 0) { tst_brkm(TBROK | TERRNO, cleanup, "fanotify_mark (%d, FAN_MARK REMOVE, " "FAN_EVENT_ON_CHILD, AT_FDCWD, '.') failed", fd_notify); } /* * Do something to verify events didn't get generated */ fd = SAFE_OPEN(cleanup, fname, O_RDONLY); SAFE_CLOSE(cleanup, fd); fd = SAFE_OPEN(cleanup, ".", O_RDONLY | O_DIRECTORY); event_set[tst_count] = FAN_OPEN; tst_count++; SAFE_CLOSE(cleanup, fd); event_set[tst_count] = FAN_CLOSE_NOWRITE; tst_count++; /* * Check events got generated only for the directory */ ret = SAFE_READ(cleanup, 0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); len += ret; if (TST_TOTAL != tst_count) { tst_brkm(TBROK, cleanup, "TST_TOTAL and tst_count are not equal"); } tst_count = 0; /* * check events */ while (i < len) { struct fanotify_event_metadata *event; event = (struct fanotify_event_metadata *)&event_buf[i]; if (test_num >= TST_TOTAL) { tst_resm(TFAIL, "get unnecessary event: mask=%llx " "pid=%u fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, event->fd); } else if (!(event->mask & event_set[test_num])) { tst_resm(TFAIL, "get event: mask=%llx (expected %llx) " "pid=%u fd=%u", (unsigned long long)event->mask, event_set[test_num], (unsigned)event->pid, event->fd); } else if (event->pid != getpid()) { tst_resm(TFAIL, "get event: mask=%llx pid=%u " "(expected %u) fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, (unsigned)getpid(), event->fd); } else { tst_resm(TPASS, "get event: mask=%llx pid=%u fd=%u", (unsigned long long)event->mask, (unsigned)event->pid, event->fd); } event->mask &= ~event_set[test_num]; /* No events left in current mask? Go for next event */ if (event->mask == 0) { i += event->event_len; close(event->fd); } test_num++; } for (; test_num < TST_TOTAL; test_num++) { tst_resm(TFAIL, "didn't get event: mask=%llx", event_set[test_num]); } } cleanup(); tst_exit(); }
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 != '\n') continue; break; } if (fds[1].revents & POLLIN) { /* Fanotify events are available */ handle_events(fd); } } } printf("Listening for events stopped.\n"); exit(EXIT_SUCCESS); }