syscalls/fanotify: new test for mount ignore mask

Message ID 20180908142453.21532-2-amir73il@gmail.com
State New
Headers show
Series
  • syscalls/fanotify: new test for mount ignore mask
Related show

Commit Message

Amir Goldstein Sept. 8, 2018, 2:24 p.m.
This is a regression test for commit:
    9bdda4e9cf2d fsnotify: fix ignore mask logic in fsnotify()

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 runtest/syscalls                              |   1 +
 testcases/kernel/syscalls/fanotify/.gitignore |   1 +
 .../kernel/syscalls/fanotify/fanotify10.c     | 248 ++++++++++++++++++
 3 files changed, 250 insertions(+)
 create mode 100644 testcases/kernel/syscalls/fanotify/fanotify10.c

Comments

Richard Palethorpe Sept. 11, 2018, 9:07 a.m. | #1
Hello,

If a test can handle two different regressions with only slight
modifications then we should use the test index feature:
https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial#6-split-the-test

In more complex cases common code can be put into a shared header file
or library. Whatever is simpler and cleaner overall. A lot of older
tests have been copied and pasted which leads to big problems in the
long term.

Amir Goldstein <amir73il@gmail.com> writes:

> This is a regression test for commit:
>     9bdda4e9cf2d fsnotify: fix ignore mask logic in fsnotify()
>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> ---
>  runtest/syscalls                              |   1 +
>  testcases/kernel/syscalls/fanotify/.gitignore |   1 +
>  .../kernel/syscalls/fanotify/fanotify10.c     | 248 ++++++++++++++++++
>  3 files changed, 250 insertions(+)
>  create mode 100644 testcases/kernel/syscalls/fanotify/fanotify10.c
>
> diff --git a/runtest/syscalls b/runtest/syscalls
> index 62fb890cd..33b8d8625 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -497,6 +497,7 @@ fanotify06 fanotify06
>  fanotify07 fanotify07
>  fanotify08 fanotify08
>  fanotify09 fanotify09
> +fanotify10 fanotify10
>
>  ioperm01 ioperm01
>  ioperm02 ioperm02
> diff --git a/testcases/kernel/syscalls/fanotify/.gitignore b/testcases/kernel/syscalls/fanotify/.gitignore
> index ec7553995..c26f2bd27 100644
> --- a/testcases/kernel/syscalls/fanotify/.gitignore
> +++ b/testcases/kernel/syscalls/fanotify/.gitignore
> @@ -7,3 +7,4 @@
>  /fanotify07
>  /fanotify08
>  /fanotify09
> +/fanotify10
> diff --git a/testcases/kernel/syscalls/fanotify/fanotify10.c b/testcases/kernel/syscalls/fanotify/fanotify10.c
> new file mode 100644
> index 000000000..5245eb6f8
> --- /dev/null
> +++ b/testcases/kernel/syscalls/fanotify/fanotify10.c
> @@ -0,0 +1,248 @@
> +/*
> + * Copyright (c) 2014 SUSE.  All Rights Reserved.
> + * Copyright (c) 2018 CTERA Networks.  All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> + *
> + * Further, this software is distributed without any warranty that it is
> + * free of the rightful claim of any third person regarding infringement
> + * or the like.  Any license provided herein, whether implied or
> + * otherwise, applies only to this software file.  Patent licenses, if
> + * any, provided herein do not apply to combinations of this program with
> + * other software, or any other product whatsoever.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Started by Jan Kara <jack@suse.cz>
> + * Forked from fanotify06.c by Amir Goldstein <amir73il@gmail.com>
> + *
> + * DESCRIPTION
> + *     Check that fanotify properly merges ignore mask of a mount mark
> + *     with a mask of an inode mark on the same group.  Unlike the
> + *     prototype test fanotify06, do not use FAN_MODIFY event for the
> + *     test mask, because it hides the bug.
> + *
> + * This is a regression test for commit:
> + *
> + *     9bdda4e9cf2d fsnotify: fix ignore mask logic in fsnotify()
> + */
> +#define _GNU_SOURCE
> +#include "config.h"
> +
> +#include <stdio.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <sys/mount.h>
> +#include <sys/syscall.h>
> +#include "tst_test.h"
> +#include "fanotify.h"
> +
> +#if defined(HAVE_SYS_FANOTIFY_H)
> +#include <sys/fanotify.h>
> +
> +#define EVENT_MAX 1024
> +/* size of the event structure, not counting name */
> +#define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
> +/* reasonable guess as to size of 1024 events */
> +#define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
> +
> +unsigned int fanotify_prio[] = {
> +	FAN_CLASS_PRE_CONTENT,
> +	FAN_CLASS_CONTENT,
> +	FAN_CLASS_NOTIF
> +};
> +#define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio)
> +
> +#define GROUPS_PER_PRIO 3
> +
> +#define BUF_SIZE 256
> +static char fname[BUF_SIZE];
> +static int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO];
> +
> +static char event_buf[EVENT_BUF_LEN];
> +
> +#define MOUNT_NAME "mntpoint"
> +static int mount_created;
> +
> +static void create_fanotify_groups(void)
> +{
> +	unsigned int p, i;
> +	int ret;
> +
> +	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
> +		for (i = 0; i < GROUPS_PER_PRIO; i++) {
> +			fd_notify[p][i] = SAFE_FANOTIFY_INIT(fanotify_prio[p] |
> +							     FAN_NONBLOCK,
> +							     O_RDONLY);
> +
> +			/* Add mount mark for each group */
> +			ret = fanotify_mark(fd_notify[p][i],
> +					    FAN_MARK_ADD,
> +					    FAN_OPEN,
> +					    AT_FDCWD, fname);
> +			if (ret < 0) {
> +				tst_brk(TBROK | TERRNO,
> +					"fanotify_mark(%d, FAN_MARK_ADD | "
> +					"FAN_MARK_MOUNT, FAN_OPEN, AT_FDCWD,"
> +					" %s) failed", fd_notify[p][i],
> +					fname);
> +			}
> +			/* Add ignore mark for groups with higher priority */
> +			if (p == 0)
> +				continue;
> +			ret = fanotify_mark(fd_notify[p][i],
> +					    FAN_MARK_ADD | FAN_MARK_MOUNT |
> +					    FAN_MARK_IGNORED_MASK |
> +					    FAN_MARK_IGNORED_SURV_MODIFY,
> +					    FAN_OPEN, AT_FDCWD, fname);
> +			if (ret < 0) {
> +				tst_brk(TBROK | TERRNO,
> +					"fanotify_mark(%d, FAN_MARK_ADD | "
> +					"FAN_MARK_IGNORED_MASK | "
> +					"FAN_MARK_IGNORED_SURV_MODIFY, "
> +					"FAN_OPEN, AT_FDCWD, %s) failed",
> +					fd_notify[p][i], fname);
> +			}
> +		}
> +	}
> +}
> +
> +static void cleanup_fanotify_groups(void)
> +{
> +	unsigned int i, p;
> +
> +	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
> +		for (i = 0; i < GROUPS_PER_PRIO; i++) {
> +			if (fd_notify[p][i] > 0)
> +				SAFE_CLOSE(fd_notify[p][i]);
> +		}
> +	}
> +}
> +
> +static void verify_event(int group, struct fanotify_event_metadata *event)
> +{
> +	if (event->mask != FAN_OPEN) {
> +		tst_res(TFAIL, "group %d get event: mask %llx (expected %llx) "
> +			"pid=%u fd=%u", group, (unsigned long long)event->mask,
> +			(unsigned long long)FAN_OPEN,
> +			(unsigned)event->pid, event->fd);
> +	} else if (event->pid != getpid()) {
> +		tst_res(TFAIL, "group %d get event: mask %llx pid=%u "
> +			"(expected %u) fd=%u", group,
> +			(unsigned long long)event->mask, (unsigned)event->pid,
> +			(unsigned)getpid(), event->fd);
> +	} else {
> +		tst_res(TPASS, "group %d get event: mask %llx pid=%u fd=%u",
> +			group, (unsigned long long)event->mask,
> +			(unsigned)event->pid, event->fd);
> +	}
> +}
> +
> +void test01(void)
> +{
> +	int ret, fd;
> +	unsigned int p, i;
> +	struct fanotify_event_metadata *event;
> +
> +	create_fanotify_groups();
> +
> +	/*
> +	 * generate sequence of events
> +	 */
> +	fd = SAFE_OPEN(fname, O_RDONLY);
> +	SAFE_CLOSE(fd);
> +
> +	/* First verify all groups without ignore mask got the event */
> +	for (i = 0; i < GROUPS_PER_PRIO; i++) {
> +		ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN);
> +		if (ret < 0) {
> +			if (errno == EAGAIN) {
> +				tst_res(TFAIL, "group %d did not get "
> +					"event", i);
> +			}
> +			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 = (struct fanotify_event_metadata *)event_buf;
> +		if (ret > (int)event->event_len) {
> +			tst_res(TFAIL, "group %d got more than one "
> +				"event (%d > %d)", i, ret,
> +				event->event_len);
> +		} else {
> +			verify_event(i, event);
> +		}
> +		if (event->fd != FAN_NOFD)
> +			SAFE_CLOSE(event->fd);
> +	}
> +	for (p = 1; p < FANOTIFY_PRIORITIES; p++) {
> +		for (i = 0; i < GROUPS_PER_PRIO; i++) {
> +			ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
> +			if (ret > 0) {
> +				tst_res(TFAIL, "group %d got event",
> +					p*GROUPS_PER_PRIO + i);
> +				if (event->fd != FAN_NOFD)
> +					SAFE_CLOSE(event->fd);
> +			} else if (ret == 0) {
> +				tst_brk(TBROK, "zero length "
> +					"read from fanotify fd");
> +			} else if (errno != EAGAIN) {
> +				tst_brk(TBROK | TERRNO,
> +					"reading fanotify events failed");
> +			} else {
> +				tst_res(TPASS, "group %d got no event",
> +					p*GROUPS_PER_PRIO + i);
> +			}
> +		}
> +	}
> +	cleanup_fanotify_groups();
> +}
> +
> +static void setup(void)
> +{
> +	SAFE_MKDIR(MOUNT_NAME, 0755);
> +	SAFE_MOUNT(MOUNT_NAME, MOUNT_NAME, "none", MS_BIND, NULL);
> +	mount_created = 1;
> +	SAFE_CHDIR(MOUNT_NAME);
> +
> +	sprintf(fname, "tfile_%d", getpid());
> +	SAFE_FILE_PRINTF(fname, "1");
> +}
> +
> +static void cleanup(void)
> +{
> +	cleanup_fanotify_groups();
> +
> +	SAFE_CHDIR("../");
> +
> +	if (mount_created && tst_umount(MOUNT_NAME) < 0)
> +		tst_brk(TBROK | TERRNO, "umount failed");
> +}
> +
> +static struct tst_test test = {
> +	.test_all = test01,
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.needs_tmpdir = 1,
> +	.needs_root = 1
> +};
> +
> +#else
> +	TST_TEST_TCONF("system doesn't have required fanotify support");
> +#endif
> --
> 2.17.1


--
Thank you,
Richard.
Amir Goldstein Sept. 11, 2018, 2:32 p.m. | #2
On Tue, Sep 11, 2018 at 12:07 PM Richard Palethorpe <rpalethorpe@suse.de> wrote:
>
> Hello,
>
> If a test can handle two different regressions with only slight
> modifications then we should use the test index feature:
> https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial#6-split-the-test
>
> In more complex cases common code can be put into a shared header file
> or library. Whatever is simpler and cleaner overall. A lot of older
> tests have been copied and pasted which leads to big problems in the
> long term.
>

All right. I have already written this test with a test index to cover
all possible
mixes of mark types include and exclude masks:
https://github.com/amir73il/ltp/blob/fanotify_sb/testcases/kernel/syscalls/fanotify/fanotify13.c
This gives better coverage than fanotify06 and fanotify10 combined.

However, if I modify test fanotify06 instead of forking test fanotify10, the
test (fanotify06) is going to start failing on non-master kernels.
Is that acceptable for LTP? I am asking because in fstests project, we have
the practice not to change an existing test to failing. When we find a
new regression
we write a new variant of the test for it.

If changing an existing test to cover more cases is appropriate than I am
going to generalize fanotify06 (as fanotify13 linked above) and then
when FAN_MARK_FILESYSTEM mark type support is added to kernel
all I will need to do is change the test again to add another mark type
to  fanotify_mark_types array.

Thoughts?

Thanks,
Amir.
Cyril Hrubis Sept. 13, 2018, 11:45 a.m. | #3
Hi!
> All right. I have already written this test with a test index to cover
> all possible
> mixes of mark types include and exclude masks:
> https://github.com/amir73il/ltp/blob/fanotify_sb/testcases/kernel/syscalls/fanotify/fanotify13.c
> This gives better coverage than fanotify06 and fanotify10 combined.
> 
> However, if I modify test fanotify06 instead of forking test fanotify10, the
> test (fanotify06) is going to start failing on non-master kernels.
> Is that acceptable for LTP? I am asking because in fstests project, we have
> the practice not to change an existing test to failing. When we find a
> new regression we write a new variant of the test for it.

We do not have a rule lik this but it sounds like a reasonable rule to
me, since when existing test starts to fail it does look like a
regression in the tests itself.

> If changing an existing test to cover more cases is appropriate than I am
> going to generalize fanotify06 (as fanotify13 linked above) and then
> when FAN_MARK_FILESYSTEM mark type support is added to kernel
> all I will need to do is change the test again to add another mark type
> to  fanotify_mark_types array.

I guess that either would be fine. In the end someone has to look
closely at failing tests anyway to say what exactly happened.
Amir Goldstein Sept. 13, 2018, 12:19 p.m. | #4
On Thu, Sep 13, 2018 at 2:45 PM Cyril Hrubis <chrubis@suse.cz> wrote:
>
> Hi!
> > All right. I have already written this test with a test index to cover
> > all possible
> > mixes of mark types include and exclude masks:
> > https://github.com/amir73il/ltp/blob/fanotify_sb/testcases/kernel/syscalls/fanotify/fanotify13.c
> > This gives better coverage than fanotify06 and fanotify10 combined.
> >
> > However, if I modify test fanotify06 instead of forking test fanotify10, the
> > test (fanotify06) is going to start failing on non-master kernels.
> > Is that acceptable for LTP? I am asking because in fstests project, we have
> > the practice not to change an existing test to failing. When we find a
> > new regression we write a new variant of the test for it.
>
> We do not have a rule lik this but it sounds like a reasonable rule to
> me, since when existing test starts to fail it does look like a
> regression in the tests itself.
>
> > If changing an existing test to cover more cases is appropriate than I am
> > going to generalize fanotify06 (as fanotify13 linked above) and then
> > when FAN_MARK_FILESYSTEM mark type support is added to kernel
> > all I will need to do is change the test again to add another mark type
> > to  fanotify_mark_types array.
>
> I guess that either would be fine. In the end someone has to look
> closely at failing tests anyway to say what exactly happened.
>

So here is what I suggest, which seems most reasonable w.r.t code reuse
and friendliness to testers:
- Leave fanotify06 untouched because it checks the common case
  (ignore on inode mark)
- Add new test fanotify10 that checks all combinations of marks types
  (like referenced fanotify13)
- After FAN_MARK_FILESYSTEM patches are merged, add the new mark
  type to fanotify10 test matrix, but disable it on runtime if new mark type
  is not supported by kernel.

Thanks,
Amir.
Richard Palethorpe Sept. 13, 2018, 1:12 p.m. | #5
Hello,

Amir Goldstein <amir73il@gmail.com> writes:

> On Thu, Sep 13, 2018 at 2:45 PM Cyril Hrubis <chrubis@suse.cz> wrote:
>>
>> Hi!
>> > All right. I have already written this test with a test index to cover
>> > all possible
>> > mixes of mark types include and exclude masks:
>> > https://github.com/amir73il/ltp/blob/fanotify_sb/testcases/kernel/syscalls/fanotify/fanotify13.c
>> > This gives better coverage than fanotify06 and fanotify10 combined.
>> >
>> > However, if I modify test fanotify06 instead of forking test fanotify10, the
>> > test (fanotify06) is going to start failing on non-master kernels.
>> > Is that acceptable for LTP? I am asking because in fstests project, we have
>> > the practice not to change an existing test to failing. When we find a
>> > new regression we write a new variant of the test for it.
>>
>> We do not have a rule lik this but it sounds like a reasonable rule to
>> me, since when existing test starts to fail it does look like a
>> regression in the tests itself.
>>
>> > If changing an existing test to cover more cases is appropriate than I am
>> > going to generalize fanotify06 (as fanotify13 linked above) and then
>> > when FAN_MARK_FILESYSTEM mark type support is added to kernel
>> > all I will need to do is change the test again to add another mark type
>> > to  fanotify_mark_types array.
>>
>> I guess that either would be fine. In the end someone has to look
>> closely at failing tests anyway to say what exactly happened.
>>
>
> So here is what I suggest, which seems most reasonable w.r.t code reuse
> and friendliness to testers:
> - Leave fanotify06 untouched because it checks the common case
>   (ignore on inode mark)
> - Add new test fanotify10 that checks all combinations of marks types
>   (like referenced fanotify13)
> - After FAN_MARK_FILESYSTEM patches are merged, add the new mark
>   type to fanotify10 test matrix, but disable it on runtime if new mark type
>   is not supported by kernel.
>
> Thanks,
> Amir.

Maybe it does make sense to introduce a new test if you are testing a
new feature (even if it is only a minor change). But to clarify, I also
think that for long term maintainability, it makes sense to refactor
common code into a header file.

There are instances in the LTP where a test has been copied, then
various fixes and improvements applied to one copy, but not the others
(sometimes a fix is not needed straight away in a particular test copy,
but then something else changes and it starts failing). Eventually
someone has to combine all the copies in one big painful refactoring,
instead of appying incremental changes along the way.

Obviously if you touch fanotify06 in any way you are increasing the risk
of breaking the test, but I think it is worth the risk over time.

--
Thank you,
Richard.

Patch

diff --git a/runtest/syscalls b/runtest/syscalls
index 62fb890cd..33b8d8625 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -497,6 +497,7 @@  fanotify06 fanotify06
 fanotify07 fanotify07
 fanotify08 fanotify08
 fanotify09 fanotify09
+fanotify10 fanotify10
 
 ioperm01 ioperm01
 ioperm02 ioperm02
diff --git a/testcases/kernel/syscalls/fanotify/.gitignore b/testcases/kernel/syscalls/fanotify/.gitignore
index ec7553995..c26f2bd27 100644
--- a/testcases/kernel/syscalls/fanotify/.gitignore
+++ b/testcases/kernel/syscalls/fanotify/.gitignore
@@ -7,3 +7,4 @@ 
 /fanotify07
 /fanotify08
 /fanotify09
