Skip to contentSkip to conversation

fstests: add test coverage for cloned filesystem ids

PATCH In review Anand Jain 3 participants 42 messages Jul 25 – Sep 28 Btrfs file system

lore
Share & tools
lore

Anand Jain

author
#0 lore raw

v8 cover fstests: add test coverage for cloned filesystem ids

v8:
- _require_fs_space: double the required space (x2) to account for both source and clone
- Use proper quoting/unquoting for _mkfs_dev (Thanks Darrick)
- Call _fail() if cp fails
- _require_fanotify_function: check for kernel fsnotify support
- _require_unique_f_fsid: reorganize comments and replace 'if' with 'case' statement (Thanks Darrick)
- Add SEMANAGE_PROG variable (Thanks Darrick)
- fanotify verification: add checks for SEMANAGE_PROG and _require_fanotify_function
- Verify that the monitoring process is actually created

v7:
https://lore.kernel.org/fstests/[email protected]

v6:
https://lore.kernel.org/fstests/[email protected]

v5:
https://lore.kernel.org/fstests/[email protected]

v4:
https://lore.kernel.org/fstests/[email protected]

v3:
https://lore.kernel.org/fstests/[email protected]

v2:
https://lore.kernel.org/fstests/[email protected]

v1:
https://lore.kernel.org/fstests/[email protected]

This series adds fstests infrastructure and test cases to verify correct filesystem identity when a filesystem is cloned (block-level copy). Test covers inotify, fanotify, f_fsid, libblkid, IMA, exportfs file handles and libblkid tools verify with metadata_uuid.
New helpers:
_loop_image_create_clone() and _loop_image_destroy() to help create fs and clone _clone_mount_option() helper to apply per-filesystem clone mount options _require_fanotify_function check if kernel supports fs notification _require_unique_f_fsid() helper to handle fs difference in fsid implementation

New tests:
- fanotify events are isolated between cloned filesystems
- f_fsid is unique across cloned filesystem instances
- libblkid correctly resolves duplicate UUIDs to distinct devices with and without metadata_uuid
- IMA distinct identity for each cloned filesystem
- exportfs file handles resolve correctly on cloned filesystems

Kernel Patches:
Requires Btrfs kernel patches for all tests to pass.
[1] https://lore.kernel.org/linux-btrfs/[email protected]

Anand Jain (13):
fstests: add _loop_image_create_clone() helper
fstests: add _clone_mount_option() helper
fstests: add FSNOTIFYWAIT_PROG
fstests: add _require_fanotify_function
fstests: add _require_unique_f_fsid() helper
fstests: add SEMANAGE_PROG
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
fstests: add _change_metadata_uuid helper
fstests: test UUID consistency for clones with metadata_uuid

 common/config         |   2 +
 common/rc             | 135 +++++++++++++++++++++++++++++++++++++++
 tests/generic/801     | 143 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/801.out |   7 +++
 tests/generic/802     |  64 +++++++++++++++++++
 tests/generic/802.out |   4 ++
 tests/generic/803     |  72 +++++++++++++++++++++
 tests/generic/803.out |   6 ++
 tests/generic/804     | 108 +++++++++++++++++++++++++++++++
 tests/generic/804.out |  10 +++
 tests/generic/805     |  80 +++++++++++++++++++++++
 tests/generic/805.out |   2 +
 tests/generic/806     |  74 ++++++++++++++++++++++
 tests/generic/806.out |   6 ++
 14 files changed, 713 insertions(+)
 create mode 100644 tests/generic/801
 create mode 100644 tests/generic/801.out
 create mode 100644 tests/generic/802
 create mode 100644 tests/generic/802.out
 create mode 100644 tests/generic/803
 create mode 100644 tests/generic/803.out
 create mode 100644 tests/generic/804
 create mode 100644 tests/generic/804.out
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out
 create mode 100644 tests/generic/806
 create mode 100644 tests/generic/806.out
Signature · 1 line
2.43.0
author
#1 lore raw

PATCH v8 1/13 fstests: add _loop_image_create_clone() helper

Introduce _loop_image_create_clone() and _loop_image_destroy() to mkfs an image file and clone it to another image file, and attach a loop device to them. And its destroy part.

---
 common/rc | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
common/rc +65 -0
index 106f044adc2d..e8ff5be0c547 100644
@@ -1520,6 +1520,71 @@ _scratch_resvblks()
 	esac
 }
 
+# Create a small loop image, run an optional tuning function ($2) on it,
+# clone it, and attach both to loop devices, returned in ($1).
+# Args:
+#   $1: Nameref to return the array of allocated loop devices [base, clone].
+#   $2: Optional callback function to tune the base filesystem before cloning.
+_loop_image_create_clone()
+{
+	local -n _ret=$1
+	local pre_clone_tune_func="$2"
+	local img_file=$TEST_DIR/${seq}.img
+	local img_file_clone=$TEST_DIR/${seq}_clone.img
+	local size=$(_small_fs_size_mb 128) # Smallest possible
+	local loop_devs=()
+
+	# Since we copy the block device image, we keep its size small.
+	_require_fs_space $TEST_DIR $((size * 1024 * 2))
+
+	_create_file_sized $((size * 1024 * 1024)) $img_file ||
+				_fail "Failed: Create $img_file $size"
+
+	loop_devs=("$(_create_loop_device $img_file)")
+
+	case $FSTYP in
+	xfs)
+		_mkfs_dev -s size=4096 "${loop_devs[0]}"
+		;;
+	btrfs)
+		_mkfs_dev "${loop_devs[0]}"
+		;;
+	*)
+		_mkfs_dev "${loop_devs[0]}"
+		;;
+	esac
+
+	# Only execute if the function argument is not empty
+	if [ -n "$pre_clone_tune_func" ]; then
+		$pre_clone_tune_func "${loop_devs[0]}"
+	fi
+
+	sync "${loop_devs[0]}"
+	cp $img_file $img_file_clone || _fail "Failed to copy cloned image"
+
+	loop_devs+=("$(_create_loop_device $img_file_clone)")
+
+	_ret=()
+	for i in "${loop_devs[@]}"; do
+		_ret+=("$i")
+	done
+}
+
+# Teardown loop devices and delete their underlying backing image files.
+# Accepts a list of loop device paths (e.g., /dev/loop0 /dev/loop1).
+_loop_image_destroy()
+{
+	for d in "$@"; do
+		# Retrieve the path of the backing file
+		local f=$(losetup --noheadings --output BACK-FILE $d)
+
+		# Detach the loop device from the backing file
+		_destroy_loop_device "$d"
+
+		# Clean up the backing disk image file
+		[ -n "$f" ] && rm -f "$f"
+	done
+}
 
 # 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
#2 lore raw

Re: [PATCH v8 01/13] fstests: add _loop_image_create_clone() helper

Quoted 14 lines · Anand Jain

Introduce _loop_image_create_clone() and _loop_image_destroy() to mkfs an image file and clone it to another image file, and attach a loop device to them. And its destroy part.

---
 common/rc | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
common/rc +0 -0
index 106f044adc2d..e8ff5be0c547 100644

common/rc _scratch_resvblks() · line 1528

Quoted 9 lines
common/rc +6 -0
@@ -1520,6 +1520,71 @@ _scratch_resvblks()
 	esac
 }
 
+# Create a small loop image, run an optional tuning function ($2) on it,
+# clone it, and attach both to loop devices, returned in ($1).
+# Args:
+#   $1: Nameref to return the array of allocated loop devices [base, clone].
+#   $2: Optional callback function to tune the base filesystem before cloning.
+_loop_image_create_clone()

To capture both the relationship and distinction between this function and existing _create_loop_device, I'd like to rename it to *_create_cloned_loop_devs*.

Quoted 9 lines
common/rc +9 -0
+{
+	local -n _ret=$1
+	local pre_clone_tune_func="$2"
+	local img_file=$TEST_DIR/${seq}.img
+	local img_file_clone=$TEST_DIR/${seq}_clone.img
+	local size=$(_small_fs_size_mb 128) # Smallest possible
+	local loop_devs=()
+
+	# Since we copy the block device image, we keep its size small.

common/rc _scratch_resvblks() · line 1547

Quoted 10 lines
common/rc +10 -0
+	_require_fs_space $TEST_DIR $((size * 1024 * 2))
+
+	_create_file_sized $((size * 1024 * 1024)) $img_file ||
+				_fail "Failed: Create $img_file $size"
+
+	loop_devs=("$(_create_loop_device $img_file)")
+
+	case $FSTYP in
+	xfs)
+		_mkfs_dev -s size=4096 "${loop_devs[0]}"

Why xfs needs a specific "-s size=4096" ? Hardcoding this parameter might cause failures in certain incompatible environments.

common/rc _scratch_resvblks() · line 1550

Quoted 3 lines
common/rc +3 -0
+		;;
+	btrfs)
+		_mkfs_dev "${loop_devs[0]}"

If btrfs is handled the same way as the default *) branch below, there is no need to have a dedicated branch for it.

common/rc _scratch_resvblks() · line 1562

Quoted 12 lines
common/rc +12 -0
+		;;
+	*)
+		_mkfs_dev "${loop_devs[0]}"
+		;;
+	esac
+
+	# Only execute if the function argument is not empty
+	if [ -n "$pre_clone_tune_func" ]; then
+		$pre_clone_tune_func "${loop_devs[0]}"
+	fi
+
+	sync "${loop_devs[0]}"

Since the mkfs command above operates directly on a loop device, I wanted to make sure, can the sync command be targeted at a block device, and does it guarantee a cache flush or blockdev page cache sync ?

How about:
sync -d "${loop_devs[0]}"
blockdev --flushbufs "${loop_devs[0]}"
or any better idea?

common/rc _scratch_resvblks() · line 1570

Quoted 8 lines
common/rc +8 -0
+	cp $img_file $img_file_clone || _fail "Failed to copy cloned image"
+
+	loop_devs+=("$(_create_loop_device $img_file_clone)")
+
+	_ret=()
+	for i in "${loop_devs[@]}"; do
+		_ret+=("$i")
+	done

How about:
_ret=("${loop_devs[@]}")
directly?

common/rc _scratch_resvblks() · line 1575

Quoted 5 lines
common/rc +5 -0
+}
+
+# Teardown loop devices and delete their underlying backing image files.
+# Accepts a list of loop device paths (e.g., /dev/loop0 /dev/loop1).
+_loop_image_destroy()

To keep it consistent with the renamed function above, we could rename this to *_destroy_cloned_loop_devs*. Alternatively, if we don't consider it clone-specific, *destroy_loop_devs* would also be good to me.

Thanks,
Zorro

Quoted 18 lines
common/rc +12 -0
+{
+	for d in "$@"; do
+		# Retrieve the path of the backing file
+		local f=$(losetup --noheadings --output BACK-FILE $d)
+
+		# Detach the loop device from the backing file
+		_destroy_loop_device "$d"
+
+		# Clean up the backing disk image file
+		[ -n "$f" ] && rm -f "$f"
+	done
+}
 
 # 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
#3 lore raw

Re: [PATCH v8 01/13] fstests: add _loop_image_create_clone() helper

Quoted 18 lines · Zorro Lang
Quoted 17 lines · Anand Jain

Introduce _loop_image_create_clone() and _loop_image_destroy() to mkfs an image file and clone it to another image file, and attach a loop device to them. And its destroy part.

---
 common/rc | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
common/rc +0 -0
index 106f044adc2d..e8ff5be0c547 100644
@@ -1520,6 +1520,71 @@ _scratch_resvblks()
 	esac
 }
 
Quoted 9 lines

common/rc _scratch_resvblks() · line 1528

Quoted 6 lines
common/rc +6 -0
+# Create a small loop image, run an optional tuning function ($2) on it,
+# clone it, and attach both to loop devices, returned in ($1).
+# Args:
+#   $1: Nameref to return the array of allocated loop devices [base, clone].
+#   $2: Optional callback function to tune the base filesystem before cloning.
+_loop_image_create_clone()

To capture both the relationship and distinction between this function and existing _create_loop_device, I'd like to rename it to *_create_cloned_loop_devs*.

I'm fine with _create_cloned_loop_devs() and other patches must be
updated accordingly.

Quoted 48 lines
Quoted 11 lines
common/rc +11 -0
+{
+	local -n _ret=$1
+	local pre_clone_tune_func="$2"
+	local img_file=$TEST_DIR/${seq}.img
+	local img_file_clone=$TEST_DIR/${seq}_clone.img
+	local size=$(_small_fs_size_mb 128) # Smallest possible
+	local loop_devs=()
+
+	# Since we copy the block device image, we keep its size small.
+	_require_fs_space $TEST_DIR $((size * 1024 * 2))
+

common/rc _scratch_resvblks() · line 1549

Quoted 10 lines
common/rc +8 -0
+	_create_file_sized $((size * 1024 * 1024)) $img_file ||
+				_fail "Failed: Create $img_file $size"
+
 
 
+	loop_devs=("$(_create_loop_device $img_file)")
+
+	case $FSTYP in
+	xfs)
+		_mkfs_dev -s size=4096 "${loop_devs[0]}"

