fstests: add test coverage for cloned filesystem ids
v6 cover fstests: add test coverage for cloned filesystem ids
v6:
. Renamed pre_clone_tune_uuid() to _change_metadata_uuid().
. Created the _require_unique_f_fsid() helper instead of handling it inside the test case (patch 5/11). . Separated FSNOTIFYWAIT_PROG into its own patch.
. Dropped the inotify test case in favor of fsnotify.
. Added comments throughout, especially for helper functions.
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 _change_metadata_uuid() changes the UUID before the clone
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 (11):
fstests: add _loop_image_create_clone() helper
fstests: add _clone_mount_option() helper
fstests: add FSNOTIFYWAIT_PROG
fstests: add _require_unique_f_fsid() helper
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 | 1 + common/rc | 124 ++++++++++++++++++++++++++++++++++++++ tests/generic/801 | 135 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/801.out | 7 +++ tests/generic/802 | 67 +++++++++++++++++++++ tests/generic/802.out | 7 +++ tests/generic/803 | 84 ++++++++++++++++++++++++++ tests/generic/803.out | 19 ++++++ tests/generic/804 | 108 +++++++++++++++++++++++++++++++++ tests/generic/804.out | 10 ++++ tests/generic/805 | 80 +++++++++++++++++++++++++ tests/generic/805.out | 2 + tests/generic/806 | 84 ++++++++++++++++++++++++++ tests/generic/806.out | 19 ++++++ 14 files changed, 747 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 v6 1/11 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 | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)
common/rc +63 -0
@@ -1520,6 +1520,69 @@ _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)) + + _create_file_sized $((size * 1024 * 1024)) $img_file || + _fail "Failed: Create $img_file $size" + + loop_devs=$(_create_loop_device $img_file) + _ret=($loop_devs) + + 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 + + loop_devs="$loop_devs $(_create_loop_device $img_file_clone)" + + _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() +{ + 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 v6 01/11] fstests: add _loop_image_create_clone() helper
Quoted 15 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 | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)common/rc +0
-0@@ -1520,6 +1520,69 @@ _scratch_resvblks() esac
common/rc _scratch_resvblks() · line 1530
Quoted 10 lines
common/rc +8
-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() +{ + local -n _ret=$1
That switch ^^ is very clever. I always wondered how one did indirect
variables in bash.
Quoted 4 lines
common/rc +4
-0+ 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
common/rc _scratch_resvblks() · line 1544
Quoted 10 lines
common/rc +10
-0+ local loop_devs + + # Since we copy the block device image, we keep its size small. + _require_fs_space $TEST_DIR $((size * 1024)) + + _create_file_sized $((size * 1024 * 1024)) $img_file || + _fail "Failed: Create $img_file $size" + + loop_devs=$(_create_loop_device $img_file) + _ret=($loop_devs)
Should this check that a loopdev actually got created?
Quoted 11 lines
common/rc +11
-0+ case $FSTYP in + xfs) + _mkfs_dev "-s size=4096" ${loop_devs[0]} + ;; + btrfs) + _mkfs_dev ${loop_devs[0]} + ;; + *) + _mkfs_dev ${loop_devs[0]} + ;; + esac
common/rc _scratch_resvblks() · line 1566
Quoted 10 lines
common/rc +10
-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]} + cp $img_file $img_file_clone + + loop_devs="$loop_devs $(_create_loop_device $img_file_clone)"
local lodev="$(_create_loop_device ...)"
test -z "$lodev" && _fail "second loopdev not created"
_ret+=("$lodev")?
Quoted 26 lines
common/rc +19
-0+ + _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() +{ + 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 v6 01/11] fstests: add _loop_image_create_clone() helper
Quoted 30 lines · Darrick J. Wong
Quoted 15 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 | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)common/rc +0
-0@@ -1520,6 +1520,69 @@ _scratch_resvblks() esaccommon/rc _scratch_resvblks() · line 1530
Quoted 10 lines
common/rc +8
-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() +{ + local -n _ret=$1That switch ^^ is very clever. I always wondered how one did indirect
variables in bash.
Quoted 17 lines
Quoted 4 lines
common/rc +4
-0+ 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 possiblecommon/rc _scratch_resvblks() · line 1544
Quoted 10 lines
common/rc +10
-0+ local loop_devs + + # Since we copy the block device image, we keep its size small. + _require_fs_space $TEST_DIR $((size * 1024)) + + _create_file_sized $((size * 1024 * 1024)) $img_file || + _fail "Failed: Create $img_file $size" + + loop_devs=$(_create_loop_device $img_file) + _ret=($loop_devs)Should this check that a loopdev actually got created?
Hmm, in the function _create_loop_device(), we are calling _fail if create fails, so no need to duplicate, right?
Quoted 22 lines
Quoted 20 lines
common/rc +20
-0+ 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 +
Quoted 8 lines
common/rc _scratch_resvblks() · line 1566
Quoted 1 line
common/rc +1
-0+ loop_devs="$loop_devs $(_create_loop_device $img_file_clone)"local lodev="$(_create_loop_device ...)" test -z "$lodev" && _fail "second loopdev not created" _ret+=("$lodev")?
If the second _create_loop_device() happens to fail, it will already have called _fail, so "second loopdev..." won't be used at all.
Thanks, Anand
Quoted 26 lines
Quoted 26 lines
common/rc +19
-0+ + _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() +{ + 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 v6 01/11] fstests: add _loop_image_create_clone() helper
Quoted 48 lines · Anand Suveer Jain
Quoted 29 lines · Darrick J. Wong
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 | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)common/rc +0
-0common/rc _scratch_resvblks() · line 1529
Quoted 10 lines
common/rc +8
-0@@ -1520,6 +1520,69 @@ _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=$1That switch ^^ is very clever. I always wondered how one did indirect
variables in bash.Quoted 17 lines
Quoted 4 lines
common/rc +4
-0+ 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 possiblecommon/rc _scratch_resvblks() · line 1544
Quoted 10 lines
common/rc +10
-0+ local loop_devs + + # Since we copy the block device image, we keep its size small. + _require_fs_space $TEST_DIR $((size * 1024)) + + _create_file_sized $((size * 1024 * 1024)) $img_file || + _fail "Failed: Create $img_file $size" + + loop_devs=$(_create_loop_device $img_file) + _ret=($loop_devs)Should this check that a loopdev actually got created?
Quoted 2 lines
Hmm, in the function
_create_loop_device(), we are calling_failif create fails, so no need to duplicate, right?
Oh right. Question withdrawn.
Quoted 26 lines
Quoted 26 lines
Quoted 13 lines
common/rc +13
-0+ 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 emptycommon/rc _scratch_resvblks()
Quoted 10 lines
common/rc +8
-0+ 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 + + loop_devs="$loop_devs $(_create_loop_device $img_file_clone)"local lodev="$(_create_loop_device ...)"
Quoted 8 lines
Quoted 4 lines
test -z "$lodev" && _fail "second loopdev not created" _ret+=("$lodev")?
If the second
_create_loop_device()happens to fail, it will already have called_fail, so "second loopdev..." won't be used at all.
<nod> Both comments withdrawn :)
--D
Quoted 32 lines
Thanks, Anand
Quoted 25 lines
Quoted 25 lines
common/rc +19
-0+ + _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() +{ + 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 v6 2/11 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 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
common/rc +17 -0
@@ -414,6 +414,23 @@ _scratch_mount_options() $SCRATCH_DEV $SCRATCH_MNT } +# Return filesystem-specific mount options required for mounting clone/snapshot +# devices. +_clone_mount_option() +{ + local mount_opts="" + + case "$FSTYP" in + xfs) + # Allow mounting a duplicate filesystem on the same host + mount_opts="-o nouuid" + ;; + *) + esac + + echo $mount_opts +} + _supports_filetype() { local dir=$1
Signature · 1 line
2.43.0
Re: [PATCH v6 02/11] fstests: add _clone_mount_option() helper
Quoted 22 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]>--- common/rc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)common/rc +5
-0@@ -414,6 +414,23 @@ _scratch_mount_options() $SCRATCH_DEV $SCRATCH_MNT } +# Return filesystem-specific mount options required for mounting clone/snapshot +# devices. +_clone_mount_option() +{ + local mount_opts=""
common/rc _scratch_mount_options() · line 431
Quoted 10 lines
common/rc +10
-0+ + case "$FSTYP" in + xfs) + # Allow mounting a duplicate filesystem on the same host + mount_opts="-o nouuid" + ;; + *) + esac + + echo $mount_opts
I probably would've just echo'd straight from inside the case statement, but this otherwise looks ok,
--D
Quoted 9 lines
common/rc +2
-0+} + _supports_filetype() { local dir=$1Signature · 1 line
2.43.0
Re: [PATCH v6 02/11] fstests: add _clone_mount_option() helper
Quoted 27 lines · Darrick J. Wong
Quoted 26 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]>--- common/rc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)common/rc +9
-0@@ -414,6 +414,23 @@ _scratch_mount_options() $SCRATCH_DEV $SCRATCH_MNT } +# Return filesystem-specific mount options required for mounting clone/snapshot +# devices. +_clone_mount_option() +{ + local mount_opts="" + + case "$FSTYP" in + xfs) + # Allow mounting a duplicate filesystem on the same host
Quoted 8 lines
common/rc _scratch_mount_options() · line 431
Quoted 6 lines
common/rc +6
-0+ mount_opts="-o nouuid" + ;; + *) + esac + + echo $mount_optsI probably would've just echo'd straight from inside the case statement,
Nice. Let's see if there is v7, I will change this to as below.
Quoted 5 lines
but this otherwise looks ok,
Reviewed-by "Darrick J. Wong" <[email protected]>--D
common/rc +1 -5
@@ -418,17 +418,13 @@ _scratch_mount_options() # devices. _clone_mount_option() { - local mount_opts="" - case "$FSTYP" in xfs) # Allow mounting a duplicate filesystem on the same host - mount_opts="-o nouuid" + echo "-o nouuid" ;; *) esac - - echo $mount_opts } _supports_filetype()
Quoted 9 lines
Quoted 9 lines
common/rc +2
-0+} + _supports_filetype() { local dir=$1Signature · 1 line
2.43.0
PATCH v6 3/11 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 v6 03/11] fstests: add FSNOTIFYWAIT_PROG
Quoted 3 lines · Anand Jain
Define
FSNOTIFYWAIT_PROGfor an upcoming test case that usesfsnotifywait.Signed-off-by Anand Jain <[email protected]>
Seems fine to me
--D
Quoted 20 lines
--- 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
PATCH v6 4/11 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 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
common/rc +21 -0
@@ -6314,6 +6314,27 @@ _require_fanotify_ioerrors() _notrun "$FSTYP does not support fanotify ioerrors" } +# Ext4 derives f_fsid from the superblock UUID, meaning clones share the +# same f_fsid until their UUIDs diverge. Conversely, XFS, Btrfs, +# and F2FS ensure f_fsid remains unique per filesystem instance (often by +# deriving it from the UUID and underlying block device.) +# +# 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 titled: +# "ext4: derive f_fsid from block device to avoid collisions". +_require_unique_f_fsid() +{ + # 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. + if [ "$FSTYP" == "ext4" ]; then + _notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones." + fi +} + + # 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 v6 04/11] fstests: add _require_unique_f_fsid() helper
Quoted 21 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 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)common/rc +0
-0
common/rc _require_fanotify_ioerrors() · line 6323
Quoted 10 lines
common/rc +7
-0@@ -6314,6 +6314,27 @@ _require_fanotify_ioerrors() _notrun "$FSTYP does not support fanotify ioerrors" } +# Ext4 derives f_fsid from the superblock UUID, meaning clones share the +# same f_fsid until their UUIDs diverge. Conversely, XFS, Btrfs, +# and F2FS ensure f_fsid remains unique per filesystem instance (often by +# deriving it from the UUID and underlying block device.) +# +# Across all filesystems, a UUID collision causes libblkid tools to return +# non-deterministic device mappings. It is ultimately the responsibility
"device mappings", as in /dev/disk/by-id/$UUID ?
common/rc _require_fanotify_ioerrors() · line 6326
Quoted 3 lines
common/rc +3
-0+# of the userspace utility or use-case to enforce uniqueness when a clone +# diverges. For details, see mailing list thread discussions titled: +# "ext4: derive f_fsid from block device to avoid collisions".
How about providing a direct lore link?
--D
Quoted 18 lines
common/rc +11
-0+_require_unique_f_fsid() +{ + # 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. + if [ "$FSTYP" == "ext4" ]; then + _notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones." + fi +} + + # 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 v6 04/11] fstests: add _require_unique_f_fsid() helper
Quoted 25 lines · Darrick J. Wong
Quoted 24 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 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)common/rc +0
-0@@ -6314,6 +6314,27 @@ _require_fanotify_ioerrors() _notrun "$FSTYP does not support fanotify ioerrors" }
Quoted 10 lines
common/rc _require_fanotify_ioerrors() · line 6323
Quoted 7 lines
common/rc +7
-0+# Ext4 derives f_fsid from the superblock UUID, meaning clones share the +# same f_fsid until their UUIDs diverge. Conversely, XFS, Btrfs, +# and F2FS ensure f_fsid remains unique per filesystem instance (often by +# deriving it from the UUID and underlying block device.) +# +# Across all filesystems, a UUID collision causes libblkid tools to return +# non-deterministic device mappings. It is ultimately the responsibility"device mappings", as in
/dev/disk/by-id/$UUID?
Correct.. I'll make it specific.
Quoted 6 lines
common/rc _require_fanotify_ioerrors() · line 6326
Quoted 3 lines
common/rc +3
-0+# of the userspace utility or use-case to enforce uniqueness when a clone +# diverges. For details, see mailing list thread discussions titled: +# "ext4: derive f_fsid from block device to avoid collisions".How about providing a direct lore link?
Sure, that will be..
Link:
https://lore.kernel.org/linux-ext4/[email protected]/
instead of the title.
Thanks, Anand
Quoted 24 lines
--D
Quoted 18 lines
common/rc +11
-0+_require_unique_f_fsid() +{ + # 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. + if [ "$FSTYP" == "ext4" ]; then + _notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones." + fi +} + + # 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 v6 5/11 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 | 135 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/801.out | 7 +++ 2 files changed, 142 insertions(+) create mode 100644 tests/generic/801 create mode 100644 tests/generic/801.out
tests/generic/801 new file +135 -0
@@ -0,0 +1,135 @@ +#! /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 "$FSNOTIFYWAIT_PROG" fsnotifywait +_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 2>/dev/null + _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 + +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 v6 05/11] fstests: verify fanotify isolation on cloned filesystems
Quoted 47 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 | 135 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/801.out | 7 +++ 2 files changed, 142 insertions(+) create mode 100644 tests/generic/801 create mode 100644 tests/generic/801.outtests/generic/801 new file +28
-0@@ -0,0 +1,135 @@ +#! /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 "$FSNOTIFYWAIT_PROG" fsnotifywait +_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 +
tests/generic/801 line 38
Quoted 10 lines
tests/generic/801 new file +10
-0+ umount $mnt1 $mnt2 2>/dev/null + _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
I guess you need stdbuf to force fsnotifywait to run in linebuffered mode even if you pipe/redirect it somewhere?
Quoted 37 lines
tests/generic/801 new file +37
-0+} + +# 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 85
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
Is there a cleaner way to manage setting up and automatically undoing this step?
There might not be, since iirc the suggestion to register cleanup functions in a cleanups=() array and call them all in reverse order didn't go anywhere.
Quoted 67 lines
tests/generic/801 new file +50
-0+ +# Start asynchronous fanotify monitors +( monitor_fanotify "$mnt1" > "$log1" ) & +pid1=$! +( monitor_fanotify "$mnt2" > "$log2" ) & +pid2=$! +sleep 2 + +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 +exittests/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 v6 05/11] fstests: verify fanotify isolation on cloned filesystems
Quoted 56 lines · Darrick J. Wong
Quoted 52 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 | 135 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/801.out | 7 +++ 2 files changed, 142 insertions(+) create mode 100644 tests/generic/801 create mode 100644 tests/generic/801.outtests/generic/801 new file +33
-0@@ -0,0 +1,135 @@ +#! /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 "$FSNOTIFYWAIT_PROG" fsnotifywait +_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 2>/dev/null + _loop_image_destroy "${devs[@]}" 2> /dev/null + rm -r -f $tmp.* +} +
Quoted 9 lines
tests/generic/801 line 38
Quoted 5 lines
tests/generic/801 new file +5
-0+# 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>&1I guess you need stdbuf to force fsnotifywait to run in linebuffered mode even if you pipe/redirect it somewhere?
yeah, stdbuf helps get the output as and when created.
Quoted 51 lines
Quoted 37 lines
tests/generic/801 new file +37
-0+} + +# 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 85
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 +fiIs there a cleaner way to manage setting up and automatically undoing this step?
Quoted 4 lines
There might not be, since iirc the suggestion to register cleanup functions in a cleanups=() array and call them all in reverse order didn't go anywhere.
If there are multiple use cases, we could wrap it up in a helper, similar to _scratch_dev_pool_{get|put}, if it helps.
Thanks, Anand
Quoted 67 lines
Quoted 67 lines
tests/generic/801 new file +50
-0+ +# Start asynchronous fanotify monitors +( monitor_fanotify "$mnt1" > "$log1" ) & +pid1=$! +( monitor_fanotify "$mnt2" > "$log2" ) & +pid2=$! +sleep 2 + +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 +exittests/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 v6 6/11 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 | 67 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/802.out | 7 +++++ 2 files changed, 74 insertions(+) create mode 100644 tests/generic/802 create mode 100644 tests/generic/802.out
tests/generic/802 new file +67 -0
@@ -0,0 +1,67 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 802 +# Verify f_fsid and s_uuid of cloned filesystems across mount cycle. + +. ./common/preamble + +_begin_fstest auto quick mount clone + +_require_test +_require_block_device $TEST_DEV +_require_loop + +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts" +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: derive f_fsid from on-disk fsuuid and dev_t" + +_cleanup() +{ + cd / + rm -r -f $tmp.* + umount $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) + +echo "**** fsid initially ****" +echo $fsid_scratch | sed -e "s/$fsid_scratch/FSID_SCRATCH/g" +echo $fsid_clone | sed -e "s/$fsid_clone/FSID_CLONE/g" + +# 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 +7 -0
@@ -0,0 +1,7 @@ +QA output created by 802 +**** fsid initially **** +FSID_SCRATCH +FSID_CLONE +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONE
Signature · 1 line
2.43.0
Re: [PATCH v6 06/11] fstests: verify f_fsid for cloned filesystems
Quoted 2 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.
Might want to add that last part to the test description itself, because otherwise I don't know what 'verify' means.
Quoted 23 lines
Signed-off-by Anand Jain <[email protected]>--- tests/generic/802 | 67 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/802.out | 7 +++++ 2 files changed, 74 insertions(+) create mode 100644 tests/generic/802 create mode 100644 tests/generic/802.outtests/generic/802 new file +9
-0@@ -0,0 +1,67 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 802 +# Verify f_fsid and s_uuid of cloned filesystems across mount cycle. + +. ./common/preamble +
tests/generic/802 line 19
Quoted 10 lines
tests/generic/802 new file +10
-0+_begin_fstest auto quick mount clone + +_require_test +_require_block_device $TEST_DEV +_require_loop + +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts" +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: derive f_fsid from on-disk fsuuid and dev_t"
_fixed_by_fs_commit?
Quoted 21 lines
tests/generic/802 new file +21
-0+ +_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"
tests/generic/802 line 50
Quoted 10 lines
tests/generic/802 new file +10
-0+_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) + +echo "**** fsid initially ****" +echo $fsid_scratch | sed -e "s/$fsid_scratch/FSID_SCRATCH/g" +echo $fsid_clone | sed -e "s/$fsid_clone/FSID_CLONE/g"
Why echo only to sed?
--D
Quoted 34 lines
tests/generic/802 new file +17
-0+ +# 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 +exittests/generic/802.out new file +7
-0@@ -0,0 +1,7 @@ +QA output created by 802 +**** fsid initially **** +FSID_SCRATCH +FSID_CLONE +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONESignature · 1 line
2.43.0
Re: [PATCH v6 06/11] fstests: verify f_fsid for cloned filesystems
Quoted 7 lines · Darrick J. Wong
Quoted 2 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.Might want to add that last part to the test description itself, because otherwise I don't know what 'verify' means.
Looks like there's going to be v7, I have updated the test description..
-------
+# Check that the cloned filesystem provides an f_fsid that is persistent +# across mount cycles if the block device maj:min remains unchanged.
-------
Quoted 27 lines
Quoted 27 lines
Signed-off-by Anand Jain <[email protected]>--- tests/generic/802 | 67 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/802.out | 7 +++++ 2 files changed, 74 insertions(+) create mode 100644 tests/generic/802 create mode 100644 tests/generic/802.outtests/generic/802 new file +13
-0@@ -0,0 +1,67 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 802 +# Verify f_fsid and s_uuid of cloned filesystems across mount cycle. + +. ./common/preamble + +_begin_fstest auto quick mount clone + +_require_test +_require_block_device $TEST_DEV
Quoted 9 lines
tests/generic/802 line 19
Quoted 6 lines
tests/generic/802 new file +6
-0+_require_loop + +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts" +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: derive f_fsid from on-disk fsuuid and dev_t"
_fixed_by_fs_commit?
Oh right! I completely forgot about the new helper _fixed_by_fs_commit. Now fixed. Thanks.
-[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
"btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
-[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
+_fixed_by_fs_commit btrfs xxxxxxxxxxxx \
"btrfs: derive f_fsid from on-disk fsuuid and dev_t"
Quoted 25 lines
Quoted 25 lines
tests/generic/802 new file +25
-0+ +_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
Quoted 9 lines
tests/generic/802 line 50
Quoted 6 lines
tests/generic/802 new file +6
-0+fsid_scratch=$(stat -f -c "%i" $mnt1) +fsid_clone=$(stat -f -c "%i" $mnt2) + +echo "**** fsid initially ****" +echo $fsid_scratch | sed -e "s/$fsid_scratch/FSID_SCRATCH/g" +echo $fsid_clone | sed -e "s/$fsid_clone/FSID_CLONE/g"Why echo only to sed?
I tried to keep both the .out log and the script readable. Since a bare echo FSID_SCRATCH and echo FSID_CLONE has no clear reference to $fsid_scratch and $fsid_clone, I just added a little extra code around them.
However, I'm completely fine with removing both viz, expected output and its script (for the first mount part). As I'm pretty sure there will be a v7 now, ;-)
I can make those changes.
Thanks, Anand
Quoted 34 lines
Quoted 34 lines
tests/generic/802 new file +17
-0+ +# 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 +exittests/generic/802.out new file +7
-0@@ -0,0 +1,7 @@ +QA output created by 802 +**** fsid initially **** +FSID_SCRATCH +FSID_CLONE +**** fsid after mount cycle **** +FSID_SCRATCH +FSID_CLONESignature · 1 line
2.43.0
PATCH v6 7/11 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 | 84 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/803.out | 19 ++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tests/generic/803 create mode 100644 tests/generic/803.out
tests/generic/803 new file +84 -0
@@ -0,0 +1,84 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Anand Jain <[email protected]>. All Rights Reserved. +# +# FS QA Test 803 +# Verify how libblkid resolve devices when multiple devices sharing the +# same FSUUID. + +. ./common/preamble +. ./common/filter + +_begin_fstest auto quick mount clone + +_require_test +_require_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 +} + +# Collect and print device tracking info from findmnt, /proc/mounts, and df. +# This checks whether user-space tools get confused or remain accurate when +# resolving a duplicate/cloned filesystem UUID. +print_info() +{ + local mntpt=$1 + local tgt=$(findmnt -no SOURCE $mntpt) + local fsuuid=$(blkid -s UUID -o value $tgt) + + echo "mntpt=$mntpt tgt=$tgt fsuuid=$fsuuid" >> $seqres.full + echo + findmnt -o SOURCE,TARGET,UUID "$tgt" | tail -n +2 | \ + sed -e "s/${fsuuid}/FSUUID/g" | filter_pool + awk -v dev="$tgt" '$1 == dev { print $1, $2 }' /proc/self/mounts | \ + filter_pool + df --all --output=source,target "$tgt" | tail -n +2 | filter_pool +} + +# 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" + +print_info $mnt1 +print_info $mnt2 + +# Cycle mounts and reverse the initialization order to see if libblkid / findmnt +# resolution changes based on mount order. +echo +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" + +print_info $mnt1 +print_info $mnt2 + +status=0 +exit
tests/generic/803.out new file +19 -0
@@ -0,0 +1,19 @@ +QA output created by 803 + +DEV1 MNT1 FSUUID +DEV1 MNT1 +DEV1 MNT1 + +DEV2 MNT2 FSUUID +DEV2 MNT2 +DEV2 MNT2 + +**** mount cycle **** + +DEV1 MNT1 FSUUID +DEV1 MNT1 +DEV1 MNT1 + +DEV2 MNT2 FSUUID +DEV2 MNT2 +DEV2 MNT2
Signature · 1 line
2.43.0
PATCH v6 8/11 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 + +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts" +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ + "btrfs: derive f_fsid from on-disk fsuuid and dev_t" + +_cleanup() +{ + cd / + rm -r -f $tmp.* + _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
PATCH v6 9/11 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
PATCH v6 10/11 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
@@ -1537,6 +1537,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
PATCH v6 11/11 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 | 84 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/806.out | 19 ++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tests/generic/806 create mode 100644 tests/generic/806.out
tests/generic/806 new file +84 -0
@@ -0,0 +1,84 @@ +#! /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 +} + +# Collect and print device resolution properties across user-space tools +print_info() +{ + local mntpt=$1 + local tgt=$(findmnt -no SOURCE $mntpt) + local fsuuid=$(blkid -s UUID -o value $tgt) + + echo "mntpt=$mntpt tgt=$tgt fsuuid=$fsuuid" >> $seqres.full + echo + findmnt -o SOURCE,TARGET,UUID "$tgt" | tail -n +2 | \ + sed -e "s/${fsuuid}/FSUUID/g" | filter_pool + awk -v dev="$tgt" '$1 == dev { print $1, $2 }' /proc/self/mounts | \ + filter_pool + df --all --output=source,target "$tgt" | tail -n +2 | filter_pool +} + +# 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 + +# 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" + +print_info $mnt1 +print_info $mnt2 + +# Cycle mounts and reverse the initialization order to ensure UUID tracking +# doesn't mismatch or flip when metadata_uuid optimization is active. +echo +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" + +print_info $mnt1 +print_info $mnt2 + +status=0 +exit
tests/generic/806.out new file +19 -0
@@ -0,0 +1,19 @@ +QA output created by 806 + +DEV1 MNT1 FSUUID +DEV1 MNT1 +DEV1 MNT1 + +DEV2 MNT2 FSUUID +DEV2 MNT2 +DEV2 MNT2 + +**** mount cycle **** + +DEV1 MNT1 FSUUID +DEV1 MNT1 +DEV1 MNT1 + +DEV2 MNT2 FSUUID +DEV2 MNT2 +DEV2 MNT2
Signature · 1 line
2.43.0