fstests: add test coverage for cloned filesystem ids
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
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
@@ -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
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.Signed-off-by Anand Jain <[email protected]>--- common/rc | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)common/rc +0
-0
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 outSignature · 1 line
2.43.0
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.Signed-off-by Anand Jain <[email protected]>--- common/rc | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)common/rc +0
-0@@ -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") + doneHow 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
@@ -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,
ZorroQuoted 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 outSignature · 1 line
2.43.0
PATCH v8 2/13 fstests: add _clone_mount_option() helper
In reply to Anand JainAdds _clone_mount_option() helper function to handle filesystem-specific requirements for mounting cloned devices. Abstract the need for -o nouuid on XFS.
--- common/rc | 13 +++++++++++++ 1 file changed, 13 insertions(+)
common/rc +13 -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() +{ + 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
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.Signed-off-by Anand Jain <[email protected]>Reviewed-by "Darrick J. Wong" <[email protected]>--- common/rc | 13 +++++++++++++ 1 file changed, 13 insertions(+)common/rc +0
-0
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=$1Signature · 1 line
2.43.0
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.Signed-off-by Anand Jain <[email protected]>Reviewed-by "Darrick J. Wong" <[email protected]>--- common/rc | 13 +++++++++++++ 1 file changed, 13 insertions(+)common/rc +0
-0@@ -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_optionsfunctions (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=$1Signature · 1 line
2.43.0
PATCH v8 3/13 fstests: add FSNOTIFYWAIT_PROG
In reply to Anand JainDefine FSNOTIFYWAIT_PROG for an upcoming test case that uses fsnotifywait.
--- common/config | 1 + 1 file changed, 1 insertion(+)
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)" # udev wait functions. #
Signature · 1 line
2.43.0
Re: [PATCH v8 03/13] fstests: add FSNOTIFYWAIT_PROG
Quoted 13 lines · Anand Jain
Define
FSNOTIFYWAIT_PROGfor an upcoming test case that usesfsnotifywait.Signed-off-by Anand Jain <[email protected]>Reviewed-by "Darrick J. Wong" <[email protected]>--- common/config | 1 + 1 file changed, 1 insertion(+)common/config +0
-0
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
PATCH v8 4/13 fstests: add _require_fanotify_function
In reply to Anand Jainfanotify 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
@@ -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
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_functionto skip tests when fanotify is disabled.Signed-off-by Anand Jain <[email protected]>--- common/rc | 7 +++++++ 1 file changed, 7 insertions(+)common/rc +0
-0
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 xSignature · 1 line
2.43.0
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_functionto skip tests when fanotify is disabled.Signed-off-by Anand Jain <[email protected]>--- common/rc | 7 +++++++ 1 file changed, 7 insertions(+)common/rc +0
-0@@ -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" + fiWhat 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,
ZorroQuoted 8 lines
common/rc +2
-0+} + _require_fanotify_ioerrors() { local xSignature · 1 line
2.43.0
PATCH v8 5/13 fstests: add _require_unique_f_fsid() helper
In reply to Anand JainAdd 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
@@ -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
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_fsidtracking across cloned or snapshot instances.Certain filesystems like XFS, Btrfs, and F2FS ensure unique
f_fsididentifiers per filesystem instance. However, Ext4 derives itsf_fsiddirectly from its superblock UUID, which leads to identicalf_fsidvalues on cloned images until the UUID is manually modified by userspace.Introduce
_require_unique_f_fsid()to allow test cases requiring strictf_fsiduniqueness to skip gracefully on unsupported filesystems.Signed-off-by Anand Jain <[email protected]>--- common/rc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)common/rc +14
-0@@ -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
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_fsidtracking across cloned or snapshot instances.Certain filesystems like XFS, Btrfs, and F2FS ensure unique
f_fsididentifiers per filesystem instance. However, Ext4 derives itsf_fsiddirectly from its superblock UUID, which leads to identicalf_fsidvalues on cloned images until the UUID is manually modified by userspace.Introduce
_require_unique_f_fsid()to allow test cases requiring strictf_fsiduniqueness to skip gracefully on unsupported filesystems.Signed-off-by Anand Jain <[email protected]>--- common/rc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)common/rc +20
-0@@ -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.) + ;; + esacI 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_notrunby default and only allow filesystems that explicitly guarantee uniquef_fsidsupport?case "$FSTYP" in
btrfs|xfs|f2fs);;*)
_notrun"Target filesystem ($FSTYP) does not guarantee uniquef_fsidon clones." ;;
esac
So _notrun() unless confirmed to have unique f_fsid for the cloned filesystem. Works for me.
Thanks, Anand
Quoted 12 lines
Thanks,
ZorroQuoted 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
PATCH v8 6/13 fstests: add SEMANAGE_PROG
In reply to Anand JainDefine SEMANAGE_PROG for an upcoming test case that uses semanage(8).
--- common/config | 1 + 1 file changed, 1 insertion(+)
common/config +1 -0
@@ -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
PATCH v8 7/13 fstests: verify fanotify isolation on cloned filesystems
In reply to Anand JainVerify that fanotify events are correctly routed to the appropriate watcher when cloned filesystems are mounted.
Helps verify kernel's event notification distinguishes between devices sharing the same FSID/UUID.
--- 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
@@ -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
@@ -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
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.Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/801 new file +4
-0@@ -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
firight? 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@@ -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 foundSignature · 1 line
2.43.0
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
Signed-off-by Anand Jain <[email protected]>--- tests/generic/801 | 143 ++++++++++++++++++++++++++++++++++++++++++
g/801is 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.outtests/generic/801 new file +8
-0@@ -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_DEVWhich test condition requires
TEST_DEVto 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 +fiLooks like semanage/SELinux isn't a necessary requirement of this test case, if so that
_require_command "$SEMANAGE_PROG" semanagealso can be:
Quoted 6 lines
if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then _require_command "$SEMANAGE_PROG" semanage firight? 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 2Are you using
sleep 2to ensurefsnotifywaitis fully set up? I'm not sure ifsleep 2is 100% reliable here. Since the subsequent tests strictly depend onfsnotifywaitstarting up properly, is there a more robust approach thansleep 2? For example, could we check the output in$log1and$log2to 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 0only 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 1tests/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
_exit0
Added.
Thanks.
Anand
Quoted 19 lines
Thanks,
ZorroQuoted 16 lines
tests/generic/801.out new file +7
-0@@ -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 foundSignature · 1 line
2.43.0
PATCH v8 8/13 fstests: verify f_fsid for cloned filesystems
In reply to Anand JainVerify that the cloned filesystem provides an f_fsid that is persistent across mount cycles, yet unique from the original filesystem's f_fsid.
--- tests/generic/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
@@ -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
@@ -0,0 +1,4 @@ +QA output created by 802 +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONE
Signature · 1 line
2.43.0
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_fsidthat is persistent across mount cycles, yet unique from the original filesystem'sf_fsid.Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/802 new file +23
-0@@ -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@@ -0,0 +1,4 @@ +QA output created by 802 +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONESignature · 1 line
2.43.0
Anand Suveer Jain
AppliedRe: [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_fsidthat is persistent across mount cycles, yet unique from the original filesystem'sf_fsid.Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/802 new file +27
-0@@ -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/$seqBetter to
rm -rf $TEST_DIR/$seqbefore 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
_exit0
Applied.
Quoted 14 lines
Quoted 14 lines
tests/generic/802.out new file +4
-0@@ -0,0 +1,4 @@ +QA output created by 802 +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONESignature · 1 line
2.43.0
PATCH v8 9/13 fstests: verify libblkid resolution of duplicate UUIDs
In reply to Anand JainVerify how findmnt, df (libblkid) resolve device paths when multiple block devices share the same FSUUID.
--- tests/generic/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
@@ -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
@@ -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
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.
Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/803 new file +42
-0@@ -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@@ -0,0 +1,6 @@ +QA output created by 803 +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUID +**** mount cycle **** +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUIDSignature · 1 line
2.43.0
Anand Suveer Jain
AppliedRe: [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
dftest 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.
Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/803 new file +48
-0@@ -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_poolfindmnt -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_poolSame 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_poolSame 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_poolSame here
applied
Quoted 6 lines
tests/generic/803 line 72
Quoted 4 lines
tests/generic/803 new file +3
-0+ +status=0 +exit
_exit0
applied
Thanks.>
Quoted 15 lines
Quoted 15 lines
tests/generic/803.out new file +6
-0@@ -0,0 +1,6 @@ +QA output created by 803 +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUID +**** mount cycle **** +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUIDSignature · 1 line
2.43.0
PATCH v8 10/13 fstests: verify IMA isolation on cloned filesystems
In reply to Anand JainAdd testcase to verify IMA measurement isolation when multiple devices share the same FSUUID.
--- tests/generic/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
@@ -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
@@ -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
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.
Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/804 new file +47
-0@@ -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 +exittests/generic/804.out new file +10
-0@@ -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_FILESignature · 1 line
2.43.0
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.
Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/804 new file +52
-0@@ -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_imato make sure there's securityfs and IMA supporting in current kernel.
sure. added.
_require_security_ima()
{
[ -d /sys/kernel/security ] || _notrun "kernel doesn't supportsecurityfs"
# 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
unsharecommand to rewrite thedo_imaasdo_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,
ZorroQuoted 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 +exittests/generic/804.out new file +10
-0@@ -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_FILESignature · 1 line
2.43.0
PATCH v8 11/13 fstests: verify exportfs file handles on cloned filesystems
In reply to Anand JainEnsure that exportfs can correctly decode file handles on a cloned filesystem across a mount cycle, by file handles generated on a cloned device remain valid after mount cycle.
--- tests/generic/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
@@ -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
@@ -0,0 +1,2 @@ +QA output created by 805 +test_file_handles after cycle mount
Signature · 1 line
2.43.0
Re: [PATCH v8 11/13] fstests: verify exportfs file handles on cloned filesystems
Re: [PATCH v8 11/13] fstests: verify exportfs file handles on cloned filesystems
In reply to Anand JainQuoted 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.
Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/805 new file +51
-0@@ -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 +exittests/generic/805.out new file +2
-0@@ -0,0 +1,2 @@ +QA output created by 805 +test_file_handles after cycle mountSignature · 1 line
2.43.0
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.
Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/805 new file +60
-0@@ -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=1NUMFILES=${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@@ -0,0 +1,2 @@ +QA output created by 805 +test_file_handles after cycle mountSignature · 1 line
2.43.0
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
@@ -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
Re: [PATCH v8 12/13] fstests: add _change_metadata_uuid helper
Quoted 21 lines · Anand Jain
_change_metadata_uuidchanges the UUID of the golden filesystem before it is cloned.Signed-off-by Anand Jain <[email protected]>--- common/rc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)common/rc +5
-0@@ -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
Re: [PATCH v8 12/13] fstests: add _change_metadata_uuid helper
Quoted 27 lines · Zorro Lang
Quoted 24 lines · Anand Jain
_change_metadata_uuidchanges the UUID of the golden filesystem before it is cloned.Signed-off-by Anand Jain <[email protected]>--- common/rc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)common/rc +8
-0@@ -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_tooland_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
PATCH v8 13/13 fstests: test UUID consistency for clones with metadata_uuid
In reply to Anand JainBtrfs 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
@@ -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
@@ -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
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_uuidsuperblock 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 withmetadata_uuidenabled is cloned.Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/806 new file +27
-0@@ -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 +exittests/generic/806.out new file +6
-0@@ -0,0 +1,6 @@ +QA output created by 806 +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUID +**** mount cycle **** +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUIDSignature · 1 line
2.43.0
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_uuidsuperblock 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 withmetadata_uuidenabled is cloned.Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/806 new file +33
-0@@ -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_uuidShould we have and run a
_require_metadata_uuidbefore 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 +exittests/generic/806.out new file +6
-0@@ -0,0 +1,6 @@ +QA output created by 806 +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUID +**** mount cycle **** +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUIDSignature · 1 line
2.43.0
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_uuidsuperblock 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 withmetadata_uuidenabled is cloned.Signed-off-by Anand Jain <[email protected]>--- 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.outtests/generic/806 new file +33
-0@@ -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_uuidShould we have and run a
_require_metadata_uuidbefore 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_uuidcase $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 +exittests/generic/806.out new file +6
-0@@ -0,0 +1,6 @@ +QA output created by 806 +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUID +**** mount cycle **** +DEV1 MNT1 FSUUID +DEV2 MNT2 FSUUIDSignature · 1 line
2.43.0
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_admindoesn'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()).
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)
- AddSEMANAGE_PROGvariable (Thanks Darrick)
- fanotify verification: add checks forSEMANAGE_PROGand_require_fanotify_function
- Verify that the monitoring process is actually createdv7:
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 withmetadata_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_functioncheck if kernel supports fs notification_require_unique_f_fsid()helper to handle fs difference in fsid implementationNew tests:
- fanotify events are isolated between cloned filesystems
-f_fsidis unique across cloned filesystem instances
- libblkid correctly resolves duplicate UUIDs to distinct devices with and withoutmetadata_uuid
- IMA distinct identity for each cloned filesystem
- exportfs file handles resolve correctly on cloned filesystemsKernel 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: addFSNOTIFYWAIT_PROG
fstests: add_require_fanotify_function
fstests: add_require_unique_f_fsid()helper
fstests: addSEMANAGE_PROG
fstests: verify fanotify isolation on cloned filesystems
fstests: verifyf_fsidfor 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_uuidhelper
fstests: test UUID consistency for clones withmetadata_uuidcommon/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
Thanks for the review, Zorro. Based on the recent regression report [1] and its fix [2], I will likely need to adjust one or two of these test cases and add a new one. I'm waiting for the kernel fix to land in for-next to confirm the behavior before updating the tests accordingly.
[1]
https://lore.kernel.org/linux-btrfs/[email protected]/
[2]
https://lore.kernel.org/linux-btrfs/[email protected]/
Anand