tools/testing/selftests/filesystems/Makefile | 7 + .../selftests/filesystems/fanotify_basic.c | 122 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 tools/testing/selftests/filesystems/Makefile create mode 100644 tools/testing/selftests/filesystems/fanotify_basic.c
Hello,
Currently there are almost no automated selftests for fanotify notification
events in tools/testing/selftests/ (only mount namespace
related tests exist).
This patch adds a very basic selftest that exercises three fundamental
fanotify events:
- FAN_CREATE (file creation)
- FAN_MODIFY (file content modification via write())
- FAN_DELETE (file removal)
The test
- creates a test file, appends data, and removes it
- verifies that corresponding events are received and the masks contain
the expected bits (0x100, 0x2, 0x200)
Test TAP output:
ok 1 FAN_CREATE detected
ok 2 FAN_MODIFY detected
ok 3 FAN_DELETE detected
# PASSED: 1 / 1 tests passed.
This is intentionally kept minimal as a starting point.
Future work ideas (not in this patch):
- Test permission events
- Test rename/move events
- Verify file names
- Run under different filesystems
Any feedback on the direction, style, or additional test cases
would be greatly appreciated.
Thanks,
Jinseok.
Signed-off-by: Jinseok Kim <always.starving0@gmail.com>
---
tools/testing/selftests/filesystems/Makefile | 7 +
.../selftests/filesystems/fanotify_basic.c | 122 ++++++++++++++++++
2 files changed, 129 insertions(+)
create mode 100644 tools/testing/selftests/filesystems/Makefile
create mode 100644 tools/testing/selftests/filesystems/fanotify_basic.c
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
new file mode 100644
index 0000000..c0e0242
--- /dev/null
+++ b/tools/testing/selftests/filesystems/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS += $(KHDR_INCLUDES)
+TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog fanotify_basic
+TEST_GEN_PROGS_EXTENDED := dnotify_test
+
+include ../lib.mk
diff --git a/tools/testing/selftests/filesystems/fanotify_basic.c b/tools/testing/selftests/filesystems/fanotify_basic.c
new file mode 100644
index 0000000..4a4fbb4
--- /dev/null
+++ b/tools/testing/selftests/filesystems/fanotify_basic.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/fanotify.h>
+#include <linux/fanotify.h>
+#include "../kselftest_harness.h"
+#include "wrappers.h"
+
+static void create_file(const char *filename)
+{
+ int fd;
+ int ret;
+
+ fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd == -1)
+ ksft_exit_fail_msg("(create)open failed: %s\n", strerror(errno));
+ ret = write(fd, "create_file", 11);
+ if (ret == -1)
+ ksft_exit_fail_msg("(create) writing failed: %s\n", strerror(errno));
+ close(fd);
+}
+
+static void modify_file(const char *filename)
+{
+ int fd;
+ int ret;
+
+ fd = open(filename, O_RDWR);
+ if (fd == -1)
+ ksft_exit_fail_msg("(modify)open failed :%s\n", strerror(errno));
+ if (lseek(fd, 0, SEEK_END) < 0)
+ ksft_exit_fail_msg("(modify)lseek failed");
+ ret = write(fd, "modify_file", 11);
+ if (ret == -1)
+ ksft_exit_fail_msg("(modify)write failed :%s\n", strerror(errno));
+ if (fsync(fd) == -1)
+ ksft_exit_fail_msg("(modify)fsync failed: %s\n", strerror(errno));
+
+ close(fd);
+}
+
+TEST(fanotify_cud_test)
+{
+ int fan_fd;
+ char buf[4096];
+ int ret;
+ ssize_t len;
+ struct fanotify_event_metadata *meta;
+
+ fan_fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
+ ASSERT_GE(fan_fd, 0)
+ TH_LOG("fanotify_init failed: %s", strerror(errno));
+
+ ret = fanotify_mark(fan_fd, FAN_MARK_ADD,
+ FAN_EVENT_ON_CHILD | FAN_CREATE |
+ FAN_MODIFY | FAN_DELETE,
+ AT_FDCWD, "/tmp");
+ ASSERT_GE(ret, 0)
+ TH_LOG("fanotify_mark failed: %s", strerror(errno));
+
+ // FAN_CREATE Test
+ create_file("/tmp/fanotify_test");
+ len = read(fan_fd, buf, sizeof(buf));
+ ASSERT_GT(len, 0)
+ TH_LOG("No event after create_file");
+
+ meta = (void *)buf;
+ if (FAN_EVENT_OK(meta, len)) {
+ TH_LOG("Event after create: mask = 0x%llx, pid=%d",
+ (unsigned long long)meta->mask, meta->pid);
+ if (meta->mask & FAN_CREATE)
+ ksft_test_result_pass("FAN_CREATE detected\n");
+ else
+ TH_LOG("FAN_CREATE missing");
+ } else
+ ksft_test_result_fail("Invalid event metadata after create\n");
+
+ // FAN_MODIFY Test
+ modify_file("/tmp/fanotify_test");
+ len = read(fan_fd, buf, sizeof(buf));
+ ASSERT_GT(len, 0)
+ TH_LOG("No event after modify_file");
+
+ meta = (void *)buf;
+ if (FAN_EVENT_OK(meta, len)) {
+ TH_LOG("Event after modify: mask = 0x%llx, pid=%d",
+ (unsigned long long)meta->mask, meta->pid);
+ if (meta->mask & FAN_MODIFY)
+ ksft_test_result_pass("FAN_MODIFY detected\n");
+ else
+ ksft_test_result_fail("FAN_MODIFY missing\n");
+ } else
+ ksft_test_result_fail("Invalid event metadata after modify\n");
+
+ // FAN_DELETE
+ ASSERT_EQ(unlink("/tmp/fanotify_test"), 0)
+ TH_LOG("unlink failed: %s", strerror(errno));
+
+ len = read(fan_fd, buf, sizeof(buf));
+ ASSERT_GT(len, 0)
+ TH_LOG("No event after unlink");
+
+ meta = (void *)buf;
+ if (FAN_EVENT_OK(meta, len)) {
+ TH_LOG("Event after delete: mask = 0x%llx, pid=%d",
+ (unsigned long long)meta->mask, meta->pid);
+ if (meta->mask & FAN_DELETE)
+ ksft_test_result_pass("FAN_DELETE detected\n");
+ else
+ ksft_test_result_fail("FAN_DELETE missing\n");
+ } else
+ ksft_test_result_fail("Invalid event metadata after delete\n");
+
+ // Clean up
+ if (fan_fd >= 0) {
+ fanotify_mark(fan_fd, FAN_MARK_REMOVE, 0, AT_FDCWD, ".");
+ close(fan_fd);
+ }
+}
+
+TEST_HARNESS_MAIN
--
2.43.0
Hello!
On Wed 04-02-26 03:15:43, Jinseok Kim wrote:
> Currently there are almost no automated selftests for fanotify notification
> events in tools/testing/selftests/ (only mount namespace
> related tests exist).
Thanks for the patch but we have a very comprehensive tests for inotify and
fanotify in LTP so I don't see a reason to duplicate those inside the
kernel...
Honza
>
> This patch adds a very basic selftest that exercises three fundamental
> fanotify events:
> - FAN_CREATE (file creation)
> - FAN_MODIFY (file content modification via write())
> - FAN_DELETE (file removal)
>
> The test
> - creates a test file, appends data, and removes it
> - verifies that corresponding events are received and the masks contain
> the expected bits (0x100, 0x2, 0x200)
>
> Test TAP output:
> ok 1 FAN_CREATE detected
> ok 2 FAN_MODIFY detected
> ok 3 FAN_DELETE detected
> # PASSED: 1 / 1 tests passed.
>
> This is intentionally kept minimal as a starting point.
>
> Future work ideas (not in this patch):
> - Test permission events
> - Test rename/move events
> - Verify file names
> - Run under different filesystems
>
> Any feedback on the direction, style, or additional test cases
> would be greatly appreciated.
>
> Thanks,
> Jinseok.
>
> Signed-off-by: Jinseok Kim <always.starving0@gmail.com>
> ---
> tools/testing/selftests/filesystems/Makefile | 7 +
> .../selftests/filesystems/fanotify_basic.c | 122 ++++++++++++++++++
> 2 files changed, 129 insertions(+)
> create mode 100644 tools/testing/selftests/filesystems/Makefile
> create mode 100644 tools/testing/selftests/filesystems/fanotify_basic.c
>
> diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
> new file mode 100644
> index 0000000..c0e0242
> --- /dev/null
> +++ b/tools/testing/selftests/filesystems/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +CFLAGS += $(KHDR_INCLUDES)
> +TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog fanotify_basic
> +TEST_GEN_PROGS_EXTENDED := dnotify_test
> +
> +include ../lib.mk
> diff --git a/tools/testing/selftests/filesystems/fanotify_basic.c b/tools/testing/selftests/filesystems/fanotify_basic.c
> new file mode 100644
> index 0000000..4a4fbb4
> --- /dev/null
> +++ b/tools/testing/selftests/filesystems/fanotify_basic.c
> @@ -0,0 +1,122 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <sys/fanotify.h>
> +#include <linux/fanotify.h>
> +#include "../kselftest_harness.h"
> +#include "wrappers.h"
> +
> +static void create_file(const char *filename)
> +{
> + int fd;
> + int ret;
> +
> + fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
> + if (fd == -1)
> + ksft_exit_fail_msg("(create)open failed: %s\n", strerror(errno));
> + ret = write(fd, "create_file", 11);
> + if (ret == -1)
> + ksft_exit_fail_msg("(create) writing failed: %s\n", strerror(errno));
> + close(fd);
> +}
> +
> +static void modify_file(const char *filename)
> +{
> + int fd;
> + int ret;
> +
> + fd = open(filename, O_RDWR);
> + if (fd == -1)
> + ksft_exit_fail_msg("(modify)open failed :%s\n", strerror(errno));
> + if (lseek(fd, 0, SEEK_END) < 0)
> + ksft_exit_fail_msg("(modify)lseek failed");
> + ret = write(fd, "modify_file", 11);
> + if (ret == -1)
> + ksft_exit_fail_msg("(modify)write failed :%s\n", strerror(errno));
> + if (fsync(fd) == -1)
> + ksft_exit_fail_msg("(modify)fsync failed: %s\n", strerror(errno));
> +
> + close(fd);
> +}
> +
> +TEST(fanotify_cud_test)
> +{
> + int fan_fd;
> + char buf[4096];
> + int ret;
> + ssize_t len;
> + struct fanotify_event_metadata *meta;
> +
> + fan_fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
> + ASSERT_GE(fan_fd, 0)
> + TH_LOG("fanotify_init failed: %s", strerror(errno));
> +
> + ret = fanotify_mark(fan_fd, FAN_MARK_ADD,
> + FAN_EVENT_ON_CHILD | FAN_CREATE |
> + FAN_MODIFY | FAN_DELETE,
> + AT_FDCWD, "/tmp");
> + ASSERT_GE(ret, 0)
> + TH_LOG("fanotify_mark failed: %s", strerror(errno));
> +
> + // FAN_CREATE Test
> + create_file("/tmp/fanotify_test");
> + len = read(fan_fd, buf, sizeof(buf));
> + ASSERT_GT(len, 0)
> + TH_LOG("No event after create_file");
> +
> + meta = (void *)buf;
> + if (FAN_EVENT_OK(meta, len)) {
> + TH_LOG("Event after create: mask = 0x%llx, pid=%d",
> + (unsigned long long)meta->mask, meta->pid);
> + if (meta->mask & FAN_CREATE)
> + ksft_test_result_pass("FAN_CREATE detected\n");
> + else
> + TH_LOG("FAN_CREATE missing");
> + } else
> + ksft_test_result_fail("Invalid event metadata after create\n");
> +
> + // FAN_MODIFY Test
> + modify_file("/tmp/fanotify_test");
> + len = read(fan_fd, buf, sizeof(buf));
> + ASSERT_GT(len, 0)
> + TH_LOG("No event after modify_file");
> +
> + meta = (void *)buf;
> + if (FAN_EVENT_OK(meta, len)) {
> + TH_LOG("Event after modify: mask = 0x%llx, pid=%d",
> + (unsigned long long)meta->mask, meta->pid);
> + if (meta->mask & FAN_MODIFY)
> + ksft_test_result_pass("FAN_MODIFY detected\n");
> + else
> + ksft_test_result_fail("FAN_MODIFY missing\n");
> + } else
> + ksft_test_result_fail("Invalid event metadata after modify\n");
> +
> + // FAN_DELETE
> + ASSERT_EQ(unlink("/tmp/fanotify_test"), 0)
> + TH_LOG("unlink failed: %s", strerror(errno));
> +
> + len = read(fan_fd, buf, sizeof(buf));
> + ASSERT_GT(len, 0)
> + TH_LOG("No event after unlink");
> +
> + meta = (void *)buf;
> + if (FAN_EVENT_OK(meta, len)) {
> + TH_LOG("Event after delete: mask = 0x%llx, pid=%d",
> + (unsigned long long)meta->mask, meta->pid);
> + if (meta->mask & FAN_DELETE)
> + ksft_test_result_pass("FAN_DELETE detected\n");
> + else
> + ksft_test_result_fail("FAN_DELETE missing\n");
> + } else
> + ksft_test_result_fail("Invalid event metadata after delete\n");
> +
> + // Clean up
> + if (fan_fd >= 0) {
> + fanotify_mark(fan_fd, FAN_MARK_REMOVE, 0, AT_FDCWD, ".");
> + close(fan_fd);
> + }
> +}
> +
> +TEST_HARNESS_MAIN
> --
> 2.43.0
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
Thanks for the feedback!
I agree LTP has very comprehensive fanotify/inotify tests.
However, the motivation for adding basic tests to kernel selftests is:
- Quick and lightweight regression checking during kernel
development/boot (no external LTP install needed)
- Non-root basic cases (many LTP tests require root or complex setup)
Similar to how selftests/mm or selftests/net have basic syscall wrappers
even though LTP covers them deeply.
Do you think a different approach (LTP improvement instead)
would be better?
Thanks,
Jinseok
Hello! On Thu 05-02-26 19:04:34, Jinseok Kim wrote: > Thanks for the feedback! > > I agree LTP has very comprehensive fanotify/inotify tests. > > However, the motivation for adding basic tests to kernel selftests is: > - Quick and lightweight regression checking during kernel > development/boot (no external LTP install needed) > - Non-root basic cases (many LTP tests require root or complex setup) Hum, I don't quite buy the "LTP is difficult to run" argument. I find it as hard as running kernel selftests to be honest :). I don't even bother installing LTP and just directly run testcases from LTP source tree. The "LTP tests require root" is a valid argument but not really problematic for the setup I use. The point I'm trying to make is: I'm not strictly opposed to fanotify kernel selftests but they do add some maintenance burden and I don't see the usefulness of this. So maybe can you start with explaining your usecase - how are these tests going to make your life easier? > Do you think a different approach (LTP improvement instead) > would be better? As Amir wrote we definitely don't plan on moving all tests from LTP to selftests so if you are missing some functionality from tests in LTP, it would be good to add it there... Honza -- Jan Kara <jack@suse.com> SUSE Labs, CR
On Thu, Feb 5, 2026 at 11:05 AM Jinseok Kim <always.starving0@gmail.com> wrote: > > Thanks for the feedback! > > I agree LTP has very comprehensive fanotify/inotify tests. > > However, the motivation for adding basic tests to kernel selftests is: > - Quick and lightweight regression checking during kernel > development/boot (no external LTP install needed) > - Non-root basic cases (many LTP tests require root or complex setup) > > Similar to how selftests/mm or selftests/net have basic syscall wrappers > even though LTP covers them deeply. > If you just want to add some basic sanity tests, maybe this is ok, but... > Do you think a different approach (LTP improvement instead) > would be better? > Using the word "improvement" here suggest that you want more than basic sanity and if you want more than basic sanity then by all means, we do not want to duplicate all of the test LTP test coverage here so please invest your time in improving LTP tests coverage. mount-notify was written as selftest because there was no good infrastructure to test user namespaces in LTP. If anything, I would recommend trying to move those selftests to LTP. Thanks, Amir.
Thanks again for the detailed feedback and explanations! I completely understand your concerns about maintenance burden and overlap with LTP's comprehensive coverage. I agree that improving LTP's fanotify tests would be a better use of time rather than adding to selftests. I'll shift my focus to LTP contributions instead. Thanks for the guidance — really appreciate your time! Jinseok
© 2016 - 2026 Red Hat, Inc.