Linux-EROFS Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] erofs-utils: tests: check SEEK_HOLE/SEEK_DATA in inode_share mode
@ 2026-09-09  3:43 Jingbo Xu
  0 siblings, 0 replies; only message in thread
From: Jingbo Xu @ 2026-09-09  3:43 UTC (permalink / raw)
  To: xiang, linux-erofs

In inode_share mode erofs_ishare_fops used to fall back to
generic_file_llseek(), which considers the whole file to be data, so
SEEK_HOLE always returned i_size and the real holes of sparse files
were hidden.  This is fixed by kernel commit 8ebf95897885 ("erofs:
support SEEK_HOLE/SEEK_DATA in inode_share mode").

Add a regression test for it.  xfs_io is requisite for seeking
SEEK_HOLE and SEEK_DATA.

Signed-off-by: Jingbo Xu <jefflexu@linux.alibaba.com>
---
 tests/Makefile.am   |   3 +
 tests/erofs/033     | 157 ++++++++++++++++++++++++++++++++++++++++++++
 tests/erofs/033.out |   2 +
 3 files changed, 162 insertions(+)
 create mode 100755 tests/erofs/033
 create mode 100644 tests/erofs/033.out

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 363b93dc..b60a6cc9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -135,6 +135,9 @@ TESTS += erofs/031
 # 032 - check that the xattr layout does not depend on listxattr(2) order
 TESTS += erofs/032
 
+# 033 - regression test for SEEK_HOLE/SEEK_DATA in inode_share mode
+TESTS += erofs/033
+
 # NEW TEST CASE HERE
 # TESTS += erofs/999
 
diff --git a/tests/erofs/033 b/tests/erofs/033
new file mode 100755
index 00000000..1ec0aac0
--- /dev/null
+++ b/tests/erofs/033
@@ -0,0 +1,157 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Check that SEEK_HOLE/SEEK_DATA see the real holes in inode_share mode
+#
+# In an inode_share mount regular files are served by erofs_ishare_fops,
+# whose .llseek used to be generic_file_llseek().  That one considers the
+# whole file to be data, so SEEK_HOLE always returned i_size and SEEK_DATA
+# always returned the offset it was handed.
+#
+# Regression test for Linux commit 8ebf95897885 ("erofs: support
+# SEEK_HOLE/SEEK_DATA in inode_share mode").
+#
+seq=`basename $0`
+seqres=$RESULT_DIR/$(echo $0 | awk '{print $((NF-1))"/"$NF}' FS="/")
+
+# get standard environment, filters and checks
+. "${srcdir}/common/rc"
+
+cleanup()
+{
+	cd /
+	rm -rf $tmp.*
+}
+
+_require_erofs
+_require_erofs_inode_sharing
+_require_mkfs_blksize
+
+XFS_IO_PROG="$(command -v xfs_io)"
+[ -z "$XFS_IO_PROG" ] && _notrun "xfs_io is not found"
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+echo "QA output created by $seq"
+
+if [ -z $SCRATCH_DEV ]; then
+	SCRATCH_DEV=$tmp/erofs_$seq.img
+	rm -f $SCRATCH_DEV
+fi
+
+localdir="$tmp/$seq"
+rm -rf $localdir
+mkdir -p $localdir
+
+# eight chunks, of which chunk 1, 3, 4, 6, 7 stay holes
+chunksize=4096
+truncate -s $((8 * chunksize)) $localdir/sparse ||
+	_fail "failed to truncate $localdir/sparse"
+for i in 0 2 5; do
+	dd if=/dev/urandom of=$localdir/sparse bs=$chunksize seek=$i count=1 \
+		conv=notrunc >> $seqres.full 2>&1 ||
+		_fail "failed to write chunk $i of $localdir/sparse"
+done
+
+# the same size without any hole at all
+dd if=/dev/urandom of=$localdir/dense bs=$chunksize count=8 \
+	>> $seqres.full 2>&1 || _fail "failed to write $localdir/dense"
+
+sparse_layout="data 0 4096
+hole 4096 4096
+data 8192 4096
+hole 12288 8192
+data 20480 4096
+hole 24576 8192"
+dense_layout="data 0 32768"
+
+# seek to the next data (-d) or hole (-h), the offset ends up in $seek_result
+_seek()
+{
+	local whence=$1
+	local offset=$2
+	local file=$3
+
+	$XFS_IO_PROG -r -c "seek -$whence $offset" "$file" > $tmp.seek ||
+		_fail "xfs_io failed to seek $whence at $offset in $file"
+	seek_result=`tail -n 1 $tmp.seek | awk '{print $2}'`
+	[ -n "$seek_result" ] ||
+		_fail "cannot parse the seek output of xfs_io for $file"
+}
+
+# write the data/hole layout of $1 to $tmp.layout as "<type> <off> <len>"
+_seek_layout()
+{
+	local file="$1"
+	local size=`_get_filesize "$file"`
+	local pos=0
+
+	: > $tmp.layout
+	while [ $pos -lt $size ]; do
+		_seek d $pos "$file"
+		if [ "$seek_result" = "EOF" ]; then
+			echo "hole $pos $((size - pos))" >> $tmp.layout
+			break
+		fi
+		if [ $seek_result -gt $pos ]; then
+			echo "hole $pos $((seek_result - pos))" >> $tmp.layout
+		fi
+		pos=$seek_result
+
+		_seek h $pos "$file"
+		[ "$seek_result" = "EOF" ] && seek_result=$size
+		echo "data $pos $((seek_result - pos))" >> $tmp.layout
+		pos=$seek_result
+	done
+}
+
+_check_layout()
+{
+	local tag="$1"
+	local file="$2"
+	local expected="$3"
+
+	_seek_layout "$file"
+	if [ "`cat $tmp.layout`" != "$expected" ]; then
+		{
+			echo "--- $tag expected ---"
+			echo "$expected"
+			echo "--- $tag reported ---"
+			cat $tmp.layout
+		} >> $seqres.full
+		_fail "$tag: SEEK_DATA/SEEK_HOLE reported an unexpected layout"
+	fi
+}
+
+for format in force-chunk-indexes force-inode-blockmap; do
+	echo "=== -E$format ===" >> $seqres.full
+
+	_scratch_mkfs -b$chunksize --chunksize=$chunksize -E$format \
+		--xattr-inode-digest=system.erofs.fingerprint \
+		$localdir >> $seqres.full 2>&1 ||
+		_fail "failed to mkfs (-E$format)"
+
+	# make sure that mkfs wrote ishare_xattrs feature bit
+	$DUMP_EROFS_PROG -s $SCRATCH_DEV | grep -q ishare_xattrs ||
+		_fail "the image carries no inode fingerprints (-E$format)"
+
+	# the very same image without inode_share tells them apart
+	_scratch_mount 2>> $seqres.full
+	_check_layout "sparse (-E$format)" \
+		$SCRATCH_MNT/sparse "$sparse_layout"
+	_check_layout "dense (-E$format)" $SCRATCH_MNT/dense "$dense_layout"
+	_scratch_unmount
+
+	_scratch_mount -oinode_share,domain_id=erofs-$seq-$format 2>> $seqres.full
+	_check_layout "sparse (-E$format, inode_share)" \
+		$SCRATCH_MNT/sparse "$sparse_layout"
+	_check_layout "dense (-E$format, inode_share)" \
+		$SCRATCH_MNT/dense "$dense_layout"
+	_scratch_unmount
+done
+
+echo Silence is golden
+status=0
+exit 0
diff --git a/tests/erofs/033.out b/tests/erofs/033.out
new file mode 100644
index 00000000..c5d29149
--- /dev/null
+++ b/tests/erofs/033.out
@@ -0,0 +1,2 @@
+QA output created by 033
+Silence is golden
-- 
2.19.1.6.gb485710b



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-09  3:43 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-09  3:43 [PATCH] erofs-utils: tests: check SEEK_HOLE/SEEK_DATA in inode_share mode Jingbo Xu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox