[LTP] [PATCH 2/3] fanotify10: Add support for multiple event files

Jan Kara
2022-11-15T12:48:17Z

Add support for marking and generating events on multiple files. We'll
use this for more reliable checking of evictable marks.

Signed-off-by: Jan Kara <jack@suse.cz>
---
.../kernel/syscalls/fanotify/fanotify10.c | 417 ++++++++++--------
1 file changed, 245 insertions(+), 172 deletions(-)

diff --git a/testcases/kernel/syscalls/fanotify/fanotify10.c b/testcases/kernel/syscalls/fanotify/fanotify10.c
index 6f21094bed2c..e19bd907470f 100644
--- a/testcases/kernel/syscalls/fanotify/fanotify10.c
+++ b/testcases/kernel/syscalls/fanotify/fanotify10.c
@@ -43,6 +43,7 @@
#include <sys/mount.h>
#include <sys/syscall.h>
#include "tst_test.h"
+#include "tst_safe_stdio.h"

#ifdef HAVE_SYS_FANOTIFY_H
#include "fanotify.h"
@@ -69,6 +70,7 @@ static int fd_notify[NUM_CLASSES][GROUPS_PER_PRIO];
static int fd_syncfs;

static char event_buf[EVENT_BUF_LEN];
+static int event_buf_pos, event_buf_len;
static int exec_events_unsupported;
static int fan_report_dfid_unsupported;
static int filesystem_mark_unsupported;
@@ -123,346 +125,377 @@ static struct fanotify_mark_type fanotify_mark_types[] = {

static struct tcase {
const char *tname;
- const char *mark_path;
+ int mark_path_cnt;
+ const char *mark_path_fmt;
int mark_type;
- const char *ignore_path;
+ int ignore_path_cnt;
+ const char *ignore_path_fmt;
int ignore_mark_type;
unsigned int ignored_flags;
- const char *event_path;
+ int event_path_cnt;
+ const char *event_path_fmt;
unsigned long long expected_mask_with_ignore;
unsigned long long expected_mask_without_ignore;
} tcases[] = {
{
.tname = "ignore mount events created on a specific file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = FILE_MNT2,
+ .ignore_path_fmt = FILE_MNT2,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore exec mount events created on a specific file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = FILE_EXEC_PATH2,
+ .ignore_path_fmt = FILE_EXEC_PATH2,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE_EXEC_PATH,
+ .event_path_fmt = FILE_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "don't ignore mount events created on another file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = FILE_PATH,
+ .ignore_path_fmt = FILE_PATH,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE2_PATH,
+ .event_path_fmt = FILE2_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore exec mount events created on another file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = FILE_EXEC_PATH,
+ .ignore_path_fmt = FILE_EXEC_PATH,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE2_EXEC_PATH,
+ .event_path_fmt = FILE2_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "ignore inode events created on a specific mount point",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_INODE,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_MNT2,
+ .event_path_fmt = FILE_MNT2,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore exec inode events created on a specific mount point",
- .mark_path = FILE_EXEC_PATH,
+ .mark_path_fmt = FILE_EXEC_PATH,
.mark_type = FANOTIFY_INODE,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_EXEC_PATH2,
+ .event_path_fmt = FILE_EXEC_PATH2,
.expected_mask_with_ignore = FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "don't ignore inode events created on another mount point",
- .mark_path = FILE_MNT2,
+ .mark_path_fmt = FILE_MNT2,
.mark_type = FANOTIFY_INODE,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore exec inode events created on another mount point",
- .mark_path = FILE_EXEC_PATH2,
+ .mark_path_fmt = FILE_EXEC_PATH2,
.mark_type = FANOTIFY_INODE,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_EXEC_PATH,
+ .event_path_fmt = FILE_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "ignore fs events created on a specific file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = FILE_PATH,
+ .ignore_path_fmt = FILE_PATH,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore exec fs events created on a specific file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = FILE_EXEC_PATH,
+ .ignore_path_fmt = FILE_EXEC_PATH,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE_EXEC_PATH,
+ .event_path_fmt = FILE_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "don't ignore mount events created on another file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = FILE_PATH,
+ .ignore_path_fmt = FILE_PATH,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE2_PATH,
+ .event_path_fmt = FILE2_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore exec mount events created on another file",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = FILE_EXEC_PATH,
+ .ignore_path_fmt = FILE_EXEC_PATH,
.ignore_mark_type = FANOTIFY_INODE,
- .event_path = FILE2_EXEC_PATH,
+ .event_path_fmt = FILE2_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "ignore fs events created on a specific mount point",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_MNT2,
+ .event_path_fmt = FILE_MNT2,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore exec fs events created on a specific mount point",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_EXEC_PATH2,
+ .event_path_fmt = FILE_EXEC_PATH2,
.expected_mask_with_ignore = FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "don't ignore fs events created on another mount point",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore exec fs events created on another mount point",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = MNT2_PATH,
+ .ignore_path_fmt = MNT2_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_EXEC_PATH,
+ .event_path_fmt = FILE_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "ignore child exec events created on a specific mount point",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_PARENT,
- .ignore_path = MOUNT_PATH,
+ .ignore_path_fmt = MOUNT_PATH,
.ignore_mark_type = FANOTIFY_MOUNT,
- .event_path = FILE_EXEC_PATH,
+ .event_path_fmt = FILE_EXEC_PATH,
.expected_mask_with_ignore = FAN_OPEN_EXEC,
.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
},
{
.tname = "ignore events on children of directory created on a specific file",
- .mark_path = DIR_PATH,
+ .mark_path_fmt = DIR_PATH,
.mark_type = FANOTIFY_PARENT,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore events on file created inside a parent watching children",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_INODE,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore events on file created inside a parent not watching children",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_INODE,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore mount events created inside a parent watching children",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore mount events created inside a parent not watching children",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "ignore fs events created inside a parent watching children",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore fs events created inside a parent not watching children",
- .mark_path = FILE_PATH,
+ .mark_path_fmt = FILE_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
/* Evictable ignore mark test cases */
{
.tname = "don't ignore mount events created on file with evicted ignore mark",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = FILE_PATH,
+ .ignore_path_fmt = FILE_PATH,
.ignore_mark_type = FANOTIFY_EVICTABLE,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore fs events created on a file with evicted ignore mark",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = FILE_PATH,
+ .ignore_path_fmt = FILE_PATH,
.ignore_mark_type = FANOTIFY_EVICTABLE,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore mount events created inside a parent with evicted ignore mark",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_MOUNT,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_EVICTABLE,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
{
.tname = "don't ignore fs events created inside a parent with evicted ignore mark",
- .mark_path = MOUNT_PATH,
+ .mark_path_fmt = MOUNT_PATH,
.mark_type = FANOTIFY_FILESYSTEM,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_EVICTABLE,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = FILE_PATH,
+ .event_path_fmt = FILE_PATH,
.expected_mask_with_ignore = FAN_OPEN,
.expected_mask_without_ignore = FAN_OPEN
},
/* FAN_MARK_IGNORE specific test cases */
{
.tname = "ignore events on subdir inside a parent watching subdirs",
- .mark_path = SUBDIR_PATH,
+ .mark_path_fmt = SUBDIR_PATH,
.mark_type = FANOTIFY_SUBDIR,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_EVENT_ON_CHILD | FAN_ONDIR,
- .event_path = SUBDIR_PATH,
+ .event_path_fmt = SUBDIR_PATH,
.expected_mask_with_ignore = 0,
.expected_mask_without_ignore = FAN_OPEN | FAN_ONDIR
},
{
.tname = "don't ignore events on subdir inside a parent not watching children",
- .mark_path = SUBDIR_PATH,
+ .mark_path_fmt = SUBDIR_PATH,
.mark_type = FANOTIFY_SUBDIR,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_ONDIR,
- .event_path = SUBDIR_PATH,
+ .event_path_fmt = SUBDIR_PATH,
.expected_mask_with_ignore = FAN_OPEN | FAN_ONDIR,
.expected_mask_without_ignore = FAN_OPEN | FAN_ONDIR
},
{
.tname = "don't ignore events on subdir inside a parent watching non-dir children",
- .mark_path = SUBDIR_PATH,
+ .mark_path_fmt = SUBDIR_PATH,
.mark_type = FANOTIFY_SUBDIR,
- .ignore_path = DIR_PATH,
+ .ignore_path_fmt = DIR_PATH,
.ignore_mark_type = FANOTIFY_PARENT,
.ignored_flags = FAN_EVENT_ON_CHILD,
- .event_path = SUBDIR_PATH,
+ .event_path_fmt = SUBDIR_PATH,
.expected_mask_with_ignore = FAN_OPEN | FAN_ONDIR,
.expected_mask_without_ignore = FAN_OPEN | FAN_ONDIR
},
};

-static void show_fanotify_ignore_marks(int fd, int expected)
+static int format_path_check(char *buf, const char *fmt, int count, int i)
+{
+ int limit = count ? : 1;
+
+ if (i >= limit)
+ return 0;
+
+ if (count)
+ sprintf(buf, fmt, i);
+ else
+ strcpy(buf, fmt);
+ return 1;
+}
+
+#define foreach_path(tc, buf, pname) \
+ for (int piter = 0; format_path_check((buf), (tc)->pname##_fmt, \
+ (tc)->pname##_cnt, piter); piter++)
+
+static void show_fanotify_ignore_marks(int fd, int min, int max)
{
unsigned int mflags, mask, ignored_mask;
char procfdinfo[100];
+ char line[BUFSIZ];
+ int marks = 0;
+ FILE *f;

sprintf(procfdinfo, "/proc/%d/fdinfo/%d", (int)getpid(), fd);
- if (FILE_LINES_SCANF(procfdinfo, "fanotify ino:%*x sdev:%*x mflags: %x mask:%x ignored_mask:%x",
- &mflags, &mask, &ignored_mask) || !ignored_mask) {
- tst_res(!expected ? TPASS : TFAIL,
- "No fanotify inode ignore marks %sexpected",
- !expected ? "as " : "is un");
- } else {
- tst_res(expected ? TPASS : TFAIL,
- "Found %sexpected inode ignore mark (mflags=%x, mask=%x ignored_mask=%x)",
- expected ? "" : "un", mflags, mask, ignored_mask);
+ f = SAFE_FOPEN(procfdinfo, "r");
+ while (fgets(line, BUFSIZ, f)) {
+ if (sscanf(line, "fanotify ino:%*x sdev:%*x mflags: %x mask:%x ignored_mask:%x",
+ &mflags, &mask, &ignored_mask) == 3) {
+ if (ignored_mask != 0)
+ marks++;
+ }
}
+ if (marks < min) {
+ tst_res(TFAIL, "Found %d ignore marks but at least %d expected", marks, min);
+ return;
+ }
+ if (marks > max) {
+ tst_res(TFAIL, "Found %d ignore marks but at most %d expected", marks, max);
+ return;
+ }
+ tst_res(TPASS, "Found %d ignore marks which is in expected range %d-%d", marks, min, max);
}

static void drop_caches(void)
@@ -482,6 +515,7 @@ static int create_fanotify_groups(unsigned int n)
int evictable_ignored = (tc->ignore_mark_type == FANOTIFY_EVICTABLE);
int ignore_mark_type;
int ignored_onchild = tc->ignored_flags & FAN_EVENT_ON_CHILD;
+ char path[PATH_MAX];

mark = &fanotify_mark_types[tc->mark_type];
ignore_mark = &fanotify_mark_types[tc->ignore_mark_type];
@@ -501,11 +535,12 @@ static int create_fanotify_groups(unsigned int n)
* FAN_EVENT_ON_CHILD has no effect on filesystem/mount
* or inode mark on non-directory.
*/
- SAFE_FANOTIFY_MARK(fd_notify[p][i],
+ foreach_path(tc, path, mark_path)
+ SAFE_FANOTIFY_MARK(fd_notify[p][i],
FAN_MARK_ADD | mark->flag,
tc->expected_mask_without_ignore |
FAN_EVENT_ON_CHILD | FAN_ONDIR,
- AT_FDCWD, tc->mark_path);
+ AT_FDCWD, path);

/* Do not add ignore mark for first priority groups */
if (p == 0)
@@ -519,9 +554,10 @@ static int create_fanotify_groups(unsigned int n)
mark_ignored = tst_variant ? FAN_MARK_IGNORE_SURV : FAN_MARK_IGNORED_SURV;
mask = FAN_OPEN | tc->ignored_flags;
add_mark:
- SAFE_FANOTIFY_MARK(fd_notify[p][i],
+ foreach_path(tc, path, ignore_path)
+ SAFE_FANOTIFY_MARK(fd_notify[p][i],
FAN_MARK_ADD | ignore_mark->flag | mark_ignored,
- mask, AT_FDCWD, tc->ignore_path);
+ mask, AT_FDCWD, path);

/*
* FAN_MARK_IGNORE respects FAN_EVENT_ON_CHILD flag, but legacy
@@ -578,9 +614,24 @@ add_mark:
if (ignore_mark_type == FAN_MARK_INODE) {
for (p = 0; p < num_classes; p++) {
for (i = 0; i < GROUPS_PER_PRIO; i++) {
- if (fd_notify[p][i] > 0)
+ if (fd_notify[p][i] > 0) {
+ int minexp, maxexp;
+
+ if (p == 0) {
+ minexp = maxexp = 0;
+ } else if (evictable_ignored) {
+ minexp = 0;
+ /*
+ * Check at least half the
+ * marks get evicted by reclaim
+ */
+ maxexp = tc->ignore_path_cnt / 2;
+ } else {
+ minexp = maxexp = tc->ignore_path_cnt ? : 1;
+ }
show_fanotify_ignore_marks(fd_notify[p][i],
- p > 0 && !evictable_ignored);
+ minexp, maxexp);
+ }
}
}
}
@@ -613,7 +664,7 @@ static void mount_cycle(void)
bind_mount_created = 1;
}