Why xfs needs a specific "-s size=4096" ? Hardcoding this parameter might cause failures in certain incompatible environments.

common/rc _scratch_resvblks() · line 1552

Quoted 3 lines
common/rc +3 -0
+		;;
+	btrfs)
+		_mkfs_dev "${loop_devs[0]}"

If btrfs is handled the same way as the default *) branch below, there is no need to have a dedicated branch for it.

common/rc _scratch_resvblks() · line 1564

Quoted 12 lines
common/rc +12 -0
+		;;
+	*)
+		_mkfs_dev "${loop_devs[0]}"
+		;;
+	esac
+
+	# Only execute if the function argument is not empty
+	if [ -n "$pre_clone_tune_func" ]; then
+		$pre_clone_tune_func "${loop_devs[0]}"
+	fi
+
+	sync "${loop_devs[0]}"

Since the mkfs command above operates directly on a loop device, I wanted to make sure, can the sync command be targeted at a block device, and does it guarantee a cache flush or blockdev page cache sync ?

Quoted 5 lines

How about:
sync -d "${loop_devs[0]}"
blockdev --flushbufs "${loop_devs[0]}"
or any better idea?

Spotted stale code here while refactoring pre_clone_tune_func. The updated code with the suggested renames is below.

Agreed that <dev> in sync is redundant. Adding sync serves as a safeguard if a future mkfs fails to do so. Flushing via blockdev --flushbufs followed by a global sync properly follows the data flow path it may not be strictly required but it is safer.

Quoted 4 lines
Quoted 4 lines
common/rc +4 -0
+	cp $img_file $img_file_clone || _fail "Failed to copy cloned image"
+
+	loop_devs+=("$(_create_loop_device $img_file_clone)")
+
Quoted 9 lines

common/rc _scratch_resvblks() · line 1572

Quoted 4 lines
common/rc +4 -0
+	_ret=()
+	for i in "${loop_devs[@]}"; do
+		_ret+=("$i")
+	done

How about:
_ret=("${loop_devs[@]}")
directly?

yeah. added.

Quoted 10 lines

common/rc _scratch_resvblks() · line 1577

Quoted 5 lines
common/rc +5 -0
+}
+
+# Teardown loop devices and delete their underlying backing image files.
+# Accepts a list of loop device paths (e.g., /dev/loop0 /dev/loop1).
+_loop_image_destroy()

To keep it consistent with the renamed function above, we could rename this to *_destroy_cloned_loop_devs*. Alternatively, if we don't consider it clone-specific, *destroy_loop_devs* would also be good to me.

_destroy_cloned_loop_devs() is fine.

Updated code:

---------------------------------

common/rc +0 -0
index 45cf9360ba73..285463b4e3db 100644
@@ -1561,7 +1561,7 @@ _change_metadata_uuid()
 # Args:
 #   $1: Nameref to return the array of allocated loop devices [base,

clone].
# $2: Optional callback function to tune the base filesystem before
cloning.
-_loop_image_create_clone() +_create_cloned_loop_devs()
{
local -n _ret=$1
local pre_clone_tune_func="$2"
@@ -1578,37 +1578,28 @@ _loop_image_create_clone()

loop_devs=("$(_create_loop_device $img_file)")

- case $FSTYP in
- xfs)
- _mkfs_dev -s size=4096 "${loop_devs[0]}"
- ;;
- btrfs)
- _mkfs_dev "${loop_devs[0]}"
- ;;
- *)
- _mkfs_dev "${loop_devs[0]}"
- ;;
- esac
+ _mkfs_dev "${loop_devs[0]}"

# Only execute if the function argument is not empty if [ -n "$pre_clone_tune_func" ]; then
$pre_clone_tune_func "${loop_devs[0]}"
fi

- sync "${loop_devs[0]}"
+ # We are about to copy the loop device's backing file
+ # Flush loop device's buffer cache
+ blockdev --flushbufs "${loop_devs[0]}"
+ # Sync system's dirty pages including backing file's
+ sync
cp $img_file $img_file_clone || _fail "Failed to copy cloned image"

loop_devs+=("$(_create_loop_device $img_file_clone)")

- _ret=()
- for i in "${loop_devs[@]}"; do
- _ret+=("$i")
- done
+ _ret=("${loop_devs[@]}")
}

# Teardown loop devices and delete their underlying backing image files. # Accepts a list of loop device paths (e.g., /dev/loop0 /dev/loop1).
-_loop_image_destroy()
+_destroy_cloned_loop_devs()
{
for d in "$@"; do
# Retrieve the path of the backing file
-------------------------------

Thanks.
Anand

Quoted 21 lines

Thanks,
Zorro

Quoted 18 lines
common/rc +12 -0
+{
+	for d in "$@"; do
+		# Retrieve the path of the backing file
+		local f=$(losetup --noheadings --output BACK-FILE $d)
+
+		# Detach the loop device from the backing file
+		_destroy_loop_device "$d"
+
+		# Clean up the backing disk image file
+		[ -n "$f" ] && rm -f "$f"
+	done
+}
 

# 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
author
#4 lore raw

PATCH v8 2/13 fstests: add _clone_mount_option() helper

In reply to Anand Jain

Adds _clone_mount_option() helper function to handle filesystem-specific requirements for mounting cloned devices. Abstract the need for -o nouuid on XFS.

---
 common/rc | 13 +++++++++++++
 1 file changed, 13 insertions(+)
common/rc +13 -0
index e8ff5be0c547..53e5d9d43af6 100644
@@ -414,6 +414,19 @@ _scratch_mount_options()
 					$SCRATCH_DEV $SCRATCH_MNT
 }
 
+# Return filesystem-specific mount options required for mounting clone/snapshot
+# devices.
+_clone_mount_option()
+{
+	case "$FSTYP" in
+	xfs)
+		# Allow mounting a duplicate filesystem on the same host
+		echo "-o nouuid"
+		;;
+	*)
+	esac
+}
+
 _supports_filetype()
 {
 	local dir=$1
Signature · 1 line
2.43.0
#5 lore raw

Re: [PATCH v8 02/13] fstests: add _clone_mount_option() helper

Quoted 15 lines · Anand Jain

Adds _clone_mount_option() helper function to handle filesystem-specific requirements for mounting cloned devices. Abstract the need for -o nouuid on XFS.

---
 common/rc | 13 +++++++++++++
 1 file changed, 13 insertions(+)
common/rc +0 -0
index e8ff5be0c547..53e5d9d43af6 100644

common/rc _scratch_mount_options() · line 419

Quoted 6 lines
common/rc +3 -0
@@ -414,6 +414,19 @@ _scratch_mount_options()
 					$SCRATCH_DEV $SCRATCH_MNT
 }
 
+# Return filesystem-specific mount options required for mounting clone/snapshot
+# devices.
+_clone_mount_option()

Just a minor nitpick :) For consistency with other *_mount_options functions (in common/rc), I suggest using "options" instead of "option" -- _clone_mount_options

Quoted 16 lines
common/rc +10 -0
+{
+	case "$FSTYP" in
+	xfs)
+		# Allow mounting a duplicate filesystem on the same host
+		echo "-o nouuid"
+		;;
+	*)
+	esac
+}
+
 _supports_filetype()
 {
 	local dir=$1
Signature · 1 line
2.43.0
#6 lore raw

Re: [PATCH v8 02/13] fstests: add _clone_mount_option() helper

Quoted 19 lines · Zorro Lang
Quoted 18 lines · Anand Jain

Adds _clone_mount_option() helper function to handle filesystem-specific requirements for mounting cloned devices. Abstract the need for -o nouuid on XFS.

---
 common/rc | 13 +++++++++++++
 1 file changed, 13 insertions(+)
common/rc +0 -0
index e8ff5be0c547..53e5d9d43af6 100644
@@ -414,6 +414,19 @@ _scratch_mount_options()
 					$SCRATCH_DEV $SCRATCH_MNT
 }
 
Quoted 7 lines

common/rc _scratch_mount_options() · line 419

Quoted 3 lines
common/rc +3 -0
+# Return filesystem-specific mount options required for mounting clone/snapshot
+# devices.
+_clone_mount_option()

Just a minor nitpick :) For consistency with other *_mount_options functions (in common/rc), I suggest using "options" instead of "option" -- _clone_mount_options

I'm okay with that, though this rename will impact the rest of the patch series too.

Thanks, Anand

Quoted 16 lines
Quoted 16 lines
common/rc +10 -0
+{
+	case "$FSTYP" in
+	xfs)
+		# Allow mounting a duplicate filesystem on the same host
+		echo "-o nouuid"
+		;;
+	*)
+	esac
+}
+
 _supports_filetype()
 {
 	local dir=$1
Signature · 1 line
2.43.0
author
#7 lore raw

PATCH v8 3/13 fstests: add FSNOTIFYWAIT_PROG

In reply to Anand Jain

Define FSNOTIFYWAIT_PROG for an upcoming test case that uses fsnotifywait.

---
 common/config | 1 +
 1 file changed, 1 insertion(+)
common/config +1 -0
index d5299d5b926f..5661fa0ec310 100644
@@ -242,6 +242,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 FSNOTIFYWAIT_PROG="$(type -P fsnotifywait)"
 
 # udev wait functions.
 #
Signature · 1 line
2.43.0
Reviewed-by on v8 3/13
#8 lore raw

Re: [PATCH v8 03/13] fstests: add FSNOTIFYWAIT_PROG

Quoted 13 lines · Anand Jain

Define FSNOTIFYWAIT_PROG for an upcoming test case that uses fsnotifywait.

---
 common/config | 1 +
 1 file changed, 1 insertion(+)
common/config +0 -0
index d5299d5b926f..5661fa0ec310 100644

common/config export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical) · line 245

Quoted 4 lines
common/config +1 -0
@@ -242,6 +242,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 FSNOTIFYWAIT_PROG="$(type -P fsnotifywait)"

OK, new *notifywait command.

Quoted 6 lines
common/config +0 -0
 # udev wait functions.
 #
Signature · 1 line
2.43.0
author
#9 lore raw

PATCH v8 4/13 fstests: add _require_fanotify_function

In reply to Anand Jain

fanotify support may not be compiled into the kernel. Add _require_fanotify_function to skip tests when fanotify is disabled.

---
 common/rc | 7 +++++++
 1 file changed, 7 insertions(+)
common/rc +7 -0
index 53e5d9d43af6..21b516accdf7 100644
@@ -6329,6 +6329,13 @@ _require_max_file_range_blocks()
 	esac
 }
 
+_require_fanotify_function()
+{
+        if $FSNOTIFYWAIT_PROG 2>&1 | grep -q "Function not implemented"; then
+                _notrun "Need CONFIG_FANOTIFY and CONFIG_FANOTIFY_ACCESS_PERMISSIONS to be enabled"
+        fi
+}
+
 _require_fanotify_ioerrors()
 {
 	local x
Signature · 1 line
2.43.0
#10 lore raw

Re: [PATCH v8 04/13] fstests: add _require_fanotify_function

Quoted 13 lines · Anand Jain

fanotify support may not be compiled into the kernel. Add _require_fanotify_function to skip tests when fanotify is disabled.

---
 common/rc | 7 +++++++
 1 file changed, 7 insertions(+)
common/rc +0 -0
index 53e5d9d43af6..21b516accdf7 100644

common/rc _require_max_file_range_blocks() · line 6336

Quoted 8 lines
common/rc +5 -0
@@ -6329,6 +6329,13 @@ _require_max_file_range_blocks()
 	esac
 }
 
+_require_fanotify_function()
+{
+        if $FSNOTIFYWAIT_PROG 2>&1 | grep -q "Function not implemented"; then
+                _notrun "Need CONFIG_FANOTIFY and CONFIG_FANOTIFY_ACCESS_PERMISSIONS to be enabled"
+        fi

What happens if $FSNOTIFYWAIT_PROG fails with an error other than 'Function not implemented'?

Thanks,
Zorro

Quoted 8 lines
common/rc +2 -0
+}
+
 _require_fanotify_ioerrors()
 {
 	local x
Signature · 1 line
2.43.0
#11 lore raw

Re: [PATCH v8 04/13] fstests: add _require_fanotify_function

Quoted 17 lines · Zorro Lang
Quoted 16 lines · Anand Jain

fanotify support may not be compiled into the kernel. Add _require_fanotify_function to skip tests when fanotify is disabled.

---
 common/rc | 7 +++++++
 1 file changed, 7 insertions(+)
common/rc +0 -0
index 53e5d9d43af6..21b516accdf7 100644
@@ -6329,6 +6329,13 @@ _require_max_file_range_blocks()
 	esac
 }
 
