* [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids
@ 2026-06-17 11:20 Anand Jain
2026-06-17 11:20 ` [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper Anand Jain
` (10 more replies)
0 siblings, 11 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
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/cover.1779939330.git.asj@kernel.org
v5:
https://lore.kernel.org/fstests/cover.1779367627.git.asj@kernel.org
v4:
https://lore.kernel.org/fstests/cover.1777357320.git.asj@kernel.org
v3:
https://lore.kernel.org/fstests/cover.1777281778.git.asj@kernel.org
v2:
https://lore.kernel.org/fstests/cover.1774090817.git.asj@kernel.org
v1:
https://lore.kernel.org/fstests/cover.1772095513.git.asj@kernel.org
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/cover.1777281686.git.asj@kernel.org
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
--
2.43.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-07-01 17:07 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 02/11] fstests: add _clone_mount_option() helper Anand Jain
` (9 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
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 <asj@kernel.org>
---
common/rc | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/common/rc b/common/rc
index 79189e7e6e94..d7e3e0bdfb1e 100644
--- a/common/rc
+++ b/common/rc
@@ -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
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 02/11] fstests: add _clone_mount_option() helper
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
2026-06-17 11:20 ` [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-06-17 11:20 ` [PATCH v7 03/11] fstests: add FSNOTIFYWAIT_PROG Anand Jain
` (8 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
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 <asj@kernel.org>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
common/rc | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/common/rc b/common/rc
index d7e3e0bdfb1e..968ba33686f3 100644
--- a/common/rc
+++ b/common/rc
@@ -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
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 03/11] fstests: add FSNOTIFYWAIT_PROG
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
2026-06-17 11:20 ` [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper Anand Jain
2026-06-17 11:20 ` [PATCH v7 02/11] fstests: add _clone_mount_option() helper Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-06-17 11:20 ` [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper Anand Jain
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Define `FSNOTIFYWAIT_PROG` for an upcoming test case that uses `fsnotifywait`.
Signed-off-by: Anand Jain <asj@kernel.org>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
common/config | 1 +
1 file changed, 1 insertion(+)
diff --git a/common/config b/common/config
index d5299d5b926f..5661fa0ec310 100644
--- a/common/config
+++ b/common/config
@@ -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.
#
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (2 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 03/11] fstests: add FSNOTIFYWAIT_PROG Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-07-01 17:11 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems Anand Jain
` (6 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Add a helper to check if the target filesystem supports unique f_fsid
tracking across cloned or snapshot instances.
Certain filesystems like XFS, Btrfs, and F2FS ensure unique f_fsid
identifiers per filesystem instance. However, Ext4 derives its f_fsid
directly from its superblock UUID, which leads to identical f_fsid
values on cloned images until the UUID is manually modified by userspace.
Introduce _require_unique_f_fsid() to allow test cases requiring strict
f_fsid uniqueness to skip gracefully on unsupported filesystems.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/rc | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/common/rc b/common/rc
index 968ba33686f3..d95eec94f7b7 100644
--- a/common/rc
+++ b/common/rc
@@ -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/20260409131238.GC18443@macsyma-wired.lan/
+_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 ("%").
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (3 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-07-01 17:12 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 06/11] fstests: verify f_fsid for " Anand Jain
` (5 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
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 <asj@kernel.org>
---
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
diff --git a/tests/generic/801 b/tests/generic/801
new file mode 100644
index 000000000000..3bfb87d41922
--- /dev/null
+++ b/tests/generic/801
@@ -0,0 +1,135 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
diff --git a/tests/generic/801.out b/tests/generic/801.out
new file mode 100644
index 000000000000..d7b318d9f27c
--- /dev/null
+++ b/tests/generic/801.out
@@ -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
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 06/11] fstests: verify f_fsid for cloned filesystems
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (4 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-07-01 17:33 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 07/11] fstests: verify libblkid resolution of duplicate UUIDs Anand Jain
` (4 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Verify that the cloned filesystem provides an f_fsid that is persistent
across mount cycles, yet unique from the original filesystem's f_fsid.
Signed-off-by: Anand Jain <asj@kernel.org>
---
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
diff --git a/tests/generic/802 b/tests/generic/802
new file mode 100644
index 000000000000..910807c11584
--- /dev/null
+++ b/tests/generic/802
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
diff --git a/tests/generic/802.out b/tests/generic/802.out
new file mode 100644
index 000000000000..0202a9a2c108
--- /dev/null
+++ b/tests/generic/802.out
@@ -0,0 +1,4 @@
+QA output created by 802
+**** fsid after mount cycle ****
+FSID_SCRATCH
+FSID_CLONE
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 07/11] fstests: verify libblkid resolution of duplicate UUIDs
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (5 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 06/11] fstests: verify f_fsid for " Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-06-17 11:20 ` [PATCH v7 08/11] fstests: verify IMA isolation on cloned filesystems Anand Jain
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Verify how findmnt, df (libblkid) resolve device paths when multiple
block devices share the same FSUUID.
Signed-off-by: Anand Jain <asj@kernel.org>
---
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
diff --git a/tests/generic/803 b/tests/generic/803
new file mode 100644
index 000000000000..77901592366c
--- /dev/null
+++ b/tests/generic/803
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
diff --git a/tests/generic/803.out b/tests/generic/803.out
new file mode 100644
index 000000000000..3a130c662430
--- /dev/null
+++ b/tests/generic/803.out
@@ -0,0 +1,6 @@
+QA output created by 803
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 08/11] fstests: verify IMA isolation on cloned filesystems
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (6 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 07/11] fstests: verify libblkid resolution of duplicate UUIDs Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-06-17 11:20 ` [PATCH v7 09/11] fstests: verify exportfs file handles " Anand Jain
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Add testcase to verify IMA measurement isolation when multiple devices
share the same FSUUID.
Signed-off-by: Anand Jain <asj@kernel.org>
---
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
diff --git a/tests/generic/804 b/tests/generic/804
new file mode 100644
index 000000000000..ced32e6d79dd
--- /dev/null
+++ b/tests/generic/804
@@ -0,0 +1,108 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
diff --git a/tests/generic/804.out b/tests/generic/804.out
new file mode 100644
index 000000000000..9804181d6c17
--- /dev/null
+++ b/tests/generic/804.out
@@ -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
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 09/11] fstests: verify exportfs file handles on cloned filesystems
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (7 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 08/11] fstests: verify IMA isolation on cloned filesystems Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-06-17 11:20 ` [PATCH v7 10/11] fstests: add _change_metadata_uuid helper Anand Jain
2026-06-17 11:20 ` [PATCH v7 11/11] fstests: test UUID consistency for clones with metadata_uuid Anand Jain
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Ensure that exportfs can correctly decode file handles on a cloned
filesystem across a mount cycle, by file handles generated on a
cloned device remain valid after mount cycle.
Signed-off-by: Anand Jain <asj@kernel.org>
---
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
diff --git a/tests/generic/805 b/tests/generic/805
new file mode 100644
index 000000000000..5827eee039df
--- /dev/null
+++ b/tests/generic/805
@@ -0,0 +1,80 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
diff --git a/tests/generic/805.out b/tests/generic/805.out
new file mode 100644
index 000000000000..29b11ec77ffb
--- /dev/null
+++ b/tests/generic/805.out
@@ -0,0 +1,2 @@
+QA output created by 805
+test_file_handles after cycle mount
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 10/11] fstests: add _change_metadata_uuid helper
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (8 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 09/11] fstests: verify exportfs file handles " Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
2026-06-17 11:20 ` [PATCH v7 11/11] fstests: test UUID consistency for clones with metadata_uuid Anand Jain
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
_change_metadata_uuid changes the UUID of the golden filesystem before it
is cloned.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/rc | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/common/rc b/common/rc
index d95eec94f7b7..5cd4e025293b 100644
--- a/common/rc
+++ b/common/rc
@@ -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:
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 11/11] fstests: test UUID consistency for clones with metadata_uuid
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
` (9 preceding siblings ...)
2026-06-17 11:20 ` [PATCH v7 10/11] fstests: add _change_metadata_uuid helper Anand Jain
@ 2026-06-17 11:20 ` Anand Jain
10 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2026-06-17 11:20 UTC (permalink / raw)
To: fstests
Cc: linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel, zlang, hch,
djwong
Btrfs and xfs uses the metadata_uuid superblock feature to change the
on-disk UUID without rewriting every block header. This patch adds a
sanity check to ensure UUID consistency when a filesystem with
metadata_uuid enabled is cloned.
Signed-off-by: Anand Jain <asj@kernel.org>
---
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
diff --git a/tests/generic/806 b/tests/generic/806
new file mode 100644
index 000000000000..6d3166491006
--- /dev/null
+++ b/tests/generic/806
@@ -0,0 +1,74 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
diff --git a/tests/generic/806.out b/tests/generic/806.out
new file mode 100644
index 000000000000..918f422ecddf
--- /dev/null
+++ b/tests/generic/806.out
@@ -0,0 +1,6 @@
+QA output created by 806
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
+**** mount cycle ****
+DEV1 MNT1 FSUUID
+DEV2 MNT2 FSUUID
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper
2026-06-17 11:20 ` [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper Anand Jain
@ 2026-07-01 17:07 ` Darrick J. Wong
0 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2026-07-01 17:07 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
On Wed, Jun 17, 2026 at 07:20:28PM +0800, Anand Jain wrote:
> 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 <asj@kernel.org>
> ---
> common/rc | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index 79189e7e6e94..d7e3e0bdfb1e 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -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]}
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.
> + ;;
> + 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.
> + ;;
> + *)
> + _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?
> +
> + 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
> +}
> +
> +# 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
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper
2026-06-17 11:20 ` [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper Anand Jain
@ 2026-07-01 17:11 ` Darrick J. Wong
0 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2026-07-01 17:11 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
On Wed, Jun 17, 2026 at 07:20:31PM +0800, Anand Jain wrote:
> Add a helper to check if the target filesystem supports unique f_fsid
> tracking across cloned or snapshot instances.
>
> Certain filesystems like XFS, Btrfs, and F2FS ensure unique f_fsid
> identifiers per filesystem instance. However, Ext4 derives its f_fsid
> directly from its superblock UUID, which leads to identical f_fsid
> values on cloned images until the UUID is manually modified by userspace.
>
> Introduce _require_unique_f_fsid() to allow test cases requiring strict
> f_fsid uniqueness to skip gracefully on unsupported filesystems.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> common/rc | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index 968ba33686f3..d95eec94f7b7 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -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/20260409131238.GC18443@macsyma-wired.lan/
> +_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/20260409131238.GC18443@macsyma-wired.lan/
_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
> + 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 ("%").
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems
2026-06-17 11:20 ` [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems Anand Jain
@ 2026-07-01 17:12 ` Darrick J. Wong
0 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2026-07-01 17:12 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
On Wed, Jun 17, 2026 at 07:20:32PM +0800, Anand Jain wrote:
> 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 <asj@kernel.org>
> ---
> 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
>
> diff --git a/tests/generic/801 b/tests/generic/801
> new file mode 100644
> index 000000000000..3bfb87d41922
> --- /dev/null
> +++ b/tests/generic/801
> @@ -0,0 +1,135 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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
Needs to _require_command semanage, because it's not otherwise required
by fstests. Otherwise looks ok to me.
--D
> + 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
> diff --git a/tests/generic/801.out b/tests/generic/801.out
> new file mode 100644
> index 000000000000..d7b318d9f27c
> --- /dev/null
> +++ b/tests/generic/801.out
> @@ -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
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v7 06/11] fstests: verify f_fsid for cloned filesystems
2026-06-17 11:20 ` [PATCH v7 06/11] fstests: verify f_fsid for " Anand Jain
@ 2026-07-01 17:33 ` Darrick J. Wong
0 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2026-07-01 17:33 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
On Wed, Jun 17, 2026 at 07:20:33PM +0800, Anand Jain wrote:
> Verify that the cloned filesystem provides an f_fsid that is persistent
> across mount cycles, yet unique from the original filesystem's f_fsid.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> 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
>
> diff --git a/tests/generic/802 b/tests/generic/802
> new file mode 100644
> index 000000000000..910807c11584
> --- /dev/null
> +++ b/tests/generic/802
> @@ -0,0 +1,64 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2026 Anand Jain <asj@kernel.org>. 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"
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
> +
> +status=0
> +exit
> diff --git a/tests/generic/802.out b/tests/generic/802.out
> new file mode 100644
> index 000000000000..0202a9a2c108
> --- /dev/null
> +++ b/tests/generic/802.out
> @@ -0,0 +1,4 @@
> +QA output created by 802
> +**** fsid after mount cycle ****
> +FSID_SCRATCH
> +FSID_CLONE
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-07-01 17:33 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-17 11:20 [PATCH v7 0/11] fstests: add test coverage for cloned filesystem ids Anand Jain
2026-06-17 11:20 ` [PATCH v7 01/11] fstests: add _loop_image_create_clone() helper Anand Jain
2026-07-01 17:07 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 02/11] fstests: add _clone_mount_option() helper Anand Jain
2026-06-17 11:20 ` [PATCH v7 03/11] fstests: add FSNOTIFYWAIT_PROG Anand Jain
2026-06-17 11:20 ` [PATCH v7 04/11] fstests: add _require_unique_f_fsid() helper Anand Jain
2026-07-01 17:11 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 05/11] fstests: verify fanotify isolation on cloned filesystems Anand Jain
2026-07-01 17:12 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 06/11] fstests: verify f_fsid for " Anand Jain
2026-07-01 17:33 ` Darrick J. Wong
2026-06-17 11:20 ` [PATCH v7 07/11] fstests: verify libblkid resolution of duplicate UUIDs Anand Jain
2026-06-17 11:20 ` [PATCH v7 08/11] fstests: verify IMA isolation on cloned filesystems Anand Jain
2026-06-17 11:20 ` [PATCH v7 09/11] fstests: verify exportfs file handles " Anand Jain
2026-06-17 11:20 ` [PATCH v7 10/11] fstests: add _change_metadata_uuid helper Anand Jain
2026-06-17 11:20 ` [PATCH v7 11/11] fstests: test UUID consistency for clones with metadata_uuid Anand Jain
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox