#!/bin/sh set -u test_dir=/root/repo22-namei mount_dir=/mnt/repo22-namei module_id= md_device= fail() { echo "FAIL: $*" >&2 exit 1 } cleanup() { set +e if mount | grep -q " on $mount_dir "; then umount "$mount_dir" fi if [ -n "$md_device" ]; then mdconfig -d -u "${md_device#md}" fi if [ -n "$module_id" ] && kldstat -q -i "$module_id"; then kldunload -i "$module_id" fi } trap cleanup EXIT INT TERM mount_image() { image=$1 md_device=$(mdconfig -a -t vnode -f "$test_dir/$image") || fail "mdconfig $image" mount -t erofs "/dev/$md_device" "$mount_dir" || fail "mount $image" echo "mounted image=$image device=$md_device" } unmount_image() { umount "$mount_dir" || fail "umount $md_device" mdconfig -d -u "${md_device#md}" || fail "detach $md_device" md_device= } expect_integrity_failure() { image=$1 lookup_path=$2 readdir_path=$3 label=${image%.erofs} mount_image "$image" attempt=1 while [ "$attempt" -le 2 ]; do output="$test_dir/$label-lookup-$attempt.txt" if stat "$mount_dir/$lookup_path" >"$output" 2>&1; then fail "$image lookup attempt $attempt unexpectedly succeeded" fi if grep -qi "No such file" "$output"; then fail "$image lookup attempt $attempt became ENOENT" fi attempt=$((attempt + 1)) done output="$test_dir/$label-readdir.txt" if ./readdir_probe "$mount_dir/$readdir_path" 0 512 2 \ >"$output" 2>&1; then fail "$image readdir unexpectedly succeeded" fi unmount_image echo "integrity image=$image repeated-lookup=error readdir=error" } cd "$test_dir" || exit 1 mkdir -p "$mount_dir" if mount | grep -qi erofs; then fail "pre-existing EROFS mount" fi if kldstat | grep -qi erofs; then fail "pre-existing EROFS module" fi if [ -n "$(mdconfig -l)" ]; then fail "pre-existing md device" fi echo "== guest ==" uname -a date -u echo "== module load ==" kldload "$test_dir/erofs.ko" || fail "kldload" module_id=$(kldstat | awk '$NF == "erofs.ko" { print $1 }') [ -n "$module_id" ] || fail "loaded module not found" kldstat -v -i "$module_id" echo "== cold nested lookup ==" mount_image nested.erofs payload=$(cat "$mount_dir/alpha/bravo/charlie/payload.txt") || fail "cold nested lookup" [ "$payload" = "cold nested lookup payload" ] || fail "cold payload mismatch" payload=$(cat "$mount_dir/alpha/bravo/charlie/payload.txt") || fail "repeated nested lookup" [ "$payload" = "cold nested lookup payload" ] || fail "repeat payload mismatch" payload=$(cat "$mount_dir/alpha/bravo/repeat.txt") || fail "sibling nested lookup" [ "$payload" = "repeat lookup payload" ] || fail "sibling payload mismatch" attempt=1 while [ "$attempt" -le 2 ]; do output="$test_dir/missing-$attempt.txt" if stat "$mount_dir/alpha/bravo/missing" >"$output" 2>&1; then fail "missing lookup attempt $attempt unexpectedly succeeded" fi grep -qi "No such file" "$output" || fail "missing lookup was not ENOENT" attempt=$((attempt + 1)) done payload=$(cat "$mount_dir/alpha/bravo/charlie/payload.txt") || fail "existing lookup after negative cache" [ "$payload" = "cold nested lookup payload" ] || fail "post-negative payload mismatch" echo "cold lookup=pass repeat=pass negative-cache=pass" echo "== large readdir and cookies ==" wide_count=$(ls -A1 "$mount_dir/wide" | wc -l | tr -d ' ') [ "$wide_count" = 320 ] || fail "wide entry count $wide_count" ./readdir_probe "$mount_dir/wide" 0 128 400 > wide-probe.txt || fail "wide readdir probe" awk '/^ off=/ { off = $1; sub(/^off=/, "", off); name = $5; sub(/^name=/, "", name); print off, name; }' wide-probe.txt > wide-cookies.txt wide_dirents=$(wc -l < wide-cookies.txt | tr -d ' ') [ "$wide_dirents" = 322 ] || fail "wide dirent count $wide_dirents" awk '{ cookie[NR] = $1; name[NR] = $2 } END { for (i = 1; i <= NR; i++) print cookie[i], (i < NR ? name[i + 1] : ""); }' wide-cookies.txt > wide-resume-cases.txt while read -r cookie expected; do ./readdir_probe "$mount_dir/wide" "$cookie" 128 1 > wide-resume.txt || fail "resume cookie $cookie" if [ "$expected" = "" ]; then grep -q 'bytes=0$' wide-resume.txt || fail "cookie $cookie not EOF" else actual=$(awk '/^ off=/ { name = $5; sub(/^name=/, "", name); print name; exit; }' wide-resume.txt) [ "$actual" = "$expected" ] || fail "cookie $cookie expected $expected got $actual" fi done < wide-resume-cases.txt echo "wide entries=$wide_dirents all-resume-cookies=pass" unmount_image echo "== dot omitted cookies ==" mount_image dot-omitted.erofs ./readdir_probe "$mount_dir" 0 512 8 > dot-probe.txt || fail "dot probe" awk '/^ off=/ { off = $1; sub(/^off=/, "", off); name = $5; sub(/^name=/, "", name); print off, name; }' dot-probe.txt > dot-cookies.txt cat > dot-expected.txt <<'EOF' 12 .. 24 alpha 47 wide 48 . EOF cmp dot-cookies.txt dot-expected.txt || fail "dot cookie sequence" ./readdir_probe "$mount_dir" 47 128 1 > dot-resume-47.txt || fail "dot resume 47" grep -q '^ off=48 .* name=\.$' dot-resume-47.txt || fail "dot resume 47 result" ./readdir_probe "$mount_dir" 48 128 1 > dot-resume-48.txt || fail "dot resume 48" grep -q 'bytes=0$' dot-resume-48.txt || fail "dot resume 48 not EOF" echo "dot cookies=12,24,47,48 resume=pass" unmount_image echo "== corrupted directories ==" expect_integrity_failure corrupt-short-block.erofs alpha . expect_integrity_failure corrupt-nameoff.erofs alpha . expect_integrity_failure \ corrupt-padding.erofs \ wide/entry-000-abcdefghijklmnopqrstuvwxyz.txt \ wide echo "== module unload ==" kldunload -i "$module_id" || fail "kldunload" module_id= if kldstat | grep -qi erofs; then fail "module remains loaded" fi if mount | grep -qi erofs; then fail "mount remains" fi if [ -n "$(mdconfig -l)" ]; then fail "md remains" fi dmesg | tail -120 > dmesg-tail.txt echo "PASS: all VM regressions"