Quoted 8 lines

common/rc _require_max_file_range_blocks() · line 6336

Quoted 5 lines
common/rc +5 -0
+_require_fanotify_function()
+{
+        if $FSNOTIFYWAIT_PROG 2>&1 | grep -q "Function not implemented"; then
+                _notrun "Need CONFIG_FANOTIFY and CONFIG_FANOTIFY_ACCESS_PERMISSIONS to be enabled"
+        fi

What happens if $FSNOTIFYWAIT_PROG fails with an error other than 'Function not implemented'?

The known failure conditions, the availability of the fsnotifywait userspace tool and the kernel configuration are checked.

For any other errors, the test case (generic/801) will fail during monitor setup and log the error to stdout for debugging.

Thanks, Anand

Quoted 11 lines

Thanks,
Zorro

Quoted 8 lines
common/rc +2 -0
+}
+
 _require_fanotify_ioerrors()
 {
 	local x
Signature · 1 line
2.43.0
author
#12 lore raw

PATCH v8 5/13 fstests: add _require_unique_f_fsid() helper

In reply to Anand Jain

Add a helper to check if the target filesystem supports unique f_fsid tracking across cloned or snapshot instances.

Certain filesystems like XFS, Btrfs, and F2FS ensure unique f_fsid identifiers per filesystem instance. However, Ext4 derives its f_fsid directly from its superblock UUID, which leads to identical f_fsid values on cloned images until the UUID is manually modified by userspace.

Introduce _require_unique_f_fsid() to allow test cases requiring strict f_fsid uniqueness to skip gracefully on unsupported filesystems.

---
 common/rc | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
common/rc +27 -0
index 21b516accdf7..f73251365a60 100644
@@ -6356,6 +6356,33 @@ _require_fanotify_ioerrors()
 	_notrun "$FSTYP does not support fanotify ioerrors"
 }
 
+# Skip the test if the filesystem does not enforce unique f_fsids
+# natively. Checking this dynamically requires recreating a clone
+# layout, so we use a static lookup based on FSTYP.
+#
+# Across all filesystems, a UUID collision causes libblkid tools to return
+# non-deterministic device mappings. It is ultimately the responsibility
+# of the userspace utility or use-case to enforce uniqueness when a clone
+# diverges. For details, see mailing list thread discussions:
+#   Link: https://lore.kernel.org/linux-ext4/[email protected]/
+_require_unique_f_fsid()
+{
+	case "$FSTYP" in
+	ext*)
+		# Ext4 derives f_fsid from the superblock UUID, meaning
+		# clones share the same f_fsid until their UUIDs
+		# diverge.
+		_notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones."
+		;;
+	*)
+		# Conversely, XFS, Btrfs, and F2FS ensure f_fsid remains
+		# unique per filesystem instance (often by deriving it
+		# from the UUID and underlying block device.)
+		;;
+	esac
+}
+
+
 # Computes a percentage of the available space in a filesystem and
 # returns that quantity in MB. The percentage must not contain a percent
 # sign ("%").
Signature · 1 line
2.43.0
#13 lore raw

Re: [PATCH v8 05/13] fstests: add _require_unique_f_fsid() helper

Quoted 38 lines · Anand Jain

Add a helper to check if the target filesystem supports unique f_fsid tracking across cloned or snapshot instances.

Certain filesystems like XFS, Btrfs, and F2FS ensure unique f_fsid identifiers per filesystem instance. However, Ext4 derives its f_fsid directly from its superblock UUID, which leads to identical f_fsid values on cloned images until the UUID is manually modified by userspace.

Introduce _require_unique_f_fsid() to allow test cases requiring strict f_fsid uniqueness to skip gracefully on unsupported filesystems.

---
 common/rc | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
common/rc +14 -0
index 21b516accdf7..f73251365a60 100644
@@ -6356,6 +6356,33 @@ _require_fanotify_ioerrors()
 	_notrun "$FSTYP does not support fanotify ioerrors"
 }
 
+# Skip the test if the filesystem does not enforce unique f_fsids
+# natively. Checking this dynamically requires recreating a clone
+# layout, so we use a static lookup based on FSTYP.
+#
+# Across all filesystems, a UUID collision causes libblkid tools to return
+# non-deterministic device mappings. It is ultimately the responsibility
+# of the userspace utility or use-case to enforce uniqueness when a clone
+# diverges. For details, see mailing list thread discussions:
+#   Link: https://lore.kernel.org/linux-ext4/[email protected]/
+_require_unique_f_fsid()
+{
+	case "$FSTYP" in
+	ext*)
+		# Ext4 derives f_fsid from the superblock UUID, meaning

common/rc _require_fanotify_ioerrors() · line 6382

Quoted 10 lines
common/rc +10 -0
+		# clones share the same f_fsid until their UUIDs
+		# diverge.
+		_notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones."
+		;;
+	*)
+		# Conversely, XFS, Btrfs, and F2FS ensure f_fsid remains
+		# unique per filesystem instance (often by deriving it
+		# from the UUID and underlying block device.)
+		;;
+	esac

I would prefer to let the experts of each filesystem confirm whether their fs guarantees a unique f_fsid. How about switching this logic to an *allowlist* *(whitelist)* approach, where we _notrun by default and only allow filesystems that explicitly guarantee unique f_fsid support?

case "$FSTYP" in
btrfs|xfs|f2fs)

        ;;

*)
_notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones." ;;
esac

Thanks,
Zorro

Quoted 9 lines
common/rc +3 -0
+}
+
+
 # Computes a percentage of the available space in a filesystem and
 # returns that quantity in MB. The percentage must not contain a percent
 # sign ("%").
Signature · 1 line
2.43.0
#14 lore raw

Re: [PATCH v8 05/13] fstests: add _require_unique_f_fsid() helper

Quoted 45 lines · Zorro Lang
Quoted 44 lines · Anand Jain

Add a helper to check if the target filesystem supports unique f_fsid tracking across cloned or snapshot instances.

Certain filesystems like XFS, Btrfs, and F2FS ensure unique f_fsid identifiers per filesystem instance. However, Ext4 derives its f_fsid directly from its superblock UUID, which leads to identical f_fsid values on cloned images until the UUID is manually modified by userspace.

Introduce _require_unique_f_fsid() to allow test cases requiring strict f_fsid uniqueness to skip gracefully on unsupported filesystems.

---
 common/rc | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
common/rc +20 -0
index 21b516accdf7..f73251365a60 100644
@@ -6356,6 +6356,33 @@ _require_fanotify_ioerrors()
 	_notrun "$FSTYP does not support fanotify ioerrors"
 }
 
+# Skip the test if the filesystem does not enforce unique f_fsids
+# natively. Checking this dynamically requires recreating a clone
+# layout, so we use a static lookup based on FSTYP.
+#
+# Across all filesystems, a UUID collision causes libblkid tools to return
+# non-deterministic device mappings. It is ultimately the responsibility
+# of the userspace utility or use-case to enforce uniqueness when a clone
+# diverges. For details, see mailing list thread discussions:
+#   Link: https://lore.kernel.org/linux-ext4/[email protected]/
+_require_unique_f_fsid()
+{
+	case "$FSTYP" in
+	ext*)
+		# Ext4 derives f_fsid from the superblock UUID, meaning
+		# clones share the same f_fsid until their UUIDs
+		# diverge.
+		_notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones."
+		;;
+	*)
+		# Conversely, XFS, Btrfs, and F2FS ensure f_fsid remains
Quoted 8 lines

common/rc _require_fanotify_ioerrors() · line 6382

Quoted 4 lines
common/rc +4 -0
+		# unique per filesystem instance (often by deriving it
+		# from the UUID and underlying block device.)
+		;;
+	esac

I would prefer to let the experts of each filesystem confirm whether their fs guarantees a unique f_fsid.

Theoretically, whether f_fsid is unique per clone depends on how it is derived. If it includes the device major:minor, it provides a unique ID per clone.

Btrfs does it since commit
c2a74ed0494c btrfs: derive f_fsid from on-disk fsid and dev_t

Quoted 11 lines

How about switching this logic to an *allowlist*
*(whitelist)* approach, where we _notrun by default and only allow filesystems that explicitly guarantee unique f_fsid support?

case "$FSTYP" in
btrfs|xfs|f2fs)

        ;;

*)
_notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones." ;;
esac

So _notrun() unless confirmed to have unique f_fsid for the cloned filesystem. Works for me.

Thanks, Anand

Quoted 12 lines

Thanks,
Zorro

Quoted 9 lines
common/rc +3 -0
+}
+
+
 # Computes a percentage of the available space in a filesystem and
 # returns that quantity in MB. The percentage must not contain a percent
 # sign ("%").
Signature · 1 line
2.43.0
author
#15 lore raw

PATCH v8 6/13 fstests: add SEMANAGE_PROG

In reply to Anand Jain

Define SEMANAGE_PROG for an upcoming test case that uses semanage(8).

---
 common/config | 1 +
 1 file changed, 1 insertion(+)
common/config +1 -0
index 5661fa0ec310..d760b796ce86 100644
@@ -243,6 +243,7 @@ export PARTED_PROG="$(type -P parted)"
 export XFS_PROPERTY_PROG="$(type -P xfs_property)"
 export FSCRYPTCTL_PROG="$(type -P fscryptctl)"
 export FSNOTIFYWAIT_PROG="$(type -P fsnotifywait)"
+export SEMANAGE_PROG="$(type -P semanage)"
 
 # udev wait functions.
 #
Signature · 1 line
2.43.0
author
#16 lore raw

PATCH v8 7/13 fstests: verify fanotify isolation on cloned filesystems

In reply to 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.

---
 tests/generic/801     | 143 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/801.out |   7 +++
 2 files changed, 150 insertions(+)
 create mode 100644 tests/generic/801
 create mode 100644 tests/generic/801.out
tests/generic/801 new file +143 -0
new file mode 100644
index 000000000000..904ba9440b3d
@@ -0,0 +1,143 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 801
+# 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_block_device $TEST_DEV
+_require_loop
+_require_command "$SEMANAGE_PROG" semanage
+_require_command "$FSNOTIFYWAIT_PROG" fsnotifywait
+_require_fanotify_function
+_require_unique_f_fsid
+
+_cleanup()
+{
+	cd /
+	[[ -n $pid1 ]] && { kill -TERM "$pid1" 2> /dev/null; wait $pid1; }
+	[[ -n $pid2 ]] && { kill -TERM "$pid2" 2> /dev/null; wait $pid2; }
+
+	if [ "$semanage_added" = "yes" ]; then
+		semanage permissive -d unconfined_t >/dev/null 2>&1 || true
+	fi
+
+	umount $mnt1 $mnt2 >/dev/null 2>&1
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+	rm -r -f $tmp.*
+}
+
+# Run fsnotifywait in unbuffered mode to watch filesystem-wide create events
+monitor_fanotify()
+{
+	local mmnt=$1
+	exec stdbuf -oL $FSNOTIFYWAIT_PROG -m -F -S -e create "$mmnt" 2>&1
+}
+
+# Transform f_fsid into the hi.lo format used in fanotify FID logs
+fsid_to_fid_parts()
+{
+	local fsid=$1
+	# Pad to 16 hex chars (64-bit), then split into two 32-bit halves
+	local padded=$(printf '%016x' "0x${fsid}")
+	local hi=$(printf '%x' "0x${padded:0:8}")   # strips leading zeros
+	local lo=$(printf '%x' "0x${padded:8:8}")   # strips leading zeros
+	echo "${hi}.${lo}"
+}
+
+# Create base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both base and clone filesystems using required clone mount options
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+# Fetch filesystem IDs to verify the kernel can differentiate between them
+fsid1=$(stat -f -c "%i" $mnt1)
+fsid2=$(stat -f -c "%i" $mnt2)
+
+log1=$tmp.fanotify1
+log2=$tmp.fanotify2
+
+pid1=""
+pid2=""
+echo "Setup FID fanotify watchers on both mnt1 and mnt2"
+
+# Permit unconfined_t domains when SELinux is enforcing to prevent fanotify
+# blockages
+semanage_added="no"
+if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
+    if ! semanage permissive -l | grep -q "unconfined_t"; then
+        semanage permissive -a unconfined_t >/dev/null 2>&1 && semanage_added="yes"
+    fi
+fi
+
+# Start asynchronous fanotify monitors
+( monitor_fanotify "$mnt1" > "$log1" ) &
+pid1=$!
+( monitor_fanotify "$mnt2" > "$log2" ) &
+pid2=$!
+sleep 2
+
+if ! kill -0 "$pid1" 2>/dev/null || ! kill -0 "$pid2" 2>/dev/null; then
+	cat "$log1"
+	cat "$log2"
+	_fail "$FSNOTIFYWAIT_PROG setup failed"
+fi
+
+echo "Trigger file creation on mnt1"
+touch $mnt1/file_on_mnt1
+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
+pid1=""
+pid2=""
+
+e_fsid1=$(fsid_to_fid_parts "$fsid1")
+e_fsid2=$(fsid_to_fid_parts "$fsid2")
+
+# Dump debug details to the full log
+echo $fsid1 $e_fsid1 $fsid2 $e_fsid2 >> $seqres.full
+cat $log1 >> $seqres.full
+cat $log2 >> $seqres.full
+
+# Ensure monitor 1 only captured events belonging to mnt 1 and fsid 1
+if grep -qF "$e_fsid1" "$log1" && ! grep -qF "$e_fsid2" "$log1"; then
+	echo "SUCCESS: mnt1 events found"
+else
+	[ ! -s "$log1" ] && echo "  - mnt1 received no events."
+	grep -qF "$e_fsid2" "$log1" && echo "  - mnt1 received event from mnt2."
+fi
+
+# Ensure monitor 2 only captured events belonging to mnt 2 and fsid 2
+if grep -qF "$e_fsid2" "$log2" && ! grep -qF "$e_fsid1" "$log2"; then
+	echo "SUCCESS: mnt2 events found"
+else
+	[ ! -s "$log2" ] && echo "  - mnt2 received no events."
+	grep -qF "$e_fsid1" "$log2" && echo "  - mnt2 received event from mnt1."
+fi
+
+status=0
+exit
tests/generic/801.out new file +7 -0
new file mode 100644
index 000000000000..d7b318d9f27c
@@ -0,0 +1,7 @@
+QA output created by 801
+Setup FID fanotify watchers on both mnt1 and mnt2
+Trigger file creation on mnt1
+Trigger file creation on mnt2
+Verify fsid in the fanotify
+SUCCESS: mnt1 events found
+SUCCESS: mnt2 events found
Signature · 1 line
2.43.0
#17 lore raw

Re: [PATCH v8 07/13] fstests: verify fanotify isolation on cloned filesystems

Quoted 8 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.

---
 tests/generic/801     | 143 ++++++++++++++++++++++++++++++++++++++++++

g/801 is already taken. To avoid merge conflicts, you can use a higher temporary number, and I'll assign the proper number when merging.

Quoted 15 lines
 tests/generic/801.out |   7 +++
 2 files changed, 150 insertions(+)
 create mode 100644 tests/generic/801
 create mode 100644 tests/generic/801.out
tests/generic/801 new file +4 -0
new file mode 100644
index 000000000000..904ba9440b3d
@@ -0,0 +1,143 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#

tests/generic/801 line 14

Quoted 10 lines
tests/generic/801 new file +10 -0
+# FS QA Test 801
+# 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_block_device $TEST_DEV

Which test condition requires TEST_DEV to be a block device?

Quoted 63 lines
tests/generic/801 new file +63 -0
+_require_loop
+_require_command "$SEMANAGE_PROG" semanage
+_require_command "$FSNOTIFYWAIT_PROG" fsnotifywait
+_require_fanotify_function
+_require_unique_f_fsid
+
+_cleanup()
+{
+	cd /
+	[[ -n $pid1 ]] && { kill -TERM "$pid1" 2> /dev/null; wait $pid1; }
+	[[ -n $pid2 ]] && { kill -TERM "$pid2" 2> /dev/null; wait $pid2; }
+
+	if [ "$semanage_added" = "yes" ]; then
+		semanage permissive -d unconfined_t >/dev/null 2>&1 || true
+	fi
+
+	umount $mnt1 $mnt2 >/dev/null 2>&1
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+	rm -r -f $tmp.*
+}
+
+# Run fsnotifywait in unbuffered mode to watch filesystem-wide create events
+monitor_fanotify()
+{
+	local mmnt=$1
+	exec stdbuf -oL $FSNOTIFYWAIT_PROG -m -F -S -e create "$mmnt" 2>&1
+}
+
+# Transform f_fsid into the hi.lo format used in fanotify FID logs
+fsid_to_fid_parts()
+{
+	local fsid=$1
+	# Pad to 16 hex chars (64-bit), then split into two 32-bit halves
+	local padded=$(printf '%016x' "0x${fsid}")
+	local hi=$(printf '%x' "0x${padded:0:8}")   # strips leading zeros
+	local lo=$(printf '%x' "0x${padded:8:8}")   # strips leading zeros
+	echo "${hi}.${lo}"
+}
+
+# Create base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both base and clone filesystems using required clone mount options
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+# Fetch filesystem IDs to verify the kernel can differentiate between them
+fsid1=$(stat -f -c "%i" $mnt1)
+fsid2=$(stat -f -c "%i" $mnt2)
+
+log1=$tmp.fanotify1
+log2=$tmp.fanotify2
+
+pid1=""
+pid2=""

tests/generic/801 line 87

Quoted 10 lines
tests/generic/801 new file +10 -0
+echo "Setup FID fanotify watchers on both mnt1 and mnt2"
+
+# Permit unconfined_t domains when SELinux is enforcing to prevent fanotify
+# blockages
+semanage_added="no"
+if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
+    if ! semanage permissive -l | grep -q "unconfined_t"; then
+        semanage permissive -a unconfined_t >/dev/null 2>&1 && semanage_added="yes"
+    fi
+fi

Looks like semanage/SELinux isn't a necessary requirement of this test case, if so that _require_command "$SEMANAGE_PROG" semanage also can be:

  if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
      _require_command "$SEMANAGE_PROG" semanage
  fi

right? And please replace semanage with $SEMANAGE_PROG.

tests/generic/801 line 94

Quoted 7 lines
tests/generic/801 new file +7 -0
+
+# Start asynchronous fanotify monitors
+( monitor_fanotify "$mnt1" > "$log1" ) &
+pid1=$!
+( monitor_fanotify "$mnt2" > "$log2" ) &
+pid2=$!
+sleep 2

Are you using sleep 2 to ensure fsnotifywait is fully set up? I'm not sure if sleep 2 is 100% reliable here. Since the subsequent tests strictly depend on fsnotifywait starting up properly, is there a more robust approach than sleep 2? For example, could we check the output in $log1 and $log2 to make sure that? Or any other better idea?

tests/generic/801 line 96

Quoted 2 lines
tests/generic/801 new file +2 -0
+
+if ! kill -0 "$pid1" 2>/dev/null || ! kill -0 "$pid2" 2>/dev/null; then

kill 0 only can make sure the process is running, can't make sure it's fully set up, right?

Quoted 9 lines
tests/generic/801 new file +9 -0
+	cat "$log1"
+	cat "$log2"
+	_fail "$FSNOTIFYWAIT_PROG setup failed"
+fi
+
+echo "Trigger file creation on mnt1"
+touch $mnt1/file_on_mnt1
+sync
+sleep 1

tests/generic/801 line 115

Quoted 10 lines
tests/generic/801 new file +10 -0
+
+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
+pid1=""

unset pid1

tests/generic/801 line 116

Quoted 1 line
tests/generic/801 new file +1 -0
+pid2=""

unset pid2

Quoted 17 lines
tests/generic/801 new file +17 -0
+
+e_fsid1=$(fsid_to_fid_parts "$fsid1")
+e_fsid2=$(fsid_to_fid_parts "$fsid2")
+
+# Dump debug details to the full log
+echo $fsid1 $e_fsid1 $fsid2 $e_fsid2 >> $seqres.full
+cat $log1 >> $seqres.full
+cat $log2 >> $seqres.full
+
+# Ensure monitor 1 only captured events belonging to mnt 1 and fsid 1
+if grep -qF "$e_fsid1" "$log1" && ! grep -qF "$e_fsid2" "$log1"; then
+	echo "SUCCESS: mnt1 events found"
+else
+	[ ! -s "$log1" ] && echo "  - mnt1 received no events."
+	grep -qF "$e_fsid2" "$log1" && echo "  - mnt1 received event from mnt2."
+fi
+

tests/generic/801 line 143

Quoted 10 lines
tests/generic/801 new file +10 -0
+# Ensure monitor 2 only captured events belonging to mnt 2 and fsid 2
+if grep -qF "$e_fsid2" "$log2" && ! grep -qF "$e_fsid1" "$log2"; then
+	echo "SUCCESS: mnt2 events found"
+else
+	[ ! -s "$log2" ] && echo "  - mnt2 received no events."
+	grep -qF "$e_fsid1" "$log2" && echo "  - mnt2 received event from mnt1."
+fi
+
+status=0
+exit

_exit 0

Thanks,
Zorro

Quoted 16 lines
tests/generic/801.out new file +7 -0
new file mode 100644
index 000000000000..d7b318d9f27c
@@ -0,0 +1,7 @@
+QA output created by 801
+Setup FID fanotify watchers on both mnt1 and mnt2
+Trigger file creation on mnt1
+Trigger file creation on mnt2
+Verify fsid in the fanotify
+SUCCESS: mnt1 events found
+SUCCESS: mnt2 events found
Signature · 1 line
2.43.0
#18 lore raw

Re: [PATCH v8 07/13] fstests: verify fanotify isolation on cloned filesystems

Quoted 6 lines · Zorro Lang
Quoted 5 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.

Quoted 7 lines
Quoted 3 lines
---
 tests/generic/801     | 143 ++++++++++++++++++++++++++++++++++++++++++

g/801 is already taken. To avoid merge conflicts, you can use a higher temporary number, and I'll assign the proper number when merging.

Let me bump-it up to 90x.

Quoted 19 lines
Quoted 19 lines
 tests/generic/801.out |   7 +++
 2 files changed, 150 insertions(+)
 create mode 100644 tests/generic/801
 create mode 100644 tests/generic/801.out
tests/generic/801 new file +8 -0
new file mode 100644
index 000000000000..904ba9440b3d
@@ -0,0 +1,143 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 801
+# Verify fanotify FID functionality on cloned filesystems by setting up
+# watchers and making sure notifications are in the correct logs files.
+
Quoted 8 lines

tests/generic/801 line 14

Quoted 6 lines
tests/generic/801 new file +6 -0
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV

Which test condition requires TEST_DEV to be a block device?

You're right, I think this is a leftover from earlier prototyping with scsi_device. The test only needs loop devices backed by $TEST_DIR. I'll remove it in the following ver.

Quoted 78 lines
Quoted 64 lines
tests/generic/801 new file +63 -0
+_require_loop
+_require_command "$SEMANAGE_PROG" semanage
+_require_command "$FSNOTIFYWAIT_PROG" fsnotifywait
+_require_fanotify_function
+_require_unique_f_fsid
+
+_cleanup()
+{
+	cd /
+	[[ -n $pid1 ]] && { kill -TERM "$pid1" 2> /dev/null; wait $pid1; }
+	[[ -n $pid2 ]] && { kill -TERM "$pid2" 2> /dev/null; wait $pid2; }
+
+	if [ "$semanage_added" = "yes" ]; then
+		semanage permissive -d unconfined_t >/dev/null 2>&1 || true
+	fi
+
+	umount $mnt1 $mnt2 >/dev/null 2>&1
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+	rm -r -f $tmp.*
+}
+
+# Run fsnotifywait in unbuffered mode to watch filesystem-wide create events
+monitor_fanotify()
+{
+	local mmnt=$1
+	exec stdbuf -oL $FSNOTIFYWAIT_PROG -m -F -S -e create "$mmnt" 2>&1
+}
+
+# Transform f_fsid into the hi.lo format used in fanotify FID logs
+fsid_to_fid_parts()
+{
+	local fsid=$1
+	# Pad to 16 hex chars (64-bit), then split into two 32-bit halves
+	local padded=$(printf '%016x' "0x${fsid}")
+	local hi=$(printf '%x' "0x${padded:0:8}")   # strips leading zeros
+	local lo=$(printf '%x' "0x${padded:8:8}")   # strips leading zeros
+	echo "${hi}.${lo}"
+}
+
+# Create base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both base and clone filesystems using required clone mount options
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+# Fetch filesystem IDs to verify the kernel can differentiate between them
+fsid1=$(stat -f -c "%i" $mnt1)
+fsid2=$(stat -f -c "%i" $mnt2)
+
+log1=$tmp.fanotify1
+log2=$tmp.fanotify2
+
+pid1=""
+pid2=""

tests/generic/801 line 87

Quoted 10 lines
tests/generic/801 new file +10 -0
+echo "Setup FID fanotify watchers on both mnt1 and mnt2"
+
+# Permit unconfined_t domains when SELinux is enforcing to prevent fanotify
+# blockages
+semanage_added="no"
+if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
+    if ! semanage permissive -l | grep -q "unconfined_t"; then
+        semanage permissive -a unconfined_t >/dev/null 2>&1 && semanage_added="yes"
+    fi
+fi

Looks like semanage/SELinux isn't a necessary requirement of this test case, if so that _require_command "$SEMANAGE_PROG" semanage also can be:

Quoted 6 lines
  if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
      _require_command "$SEMANAGE_PROG" semanage
  fi

right? And please replace semanage with $SEMANAGE_PROG.

Absolutely, that is the right way. I'll update it. Thanks!. More below.

Quoted 14 lines

tests/generic/801 line 94

Quoted 7 lines
tests/generic/801 new file +7 -0
+
+# Start asynchronous fanotify monitors
+( monitor_fanotify "$mnt1" > "$log1" ) &
+pid1=$!
+( monitor_fanotify "$mnt2" > "$log2" ) &
+pid2=$!
+sleep 2

Are you using sleep 2 to ensure fsnotifywait is fully set up? I'm not sure if sleep 2 is 100% reliable here. Since the subsequent tests strictly depend on fsnotifywait starting up properly, is there a more robust approach than sleep 2? For example, could we check the output in $log1 and $log2 to make sure that? Or any other better idea?

Quoted 6 lines

tests/generic/801 line 96

Quoted 2 lines
tests/generic/801 new file +2 -0
+
+if ! kill -0 "$pid1" 2>/dev/null || ! kill -0 "$pid2" 2>/dev/null; then

kill 0 only can make sure the process is running, can't make sure it's fully set up, right?

I rely on checking whether the userspace process is still running after a 2 second delay.

Matching specific stdout strings a common pattern in fstests tends to be fragile across different versions of userspace utilities. For instance, the "Watches established" string in fsnotifywait isn't guaranteed across versions:

$ fsnotifywait -m -F -S -e create /btrfs
Setting up filesystem watches.
Watches established.
^C
Quoted 22 lines
Quoted 9 lines
tests/generic/801 new file +9 -0
+	cat "$log1"
+	cat "$log2"
+	_fail "$FSNOTIFYWAIT_PROG setup failed"
+fi
+
+echo "Trigger file creation on mnt1"
+touch $mnt1/file_on_mnt1
+sync
+sleep 1

tests/generic/801 line 115

Quoted 10 lines
tests/generic/801 new file +10 -0
+
+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
+pid1=""

unset pid1

Quoted 4 lines

tests/generic/801 line 116

Quoted 1 line
tests/generic/801 new file +1 -0
+pid2=""

unset pid2

Added.

Quoted 21 lines
Quoted 21 lines
tests/generic/801 new file +21 -0
+
+e_fsid1=$(fsid_to_fid_parts "$fsid1")
+e_fsid2=$(fsid_to_fid_parts "$fsid2")
+
+# Dump debug details to the full log
+echo $fsid1 $e_fsid1 $fsid2 $e_fsid2 >> $seqres.full
+cat $log1 >> $seqres.full
+cat $log2 >> $seqres.full
+
+# Ensure monitor 1 only captured events belonging to mnt 1 and fsid 1
+if grep -qF "$e_fsid1" "$log1" && ! grep -qF "$e_fsid2" "$log1"; then
+	echo "SUCCESS: mnt1 events found"
+else
+	[ ! -s "$log1" ] && echo "  - mnt1 received no events."
+	grep -qF "$e_fsid2" "$log1" && echo "  - mnt1 received event from mnt2."
+fi
+
+# Ensure monitor 2 only captured events belonging to mnt 2 and fsid 2
+if grep -qF "$e_fsid2" "$log2" && ! grep -qF "$e_fsid1" "$log2"; then
+	echo "SUCCESS: mnt2 events found"
+else
Quoted 9 lines

tests/generic/801 line 143

Quoted 6 lines
tests/generic/801 new file +6 -0
+	[ ! -s "$log2" ] && echo "  - mnt2 received no events."
+	grep -qF "$e_fsid1" "$log2" && echo "  - mnt2 received event from mnt1."
+fi
+
+status=0
+exit

_exit 0

Added.

Thanks.
Anand

Quoted 19 lines

Thanks,
Zorro

Quoted 16 lines
tests/generic/801.out new file +7 -0
new file mode 100644
index 000000000000..d7b318d9f27c
@@ -0,0 +1,7 @@
+QA output created by 801
+Setup FID fanotify watchers on both mnt1 and mnt2
+Trigger file creation on mnt1
+Trigger file creation on mnt2
+Verify fsid in the fanotify
+SUCCESS: mnt1 events found
+SUCCESS: mnt2 events found
Signature · 1 line
2.43.0
author
#19 lore raw

PATCH v8 8/13 fstests: verify f_fsid for cloned filesystems

In reply to Anand Jain

Verify 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/802     | 64 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/802.out |  4 +++
 2 files changed, 68 insertions(+)
 create mode 100644 tests/generic/802
 create mode 100644 tests/generic/802.out
tests/generic/802 new file +64 -0
new file mode 100644
index 000000000000..910807c11584
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 802
+# Check that the cloned filesystem provides an f_fsid that is persistent
+# across mount cycles if the block device maj:min remains unchanged.
+
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both filesystems simultaneously using mandatory clone mount options
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+# Capture baseline filesystem IDs for comparison
+fsid_scratch=$(stat -f -c "%i" $mnt1)
+fsid_clone=$(stat -f -c "%i" $mnt2)
+
+# Verify that the fsids remain stable after a mount cycle, even when the
+# mount order is reversed.
+echo "**** fsid after mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+# Compare post mount-cycle values against the baseline
+stat -f -c "%i" $mnt1 | 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/802.out new file +4 -0
new file mode 100644
index 000000000000..0202a9a2c108
@@ -0,0 +1,4 @@
+QA output created by 802
+**** fsid after mount cycle ****
+FSID_SCRATCH
+FSID_CLONE
Signature · 1 line
2.43.0
#20 lore raw

Re: [PATCH v8 08/13] fstests: verify f_fsid for cloned filesystems

Quoted 40 lines · Anand Jain

Verify 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/802     | 64 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/802.out |  4 +++
 2 files changed, 68 insertions(+)
 create mode 100644 tests/generic/802
 create mode 100644 tests/generic/802.out
tests/generic/802 new file +23 -0
new file mode 100644
index 000000000000..910807c11584
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 802
+# Check that the cloned filesystem provides an f_fsid that is persistent
+# across mount cycles if the block device maj:min remains unchanged.
+
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
+
+_cleanup()
+{

tests/generic/802 line 33

Quoted 10 lines
tests/generic/802 new file +10 -0
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq

Better to rm -rf $TEST_DIR/$seq before creating a new one.

Quoted 18 lines
tests/generic/802 new file +18 -0
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both filesystems simultaneously using mandatory clone mount options
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+# Capture baseline filesystem IDs for comparison
+fsid_scratch=$(stat -f -c "%i" $mnt1)
+fsid_clone=$(stat -f -c "%i" $mnt2)
+
+# Verify that the fsids remain stable after a mount cycle, even when the
+# mount order is reversed.
+echo "**** fsid after mount cycle ****"

tests/generic/802 line 61

Quoted 10 lines
tests/generic/802 new file +10 -0
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+# Compare post mount-cycle values against the baseline
+stat -f -c "%i" $mnt1 | sed -e "s/$fsid_scratch/FSID_SCRATCH/g"
+stat -f -c "%i" $mnt2 | sed -e "s/$fsid_clone/FSID_CLONE/g"

Hmm... what if "$fsid_scratch" is same with "$fsid_clone"?

tests/generic/802 line 64

Quoted 3 lines
tests/generic/802 new file +3 -0
+
+status=0
+exit

_exit 0

Quoted 13 lines
tests/generic/802.out new file +4 -0
new file mode 100644
index 000000000000..0202a9a2c108
@@ -0,0 +1,4 @@
+QA output created by 802
+**** fsid after mount cycle ****
+FSID_SCRATCH
+FSID_CLONE
Signature · 1 line
2.43.0
author Applied
#21 lore raw

Re: [PATCH v8 08/13] fstests: verify f_fsid for cloned filesystems

Quoted 45 lines · Zorro Lang
Quoted 44 lines · Anand Jain

Verify 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/802     | 64 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/802.out |  4 +++
 2 files changed, 68 insertions(+)
 create mode 100644 tests/generic/802
 create mode 100644 tests/generic/802.out
tests/generic/802 new file +27 -0
new file mode 100644
index 000000000000..910807c11584
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 802
+# Check that the cloned filesystem provides an f_fsid that is persistent
+# across mount cycles if the block device maj:min remains unchanged.
+
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
Quoted 9 lines

tests/generic/802 line 33

Quoted 6 lines
tests/generic/802 new file +6 -0
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq

Better to rm -rf $TEST_DIR/$seq before creating a new one.

Applied.

Quoted 23 lines
Quoted 23 lines
tests/generic/802 new file +23 -0
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both filesystems simultaneously using mandatory clone mount options
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+# Capture baseline filesystem IDs for comparison
+fsid_scratch=$(stat -f -c "%i" $mnt1)
+fsid_clone=$(stat -f -c "%i" $mnt2)
+
+# Verify that the fsids remain stable after a mount cycle, even when the
+# mount order is reversed.
+echo "**** fsid after mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
Quoted 9 lines

tests/generic/802 line 61

Quoted 5 lines
tests/generic/802 new file +5 -0
+						_fail "Failed to mount dev1"
+
+# Compare post mount-cycle values against the baseline
+stat -f -c "%i" $mnt1 | sed -e "s/$fsid_scratch/FSID_SCRATCH/g"
+stat -f -c "%i" $mnt2 | sed -e "s/$fsid_clone/FSID_CLONE/g"

Hmm... what if "$fsid_scratch" is same with "$fsid_clone"?

That falls into the same category as ext4 (and likely Btrfs as well, pending a regression report fix that is delaying this fstests patch as well), where both the original and cloned filesystems share the same f_fsid. However, since the objective of this test case is to verify consistency within the filesystem across reboots, this behavior is fine.

Quoted 5 lines

tests/generic/802 line 64

Quoted 3 lines
tests/generic/802 new file +3 -0
+
+status=0
+exit

_exit 0

Applied.

Quoted 14 lines
Quoted 14 lines
tests/generic/802.out new file +4 -0
new file mode 100644
index 000000000000..0202a9a2c108
@@ -0,0 +1,4 @@
+QA output created by 802
+**** fsid after mount cycle ****
+FSID_SCRATCH
+FSID_CLONE
Signature · 1 line
2.43.0
author
#22 lore raw

PATCH v8 9/13 fstests: verify libblkid resolution of duplicate UUIDs

In reply to Anand Jain

Verify how findmnt, df (libblkid) resolve device paths when multiple block devices share the same FSUUID.

---
 tests/generic/803     | 72 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/803.out |  6 ++++
 2 files changed, 78 insertions(+)
 create mode 100644 tests/generic/803
 create mode 100644 tests/generic/803.out
tests/generic/803 new file +72 -0
new file mode 100644
index 000000000000..77901592366c
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 803
+# Check if the mountinfo based findmnt would resolve to the common uuid
+# as per the blkid (libblkid based).
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Normalize pool devices and mount points names
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both identical UUID filesystems simultaneously
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+# Btrfs assigned a random uuid for the clone fs before the fix.
+# Cycle mounts and reverse the initialization (source and clone fs) order.
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+status=0
+exit
tests/generic/803.out new file +6 -0
new file mode 100644
index 000000000000..3a130c662430
@@ -0,0 +1,6 @@
+QA output created by 803
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
#23 lore raw

Re: [PATCH v8 09/13] fstests: verify libblkid resolution of duplicate UUIDs

Quoted 1 line · Anand Jain

Verify how findmnt, df (libblkid) resolve device paths when multiple

I didn't find the df test part in this test case.

Quoted 58 lines

block devices share the same FSUUID.

---
 tests/generic/803     | 72 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/803.out |  6 ++++
 2 files changed, 78 insertions(+)
 create mode 100644 tests/generic/803
 create mode 100644 tests/generic/803.out
tests/generic/803 new file +42 -0
new file mode 100644
index 000000000000..77901592366c
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 803
+# Check if the mountinfo based findmnt would resolve to the common uuid
+# as per the blkid (libblkid based).
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Normalize pool devices and mount points names
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device

tests/generic/803 line 52

Quoted 10 lines
tests/generic/803 new file +10 -0
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both identical UUID filesystems simultaneously
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

findmnt -n (or --noheadings) can be used here (and below) to avoid piping to tail -n +2.

tests/generic/803 line 54

Quoted 2 lines
tests/generic/803 new file +2 -0
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

Same here

tests/generic/803 line 67

Quoted 13 lines
tests/generic/803 new file +13 -0
+
+# Btrfs assigned a random uuid for the clone fs before the fix.
+# Cycle mounts and reverse the initialization (source and clone fs) order.
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

Same here

tests/generic/803 line 69

Quoted 2 lines
tests/generic/803 new file +2 -0
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

Same here

tests/generic/803 line 72

Quoted 3 lines
tests/generic/803 new file +3 -0
+
+status=0
+exit

_exit 0

Quoted 15 lines
tests/generic/803.out new file +6 -0
new file mode 100644
index 000000000000..3a130c662430
@@ -0,0 +1,6 @@
+QA output created by 803
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
author Applied
#24 lore raw

Re: [PATCH v8 09/13] fstests: verify libblkid resolution of duplicate UUIDs

Quoted 5 lines · Zorro Lang
Quoted 1 line · Anand Jain

Verify how findmnt, df (libblkid) resolve device paths when multiple

I didn't find the df test part in this test case.

Hm, I think df was too basic and didn't add any real value, so I've removed it. I'll update the changelog accordingly.

Quoted 64 lines
Quoted 64 lines

block devices share the same FSUUID.

---
 tests/generic/803     | 72 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/803.out |  6 ++++
 2 files changed, 78 insertions(+)
 create mode 100644 tests/generic/803
 create mode 100644 tests/generic/803.out
tests/generic/803 new file +48 -0
new file mode 100644
index 000000000000..77901592366c
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 803
+# Check if the mountinfo based findmnt would resolve to the common uuid
+# as per the blkid (libblkid based).
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Normalize pool devices and mount points names
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both identical UUID filesystems simultaneously
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
Quoted 8 lines

tests/generic/803 line 52

Quoted 4 lines
tests/generic/803 new file +4 -0
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

findmnt -n (or --noheadings) can be used here (and below) to avoid piping to tail -n +2.

applied.

Quoted 5 lines

tests/generic/803 line 54

Quoted 3 lines
tests/generic/803 new file +2 -0
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

Same here

applied

Quoted 16 lines
Quoted 4 lines
tests/generic/803 new file +3 -0
+
+# Btrfs assigned a random uuid for the clone fs before the fix.
+# Cycle mounts and reverse the initialization (source and clone fs) order.

tests/generic/803 line 67

Quoted 10 lines
tests/generic/803 new file +10 -0
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

Same here

applied

Quoted 5 lines

tests/generic/803 line 69

Quoted 3 lines
tests/generic/803 new file +2 -0
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool

Same here

applied

Quoted 6 lines

tests/generic/803 line 72

Quoted 4 lines
tests/generic/803 new file +3 -0
+	
+status=0
+exit

_exit 0

applied

Thanks.>

Quoted 15 lines
Quoted 15 lines
tests/generic/803.out new file +6 -0
new file mode 100644
index 000000000000..3a130c662430
@@ -0,0 +1,6 @@
+QA output created by 803
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
author
#25 lore raw

PATCH v8 10/13 fstests: verify IMA isolation on cloned filesystems

In reply to Anand Jain

Add testcase to verify IMA measurement isolation when multiple devices share the same FSUUID.

---
 tests/generic/804     | 108 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/804.out |  10 ++++
 2 files changed, 118 insertions(+)
 create mode 100644 tests/generic/804
 create mode 100644 tests/generic/804.out
tests/generic/804 new file +108 -0
new file mode 100644
index 000000000000..ced32e6d79dd
@@ -0,0 +1,108 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 804
+# 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_block_device $TEST_DEV
+_require_loop
+
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	_unmount $mnt1 2>/dev/null
+	_unmount $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Normalize device names and mount points
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|$mnt1|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|$mnt2|MNT2|g" | _filter_spaces
+}
+
+# Core helper to set IMA policy and check measurement logs
+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 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 $enable | filter_pool
+
+	[ -w "$ima_policy" ] || _notrun "IMA policy not writable"
+
+	fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+	# Load IMA policy to measure file access specifically for this
+	# filesystem UUID.
+	if [[ $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
+
+	# IMA log extract
+	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
+}
+
+# Initialize loop base and cloned instances
+devs=()
+_loop_image_create_clone devs
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Concurrently mount both clones
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+#  IMA response on baseline and clone configuration
+do_ima $mnt1 1
+do_ima $mnt2 0
+
+# Cycle mount on the second device.
+echo mount cycle
+_unmount $mnt2
+_mount $mount_opts ${devs[1]} $mnt2 || _fail "Failed to mount dev2"
+
+do_ima $mnt1 0
+do_ima $mnt2 0
+
+status=0
+exit
tests/generic/804.out new file +10 -0
new file mode 100644
index 000000000000..9804181d6c17
@@ -0,0 +1,10 @@
+QA output created by 804
+MNT1 1
+MNT1/FOOBAR_FILE
+MNT2 0
+MNT2/FOOBAR_FILE
+mount cycle
+MNT1 0
+MNT1/FOOBAR_FILE
+MNT2 0
+MNT2/FOOBAR_FILE
Signature · 1 line
2.43.0
#26 lore raw

Re: [PATCH v8 10/13] fstests: verify IMA isolation on cloned filesystems

Quoted 64 lines · Anand Jain

Add testcase to verify IMA measurement isolation when multiple devices share the same FSUUID.

---
 tests/generic/804     | 108 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/804.out |  10 ++++
 2 files changed, 118 insertions(+)
 create mode 100644 tests/generic/804
 create mode 100644 tests/generic/804.out
tests/generic/804 new file +47 -0
new file mode 100644
index 000000000000..ced32e6d79dd
@@ -0,0 +1,108 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 804
+# 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_block_device $TEST_DEV
+_require_loop
+
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	_unmount $mnt1 2>/dev/null
+	_unmount $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Normalize device names and mount points
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|$mnt1|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|$mnt2|MNT2|g" | _filter_spaces
+}
+
+# Core helper to set IMA policy and check measurement logs
+do_ima()
+{
+	local ima_policy="/sys/kernel/security/ima/policy"
+	local ima_log="/sys/kernel/security/ima/ascii_runtime_measurements"
+	local fsuuid

tests/generic/804 line 57

Quoted 10 lines
tests/generic/804 new file +10 -0
+	local mnt=$1
+	local 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 $enable | filter_pool
+
+	[ -w "$ima_policy" ] || _notrun "IMA policy not writable"

How about a _require_security_ima to make sure there's securityfs and IMA supporting in current kernel.

tests/generic/804 line 64

Quoted 7 lines
tests/generic/804 new file +7 -0
+
+	fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+	# Load IMA policy to measure file access specifically for this
+	# filesystem UUID.
+	if [[ $enable -eq 1 ]]; then
+		echo "measure func=FILE_CHECK fsuuid=$fsuuid" > "$ima_policy" || \

I'm not sure if we should have this test, especially add it into auto and quick group. Once an IMA policy is loaded, there is no way to clear or roll it back, and I am concerned it might potentially interfere with subsequent tests.

Hmm... how about use unshare command to rewrite the do_ima as do_ima_in_ns? For example:
do_ima_in_ns()

{
	...

    unshare -U -m -p --fork --map-root-user bash <<EOF
		mount -t securityfs securityfs /sys/kernel/security
        ima_policy="/sys/kernel/security/ima/policy"
        ima_log="/sys/kernel/security/ima/ascii_runtime_measurements"
        echo "measure func=FILE_CHECK fsuuid=$uuid" > "\$ima_policy" || exit 1
        ...
EOF
    ret=$?
}

Thanks,
Zorro

Quoted 63 lines
tests/generic/804 new file +44 -0
+			_notrun "Policy rejected"
+	fi
+
+	# Create a file to trigger measurement and verify its entry in
+	# the IMA log.
+	echo "test_data" > $mnt/$foofile
+
+	# IMA log extract
+	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
+}
+
+# Initialize loop base and cloned instances
+devs=()
+_loop_image_create_clone devs
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Concurrently mount both clones
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+#  IMA response on baseline and clone configuration
+do_ima $mnt1 1
+do_ima $mnt2 0
+
+# Cycle mount on the second device.
+echo mount cycle
+_unmount $mnt2
+_mount $mount_opts ${devs[1]} $mnt2 || _fail "Failed to mount dev2"
+
+do_ima $mnt1 0
+do_ima $mnt2 0
+
+status=0
+exit
tests/generic/804.out new file +10 -0
new file mode 100644
index 000000000000..9804181d6c17
@@ -0,0 +1,10 @@
+QA output created by 804
+MNT1 1
+MNT1/FOOBAR_FILE
+MNT2 0
+MNT2/FOOBAR_FILE
+mount cycle
+MNT1 0
+MNT1/FOOBAR_FILE
+MNT2 0
+MNT2/FOOBAR_FILE
Signature · 1 line
2.43.0
#27 lore raw

Re: [PATCH v8 10/13] fstests: verify IMA isolation on cloned filesystems

Quoted 70 lines · Zorro Lang
Quoted 69 lines · Anand Jain

Add testcase to verify IMA measurement isolation when multiple devices share the same FSUUID.

---
 tests/generic/804     | 108 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/804.out |  10 ++++
 2 files changed, 118 insertions(+)
 create mode 100644 tests/generic/804
 create mode 100644 tests/generic/804.out
tests/generic/804 new file +52 -0
new file mode 100644
index 000000000000..ced32e6d79dd
@@ -0,0 +1,108 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 804
+# 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_block_device $TEST_DEV
+_require_loop
+
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
+	"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	_unmount $mnt1 2>/dev/null
+	_unmount $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# Normalize device names and mount points
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|$mnt1|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|$mnt2|MNT2|g" | _filter_spaces
+}
+
+# Core helper to set IMA policy and check measurement logs
+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 enable=$2
+
+	# Since the in-memory IMA audit log is only cleared upon reboot,
+	# use unique random filenames to avoid log collisions.
Quoted 9 lines

tests/generic/804 line 57

Quoted 5 lines
tests/generic/804 new file +5 -0
+	local foofile=$(mktemp --dry-run foobar_XXXXX)
+
+	echo $mnt $enable | filter_pool
+
+	[ -w "$ima_policy" ] || _notrun "IMA policy not writable"

How about a _require_security_ima to make sure there's securityfs and IMA supporting in current kernel.

sure. added.

_require_security_ima()

{
        [ -d /sys/kernel/security ] || _notrun "kernel doesn't support

securityfs"

        # securityfs may not be mounted yet on the host; mount it
        # in a scratch location just to probe for IMA support.
        local se_mnt="${TEST_MNT}/${seq}/se_mnt"
        local has_ima=1
        mkdir -p "${se_mnt}"
        _mount -t securityfs securityfs "${se_mnt}" 2>/dev/null ||

_notrun "cannot mount securityfs"

        [ -d "${se_mnt}/ima" ] || has_ima=0
        [ -w "${se_mnt}/ima/policy" ] || _notrun "IMA policy not writable"

        _unmount "${se_mnt}"

        [ $has_ima -eq 1 ] || _notrun "kernel doesn't support IMA"
}
Quoted 4 lines
Quoted 4 lines
tests/generic/804 new file +4 -0
+
+	fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+	# Load IMA policy to measure file access specifically for this
Quoted 9 lines

tests/generic/804 line 64

Quoted 3 lines
tests/generic/804 new file +3 -0
+	# filesystem UUID.
+	if [[ $enable -eq 1 ]]; then
+		echo "measure func=FILE_CHECK fsuuid=$fsuuid" > "$ima_policy" || \

I'm not sure if we should have this test, especially add it into auto and quick group. Once an IMA policy is loaded, there is no way to clear or roll it back, and I am concerned it might potentially interfere with subsequent tests.

Removed quick and auto. Looks like we need to gate it behind an opt-in group or restrict it to disposable/reboot-per-run environments. New group? I'm concerned it's getting too complicated and I see no other choice.

Quoted 15 lines

Hmm... how about use unshare command to rewrite the do_ima as do_ima_in_ns? For example:
do_ima_in_ns()

{
	...

    unshare -U -m -p --fork --map-root-user bash <<EOF
		mount -t securityfs securityfs /sys/kernel/security
        ima_policy="/sys/kernel/security/ima/policy"
        ima_log="/sys/kernel/security/ima/ascii_runtime_measurements"
        echo "measure func=FILE_CHECK fsuuid=$uuid" > "\$ima_policy" || exit 1
        ...
EOF
    ret=$?
}

A new namespace doesn't help undo the loaded IMA policy; I don't see the point of using unshare.

I have the following code using unshare, which works fine. I dropped -U and --map-root-user (since fstests needs to run as root). I'm okay with including unshare, but I still don't see why.

--------

        unshare -m -p --fork bash <<-EOF | filter_pool | sed

"s/$foofile/FOOBAR_FILE/"
ima_policy="/sys/kernel/security/ima/policy"

ima_log="/sys/kernel/security/ima/ascii_runtime_measurements"

                mount -t securityfs securityfs /sys/kernel/security ||

exit 1

# Load IMA policy to measure file access specifically
for this

                # filesystem UUID.
                if [ $enable -eq 1 ]; then
                        echo "measure func=FILE_CHECK fsuuid=$fsuuid" >
