From: Jingbo Xu <jefflexu@linux.alibaba.com>
To: xiang@kernel.org, linux-erofs@lists.ozlabs.org
Subject: [PATCH] erofs-utils: tests: check SEEK_HOLE/SEEK_DATA in inode_share mode
Date: Wed, 9 Sep 2026 11:43:23 +0800 [thread overview]
Message-ID: <20260909034323.51773-1-jefflexu@linux.alibaba.com> (raw)
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
reply other threads:[~2026-09-09 3:43 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260909034323.51773-1-jefflexu@linux.alibaba.com \
--to=jefflexu@linux.alibaba.com \
--cc=linux-erofs@lists.ozlabs.org \
--cc=xiang@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox