[LTP] [PATCH v2] fanotify09: check merging of events on child subdir
Amir Goldstein
amir73il@gmail.com
Fri Nov 9 12:15:20 CET 2018
In a setup of mount mark and directory inode mark on children
mount mark should not be getting double events "on child" and "on self"
for the same action.
Add a regression test case for b469e7e47c8a ("fanotify: fix handling of
events on child sub-directory").
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
Cyril,
FYI, kernel commit b469e7e47c8a ("fanotify: fix handling of events on child
sub-directory") has been queued by Jan.
This patch adds a test case to fanotify09 which exercises another corner
case of merging inode and mount marks.
The new test case fails on current master and passes on Jan's for_next
branch. The existing test case passes on both.
Thanks,
Amir.
Changes since v1:
- Add test case instead of changing existing test
- Record expected upsteam commit id
.../kernel/syscalls/fanotify/fanotify09.c | 124 +++++++++++++-----
1 file changed, 90 insertions(+), 34 deletions(-)
diff --git a/testcases/kernel/syscalls/fanotify/fanotify09.c b/testcases/kernel/syscalls/fanotify/fanotify09.c
index 511ccc0e0..1814ae6b0 100644
--- a/testcases/kernel/syscalls/fanotify/fanotify09.c
+++ b/testcases/kernel/syscalls/fanotify/fanotify09.c
@@ -11,6 +11,10 @@
* This is a regression test for commit 54a307ba8d3c:
*
* fanotify: fix logic of events on child
+ *
+ * Test case #2 is a regression test for commit b469e7e47c8a:
+ *
+ * fanotify: fix handling of events on child sub-directory
*/
#define _GNU_SOURCE
#include "config.h"
@@ -39,14 +43,34 @@
#define BUF_SIZE 256
static char fname[BUF_SIZE];
+static char symlnk[BUF_SIZE];
+static char fdpath[BUF_SIZE];
static int fd_notify[NUM_GROUPS];
static char event_buf[EVENT_BUF_LEN];
#define MOUNT_NAME "mntpoint"
+#define DIR_NAME "testdir"
static int mount_created;
-static void create_fanotify_groups(void)
+static struct tcase {
+ const char *tname;
+ unsigned int ondir;
+ int nevents;
+} tcases[] = {
+ {
+ "Events on children with both inode and mount marks",
+ 0,
+ 1,
+ },
+ {
+ "Events on children and subdirs with both inode and mount marks",
+ FAN_ONDIR,
+ 2,
+ },
+};
+
+static void create_fanotify_groups(unsigned int ondir)
{
unsigned int i, onchild;
int ret;
@@ -57,15 +81,17 @@ static void create_fanotify_groups(void)
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,
+ FAN_CLOSE_NOWRITE | onchild,
AT_FDCWD, ".");
if (ret < 0) {
tst_brk(TBROK | TERRNO,
"fanotify_mark(%d, FAN_MARK_ADD | "
- "FAN_MARK_MOUNT, FAN_MODIFY, AT_FDCWD,"
- " '.') failed", fd_notify[i]);
+ "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
@@ -74,15 +100,15 @@ static void create_fanotify_groups(void)
* setting the DCACHE_FSNOTIFY_PARENT_WATCHED dentry
* flag.
*/
- onchild = (i == 0) ? FAN_EVENT_ON_CHILD : 0;
- ret = fanotify_mark(fd_notify[i],
- FAN_MARK_ADD,
- FAN_MODIFY | onchild, AT_FDCWD, ".");
+ 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, AT_FDCWD, '.') failed",
+ "FAN_MODIFY%s%s, AT_FDCWD, '.') failed",
fd_notify[i],
+ ondir ? " | FAN_ONDIR" : "",
onchild ? " | FAN_EVENT_ON_CHILD" : "");
}
}
@@ -98,12 +124,13 @@ static void cleanup_fanotify_groups(void)
}
}
-static void verify_event(int group, struct fanotify_event_metadata *event)
+static void verify_event(int group, struct fanotify_event_metadata *event,
+ __u32 expect)
{
- if (event->mask != FAN_MODIFY) {
+ if (event->mask != expect) {
tst_res(TFAIL, "group %d got event: mask %llx (expected %llx) "
"pid=%u fd=%d", group, (unsigned long long)event->mask,
- (unsigned long long)FAN_MODIFY,
+ (unsigned long long)expect,
(unsigned)event->pid, event->fd);
} else if (event->pid != getpid()) {
tst_res(TFAIL, "group %d got event: mask %llx pid=%u "
@@ -111,26 +138,44 @@ static void verify_event(int group, struct fanotify_event_metadata *event)
(unsigned long long)event->mask, (unsigned)event->pid,
(unsigned)getpid(), event->fd);
} else {
- tst_res(TPASS, "group %d got event: mask %llx pid=%u fd=%d",
+ int len;
+ sprintf(symlnk, "/proc/self/fd/%d", event->fd);
+ len = readlink(symlnk, fdpath, sizeof(fdpath));
+ if (len < 0)
+ len = 0;
+ fdpath[len] = 0;
+ tst_res(TPASS, "group %d got event: mask %llx pid=%u fd=%d path=%s",
group, (unsigned long long)event->mask,
- (unsigned)event->pid, event->fd);
+ (unsigned)event->pid, event->fd, fdpath);
}
}
-void test01(void)
+static void test_fanotify(unsigned int n)
{
- int ret;
+ int ret, dirfd;
unsigned int i;
- struct fanotify_event_metadata *event;
+ struct fanotify_event_metadata *event, *ev;
+ struct tcase *tc = &tcases[n];
+
+ tst_res(TINFO, "Test #%d: %s", n, tc->tname);
- create_fanotify_groups();
+ create_fanotify_groups(tc->ondir);
/*
* generate MODIFY event and no FAN_CLOSE_NOWRITE event.
*/
SAFE_FILE_PRINTF(fname, "1");
+ /*
+ * generate FAN_CLOSE_NOWRITE event on a child subdir.
+ */
+ dirfd = SAFE_OPEN(DIR_NAME, O_RDONLY);
+ if (dirfd >= 0)
+ SAFE_CLOSE(dirfd);
- /* First verify the first group got the MODIFY event */
+ /*
+ * First verify the first group got the file MODIFY event and got just
+ * one FAN_CLOSE_NOWRITE event.
+ */
ret = read(fd_notify[0], event_buf, EVENT_BUF_LEN);
if (ret < 0) {
if (errno == EAGAIN) {
@@ -140,28 +185,37 @@ void test01(void)
"reading fanotify events failed");
}
}
- if (ret < (int)FAN_EVENT_METADATA_LEN) {
+ if (ret < tc->nevents * (int)FAN_EVENT_METADATA_LEN) {
tst_brk(TBROK,
- "short read when reading fanotify "
- "events (%d < %d)", ret,
- (int)EVENT_BUF_LEN);
+ "short read when reading fanotify events (%d < %d)",
+ ret, tc->nevents * (int)FAN_EVENT_METADATA_LEN);
}
event = (struct fanotify_event_metadata *)event_buf;
- if (ret > (int)event->event_len) {
- tst_res(TFAIL, "first group got more than one "
- "event (%d > %d)", ret,
- event->event_len);
- } else {
- verify_event(0, event);
+ verify_event(0, event, FAN_MODIFY);
+ if (tc->ondir)
+ verify_event(0, event + 1, FAN_CLOSE_NOWRITE);
+ if (ret > tc->nevents * (int)FAN_EVENT_METADATA_LEN) {
+ tst_res(TFAIL,
+ "first group got more than %d events (%d > %d)",
+ tc->nevents, ret,
+ tc->nevents * (int)FAN_EVENT_METADATA_LEN);
+ }
+ /* Close all file descriptors of read events */
+ for (ev = event; ret >= (int)FAN_EVENT_METADATA_LEN; ev++) {
+ if (ev->fd != FAN_NOFD)
+ SAFE_CLOSE(ev->fd);
+ ret -= (int)FAN_EVENT_METADATA_LEN;
}
- if (event->fd != FAN_NOFD)
- SAFE_CLOSE(event->fd);
- /* Then verify the rest of the groups did not get the MODIFY event */
+ /*
+ * Then verify the rest of the groups did not get the MODIFY event and
+ * did not get the FAN_CLOSE_NOWRITE event on subdir.
+ */
for (i = 1; i < NUM_GROUPS; i++) {
- ret = read(fd_notify[i], event_buf, EVENT_BUF_LEN);
+ ret = read(fd_notify[i], event_buf, FAN_EVENT_METADATA_LEN);
if (ret > 0) {
tst_res(TFAIL, "group %d got event", i);
+ verify_event(0, event, FAN_CLOSE_NOWRITE);
if (event->fd != FAN_NOFD)
SAFE_CLOSE(event->fd);
continue;
@@ -187,6 +241,7 @@ static void setup(void)
SAFE_MOUNT(MOUNT_NAME, MOUNT_NAME, "none", MS_BIND, NULL);
mount_created = 1;
SAFE_CHDIR(MOUNT_NAME);
+ SAFE_MKDIR(DIR_NAME, 0755);
sprintf(fname, "tfile_%d", getpid());
SAFE_FILE_PRINTF(fname, "1");
@@ -203,7 +258,8 @@ static void cleanup(void)
}
static struct tst_test test = {
- .test_all = test01,
+ .test = test_fanotify,
+ .tcnt = ARRAY_SIZE(tcases),
.setup = setup,
.cleanup = cleanup,
.needs_tmpdir = 1,
--
2.17.1
More information about the ltp
mailing list