fstests: add test coverage for cloned filesystem ids
v7 cover fstests: add test coverage for cloned filesystem ids
v7:
. 803, 806: Trimmed down the UUID checks to only what is required, mountinfo
and libblkid.
. 802: Dropped the unnecessary echo statements previously used for logical
flow in the golden output. On second thought, it looks fine without them.
. Swapped _fixed_by_kernel_commit for _fixed_by_fs_commit.
. _clone_mount_option(): Now echoes directly from the case block itself. . _require_unique_f_fsid(): add the link to the ref. discussions.
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 _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 | 120 +++++++++++++++++++++++++++++++++++++ tests/generic/801 | 135 ++++++++++++++++++++++++++++++++++++++++++ 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, 689 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 v7 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
PATCH v7 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 | 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
PATCH v7 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
PATCH v7 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
@@ -6310,6 +6310,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: +# Link: https://lore.kernel.org/linux-ext4/[email protected]/ +_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 v7 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
PATCH v7 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 | 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
PATCH v7 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 | 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
PATCH v7 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 + +_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
PATCH v7 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 v7 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
@@ -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
PATCH v7 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 | 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 v7 01/11] fstests: add _loop_image_create_clone() helper
In reply to Anand JainQuoted 33 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 +16
-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))
common/rc _scratch_resvblks() · line 1548
Quoted 10 lines
common/rc +10
-0+ + _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]}
Not sure why you pass two separate cli arguments as a quoted string, but my guess is it "doesn't matter" because _try_mkfs_dev uses $* unquoted, which separates them again.
I HATE BASH.
common/rc _scratch_resvblks() · line 1551
Quoted 3 lines
common/rc +3
-0+ ;; + btrfs) + _mkfs_dev ${loop_devs[0]}
And while I'm whining: ^^^^^^^^^^^^^^^ actually should be quoted.
Not that fstests is at all good at getting this right.
common/rc _scratch_resvblks() · line 1564
Quoted 13 lines
common/rc +13
-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
What if cp doesn't create a reflink copy? Can we fill up the $TEST_DIR despite having checked it for sufficient free space? Especially on filesystems that don't support sparse holes?
common/rc _scratch_resvblks() · line 1568
Quoted 4 lines
common/rc +4
-0+ + loop_devs="$loop_devs $(_create_loop_device $img_file_clone)" + + _ret=($loop_devs)
Hmm. Should this function return nonzero if any part of the clone creation fails? Or are callers expected to notice that _ret only has one element?
--D
Quoted 24 lines
common/rc +17
-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() +{ + 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 v7 04/11] fstests: add _require_unique_f_fsid() helper
In reply to Anand JainQuoted 29 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 +5
-0@@ -6310,6 +6310,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.) +#
common/rc _require_fanotify_ioerrors() · line 6327
Quoted 10 lines
common/rc +10
-0+# 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() +{ + # 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.
I would switch these two comments. The second comment above describes what this predicate function actually determines, whereas the first comment above the function justifies the internal behavior of the function.
# 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_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.) ;; fi }
--D
Quoted 13 lines
common/rc +6
-0+ 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 v7 05/11] fstests: verify fanotify isolation on cloned filesystems
In reply to Anand JainQuoted 35 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 +16
-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
tests/generic/801 line 26
Quoted 10 lines
tests/generic/801 new file +10
-0+_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
Needs to _require_command semanage, because it's not otherwise required by fstests. Otherwise looks ok to me.
--D
Quoted 126 lines
tests/generic/801 new file +109
-0+ 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 +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 v7 06/11] fstests: verify f_fsid for cloned filesystems
In reply to Anand JainQuoted 68 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 +51
-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 ****"
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"
This is where I continue getting stuck on this patchset -- fsid is so poorly defined that I don't think the rest of these fsid tests make sense at all. Nobody mandates that fsid is persistent or stable across remounts. Nobody even mandates that two cloned filesystems don't have the same fsid value.
The statfs manpage says:
"Nobody knows what f_fsid is supposed to contain (but see below)"
and then:
"The general idea is that f_fsid contains some random stuff such that the pair (f_fsid,ino) uniquely determines a file."
Based on that very weak statement, at most it might make sense to check that two separate and simultaneously mounted filesystems don't end up with the same fsid just in case there *are* programs foolish enough to use (fsid,ino) as a uniqueness check.
--D
Quoted 17 lines
tests/generic/802 new file +3
-0+ +status=0 +exittests/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
Re: [PATCH v7 06/11] fstests: verify f_fsid for cloned filesystems
Quoted 8 lines
This is where I continue getting stuck on this patchset -- fsid is so poorly defined that I don't think the rest of these fsid tests make sense at all. Nobody mandates that fsid is persistent or stable across remounts. Nobody even mandates that two cloned filesystems don't have the same fsid value.
Quoted 8 lines
The statfs manpage says:
"Nobody knows what
f_fsidis supposed to contain (but see below)"and then:
"The general idea is that
f_fsidcontains some random stuff such that the pair (f_fsid,ino) uniquely determines a file."
Which is exactly why we need fstests guardrails.
Keeps filesystem's implementation consistent across kernel versions. Embarrassingly, I introduced a bug around this that is now fixed. The commit IDs are in the test cases.
Thus motivation for this fstests patch.
Quoted 3 lines
Based on that very weak statement, at most it might make sense to check that two separate and simultaneously mounted filesystems don't end up with the same fsid
Yes, for xfs, btrfs and f2fs. However for ext4 cloned filesystems can share the same fsid and ino because the images are identical. It is then up to the usecase to change the UUID for either filesystem if it gets modified.
Commit ("fstests: add _require_unique_f_fsid() helper") captures this, and the link in that commit includes the discussion.
Quoted 2 lines
just in case there *are* programs foolish enough to use (fsid,ino) as a uniqueness check.
On the other hand, if a program needs a unique ID, do we currently have any way to get one?
Thanks, Anand
Re: [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems
In reply to Darrick J. WongQuoted 14 lines
Quoted 14 lines
+. ./common/preamble
(quoted diff) +12
-0+ +_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 /
Quoted 9 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ [[ -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 || trueNeeds to
_require_commandsemanage, because it's not otherwise required by fstests. Otherwise looks ok to me.
Oh right. Added
In the new patch:
common/config +0 -0
::
+export SEMANAGE_PROG="$(type -P semanage)"
In this patch:
tests/generic/801 +0 -0
::
+_require_command "$SEMANAGE_PROG" semanage
Re: [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper
In reply to Darrick J. WongQuoted 16 lines
_require_unique_fsid(
{
case "$FSTYP" in
ext*)
# Ext4 derivesf_fsidfrom the superblock UUID, meaning
# clones share the samef_fsiduntil their UUIDs
# diverge.
_notrun"Target filesystem ($FSTYP) does not guarantee uniquef_fsidon clones." ;;
*)
# Conversely, XFS, Btrfs, and F2FS ensuref_fsidremains
# unique per filesystem instance (often by deriving it
# from the UUID and underlying block device.)
;;
Quoted 1 line
fi
esac
Quoted 4 lines
}
--D
Yeah sure, suggestions applied.
Thanks, Anand
Re: [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper
In reply to Darrick J. WongQuoted 7 lines
common/rc _scratch_resvblks() · line 1548
Quoted 2 lines
common/rc +1
-0+ _mkfs_dev "-s size=4096" ${loop_devs[0]}Not sure why you pass two separate cli arguments as a quoted string, but my guess is it "doesn't matter" because
_try_mkfs_devuses $* unquoted, which separates them again.
Quoted 8 lines
I HATE BASH.
common/rc _scratch_resvblks() · line 1551
Quoted 3 lines
common/rc +3
-0+ ;; + btrfs) + _mkfs_dev ${loop_devs[0]}And while I'm whining: ^^^^^^^^^^^^^^^ actually should be quoted.
Not that fstests is at all good at getting this right.
Damn! Fixed the two issues above.
Quoted 9 lines
Quoted 9 lines
common/rc +9
-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]}
Quoted 9 lines
common/rc _scratch_resvblks() · line 1564
Quoted 4 lines
common/rc +4
-0+ fi + + sync ${loop_devs[0]} + cp $img_file $img_file_cloneWhat if cp doesn't create a reflink copy? Can we fill up the $TEST_DIR despite having checked it for sufficient free space? Especially on filesystems that don't support sparse holes?
We have _require_fs_space a few lines above (yes, I have doubled the size requirement). If we reach this point, cp is unlikely to fail, but if it does, it will be messy.
_require_fs_space $TEST_DIR $((size * 1024 * 2))
Added a call to _fail to be more direct.
cp $img_file $img_file_clone || _fail "Failed to copy cloned image"
Quoted 9 lines
common/rc _scratch_resvblks() · line 1568
Quoted 4 lines
common/rc +4
-0+ + loop_devs="$loop_devs $(_create_loop_device $img_file_clone)" + + _ret=($loop_devs)Hmm. Should this function return nonzero if any part of the clone creation fails? Or are callers expected to notice that
_retonly has one element?
If we are here, both images have been created successfully; otherwise, we would have called _fail.
Quoted 1 line
--D
Thanks for the reviews Darrick.
-Anand
Re: [PATCH v7 06/11] fstests: verify f_fsid for cloned filesystems
In reply to Anand Suveer JainQuoted 34 lines · Anand Suveer Jain
Quoted 8 lines
This is where I continue getting stuck on this patchset -- fsid is so poorly defined that I don't think the rest of these fsid tests make sense at all. Nobody mandates that fsid is persistent or stable across remounts. Nobody even mandates that two cloned filesystems don't have the same fsid value.
Quoted 8 lines
The statfs manpage says:
"Nobody knows what
f_fsidis supposed to contain (but see below)"and then:
"The general idea is that
f_fsidcontains some random stuff such that the pair (f_fsid,ino) uniquely determines a file."Which is exactly why we need fstests guardrails.
Keeps filesystem's implementation consistent across kernel versions. Embarrassingly, I introduced a bug around this that is now fixed. The commit IDs are in the test cases.
Thus motivation for this fstests patch.Quoted 3 lines
Based on that very weak statement, at most it might make sense to check that two separate and simultaneously mounted filesystems don't end up with the same fsid
Yes, for xfs, btrfs and f2fs. However for ext4 cloned filesystems can share the same fsid and ino because the images are identical. It is then up to the usecase to change the UUID for either filesystem if it gets modified.
Commit ("fstests: add_require_unique_f_fsid()helper") captures this, and the link in that commit includes the discussion.
Quoted 5 lines
Quoted 2 lines
just in case there *are* programs foolish enough to use (fsid,ino) as a uniqueness check.
On the other hand, if a program needs a unique ID, do we currently have any way to get one?
I don't think there is a way to get a globally unique identifier for a filesystem image except for the meta_uuid filesystems, and even then only if the volume manager resets the user-visible uuid every time it makes a clone and nobody runs a duplicator.
--D
Quoted 4 lines
Thanks, Anand
Re: [PATCH v7 06/11] fstests: verify f_fsid for cloned filesystems
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
just in case there *are* programs foolish enough to use (fsid,ino) as a uniqueness check.
Quoted 7 lines
Quoted 2 lines
On the other hand, if a program needs a unique ID, do we currently have any way to get one?
I don't think there is a way to get a globally unique identifier for a filesystem image except for the
meta_uuidfilesystems, and even then only if the volume manager resets the user-visible uuid every time it makes a clone and nobody runs a duplicator.
Yeah, interesting problem to solve though.
Do you have any further code review comments on this test case?
Thanks.
Re: [PATCH v7 06/11] fstests: verify f_fsid for cloned filesystems
Quoted 8 lines · Anand Suveer Jain
Quoted 8 lines
Quoted 7 lines
Quoted 4 lines
just in case there *are* programs foolish enough to use (fsid,ino) as a uniqueness check.
On the other hand, if a program needs a unique ID, do we currently have any way to get one?
Quoted 7 lines
Quoted 4 lines
I don't think there is a way to get a globally unique identifier for a filesystem image except for the
meta_uuidfilesystems, and even then only if the volume manager resets the user-visible uuid every time it makes a clone and nobody runs a duplicator.Yeah, interesting problem to solve though.
Do you have any further code review comments on this test case?
Not really, no.
--D