fstests: add test coverage for cloned filesystem ids
cover fstests: add test coverage for cloned filesystem ids
This series adds fstests infrastructure and test cases to verify correct filesystem identity behaviour when a filesystem is cloned (e.g. via block-level copy), covering inotify, fanotify, f_fsid, libblkid, IMA, and exportfs file handles.
- SCRATCH_DEV_POOL support extended to non-Btrfs filesystems
- _mkfs_scratch_sized_clone() helper to create a cloned filesystem
- _clone_mount_option() helper to apply per-filesystem clone mount options
New tests verify:
- inotify and fanotify events are isolated between cloned filesystems
- f_fsid is unique across cloned filesystem instances
- libblkid correctly resolves duplicate UUIDs to distinct devices
- IMA distinct identity for each cloned filesystem
- exportfs file handles resolve correctly on cloned filesystems
Testing:
Requires kernel patches [1] for all tests to pass.
[1] https://lore.kernel.org/linux-btrfs/[email protected]/
Anand Jain (9):
fstests: allow SCRATCH_DEV_POOL for non-Btrfs filesystems
fstests: add _mkfs_scratch_clone() helper
fstests: add _clone_mount_option() helper
fstests: add test for inotify isolation on cloned devices
fstests: verify fanotify isolation on cloned filesystems
fstests: verify f_fsid for cloned filesystems
fstests: verify libblkid resolution of duplicate UUIDs
fstests: verify IMA isolation on cloned filesystems
fstests: verify exportfs file handles on cloned filesystems
.gitignore | 1 + common/config | 1 + common/rc | 55 ++++++++++++++++++----- src/Makefile | 2 +- src/fanotify.c | 66 +++++++++++++++++++++++++++ tests/generic/790 | 78 ++++++++++++++++++++++++++++++++ tests/generic/790.out | 7 +++ tests/generic/791 | 86 +++++++++++++++++++++++++++++++++++ tests/generic/791.out | 7 +++ tests/generic/792 | 57 ++++++++++++++++++++++++ tests/generic/792.out | 7 +++ tests/generic/793 | 73 ++++++++++++++++++++++++++++++ tests/generic/793.out | 22 +++++++++ tests/generic/794 | 101 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/794.out | 10 +++++ tests/generic/795 | 67 ++++++++++++++++++++++++++++ tests/generic/795.out | 2 + 17 files changed, 629 insertions(+), 13 deletions(-) create mode 100644 src/fanotify.c create mode 100644 tests/generic/790 create mode 100644 tests/generic/790.out create mode 100644 tests/generic/791 create mode 100644 tests/generic/791.out create mode 100644 tests/generic/792 create mode 100644 tests/generic/792.out create mode 100644 tests/generic/793 create mode 100644 tests/generic/793.out create mode 100644 tests/generic/794 create mode 100644 tests/generic/794.out create mode 100644 tests/generic/795 create mode 100644 tests/generic/795.out
Signature · 1 line
2.43.0
PATCH 1/9 fstests: allow SCRATCH_DEV_POOL for non-Btrfs filesystems
Tests for cloned device verification should pass on Btrfs, XFS, and ext4. We need 2 scratch devices, allow SCRATCH_DEV_POOL for other FSs.
--- common/rc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-)
common/rc +3 -12
@@ -3990,18 +3990,9 @@ _require_scratch_dev_pool() ndevs=$1 fi - # btrfs test case needs ndevs or more scratch_dev_pool; other FS not sure - # so fail it - case $FSTYP in - btrfs) - if [ "`echo $SCRATCH_DEV_POOL|wc -w`" -lt $ndevs ]; then - _notrun "btrfs and this test needs $ndevs or more disks in SCRATCH_DEV_POOL" - fi - ;; - *) - _notrun "dev_pool is not supported by fstype \"$FSTYP\"" - ;; - esac + if [ "`echo $SCRATCH_DEV_POOL|wc -w`" -lt $ndevs ]; then + _notrun "This test needs $ndevs or more disks in SCRATCH_DEV_POOL" + fi for i in $SCRATCH_DEV_POOL; do if [ "`_is_block_dev "$i"`" = "" ]; then
Signature · 1 line
2.43.0
PATCH 2/9 fstests: add _mkfs_scratch_clone() helper
In reply to Anand JainIntroduce _mkfs_scratch_clone() to mkfs the scratch device and clone it to the next device in SCRATCH_DEV_POOL.
--- common/rc | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
common/rc +32 -0
@@ -1503,6 +1503,38 @@ _scratch_resvblks() esac } +_scratch_mkfs_sized_clone() +{ + local devs=($SCRATCH_DEV_POOL) + local scratch_data="$1" + local size=$(_small_fs_size_mb 128) # Smallest possible + + size=$((size * 1024 * 1024)) + + # make sure there are two devices + if [ "${#devs[@]}" -ne 2 ]; then + _notrun "Test requires exactly 2 devices" + fi + + case "$FSTYP" in + "btrfs") + _scratch_mkfs_sized $size + _scratch_mount + $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/sv1 + _scratch_unmount + ;; + "xfs"|"ext4") + _scratch_mkfs_sized $size + ;; + *) + _notrun "fstests clone op unsupported for FS $FSTYP" + ;; + esac + + # clone SCRATCH_DEV devs[0] to devs[1]. + dd if=$SCRATCH_DEV of=${devs[1]} bs=$size status=none count=1 || \ + _fail "Clone failed" +} # Repair scratch filesystem. Returns 0 if the FS is good to go (either no # errors found or errors were fixed) and nonzero otherwise; also spits out
Signature · 1 line
2.43.0
Re: [PATCH 2/9] fstests: add _mkfs_scratch_clone() helper
Quoted 16 lines · Anand Jain
Introduce
_mkfs_scratch_clone()to mkfs the scratch device and clone it to the next device inSCRATCH_DEV_POOL.Signed-off-by Anand Jain <[email protected]>--- common/rc | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)common/rc +0
-0@@ -1503,6 +1503,38 @@ _scratch_resvblks() esac }
common/rc _scratch_resvblks() · line 1515
Quoted 10 lines
common/rc +10
-0+_scratch_mkfs_sized_clone() +{ + local devs=($SCRATCH_DEV_POOL) + local scratch_data="$1" + local size=$(_small_fs_size_mb 128) # Smallest possible + + size=$((size * 1024 * 1024)) + + # make sure there are two devices + if [ "${#devs[@]}" -ne 2 ]; then
What about if ${#devs[@]} > 2 ?
Quoted 11 lines
common/rc +11
-0+ _notrun "Test requires exactly 2 devices" + fi + + case "$FSTYP" in + "btrfs") + _scratch_mkfs_sized $size + _scratch_mount + $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/sv1 + _scratch_unmount + ;; + "xfs"|"ext4")
common/rc _scratch_resvblks() · line 1536
Quoted 10 lines
common/rc +10
-0+ _scratch_mkfs_sized $size + ;; + *) + _notrun "fstests clone op unsupported for FS $FSTYP" + ;; + esac + + # clone SCRATCH_DEV devs[0] to devs[1]. + dd if=$SCRATCH_DEV of=${devs[1]} bs=$size status=none count=1 || \ + _fail "Clone failed"
I'm wondering if we absolutely need to use SCRATCH_DEV_POOL for this test. Could we clone SCRATCH_DEV to an image file instead? Or would it be feasible to simply run the test using two image files?
Thanks,
Zorro
Quoted 8 lines
common/rc +1
-0+} # Repair scratch filesystem. Returns 0 if the FS is good to go (either no # errors found or errors were fixed) and nonzero otherwise; also spits outSignature · 1 line
2.43.0
Re: [PATCH 2/9] fstests: add _mkfs_scratch_clone() helper
Quoted 2 lines
What about if ${#devs[@]} > 2 ?
On purpose- so tests using _scratch_mkfs_sized_clone() don't pick a random higher number. If a test needs more than two devices, opens an opportunity to critically review this function.
Quoted 15 lines
Quoted 15 lines
common/rc +15
-0+ fi + + case "$FSTYP" in + "btrfs") + _scratch_mkfs_sized $size + _scratch_mount + $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/sv1 + _scratch_unmount + ;; + "xfs"|"ext4") + _scratch_mkfs_sized $size + ;; + *) + _notrun "fstests clone op unsupported for FS $FSTYP" + ;;
Quoted 8 lines
common/rc _scratch_resvblks() · line 1536
Quoted 5 lines
common/rc +5
-0+ esac + + # clone SCRATCH_DEV devs[0] to devs[1]. + dd if=$SCRATCH_DEV of=${devs[1]} bs=$size status=none count=1 || \ + _fail "Clone failed"I'm wondering if we absolutely need to use
SCRATCH_DEV_POOLfor this test. Could we cloneSCRATCH_DEVto an image file instead? Or would it be feasible to simply run the test using two image files?
Using a config file and testing directly on block devices lets us cover a wider range of devices.
When I wrote this test case, I was also considering whether a serial-number–based mount without extra mount option, would be better in the kernel.
The downside is that xfs/ext4 testers now need to use SCRATCH_DEV_POOL. Let me know what you prefer.
Re: [PATCH 2/9] fstests: add _mkfs_scratch_clone() helper
Quoted 33 lines · Anand Jain
Quoted 2 lines
What about if ${#devs[@]} > 2 ?
On purpose- so tests using
_scratch_mkfs_sized_clone()don't pick a random higher number. If a test needs more than two devices, opens an opportunity to critically review this function.Quoted 15 lines
Quoted 15 lines
common/rc +15
-0+ fi + + case "$FSTYP" in + "btrfs") + _scratch_mkfs_sized $size + _scratch_mount + $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/sv1 + _scratch_unmount + ;; + "xfs"|"ext4") + _scratch_mkfs_sized $size + ;; + *) + _notrun "fstests clone op unsupported for FS $FSTYP" + ;;Quoted 8 lines
common/rc _scratch_resvblks() · line 1536
Quoted 5 lines
common/rc +5
-0+ esac + + # clone SCRATCH_DEV devs[0] to devs[1]. + dd if=$SCRATCH_DEV of=${devs[1]} bs=$size status=none count=1 || \ + _fail "Clone failed"I'm wondering if we absolutely need to use
SCRATCH_DEV_POOLfor this test. Could we cloneSCRATCH_DEVto an image file instead? Or would it be feasible to simply run the test using two image files?Using a config file and testing directly on block devices lets us cover a wider range of devices.
Quoted 6 lines
When I wrote this test case, I was also considering whether a serial-number–based mount without extra mount option, would be better in the kernel.
The downside is that xfs/ext4 testers now need to use
SCRATCH_DEV_POOL. Let me know what you prefer.
I'm concerned that testers from other filesystems might not be familiar with SCRATCH_DEV_POOL, as it's typically not a requirement for their testing. I'm not sure if they would be willing to configure and switch to SCRATCH_DEV_POOL, just for these few test cases. If we can avoid making the pool to be a hard requirement, it might be more user-friendly for other fs list. Welcome feedback and review points from other filesystem mailing lists :)
Thanks,
Zorro
Quoted 4 lines
Re: [PATCH 2/9] fstests: add _mkfs_scratch_clone() helper
Quoted 7 lines · Zorro Lang
I'm concerned that testers from other filesystems might not be familiar with
SCRATCH_DEV_POOL, as it's typically not a requirement for their testing. I'm not sure if they would be willing to configure and switch toSCRATCH_DEV_POOL, just for these few test cases. If we can avoid making the pool to be a hard requirement, it might be more user-friendly for other fs list. Welcome feedback and review points from other filesystem mailing lists :)
Yes, doing this using loop devices or ramdisk is going to get us much better coverage.
Re: [PATCH 2/9] fstests: add _mkfs_scratch_clone() helper
Quoted 11 lines · Christoph Hellwig
Quoted 7 lines · Zorro Lang
I'm concerned that testers from other filesystems might not be familiar with
SCRATCH_DEV_POOL, as it's typically not a requirement for their testing. I'm not sure if they would be willing to configure and switch toSCRATCH_DEV_POOL, just for these few test cases. If we can avoid making the pool to be a hard requirement, it might be more user-friendly for other fs list. Welcome feedback and review points from other filesystem mailing lists :)Yes, doing this using loop devices or ramdisk is going to get us much better coverage.
Agreed. Let's stick with SCRATCH_DEV compatibility for now. v2 is in the ML.
Thanks.
PATCH 3/9 fstests: add _clone_mount_option() helper
In reply to Anand JainAdds _clone_mount_option() helper function to handle filesystem-specific requirements for mounting cloned devices. Abstract the need for -o nouuid on XFS.
--- common/rc | 8 ++++++++ 1 file changed, 8 insertions(+)
common/rc +8 -0
@@ -397,6 +397,14 @@ _scratch_mount_options() $SCRATCH_DEV $SCRATCH_MNT } +_clone_mount_option() +{ + local mount_opts="" + [[ "$FSTYP" == "xfs" ]] && mount_opts="-o nouuid" + + echo $mount_opts +} + _supports_filetype() { local dir=$1
Signature · 1 line
2.43.0
PATCH 4/9 fstests: add test for inotify isolation on cloned devices
In reply to Anand JainAdd a new test, to verify that the kernel correctly differentiates between two block devices sharing the same FSID/UUID.
--- common/config | 1 + tests/generic/790 | 78 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/790.out | 7 ++++ 3 files changed, 86 insertions(+) create mode 100644 tests/generic/790 create mode 100644 tests/generic/790.out
common/config +1 -0
@@ -228,6 +228,7 @@ export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical) export PARTED_PROG="$(type -P parted)" export XFS_PROPERTY_PROG="$(type -P xfs_property)" export FSCRYPTCTL_PROG="$(type -P fscryptctl)" +export INOTIFYWAIT_PROG="$(type -P inotifywait)" # udev wait functions. #
tests/generic/790 new file +78 -0
78 lines not on this page · raw message
tests/generic/790.out new file +7 -0
@@ -0,0 +1,7 @@ +QA output created by 790 +Setup inotify watchers on both SCRATCH_MNT and mnt2 +Trigger file creation on SCRATCH_MNT +Trigger file creation on mnt2 +Verify inotify isolation +SUCCESS: SCRATCH_MNT events isolated. +SUCCESS: mnt2 events isolated.
Signature · 1 line
2.43.0
Re: [PATCH 4/9] fstests: add test for inotify isolation on cloned devices
Quoted 79 lines · Anand Jain
Add a new test, to verify that the kernel correctly differentiates between two block devices sharing the same FSID/UUID.
Signed-off-by Anand Jain <[email protected]>--- common/config | 1 + tests/generic/790 | 78 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/790.out | 7 ++++ 3 files changed, 86 insertions(+) create mode 100644 tests/generic/790 create mode 100644 tests/generic/790.outcommon/config +1
-0@@ -228,6 +228,7 @@ export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical) export PARTED_PROG="$(type -P parted)" export XFS_PROPERTY_PROG="$(type -P xfs_property)" export FSCRYPTCTL_PROG="$(type -P fscryptctl)" +export INOTIFYWAIT_PROG="$(type -P inotifywait)" # udev wait functions. #tests/generic/790 new file +49
-0@@ -0,0 +1,78 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 790 +# +# Verify if the kernel or userspace becomes confused when two block devices +# share the same fid/fsid/uuid. Create inotify on both original and cloned +# filesystem. Monitor the notification in the respective logs. + +. ./common/preamble + +_begin_fstest auto quick mount clone + +_require_test +_require_scratch_dev_pool 2 +_require_command "$INOTIFYWAIT_PROG" inotifywait + +_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" + +log1=$tmp.inotify1 +log2=$tmp.inotify2 + +echo "Setup inotify watchers on both SCRATCH_MNT and mnt2" +$INOTIFYWAIT_PROG -m -e create --format '%f' $SCRATCH_MNT > $log1 2>&1 & +pid1=$! +$INOTIFYWAIT_PROG -m -e create --format '%f' $mnt2 > $log2 2>&1 & +pid2=$! +sleep 2 + +echo "Trigger file creation on SCRATCH_MNT" +touch $SCRATCH_MNT/file_on_scratch_mnt +sync
tests/generic/790 line 59
Quoted 10 lines
tests/generic/790 new file +10
-0+sleep 1 + +echo "Trigger file creation on mnt2" +touch $mnt2/file_on_mnt2 +sync +sleep 1 + +echo "Verify inotify isolation" +kill $pid1 $pid2 +wait $pid1 $pid2 2>/dev/null
I think you also need to take care of killing the bg process in _cleanup() so that the test could be cleanly aborted.
Thanks,
Amir.
Quoted 35 lines
tests/generic/790 new file +19
-0+ +if grep -q "file_on_scratch_mnt" $log1 && ! grep -q "file_on_mnt2" $log1; then + echo "SUCCESS: SCRATCH_MNT events isolated." +else + echo "FAIL: SCRATCH_MNT inotify confusion!" + [ ! -s $log1 ] && echo " - SCRATCH_MNT received no events." + grep -q "file_on_mnt2" $log1 && echo " - SCRATCH_MNT received event from mnt2." +fi + +if grep -q "file_on_mnt2" $log2 && ! grep -q "file_on_scratch_mnt" $log2; then + echo "SUCCESS: mnt2 events isolated." +else + echo "FAIL: mnt2 inotify confusion!" + [ ! -s $log2 ] && echo " - mnt2 received no events." + grep -q "file_on_scratch_mnt" $log2 && echo " - mnt2 received event from SCRATCH_MNT." +fi + +status=0 +exittests/generic/790.out new file +7
-0@@ -0,0 +1,7 @@ +QA output created by 790 +Setup inotify watchers on both SCRATCH_MNT and mnt2 +Trigger file creation on SCRATCH_MNT +Trigger file creation on mnt2 +Verify inotify isolation +SUCCESS: SCRATCH_MNT events isolated. +SUCCESS: mnt2 events isolated.Signature · 1 line
2.43.0
Re: [PATCH 4/9] fstests: add test for inotify isolation on cloned devices
Quoted 37 lines
Quoted 37 lines
tests/generic/790 new file +36
-0+_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" + +log1=$tmp.inotify1 +log2=$tmp.inotify2 + +echo "Setup inotify watchers on both SCRATCH_MNT and mnt2" +$INOTIFYWAIT_PROG -m -e create --format '%f' $SCRATCH_MNT > $log1 2>&1 & +pid1=$! +$INOTIFYWAIT_PROG -m -e create --format '%f' $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
Quoted 8 lines
tests/generic/790 line 59
Quoted 5 lines
tests/generic/790 new file +5
-0+sleep 1 + +echo "Verify inotify isolation" +kill $pid1 $pid2 +wait $pid1 $pid2 2>/dev/nullI think you also need to take care of killing the bg process in
_cleanup()so that the test could be cleanly aborted.
You are right, SIGINT can terminate the testcase at any point, we need kill(1) in _cleanup().
I'll fix this in v2.
Thanks, Anand
PATCH 5/9 fstests: verify fanotify isolation on cloned filesystems
In reply to Anand JainVerify 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.
--- .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
.gitignore +1 -0
@@ -212,6 +212,7 @@ tags /src/dio-writeback-race /src/unlink-fsync /src/file_attr +/src/fanotify # Symlinked files /tests/generic/035.out
src/Makefile +1 -1
@@ -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 \
src/fanotify.c new file +66 -0
66 lines not on this page · raw message
tests/generic/791 new file +86 -0
86 lines not on this page · raw message
tests/generic/791.out new file +7 -0
@@ -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
Signature · 1 line
2.43.0
Re: [PATCH 5/9] fstests: verify fanotify isolation on cloned filesystems
Quoted 34 lines · Anand Jain
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 <[email protected]>--- .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.gitignore +1
-0@@ -212,6 +212,7 @@ tags /src/dio-writeback-race /src/unlink-fsync /src/file_attr +/src/fanotify # Symlinked files /tests/generic/035.outsrc/Makefile +0
-0
src/Makefile LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ · line 39
Quoted 5 lines
src/Makefile +1
-1@@ -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
Check if you already have fsnotifywait installed on your system most likely you do. It was added to inotify-tools quite some time ago. Could save you from adding a custom prog.
Not 100% sure about fsnotifywait, but quite sure that fsnotifywatch --verbose prints the FSID of events.
Thanks,
Amir.
Quoted 183 lines
src/Makefile +0
-0EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \ btrfs_crc32c_forged_name.py popdir.pl popattr.py \src/fanotify.c new file +66
-0@@ -0,0 +1,66 @@ +/* +* SPDX-License-Identifier: GPL-2.0 +* Copyright (c) 2026 Anand Jain <[email protected]>. 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 ; +}tests/generic/791 new file +86
-0@@ -0,0 +1,86 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. 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 +exittests/generic/791.out new file +7
-0@@ -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 foundSignature · 1 line
2.43.0
Re: [PATCH 5/9] fstests: verify fanotify isolation on cloned filesystems
Quoted 18 lines
src/Makefile LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ · line 39
Quoted 11 lines
src/Makefile +1
-1@@ -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 fanotifyCheck if you already have fsnotifywait installed on your system most likely you do. It was added to inotify-tools quite some time ago. Could save you from adding a custom prog.
Not 100% sure about fsnotifywait, but quite sure that fsnotifywatch --verbose prints the FSID of events.
I missed that command. V2 switched to using fsnotifywait. V2 is not sent yet, I'll wait for any further comments.
Thanks for the review.
Anand
Quoted 2 lines
Thanks,
Amir.
PATCH 6/9 fstests: verify f_fsid for cloned filesystems
In reply to Anand JainVerify that the cloned filesystem provides an f_fsid that is persistent across mount cycles, yet unique from the original filesystem's f_fsid.
--- tests/generic/792 | 57 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/792.out | 7 ++++++ 2 files changed, 64 insertions(+) create mode 100644 tests/generic/792 create mode 100644 tests/generic/792.out
tests/generic/792 new file +57 -0
@@ -0,0 +1,57 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 792 +# Verify f_fsid and s_uuid of cloned filesystems across mount cycle. + +. ./common/preamble + +_begin_fstest auto quick mount clone + +_require_test +_require_scratch_dev_pool 2 + +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts" +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: derive f_fsid from on-disk fsuuid and dev_t" + +_cleanup() +{ + cd / + rm -r -f $tmp.* + umount $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" + +fsid_scratch=$(stat -f -c "%i" $SCRATCH_MNT) +fsid_clone=$(stat -f -c "%i" $mnt2) + +echo "**** fsid initially ****" +echo $fsid_scratch | sed -e "s/$fsid_scratch/FSID_SCRATCH/g" +echo $fsid_clone | sed -e "s/$fsid_clone/FSID_CLONE/g" + +# Make sure fsid still match across a mount cycle, also reverse the order. +echo "**** fsid after mount cycle ****" +_scratch_unmount +_unmount $mnt2 +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \ + _fail "Failed to mount dev2" +_scratch_mount $(_clone_mount_option) + +stat -f -c "%i" $SCRATCH_MNT | sed -e "s/$fsid_scratch/FSID_SCRATCH/g" +stat -f -c "%i" $mnt2 | sed -e "s/$fsid_clone/FSID_CLONE/g" + +status=0 +exit
tests/generic/792.out new file +7 -0
@@ -0,0 +1,7 @@ +QA output created by 792 +**** fsid initially **** +FSID_SCRATCH +FSID_CLONE +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONE
Signature · 1 line
2.43.0
PATCH 7/9 fstests: verify libblkid resolution of duplicate UUIDs
In reply to Anand JainVerify how findmnt, df (libblkid) resolve device paths when multiple block devices share the same FSUUID.
--- tests/generic/793 | 73 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/793.out | 22 +++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/generic/793 create mode 100644 tests/generic/793.out
tests/generic/793 new file +73 -0
@@ -0,0 +1,73 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 793 +# Verify how libblkid resolve devices when multiple devices sharing the +# same FSUUID. + +. ./common/preamble +. ./common/filter + +_begin_fstest auto quick mount clone + +_require_test +_require_scratch_dev_pool 2 + +_cleanup() +{ + cd / + rm -r -f $tmp.* + umount $mnt2 2>/dev/null + _scratch_dev_pool_put +} + +filter_pool() +{ + _filter_scratch | \ + sed -e "s|${devs[1]}|DEV2|g" \ + -e "s|$mnt2|MNT2|g" | \ + _filter_spaces +} + +print_info() +{ + local mntpt=$1 + local tgt=$(findmnt -no SOURCE $mntpt) + local fsuuid=$(blkid -s UUID -o value $tgt) + + echo "mntpt=$mntpt tgt=$tgt fsuuid=$fsuuid" >> $seqres.full + echo + findmnt -o SOURCE,TARGET,UUID "$tgt" | tail -n +2 | \ + sed -e "s/$fsuuid/FSUUID/g" | filter_pool + awk -v dev="$tgt" '$1 == dev { print $1, $2 }' /proc/self/mounts | \ + filter_pool + df --all --output=source,target "$tgt" | tail -n +2 | filter_pool + echo +} + +_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" + +print_info $SCRATCH_MNT +print_info $mnt2 + +echo "**** mount cycle ****" +_scratch_unmount +_unmount $mnt2 +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \ + _fail "Failed to mount dev2" +_scratch_mount $(_clone_mount_option) + +print_info $SCRATCH_MNT +print_info $mnt2 + +status=0 +exit
tests/generic/793.out new file +22 -0
@@ -0,0 +1,22 @@ +QA output created by 793 + +SCRATCH_DEV SCRATCH_MNT FSUUID +SCRATCH_DEV SCRATCH_MNT +SCRATCH_DEV SCRATCH_MNT + + +DEV2 MNT2 FSUUID +DEV2 MNT2 +DEV2 MNT2 + +**** mount cycle **** + +SCRATCH_DEV SCRATCH_MNT FSUUID +SCRATCH_DEV SCRATCH_MNT +SCRATCH_DEV SCRATCH_MNT + + +DEV2 MNT2 FSUUID +DEV2 MNT2 +DEV2 MNT2 +
Signature · 1 line
2.43.0
PATCH 8/9 fstests: verify IMA isolation on cloned filesystems
In reply to Anand JainAdd testcase to verify IMA measurement isolation when multiple devices share the same FSUUID.
--- tests/generic/794 | 101 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/794.out | 10 +++++ 2 files changed, 111 insertions(+) create mode 100644 tests/generic/794 create mode 100644 tests/generic/794.out
tests/generic/794 new file +101 -0
@@ -0,0 +1,101 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 794 +# Verify IMA isolation on cloned filesystems: +# . Mount two devices sharing the same FSUUID (cloned). +# . Apply an IMA policy to measure files based on that FSUUID. +# . Create unique files on each mount point to trigger measurements. +# . Confirm the IMA log correctly attributes events to the respective mounts. + +. ./common/preamble +. ./common/filter + +_begin_fstest auto quick clone + +_require_test +_require_scratch_dev_pool 2 + +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts" +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: derive f_fsid from on-disk fsuuid and dev_t" + +_cleanup() +{ + cd / + rm -r -f $tmp.* + umount $mnt2 2>/dev/null + _scratch_dev_pool_put +} + +filter_pool() +{ + _filter_scratch | \ + sed -e "s|${devs[1]}|DEV2|g" \ + -e "s|$mnt2|MNT2|g" | \ + _filter_spaces +} + +do_ima() +{ + local ima_policy="/sys/kernel/security/ima/policy" + local ima_log="/sys/kernel/security/ima/ascii_runtime_measurements" + local fsuuid + local mnt=$1 + local run_enable=$2 + + # Since the in-memory IMA audit log is only cleared upon reboot, + # use unique random filenames to avoid log collisions. + local foofile=$(mktemp --dry-run foobar_XXXXX) + + echo $mnt $run_enable | filter_pool + + [ -w "$ima_policy" ] || _notrun "IMA policy not writable" + + fsuuid=$(blkid -s UUID -o value $SCRATCH_DEV) + + # Load IMA policy to measure file access specifically for this + # filesystem UUID. + if [[ $run_enable -eq 1 ]]; then + echo "measure func=FILE_CHECK fsuuid=$fsuuid" > "$ima_policy" || \ + _notrun "Policy rejected" + fi + + # Create a file to trigger measurement and verify its entry in + # the IMA log. + echo "test_data" > $mnt/$foofile + + # For $ima_log column entry please ref to + grep $foofile "$ima_log" | awk '{ print $5 }' | filter_pool | \ + sed "s/$foofile/FOOBAR_FILE/" + + echo "dbg: $mnt $fsuuid $foofile" >> $seqres.full + cat $ima_log | tail -1 >> $seqres.full + echo >> $seqres.full +} + +_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" + +do_ima $SCRATCH_MNT 1 +do_ima $mnt2 0 + +# Btrfs uses in-memory dynamic temp_fsid +echo mount cycle +_unmount $mnt2 +_mount $mount_opts ${devs[1]} $mnt2 || _fail "Failed to mount dev2" + +do_ima $SCRATCH_MNT 0 +do_ima $mnt2 0 + +status=0 +exit
tests/generic/794.out new file +10 -0
@@ -0,0 +1,10 @@ +QA output created by 794 +SCRATCH_MNT 1 +SCRATCH_MNT/FOOBAR_FILE +MNT2 0 +MNT2/FOOBAR_FILE +mount cycle +SCRATCH_MNT 0 +SCRATCH_MNT/FOOBAR_FILE +MNT2 0 +MNT2/FOOBAR_FILE
Signature · 1 line
2.43.0
PATCH 9/9 fstests: verify exportfs file handles on cloned filesystems
In reply to Anand JainEnsure that exportfs can correctly decode file handles on a cloned filesystem across a mount cycle, by file handles generated on a cloned device remain valid after mount cycle.
--- tests/generic/795 | 67 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/795.out | 2 ++ 2 files changed, 69 insertions(+) create mode 100644 tests/generic/795 create mode 100644 tests/generic/795.out
tests/generic/795 new file +67 -0
@@ -0,0 +1,67 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test No. 795 + +. ./common/preamble + +_begin_fstest auto quick exportfs clone + +_require_test +_require_exportfs +_require_scratch_dev_pool 2 +_require_test_program "open_by_handle" + +_cleanup() +{ + cd / + rm -r -f $tmp.* + _unmount $mnt2 2>/dev/null + _scratch_dev_pool_put +} + +# Create test dir and test files, encode file handles and store to tmp file +create_test_files() +{ + rm -rf $testdir + mkdir -p $testdir + $here/src/open_by_handle -cwp -o $tmp.handles_file $testdir $NUMFILES +} + +# Decode file handles loaded from tmp file +test_file_handles() +{ + local opt=$1 + local when=$2 + + echo test_file_handles after $when + $here/src/open_by_handle $opt -i $tmp.handles_file $mnt2 $NUMFILES +} + +_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" + +NUMFILES=1 +testdir=$mnt2/testdir + +# Decode file handles of files/dir after cycle mount +create_test_files + +_scratch_unmount +_unmount $mnt2 +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \ + _fail "Failed to mount dev2" +_scratch_mount $(_clone_mount_option) + +test_file_handles -rp "cycle mount" + +status=0 +exit
tests/generic/795.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 795 +test_file_handles after cycle mount
Signature · 1 line
2.43.0