[PATCH 5/9] fstests: verify fanotify isolation on cloned filesystems

Anand Jain
2026-02-26T14:42:08Z

Verify that fanotify events are correctly routed to the appropriate
watcher when cloned filesystems are mounted.
Helps verify kernel's event notification distinguishes between devices
sharing the same FSID/UUID.

Signed-off-by: Anand Jain <asj@kernel.org>
---
.gitignore | 1 +
src/Makefile | 2 +-
src/fanotify.c | 66 +++++++++++++++++++++++++++++++++
tests/generic/791 | 86 +++++++++++++++++++++++++++++++++++++++++++
tests/generic/791.out | 7 ++++
5 files changed, 161 insertions(+), 1 deletion(-)
create mode 100644 src/fanotify.c
create mode 100644 tests/generic/791
create mode 100644 tests/generic/791.out

diff --git a/.gitignore b/.gitignore
index 82c57f415301..7f91310ce58b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -212,6 +212,7 @@ tags
/src/dio-writeback-race
/src/unlink-fsync
/src/file_attr
+/src/fanotify

# Symlinked files
/tests/generic/035.out
diff --git a/src/Makefile b/src/Makefile
index d0a4106e6be8..ff71cde936a7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -36,7 +36,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \
detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe \
uuid_ioctl t_snapshot_deleted_subvolume fiemap-fault min_dio_alignment \
- rw_hint
+ rw_hint fanotify

EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \
btrfs_crc32c_forged_name.py popdir.pl popattr.py \
diff --git a/src/fanotify.c b/src/fanotify.c
new file mode 100644
index 000000000000..e30c48dc0e52
--- /dev/null
+++ b/src/fanotify.c
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ * Copyright (c) 2026 Anand Jain <asj@kernel.org>. All Rights Reserved.
+ *
+ * Simple fanotify monitor to verify mount-point event isolation.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/fanotify.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ char buf[4096] __attribute__((aligned(8)));
+ setlinebuf(stdout);
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <path>\n", argv[0]);
+ return 1;
+ }
+
+ // Initialize with FID reporting
+ fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
+ if (fd < 0) {
+ perror("fanotify_init");
+ return 1;
+ }
+
+ if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
+ FAN_CREATE, AT_FDCWD, argv[1]) < 0) {
+ perror("fanotify_mark");
+ return 1;
+ }
+
+ printf("Listening for events on %s...\n", argv[1]);
+ while (1) {
+ struct fanotify_event_metadata *metadata = (struct fanotify_event_metadata *)buf;
+ ssize_t len = read(fd, buf, sizeof(buf));
+
+ if (len <= 0) break;
+
+ while (FAN_EVENT_OK(metadata, len)) {
+ // metadata_len is the offset to the first info record
+ if (metadata->event_len > metadata->metadata_len) {
+ struct fanotify_event_info_header *hdr =
+(struct fanotify_event_info_header *)((char *)metadata + metadata->metadata_len);
+
+ if (hdr->info_type == FAN_EVENT_INFO_TYPE_FID) {
+ struct fanotify_event_info_fid *fid = (struct fanotify_event_info_fid *)hdr;
+ printf("FSID: %08x%08x\n",
+ fid->fsid.val[0], fid->fsid.val[1]);
+ }
+ }
+ metadata = FAN_EVENT_NEXT(metadata, len);
+ }
+ }
+
+ fflush(stdout);
+ close(fd);
+ return 0;
+}
diff --git a/tests/generic/791 b/tests/generic/791
new file mode 100644
index 000000000000..fe8109083732
--- /dev/null
+++ b/tests/generic/791
@@ -0,0 +1,86 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. All Rights Reserved.
+#
+# FS QA Test 791
+# Verify fanotify FID functionality on cloned filesystems by setting up
+# watchers and making sure notifications are in the correct logs files.
+
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_scratch_dev_pool 2
+
+[ "$FSTYP" = "ext4" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
+ "ext4: derive f_fsid from block device to avoid collisions"
+
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.*
+ umount $mnt1 $mnt2 2>/dev/null
+ _scratch_dev_pool_put
+}
+
+_scratch_dev_pool_get 2
+_scratch_mkfs_sized_clone >$seqres.full 2>&1
+devs=($SCRATCH_DEV_POOL)
+mnt2=$TEST_DIR/mnt2
+mkdir -p $mnt2
+
+_scratch_mount $(_clone_mount_option)
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+ _fail "Failed to mount dev2"
+
+fsid1=$(stat -f -c "%i" $SCRATCH_MNT)
+fsid2=$(stat -f -c "%i" $mnt2)
+
+[[ "$fsid1" == "$fsid2" ]] && \
+ _notrun "Require clone filesystem with unique f_fsid"
+
+log1=$tmp.fanotify1
+log2=$tmp.fanotify2
+
+echo "Setup FID fanotify watchers on both SCRATCH_MNT and mnt2"
+$here/src/fanotify $SCRATCH_MNT > $log1 2>&1 &
+pid1=$!
+$here/src/fanotify $mnt2 > $log2 2>&1 &
+pid2=$!
+sleep 2
+
+echo "Trigger file creation on SCRATCH_MNT"
+touch $SCRATCH_MNT/file_on_scratch_mnt
+sync
+sleep 1
+
+echo "Trigger file creation on mnt2"
+touch $mnt2/file_on_mnt2
+sync
+sleep 1
+
+echo "Verify fsid in the fanotify"
+kill $pid1 $pid2
+wait $pid1 $pid2 2>/dev/null
+
+echo fsid1=$fsid1 fsid2=$fsid2 >> $seqres.full
+cat $log1 >> $seqres.full
+cat $log2 >> $seqres.full
+
+if grep -q "${fsid1}" $log1 && ! grep -q "${fsid2}" $log1; then
+ echo "SUCCESS: SCRATCH_MNT events found"
+else
+ [ ! -s $log1 ] && echo " - SCRATCH_MNT received no events."
+ grep -q "${fsid2}" $log1 && echo " - SCRATCH_MNT received event from mnt2."
+fi
+
+if grep -q "${fsid2}" $log2 && ! grep -q "${fsid1}" $log2; then
+ echo "SUCCESS: mnt2 events found"
+else
+ [ ! -s $log2 ] && echo " - mnt2 received no events."
+ grep -q "${fsid1}" $log2 && echo " - mnt2 received event from SCRATCH_MNT."
+fi
+
+status=0
+exit
diff --git a/tests/generic/791.out b/tests/generic/791.out
new file mode 100644
index 000000000000..9725c99bcb4b
--- /dev/null
+++ b/tests/generic/791.out
@@ -0,0 +1,7 @@
+QA output created by 791
+Setup FID fanotify watchers on both SCRATCH_MNT and mnt2
+Trigger file creation on SCRATCH_MNT
+Trigger file creation on mnt2
+Verify fsid in the fanotify
+SUCCESS: SCRATCH_MNT events found
+SUCCESS: mnt2 events found
--
2.43.0