-static void verify_event(int p, int group, struct fanotify_event_metadata *event,
+static int verify_event(int p, int group, struct fanotify_event_metadata *event,
unsigned long long expected_mask)
{
/* Only FAN_REPORT_FID reports the FAN_ONDIR flag in events on dirs */
@@ -626,33 +677,35 @@ static void verify_event(int p, int group, struct fanotify_event_metadata *event
(unsigned long long) event->mask,
(unsigned long long) expected_mask,
(unsigned int)event->pid, event->fd);
+ return 0;
} else if (event->pid != child_pid) {
tst_res(TFAIL, "group %d (%x) got event: mask %llx pid=%u "
"(expected %u) fd=%u", group, fanotify_class[p],
(unsigned long long)event->mask, (unsigned int)event->pid,
(unsigned int)child_pid, event->fd);
- } else {
- tst_res(TPASS, "group %d (%x) got event: mask %llx pid=%u fd=%u",
- group, fanotify_class[p], (unsigned long long)event->mask,
- (unsigned int)event->pid, event->fd);
+ return 0;
}
+ return 1;
}

-static int generate_event(const char *event_path,
- unsigned long long expected_mask)
+static int generate_event(struct tcase *tc, unsigned long long expected_mask)
{
int fd, status;

child_pid = SAFE_FORK();

if (child_pid == 0) {
- if (expected_mask & FAN_OPEN_EXEC) {
- SAFE_EXECL(event_path, event_path, NULL);
- } else {
- fd = SAFE_OPEN(event_path, O_RDONLY);
+ char path[PATH_MAX];
+
+ foreach_path(tc, path, event_path) {
+ if (expected_mask & FAN_OPEN_EXEC) {
+ SAFE_EXECL(path, path, NULL);
+ } else {
+ fd = SAFE_OPEN(path, O_RDONLY);

- if (fd > 0)
- SAFE_CLOSE(fd);
+ if (fd > 0)
+ SAFE_CLOSE(fd);
+ }
}

exit(0);
@@ -665,6 +718,37 @@ static int generate_event(const char *event_path,
return 0;
}

+struct fanotify_event_metadata *fetch_event(int fd, int *retp)
+{
+ int ret;
+ struct fanotify_event_metadata *event;
+
+ *retp = 0;
+ if (event_buf_pos >= event_buf_len) {
+ event_buf_pos = 0;
+ ret = read(fd, event_buf, EVENT_BUF_LEN);
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ return NULL;
+ tst_brk(TBROK | TERRNO, "reading fanotify events failed");
+ *retp = -1;
+ return NULL;
+ }
+ event_buf_len = ret;
+ }
+ if (event_buf_len - event_buf_pos < (int)FAN_EVENT_METADATA_LEN) {
+ tst_brk(TBROK,
+ "too short event when reading fanotify events (%d < %d)",
+ event_buf_len - event_buf_pos,
+ (int)FAN_EVENT_METADATA_LEN);
+ *retp = -1;
+ return NULL;
+ }
+ event = (struct fanotify_event_metadata *)(event_buf + event_buf_pos);
+ event_buf_pos += event->event_len;
+ return event;
+}
+
static void test_fanotify(unsigned int n)
{
struct tcase *tc = &tcases[n];
@@ -672,6 +756,7 @@ static void test_fanotify(unsigned int n)
int ret;
unsigned int p, i;
struct fanotify_event_metadata *event;
+ int event_count;

tst_res(TINFO, "Test #%d: %s", n, tc->tname);

@@ -715,7 +800,7 @@ static void test_fanotify(unsigned int n)
ignore_mark = &fanotify_mark_types[tc->ignore_mark_type];

/* Generate event in child process */
- if (!generate_event(tc->event_path, tc->expected_mask_with_ignore))
+ if (!generate_event(tc, tc->expected_mask_with_ignore))
tst_brk(TBROK, "Child process terminated incorrectly");

/* First verify all groups without matching ignore mask got the event */
@@ -724,64 +809,52 @@ static void test_fanotify(unsigned int n)
break;

for (i = 0; i < GROUPS_PER_PRIO; i++) {
- ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
- if (ret < 0) {
- if (errno == EAGAIN) {
- tst_res(TFAIL, "group %d (%x) "
- "with %s did not get event",
- i, fanotify_class[p], mark->name);
- continue;
- }
- tst_brk(TBROK | TERRNO,
- "reading fanotify events failed");
- }
- if (ret < (int)FAN_EVENT_METADATA_LEN) {
- tst_brk(TBROK,
- "short read when reading fanotify "
- "events (%d < %d)", ret,
- (int)EVENT_BUF_LEN);
+ event_count = 0;
+ event_buf_pos = event_buf_len = 0;
+ while ((event = fetch_event(fd_notify[p][i], &ret))) {
+ event_count++;
+ if (!verify_event(p, i, event, p == 0 ?
+ tc->expected_mask_without_ignore :
+ tc->expected_mask_with_ignore))
+ break;
+ if (event->fd != FAN_NOFD)
+ SAFE_CLOSE(event->fd);
}
- event = (struct fanotify_event_metadata *)event_buf;
- if (ret > (int)event->event_len) {
+ if (ret < 0)
+ continue;
+ if (event_count != (tc->event_path_cnt ? : 1)) {
tst_res(TFAIL, "group %d (%x) with %s "
- "got more than one event (%d > %d)",
- i, fanotify_class[p], mark->name, ret,
- event->event_len);
+ "got unexpected number of events (%d != %d)",
+ i, fanotify_class[p], mark->name,
+ event_count, tc->event_path_cnt);
} else {
- verify_event(p, i, event, p == 0 ?
- tc->expected_mask_without_ignore :
- tc->expected_mask_with_ignore);
+ tst_res(TPASS, "group %d (%x) got %d events: mask %llx pid=%u",
+ i, fanotify_class[p], event_count,
+ (unsigned long long)(p == 0 ?
+ tc->expected_mask_without_ignore :
+ tc->expected_mask_with_ignore),
+ (unsigned int)child_pid);
}
- if (event->fd != FAN_NOFD)
- SAFE_CLOSE(event->fd);
}
}
/* Then verify all groups with matching ignore mask did got the event */
for (p = 1; p < num_classes && !tc->expected_mask_with_ignore; p++) {
for (i = 0; i < GROUPS_PER_PRIO; i++) {
- ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
- if (ret >= 0 && ret < (int)FAN_EVENT_METADATA_LEN) {
- tst_brk(TBROK,
- "short read when reading fanotify "
- "events (%d < %d)", ret,
- (int)EVENT_BUF_LEN);
- }
- event = (struct fanotify_event_metadata *)event_buf;
- if (ret > 0) {
- tst_res(TFAIL, "group %d (%x) with %s and "
- "%s ignore mask got unexpected event (mask %llx)",
- i, fanotify_class[p], mark->name, ignore_mark->name,
- event->mask);
+ event_count = 0;
+ event_buf_pos = event_buf_len = 0;
+ while ((event = fetch_event(fd_notify[p][i], &ret))) {
+ event_count++;
if (event->fd != FAN_NOFD)
SAFE_CLOSE(event->fd);
- } else if (errno == EAGAIN) {
- tst_res(TPASS, "group %d (%x) with %s and "
- "%s ignore mask got no event",
- i, fanotify_class[p], mark->name, ignore_mark->name);
- } else {
- tst_brk(TBROK | TERRNO,
- "reading fanotify events failed");
}
+ if (ret < 0)
+ continue;
+ if (event_count > tc->event_path_cnt / 2)
+ tst_res(TFAIL, "group %d (%x) with %s and "
+ "%s ignore mask got unexpectedly many events (%d > %d)",
+ i, fanotify_class[p], mark->name,
+ ignore_mark->name, event_count,
+ tc->event_path_cnt / 2);
}
}
cleanup:
--
2.35.3