"\$ima_policy" || exit 1
                fi

# Create a file to trigger measurement and verify its
entry in

                # the IMA log.
                echo "test_data" > $mnt/$foofile

# IMA log extract
grep $foofile "\$ima_log" | awk '{ print \$5 }'

echo "dbg: $mnt $fsuuid $foofile" >> $seqres.full tail -1 "\$ima_log" >> $seqres.full echo >> $seqres.full
EOF
--------

Thanks.

Quoted 67 lines

Thanks,
Zorro

Quoted 63 lines
tests/generic/804 new file +44 -0
+			_notrun "Policy rejected"
+	fi
+
+	# Create a file to trigger measurement and verify its entry in
+	# the IMA log.
+	echo "test_data" > $mnt/$foofile
+
+	# IMA log extract
+	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
+}
+
+# Initialize loop base and cloned instances
+devs=()
+_loop_image_create_clone devs
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Concurrently mount both clones
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+#  IMA response on baseline and clone configuration
+do_ima $mnt1 1
+do_ima $mnt2 0
+
+# Cycle mount on the second device.
+echo mount cycle
+_unmount $mnt2
+_mount $mount_opts ${devs[1]} $mnt2 || _fail "Failed to mount dev2"
+
+do_ima $mnt1 0
+do_ima $mnt2 0
+
+status=0
+exit
tests/generic/804.out new file +10 -0
new file mode 100644
index 000000000000..9804181d6c17
@@ -0,0 +1,10 @@
+QA output created by 804
+MNT1 1
+MNT1/FOOBAR_FILE
+MNT2 0
+MNT2/FOOBAR_FILE
+mount cycle
+MNT1 0
+MNT1/FOOBAR_FILE
+MNT2 0
+MNT2/FOOBAR_FILE
Signature · 1 line
2.43.0
author
#28 lore raw

PATCH v8 11/13 fstests: verify exportfs file handles on cloned filesystems

In reply to Anand Jain

Ensure 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/805     | 80 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/805.out |  2 ++
 2 files changed, 82 insertions(+)
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out
tests/generic/805 new file +80 -0
new file mode 100644
index 000000000000..5827eee039df
@@ -0,0 +1,80 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test No. 805
+# Verify that file handles encoded on a cloned filesystem remain valid and
+# resolvable via open_by_handle across a mount cycle and mount order swap.
+
+. ./common/preamble
+
+_begin_fstest auto quick exportfs clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_exportfs
+_require_loop
+_require_test_program "open_by_handle"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	_unmount $mnt1 2>/dev/null
+	_unmount $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# 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
+}
+
+# Attempt to read and decode the saved file handles on the targeted mount point.
+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
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both identical UUID filesystems simultaneously
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_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
+
+# Cycle mounts and reverse initialization sequence to check if
+# file handle lookups are okay
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+# Verify file handles can still be resolved post-mount-cycle
+test_file_handles -rp "cycle mount"
+
+status=0
+exit
tests/generic/805.out new file +2 -0
new file mode 100644
index 000000000000..29b11ec77ffb
@@ -0,0 +1,2 @@
+QA output created by 805
+test_file_handles after cycle mount
Signature · 1 line
2.43.0
#29 lore raw

Re: [PATCH v8 11/13] fstests: verify exportfs file handles on cloned filesystems

#30 lore raw

Re: [PATCH v8 11/13] fstests: verify exportfs file handles on cloned filesystems

In reply to Anand Jain
Quoted 69 lines · Anand Jain

Ensure 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/805     | 80 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/805.out |  2 ++
 2 files changed, 82 insertions(+)
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out
tests/generic/805 new file +51 -0
new file mode 100644
index 000000000000..5827eee039df
@@ -0,0 +1,80 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test No. 805
+# Verify that file handles encoded on a cloned filesystem remain valid and
+# resolvable via open_by_handle across a mount cycle and mount order swap.
+
+. ./common/preamble
+
+_begin_fstest auto quick exportfs clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_exportfs
+_require_loop
+_require_test_program "open_by_handle"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	_unmount $mnt1 2>/dev/null
+	_unmount $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# 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
+}
+
+# Attempt to read and decode the saved file handles on the targeted mount point.
+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
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2

tests/generic/805 line 61

Quoted 10 lines
tests/generic/805 new file +10 -0
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both identical UUID filesystems simultaneously
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+NUMFILES=1

NUMFILES=${LOAD_FACTOR} ?

Quoted 30 lines
tests/generic/805 new file +19 -0
+testdir=$mnt2/testdir
+
+# Decode file handles of files/dir after cycle mount
+create_test_files
+
+# Cycle mounts and reverse initialization sequence to check if
+# file handle lookups are okay
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+# Verify file handles can still be resolved post-mount-cycle
+test_file_handles -rp "cycle mount"
+
+status=0
+exit
tests/generic/805.out new file +2 -0
new file mode 100644
index 000000000000..29b11ec77ffb
@@ -0,0 +1,2 @@
+QA output created by 805
+test_file_handles after cycle mount
Signature · 1 line
2.43.0
#31 lore raw

Re: [PATCH v8 11/13] fstests: verify exportfs file handles on cloned filesystems

Quoted 80 lines · Zorro Lang
Quoted 78 lines · Anand Jain

Ensure 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/805     | 80 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/805.out |  2 ++
 2 files changed, 82 insertions(+)
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out
tests/generic/805 new file +60 -0
new file mode 100644
index 000000000000..5827eee039df
@@ -0,0 +1,80 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test No. 805
+# Verify that file handles encoded on a cloned filesystem remain valid and
+# resolvable via open_by_handle across a mount cycle and mount order swap.
+
+. ./common/preamble
+
+_begin_fstest auto quick exportfs clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_exportfs
+_require_loop
+_require_test_program "open_by_handle"
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	_unmount $mnt1 2>/dev/null
+	_unmount $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+# 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
+}
+
+# Attempt to read and decode the saved file handles on the targeted mount point.
+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
+}
+
+# Setup base loop device and its clone
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Mount both identical UUID filesystems simultaneously
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
Quoted 3 lines

tests/generic/805 line 61

Quoted 1 line
tests/generic/805 new file +1 -0
+NUMFILES=1

NUMFILES=${LOAD_FACTOR} ?

Added.

Quoted 20 lines
Quoted 18 lines
tests/generic/805 new file +17 -0
+testdir=$mnt2/testdir
+
+# Decode file handles of files/dir after cycle mount
+create_test_files
+
+# Cycle mounts and reverse initialization sequence to check if
+# file handle lookups are okay
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+# Verify file handles can still be resolved post-mount-cycle
+test_file_handles -rp "cycle mount"
+
Quoted 2 lines

tests/generic/805 line 80

Quoted 2 lines
tests/generic/805 new file +2 -0
+status=0
+exit

Also this fixed to

_exit 0

Thanks.
Anand

Quoted 11 lines
Quoted 11 lines
tests/generic/805.out new file +2 -0
new file mode 100644
index 000000000000..29b11ec77ffb
@@ -0,0 +1,2 @@
+QA output created by 805
+test_file_handles after cycle mount
Signature · 1 line
2.43.0
author
#32 lore raw

PATCH v8 12/13 fstests: add _change_metadata_uuid helper

In reply to Anand Jain

_change_metadata_uuid changes the UUID of the golden filesystem before it is cloned.

---
 common/rc | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
common/rc +23 -0
index f73251365a60..45cf9360ba73 100644
@@ -1533,6 +1533,29 @@ _scratch_resvblks()
 	esac
 }
 
+# Change the metadata UUID of the given device to a newly generated one.
+# Args:
+#   $1: Block device path to modify.
+_change_metadata_uuid()
+{
+	local temp_mnt=$TEST_DIR/${seq}_mnt
+	local dev=$1
+
+	case $FSTYP in
+	xfs)
+		_require_command "$XFS_ADMIN_PROG" "xfs_admin"
+		$XFS_ADMIN_PROG -U generate $dev >> $seqres.full
+		;;
+	btrfs)
+		_require_command "$BTRFS_TUNE_PROG" "btrfstune"
+		$BTRFS_TUNE_PROG -m $dev
+		;;
+	*)
+		_notrun "Require filesystem with metadata_uuid feature"
+		;;
+	esac
+}
+
 # Create a small loop image, run an optional tuning function ($2) on it,
 # clone it, and attach both to loop devices, returned in ($1).
 # Args:
Signature · 1 line
2.43.0
#33 lore raw

Re: [PATCH v8 12/13] fstests: add _change_metadata_uuid helper

Quoted 21 lines · Anand Jain

_change_metadata_uuid changes the UUID of the golden filesystem before it is cloned.

---
 common/rc | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
common/rc +5 -0
index f73251365a60..45cf9360ba73 100644
@@ -1533,6 +1533,29 @@ _scratch_resvblks()
 	esac
 }
 
+# Change the metadata UUID of the given device to a newly generated one.
+# Args:
+#   $1: Block device path to modify.
+_change_metadata_uuid()
+{

common/rc _scratch_resvblks() · line 1550

Quoted 10 lines
common/rc +10 -0
+	local temp_mnt=$TEST_DIR/${seq}_mnt
+	local dev=$1
+
+	case $FSTYP in
+	xfs)
+		_require_command "$XFS_ADMIN_PROG" "xfs_admin"
+		$XFS_ADMIN_PROG -U generate $dev >> $seqres.full
+		;;
+	btrfs)
+		_require_command "$BTRFS_TUNE_PROG" "btrfstune"

How about split this function to _require_metadata_uuid_tool and _chanage_metadata_uuid ?

Quoted 14 lines
common/rc +8 -0
+		$BTRFS_TUNE_PROG -m $dev
+		;;
+	*)
+		_notrun "Require filesystem with metadata_uuid feature"
+		;;
+	esac
+}
+
 # Create a small loop image, run an optional tuning function ($2) on it,
 # clone it, and attach both to loop devices, returned in ($1).
 # Args:
Signature · 1 line
2.43.0
#34 lore raw

Re: [PATCH v8 12/13] fstests: add _change_metadata_uuid helper

Quoted 27 lines · Zorro Lang
Quoted 24 lines · Anand Jain

_change_metadata_uuid changes the UUID of the golden filesystem before it is cloned.

---
 common/rc | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
common/rc +8 -0
index f73251365a60..45cf9360ba73 100644
@@ -1533,6 +1533,29 @@ _scratch_resvblks()
 	esac
 }
 
+# Change the metadata UUID of the given device to a newly generated one.
+# Args:
+#   $1: Block device path to modify.
+_change_metadata_uuid()
+{
+	local temp_mnt=$TEST_DIR/${seq}_mnt
+	local dev=$1
+
Quoted 10 lines

common/rc _scratch_resvblks() · line 1550

Quoted 7 lines
common/rc +7 -0
+	case $FSTYP in
+	xfs)
+		_require_command "$XFS_ADMIN_PROG" "xfs_admin"
+		$XFS_ADMIN_PROG -U generate $dev >> $seqres.full
+		;;
+	btrfs)
+		_require_command "$BTRFS_TUNE_PROG" "btrfstune"

How about split this function to _require_metadata_uuid_tool and _chanage_metadata_uuid ?

Splitting out _require_metadata_uuid_tool() is more modular, but I'm not sure it's needed until we have multiple callers. With only a single caller, an extra helper feels unnecessary. Inlining it makes the flow easier to read and execute. Similar to how we handle this in the kernel.

Thanks, Anand

Quoted 15 lines
Quoted 15 lines
common/rc +8 -0
+		$BTRFS_TUNE_PROG -m $dev
+		;;
+	*)
+		_notrun "Require filesystem with metadata_uuid feature"
+		;;
+	esac
+}
+
 # Create a small loop image, run an optional tuning function ($2) on it,
 # clone it, and attach both to loop devices, returned in ($1).
 # Args:
Signature · 1 line
2.43.0
author
#35 lore raw

PATCH v8 13/13 fstests: test UUID consistency for clones with metadata_uuid

In reply to Anand Jain

Btrfs and xfs uses the metadata_uuid superblock feature to change the on-disk UUID without rewriting every block header. This patch adds a sanity check to ensure UUID consistency when a filesystem with metadata_uuid enabled is cloned.

---
 tests/generic/806     | 74 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/806.out |  6 ++++
 2 files changed, 80 insertions(+)
 create mode 100644 tests/generic/806
 create mode 100644 tests/generic/806.out
tests/generic/806 new file +74 -0
new file mode 100644
index 000000000000..6d3166491006
@@ -0,0 +1,74 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 806
+#
+# Verify that the cloned filesystem UUID remains consistent, even when the
+# `metadata_uuid` feature is enabled.
+#
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
+# Create base loop device and its clone, applying the metadata_uuid tuning
+# callback to the base filesystem before the copy occurs.
+devs=()
+_loop_image_create_clone devs _change_metadata_uuid
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both clone and baseline
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+# Cycle mounts and reverse the initialization order to ensure UUID tracking
+# doesn't mismatch or flip when metadata_uuid optimization is active.
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+status=0
+exit
tests/generic/806.out new file +6 -0
new file mode 100644
index 000000000000..918f422ecddf
@@ -0,0 +1,6 @@
+QA output created by 806
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
#36 lore raw

