Unfortunately, erofs-utils currently lacks image injection support, so the test has to hard-code the relevant offsets.
Assisted-by: Codex:GPT-5.5 Signed-off-by: Yifan Zhao <[email protected]> --- tests/Makefile.am | 3 + tests/erofs/032 | 135 ++++++++++++++++++++++++++++++++++++++++++++ tests/erofs/032.out | 2 + 3 files changed, 140 insertions(+) create mode 100755 tests/erofs/032 create mode 100644 tests/erofs/032.out diff --git a/tests/Makefile.am b/tests/Makefile.am index a1e31fb..3c41eb3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -132,6 +132,9 @@ TESTS += erofs/030 # 031 - test chunk-based inodes TESTS += erofs/031 +# 032 - regression test for buffered reads across device chunks +TESTS += erofs/032 + # NEW TEST CASE HERE # TESTS += erofs/999 diff --git a/tests/erofs/032 b/tests/erofs/032 new file mode 100755 index 0000000..416ff5e --- /dev/null +++ b/tests/erofs/032 @@ -0,0 +1,135 @@ +#!/bin/sh +# SPDX-License-Identifier: MIT +# +# Regression test for buffered reads crossing EROFS device chunks. +# +seq=`basename $0` +seqres=$RESULT_DIR/$(echo $0 | awk '{print $((NF-1))"/"$NF}' FS="/") + +# get standard environment, filters and checks +. "${srcdir}/common/rc" + +cleanup() +{ + [ -n "$mnt" ] && $UMOUNT_PROG "$mnt" 2>/dev/null + + for dev in "$meta_loop" "$dev1_loop" "$dev2_loop" "$dev3_loop"; do + [ -n "$dev" ] && losetup -d "$dev" 2>/dev/null + done + + rm -rf $tmp.* +} + +_require_root +[ "$FSTYP" = "erofsfuse" ] && _notrun "kernel EROFS is required" +_require_erofs +which losetup >/dev/null 2>&1 || _notrun "losetup is not found" +which blockdev >/dev/null 2>&1 || _notrun "blockdev is not found" +[ -w /proc/sys/vm/drop_caches ] || _notrun "drop_caches is not writable" + +# remove previous $seqres.full before test +rm -f $seqres.full + +# real QA test starts here +echo "QA output created by $seq" + +chunk=4096 +localdir="$tmp/$seq" +src="$localdir/src" +mnt="$localdir/mnt" +img="$localdir/merged.erofs" +blob="$localdir/blob.bin" +dev1="$localdir/device1.bin" +dev2="$localdir/device2.bin" +dev3="$localdir/device3.bin" +expected="$localdir/expected.bin" +affected="$localdir/affected.bin" +actual="$localdir/actual.bin" + +mkdir -p "$src" "$mnt" || _fail "failed to create test directories" + +emit_byte() +{ + dd if=/dev/zero bs="$1" count=1 2>/dev/null | tr '\000' "$2" +} + +write_at() +{ + printf '%b' "$1" | dd of="$2" bs=1 seek="$3" conv=notrunc \ + >>$seqres.full 2>&1 || _fail "failed to patch image" +} + +setup_loop() +{ + loopdev=`losetup -f 2>/dev/null` || _notrun "no free loop device" + losetup "$loopdev" "$1" >>$seqres.full 2>&1 || \ + _fail "failed to setup loop device" +} + +emit_byte $chunk X > "$src/mem.bin" || _fail "failed to create source file" +emit_byte $chunk Y >> "$src/mem.bin" || _fail "failed to create source file" +emit_byte $chunk Z >> "$src/mem.bin" || _fail "failed to create source file" + +emit_byte $((chunk * 3)) A > "$dev1" || _fail "failed to create device 1" +emit_byte $chunk P > "$dev2" || _fail "failed to create device 2" +emit_byte $((chunk * 2)) B >> "$dev2" || _fail "failed to create device 2" +emit_byte $((chunk * 2)) P > "$dev3" || _fail "failed to create device 3" +emit_byte $chunk C >> "$dev3" || _fail "failed to create device 3" + +emit_byte $chunk A > "$expected" || _fail "failed to create expected output" +emit_byte $chunk B >> "$expected" || _fail "failed to create expected output" +emit_byte $chunk C >> "$expected" || _fail "failed to create expected output" + +emit_byte $((chunk * 3)) A > "$affected" || _fail "failed to create affected output" + +"$MKFS_EROFS_PROG" -T0 -Eforce-inode-compact,nosbcrc --chunksize=$chunk \ + --blobdev="$blob" "$img" "$src" >>$seqres.full 2>&1 || \ + _fail "failed to create chunk-based image" + +# Convert mkfs' single blob-device image into three blob devices whose chunk +# block addresses are contiguous. A buggy kernel can merge the buffered read +# bios across those iomap/device boundaries and read A/A/A instead of A/B/C. +write_at '\003\000' "$img" $((1024 + 86)) +write_at '\030\000' "$img" $((1024 + 88)) +write_at '\000\000\001\000\000\000\000\000' "$img" 1408 +write_at '\000\000\002\000\001\000\000\000' "$img" 1416 +write_at '\000\000\003\000\002\000\000\000' "$img" 1424 +dd if=/dev/zero of="$img" bs=128 count=3 seek=24 conv=notrunc \ + >>$seqres.full 2>&1 || _fail "failed to patch device table" +write_at '\003\000\000\000' "$img" $((3072 + 64)) +write_at '\003\000\000\000' "$img" $((3072 + 128 + 64)) +write_at '\003\000\000\000' "$img" $((3072 + 256 + 64)) + +setup_loop "$img" +meta_loop="$loopdev" +setup_loop "$dev1" +dev1_loop="$loopdev" +setup_loop "$dev2" +dev2_loop="$loopdev" +setup_loop "$dev3" +dev3_loop="$loopdev" + +$MOUNT_PROG -t erofs \ + -o device=$dev1_loop,device=$dev2_loop,device=$dev3_loop \ + "$meta_loop" "$mnt" >>$seqres.full 2>&1 || \ + _notrun "this test requires EROFS multiple device support" + +for dev in "$meta_loop" "$dev1_loop" "$dev2_loop" "$dev3_loop"; do + blockdev --setra 5120 "$dev" >>$seqres.full 2>&1 || \ + _fail "failed to set readahead" +done + +sync +echo 3 > /proc/sys/vm/drop_caches || _notrun "failed to drop page cache" + +cat "$mnt/mem.bin" > "$actual" || _fail "failed to read mem.bin" + +if cmp -s "$actual" "$affected"; then + _fail "buffered read merged across device chunks" +fi + +cmp -s "$actual" "$expected" || _fail "unexpected data from device chunks" + +echo Silence is golden +status=0 +exit 0 diff --git a/tests/erofs/032.out b/tests/erofs/032.out new file mode 100644 index 0000000..34e059f --- /dev/null +++ b/tests/erofs/032.out @@ -0,0 +1,2 @@ +QA output created by 032 +Silence is golden -- 2.54.0