+/fanotify10
diff --git a/testcases/kernel/syscalls/fanotify/fanotify10.c b/testcases/kernel/syscalls/fanotify/fanotify10.c
new file mode 100644
index 000000000..5245eb6f8
--- /dev/null
+++ b/testcases/kernel/syscalls/fanotify/fanotify10.c
@@ -0,0 +1,248 @@ 
+/*
+ * Copyright (c) 2014 SUSE.  All Rights Reserved.
+ * Copyright (c) 2018 CTERA Networks.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Started by Jan Kara <jack@suse.cz>
+ * Forked from fanotify06.c by Amir Goldstein <amir73il@gmail.com>
+ *
+ * DESCRIPTION
+ *     Check that fanotify properly merges ignore mask of a mount mark
+ *     with a mask of an inode mark on the same group.  Unlike the
+ *     prototype test fanotify06, do not use FAN_MODIFY event for the
+ *     test mask, because it hides the bug.
+ *
+ * This is a regression test for commit:
+ *
+ *     9bdda4e9cf2d fsnotify: fix ignore mask logic in fsnotify()
+ */
+#define _GNU_SOURCE
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/syscall.h>
+#include "tst_test.h"
+#include "fanotify.h"
+
+#if defined(HAVE_SYS_FANOTIFY_H)
+#include <sys/fanotify.h>
+
+#define EVENT_MAX 1024
+/* size of the event structure, not counting name */
+#define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
+/* reasonable guess as to size of 1024 events */
+#define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
+
+unsigned int fanotify_prio[] = {
+	FAN_CLASS_PRE_CONTENT,
+	FAN_CLASS_CONTENT,
+	FAN_CLASS_NOTIF
+};
+#define FANOTIFY_PRIORITIES ARRAY_SIZE(fanotify_prio)
+
+#define GROUPS_PER_PRIO 3
+
+#define BUF_SIZE 256
+static char fname[BUF_SIZE];
+static int fd_notify[FANOTIFY_PRIORITIES][GROUPS_PER_PRIO];
+
+static char event_buf[EVENT_BUF_LEN];
+
+#define MOUNT_NAME "mntpoint"
+static int mount_created;
+
+static void create_fanotify_groups(void)
+{
+	unsigned int p, i;
+	int ret;
+
+	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
+		for (i = 0; i < GROUPS_PER_PRIO; i++) {
+			fd_notify[p][i] = SAFE_FANOTIFY_INIT(fanotify_prio[p] |
+							     FAN_NONBLOCK,
+							     O_RDONLY);
+
+			/* Add mount mark for each group */
+			ret = fanotify_mark(fd_notify[p][i],
+					    FAN_MARK_ADD,
+					    FAN_OPEN,
+					    AT_FDCWD, fname);
+			if (ret < 0) {
+				tst_brk(TBROK | TERRNO,
+					"fanotify_mark(%d, FAN_MARK_ADD | "
+					"FAN_MARK_MOUNT, FAN_OPEN, AT_FDCWD,"
+					" %s) failed", fd_notify[p][i],
+					fname);
+			}
+			/* Add ignore mark for groups with higher priority */
+			if (p == 0)
+				continue;
+			ret = fanotify_mark(fd_notify[p][i],
+					    FAN_MARK_ADD | FAN_MARK_MOUNT |
+					    FAN_MARK_IGNORED_MASK |
+					    FAN_MARK_IGNORED_SURV_MODIFY,
+					    FAN_OPEN, AT_FDCWD, fname);
+			if (ret < 0) {
+				tst_brk(TBROK | TERRNO,
+					"fanotify_mark(%d, FAN_MARK_ADD | "
+					"FAN_MARK_IGNORED_MASK | "
+					"FAN_MARK_IGNORED_SURV_MODIFY, "
+					"FAN_OPEN, AT_FDCWD, %s) failed",
+					fd_notify[p][i], fname);
+			}
+		}
+	}
+}
+
+static void cleanup_fanotify_groups(void)
+{
+	unsigned int i, p;
+
+	for (p = 0; p < FANOTIFY_PRIORITIES; p++) {
+		for (i = 0; i < GROUPS_PER_PRIO; i++) {
+			if (fd_notify[p][i] > 0)
+				SAFE_CLOSE(fd_notify[p][i]);
+		}
+	}
+}
+
+static void verify_event(int group, struct fanotify_event_metadata *event)
+{
+	if (event->mask != FAN_OPEN) {
+		tst_res(TFAIL, "group %d get event: mask %llx (expected %llx) "
+			"pid=%u fd=%u", group, (unsigned long long)event->mask,
+			(unsigned long long)FAN_OPEN,
+			(unsigned)event->pid, event->fd);
+	} else if (event->pid != getpid()) {
+		tst_res(TFAIL, "group %d get event: mask %llx pid=%u "
+			"(expected %u) fd=%u", group,
+			(unsigned long long)event->mask, (unsigned)event->pid,
+			(unsigned)getpid(), event->fd);
+	} else {
+		tst_res(TPASS, "group %d get event: mask %llx pid=%u fd=%u",
+			group, (unsigned long long)event->mask,
+			(unsigned)event->pid, event->fd);
+	}
+}
+
+void test01(void)
+{
+	int ret, fd;
+	unsigned int p, i;
+	struct fanotify_event_metadata *event;
+
+	create_fanotify_groups();
+
+	/*
+	 * generate sequence of events
+	 */
+	fd = SAFE_OPEN(fname, O_RDONLY);
+	SAFE_CLOSE(fd);
+
+	/* First verify all groups without ignore mask got the event */
+	for (i = 0; i < GROUPS_PER_PRIO; i++) {
+		ret = read(fd_notify[0][i], event_buf, EVENT_BUF_LEN);
+		if (ret < 0) {
+			if (errno == EAGAIN) {
+				tst_res(TFAIL, "group %d did not get "
+					"event", i);
+			}
+			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 = (struct fanotify_event_metadata *)event_buf;
+		if (ret > (int)event->event_len) {
+			tst_res(TFAIL, "group %d got more than one "
+				"event (%d > %d)", i, ret,
+				event->event_len);
+		} else {
+			verify_event(i, event);
+		}
+		if (event->fd != FAN_NOFD)
+			SAFE_CLOSE(event->fd);
+	}
+	for (p = 1; p < FANOTIFY_PRIORITIES; p++) {
+		for (i = 0; i < GROUPS_PER_PRIO; i++) {
+			ret = read(fd_notify[p][i], event_buf, EVENT_BUF_LEN);
+			if (ret > 0) {
+				tst_res(TFAIL, "group %d got event",
+					p*GROUPS_PER_PRIO + i);
+				if (event->fd != FAN_NOFD)
+					SAFE_CLOSE(event->fd);
+			} else if (ret == 0) {
+				tst_brk(TBROK, "zero length "
+					"read from fanotify fd");
+			} else if (errno != EAGAIN) {
+				tst_brk(TBROK | TERRNO,
+					"reading fanotify events failed");
+			} else {
+				tst_res(TPASS, "group %d got no event",
+					p*GROUPS_PER_PRIO + i);
+			}
+		}
+	}
+	cleanup_fanotify_groups();
+}
+
+static void setup(void)
+{
+	SAFE_MKDIR(MOUNT_NAME, 0755);
+	SAFE_MOUNT(MOUNT_NAME, MOUNT_NAME, "none", MS_BIND, NULL);
+	mount_created = 1;
+	SAFE_CHDIR(MOUNT_NAME);
+
+	sprintf(fname, "tfile_%d", getpid());
+	SAFE_FILE_PRINTF(fname, "1");
+}
+
+static void cleanup(void)
+{
+	cleanup_fanotify_groups();
+
+	SAFE_CHDIR("../");
+
+	if (mount_created && tst_umount(MOUNT_NAME) < 0)
+		tst_brk(TBROK | TERRNO, "umount failed");
+}
+
+static struct tst_test test = {
+	.test_all = test01,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_tmpdir = 1,
+	.needs_root = 1
+};
+
+#else
+	TST_TEST_TCONF("system doesn't have required fanotify support");
+#endif