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 <[email protected]>
---
 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

Reply via email to