Re: [PATCH v8 13/13] fstests: test UUID consistency for clones with metadata_uuid

Quoted 46 lines · Anand Jain

Btrfs and xfs uses the metadata_uuid superblock feature to change the on-disk UUID without rewriting every block header. This patch adds a sanity check to ensure UUID consistency when a filesystem with metadata_uuid enabled is cloned.

---
 tests/generic/806     | 74 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/806.out |  6 ++++
 2 files changed, 80 insertions(+)
 create mode 100644 tests/generic/806
 create mode 100644 tests/generic/806.out
tests/generic/806 new file +27 -0
new file mode 100644
index 000000000000..6d3166491006
@@ -0,0 +1,74 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 806
+#
+# Verify that the cloned filesystem UUID remains consistent, even when the
+# `metadata_uuid` feature is enabled.
+#
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+

tests/generic/806 line 37

Quoted 10 lines
tests/generic/806 new file +10 -0
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
+# Create base loop device and its clone, applying the metadata_uuid tuning
+# callback to the base filesystem before the copy occurs.
+devs=()
+_loop_image_create_clone devs _change_metadata_uuid

Should we have and run a _require_metadata_uuid before changing it? As I know, btrfs can do _require_btrfs_fs_feature metadata_uuid.

Quoted 52 lines
tests/generic/806 new file +37 -0
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both clone and baseline
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+# Cycle mounts and reverse the initialization order to ensure UUID tracking
+# doesn't mismatch or flip when metadata_uuid optimization is active.
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+status=0
+exit
tests/generic/806.out new file +6 -0
new file mode 100644
index 000000000000..918f422ecddf
@@ -0,0 +1,6 @@
+QA output created by 806
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
#37 lore raw

Re: [PATCH v8 13/13] fstests: test UUID consistency for clones with metadata_uuid

Quoted 55 lines · Zorro Lang
Quoted 52 lines · Anand Jain

Btrfs and xfs uses the metadata_uuid superblock feature to change the on-disk UUID without rewriting every block header. This patch adds a sanity check to ensure UUID consistency when a filesystem with metadata_uuid enabled is cloned.

---
 tests/generic/806     | 74 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/806.out |  6 ++++
 2 files changed, 80 insertions(+)
 create mode 100644 tests/generic/806
 create mode 100644 tests/generic/806.out
tests/generic/806 new file +33 -0
new file mode 100644
index 000000000000..6d3166491006
@@ -0,0 +1,74 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 806
+#
+# Verify that the cloned filesystem UUID remains consistent, even when the
+# `metadata_uuid` feature is enabled.
+#
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
Quoted 8 lines

tests/generic/806 line 37

Quoted 4 lines
tests/generic/806 new file +4 -0
+# Create base loop device and its clone, applying the metadata_uuid tuning
+# callback to the base filesystem before the copy occurs.
+devs=()
+_loop_image_create_clone devs _change_metadata_uuid

Should we have and run a _require_metadata_uuid before changing it? As I know, btrfs can do _require_btrfs_fs_feature metadata_uuid.

_require_metadata_uuid() is better than doing the same thing within _change_metadata_uuid(). Now _change_metadata_uuid() calls _require_metadata_uuid(), as shown below.

_require_metdata_uuid()
{
case $FSTYP in
xfs)
_require_command "$XFS_ADMIN_PROG" "xfs_admin" ;;
btrfs)
_require_btrfs_fs_feature "metadata_uuid" _require_command "$BTRFS_TUNE_PROG" "btrfstune" ;;
*)
_notrun "Require filesystem with metadata_uuid feature" ;;
esac
}

_change_metadata_uuid()
{
local dev=$1

_require_metdata_uuid

case $FSTYP in
xfs)
$XFS_ADMIN_PROG -U generate $dev >> $seqres.full ;;
btrfs)
$BTRFS_TUNE_PROG -m $dev
;;
esac
}

Quoted 52 lines
Quoted 52 lines
tests/generic/806 new file +37 -0
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both clone and baseline
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+# Cycle mounts and reverse the initialization order to ensure UUID tracking
+# doesn't mismatch or flip when metadata_uuid optimization is active.
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+status=0
+exit
tests/generic/806.out new file +6 -0
new file mode 100644
index 000000000000..918f422ecddf
@@ -0,0 +1,6 @@
+QA output created by 806
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
#38 lore raw

Re: [PATCH v8 13/13] fstests: test UUID consistency for clones with metadata_uuid

Quoted 70 lines · Anand Suveer Jain
Quoted 55 lines · Zorro Lang
Quoted 52 lines · Anand Jain

Btrfs and xfs uses the metadata_uuid superblock feature to change the on-disk UUID without rewriting every block header. This patch adds a sanity check to ensure UUID consistency when a filesystem with metadata_uuid enabled is cloned.

---
 tests/generic/806     | 74 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/806.out |  6 ++++
 2 files changed, 80 insertions(+)
 create mode 100644 tests/generic/806
 create mode 100644 tests/generic/806.out
tests/generic/806 new file +33 -0
new file mode 100644
index 000000000000..6d3166491006
@@ -0,0 +1,74 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <[email protected]>.  All Rights Reserved.
+#
+# FS QA Test 806
+#
+# Verify that the cloned filesystem UUID remains consistent, even when the
+# `metadata_uuid` feature is enabled.
+#
+
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_block_device $TEST_DEV
+_require_loop
+
+_cleanup()
+{
+	cd /
+	rm -r -f $tmp.*
+	umount $mnt1 $mnt2 2>/dev/null
+	_loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+filter_pool()
+{
+	sed -e "s|${devs[0]}|DEV1|g" -e "s|${mnt1}|MNT1|g" \
+	    -e "s|${devs[1]}|DEV2|g" -e "s|${mnt2}|MNT2|g" | _filter_spaces
+}
+
Quoted 8 lines

tests/generic/806 line 37

Quoted 4 lines
tests/generic/806 new file +4 -0
+# Create base loop device and its clone, applying the metadata_uuid tuning
+# callback to the base filesystem before the copy occurs.
+devs=()
+_loop_image_create_clone devs _change_metadata_uuid

Should we have and run a _require_metadata_uuid before changing it? As I know, btrfs can do _require_btrfs_fs_feature metadata_uuid.

_require_metadata_uuid() is better than doing the same thing within _change_metadata_uuid(). Now _change_metadata_uuid() calls _require_metadata_uuid(), as shown below.

Quoted 5 lines

_require_metdata_uuid()
{
case $FSTYP in
xfs)
_require_command "$XFS_ADMIN_PROG" "xfs_admin"

The presence of xfs_admin doesn't guarantee metauuid support on xfs; the script predates the creation of that feature flag + cli args.

--D

Quoted 84 lines
                ;;
        btrfs)
                _require_btrfs_fs_feature "metadata_uuid"
                _require_command "$BTRFS_TUNE_PROG" "btrfstune"
                ;;
        *)
                _notrun "Require filesystem with metadata_uuid feature"
                ;;
        esac
}

_change_metadata_uuid()
{
local dev=$1

_require_metdata_uuid

case $FSTYP in
xfs)
$XFS_ADMIN_PROG -U generate $dev >> $seqres.full ;;
btrfs)
$BTRFS_TUNE_PROG -m $dev
;;
esac
}

Quoted 52 lines
Quoted 52 lines
tests/generic/806 new file +37 -0
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+# Get the uuid from the source device
+fsuuid=$(blkid -s UUID -o value ${devs[0]})
+
+# Mount both clone and baseline
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+# Cycle mounts and reverse the initialization order to ensure UUID tracking
+# doesn't mismatch or flip when metadata_uuid optimization is active.
+echo "**** mount cycle ****"
+_unmount $mnt1
+_unmount $mnt2
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+						_fail "Failed to mount dev2"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+						_fail "Failed to mount dev1"
+
+findmnt -o SOURCE,TARGET,UUID "${devs[0]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+findmnt -o SOURCE,TARGET,UUID "${devs[1]}" | tail -n +2 | \
+				sed -e "s/${fsuuid}/FSUUID/g" | filter_pool
+
+status=0
+exit
tests/generic/806.out new file +6 -0
new file mode 100644
index 000000000000..918f422ecddf
@@ -0,0 +1,6 @@
+QA output created by 806
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
Signature · 1 line
2.43.0
#39 lore raw

Re: [PATCH v8 13/13] fstests: test UUID consistency for clones with metadata_uuid

Quoted 11 lines
Quoted 6 lines

_require_metdata_uuid()
{
case $FSTYP in
xfs)
_require_command "$XFS_ADMIN_PROG" "xfs_admin"

The presence of xfs_admin doesn't guarantee metauuid support on xfs; the script predates the creation of that feature flag + cli args.

--D

Do we need to check and guarantee metadata_uuid support on XFS?

Taking hints from xfs/077, which lacks a check for metadata_uuid support, leading to _notrun()).

#40 lore raw
In reply to Anand Jain

Zorro, Ping?

Cheers, Anand

Quoted 97 lines · Anand Jain

v8:
- _require_fs_space: double the required space (x2) to account for both source and clone
- Use proper quoting/unquoting for _mkfs_dev (Thanks Darrick)
- Call _fail() if cp fails
- _require_fanotify_function: check for kernel fsnotify support
- _require_unique_f_fsid: reorganize comments and replace 'if' with 'case' statement (Thanks Darrick)
- Add SEMANAGE_PROG variable (Thanks Darrick)
- fanotify verification: add checks for SEMANAGE_PROG and _require_fanotify_function
- Verify that the monitoring process is actually created

v7:
https://lore.kernel.org/fstests/[email protected]

v6:
https://lore.kernel.org/fstests/[email protected]

v5:
https://lore.kernel.org/fstests/[email protected]

v4:
https://lore.kernel.org/fstests/[email protected]

v3:
https://lore.kernel.org/fstests/[email protected]

v2:
https://lore.kernel.org/fstests/[email protected]

v1:
https://lore.kernel.org/fstests/[email protected]

This series adds fstests infrastructure and test cases to verify correct filesystem identity when a filesystem is cloned (block-level copy). Test covers inotify, fanotify, f_fsid, libblkid, IMA, exportfs file handles and libblkid tools verify with metadata_uuid.
New helpers:
_loop_image_create_clone() and _loop_image_destroy() to help create fs and clone _clone_mount_option() helper to apply per-filesystem clone mount options _require_fanotify_function check if kernel supports fs notification _require_unique_f_fsid() helper to handle fs difference in fsid implementation

New tests:
- fanotify events are isolated between cloned filesystems
- f_fsid is unique across cloned filesystem instances
- libblkid correctly resolves duplicate UUIDs to distinct devices with and without metadata_uuid
- IMA distinct identity for each cloned filesystem
- exportfs file handles resolve correctly on cloned filesystems

Kernel Patches:
Requires Btrfs kernel patches for all tests to pass.
[1] https://lore.kernel.org/linux-btrfs/[email protected]

Anand Jain (13):
fstests: add _loop_image_create_clone() helper
fstests: add _clone_mount_option() helper
fstests: add FSNOTIFYWAIT_PROG
fstests: add _require_fanotify_function
fstests: add _require_unique_f_fsid() helper
fstests: add SEMANAGE_PROG
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
fstests: add _change_metadata_uuid helper
fstests: test UUID consistency for clones with metadata_uuid

 common/config         |   2 +
 common/rc             | 135 +++++++++++++++++++++++++++++++++++++++
 tests/generic/801     | 143 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/801.out |   7 +++
 tests/generic/802     |  64 +++++++++++++++++++
 tests/generic/802.out |   4 ++
 tests/generic/803     |  72 +++++++++++++++++++++
 tests/generic/803.out |   6 ++
 tests/generic/804     | 108 +++++++++++++++++++++++++++++++
 tests/generic/804.out |  10 +++
 tests/generic/805     |  80 +++++++++++++++++++++++
 tests/generic/805.out |   2 +
 tests/generic/806     |  74 ++++++++++++++++++++++
 tests/generic/806.out |   6 ++
 14 files changed, 713 insertions(+)
 create mode 100644 tests/generic/801
 create mode 100644 tests/generic/801.out
 create mode 100644 tests/generic/802
 create mode 100644 tests/generic/802.out
 create mode 100644 tests/generic/803
 create mode 100644 tests/generic/803.out
 create mode 100644 tests/generic/804
 create mode 100644 tests/generic/804.out
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out
 create mode 100644 tests/generic/806
 create mode 100644 tests/generic/806.out