Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
- postmark
- dbench
- diod-regression
- qemu-9p2000.L
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KERNEL_RELEASE: ${{ inputs.kernel_release || 'kernel-main' }}
Expand All @@ -33,6 +34,17 @@ jobs:
- name: Checkout harness
uses: actions/checkout@v4

- name: Ensure GitHub CLI present (for release download)
run: |
set -euo pipefail
if command -v gh >/dev/null 2>&1; then
gh --version
exit 0
fi
sudo apt-get update
sudo apt-get install -y --no-install-recommends gh
gh --version

- name: Download published kernel Image
run: |
set -euo pipefail
Expand Down
195 changes: 195 additions & 0 deletions scripts/v9fs-9p-protocol-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#!/usr/bin/env bash
set -euo pipefail

server="${1:-}"
dialect="${2:-}"

if [ -z "${server}" ] || [ -z "${dialect}" ]; then
echo "usage: $0 <qemu|diod> <9p2000|9p2000.u|9p2000.L>" >&2
exit 2
fi

echo "9p-proto: server=${server} dialect=${dialect}"

# Kernel config probe (preferred source of truth when available).
if [ -r /proc/config.gz ]; then
echo "== kernel config (from /proc/config.gz) =="
cfg_out="/home/v9fs-test/test/logs/9p-kconfig.txt"
mkdir -p "$(dirname "$cfg_out")"
(zcat /proc/config.gz 2>/dev/null || gzip -dc /proc/config.gz 2>/dev/null || cat /proc/config.gz) \
| /usr/bin/grep -E '^(CONFIG_(NET_9P|9P|P9|VIRTIO_9P|NET_9P_VIRTIO|NET_9P_DEBUG|NET_9P_RDMA|NET_9P_XEN)|# CONFIG_(NET_9P|NET_9P_VIRTIO|NET_9P_RDMA) is not set)' \
| sort -u \
| tee "$cfg_out" || true
else
echo "== kernel config =="
echo "SKIP: /proc/config.gz not present/readable"
fi

mnt="/workspaces/tmpdir/9p-mnt"
umount_mnt() { umount -l "$mnt" 2>/dev/null || true; }
trap umount_mnt EXIT
umount_mnt
mkdir -p "$mnt"

case "$server" in
qemu)
# We are already running inside a chroot that is the virtio-9p mount,
# mounted with the requested dialect by init. Use an on-mount directory.
exp="/workspaces/tmpdir/9p-exp-qemu"
mkdir -p "$exp"
cli="$exp"
;;
diod)
# Run diod in the guest and mount it via tcp. Backing store is tmpfs so the
# diod tests do not recurse through the QEMU 9p server.
exp="/workspaces/tmpdir/9p-exp-diod"
sockdir="/workspaces/tmpdir"
mkdir -p "$exp"
mount -t tmpfs -o mode=0777 tmpfs "$exp" 2>/dev/null || true

port="${DIOD_PORT:-45670}"
diod_log="/home/v9fs-test/test/logs/diod-server.log"

# Ensure loopback is up for trans=tcp mounts.
(ip link set lo up 2>/dev/null || true)
(ip addr add 127.0.0.1/8 dev lo 2>/dev/null || true)
(ifconfig lo 127.0.0.1 up 2>/dev/null || true)

# Best-effort: load 9p network transport support if modular.
(modprobe 9pnet 2>/dev/null || true)
(modprobe 9pnet_fd 2>/dev/null || true)
(modprobe 9pnet_virtio 2>/dev/null || true)

(diod --foreground --listen "127.0.0.1:${port}" \
--config-file=/dev/null --debug=0x1 \
--runas-uid 0 --no-auth --export "$exp" \
>/dev/null 2>>"$diod_log") &
diod_pid="$!"
echo "9p-proto: started diod pid=${diod_pid} port=${port} export=${exp}"

for _ in $(seq 1 50); do
(echo >/dev/tcp/127.0.0.1/"$port") >/dev/null 2>&1 && break
sleep 0.1
done

if ! mount -t 9p -o "trans=tcp,version=${dialect},port=${port},uname=root,access=any" 127.0.0.1 "$mnt"; then
echo "9p-proto: mount failed, dmesg tail:"
dmesg | tail -n 200 || true
exit 32
fi
cli="$mnt"
;;
*)
echo "9p-proto: unknown server=${server} (expected qemu|diod)" >&2
exit 2
;;
esac

echo "9p-proto: exp=${exp}"
echo "9p-proto: cli=${cli}"

rm -rf "$exp"/tcase 2>/dev/null || true
rm -rf "$cli"/tcase 2>/dev/null || true
mkdir -p "$exp/tcase"
mkdir -p "$cli/tcase"

stat_min() { stat -c "mode=%f owner=%u:%g size=%s links=%h mtime=%Y" "$1"; }

fail() { echo "FAIL: $*"; exit 1; }
assert_eq() { [ "$1" = "$2" ] || fail "expected '$1' == '$2'"; }

echo "== create/read/write =="
echo hello >"$cli/tcase/a"
assert_eq "$(cat "$exp/tcase/a")" "hello"
dd if=/dev/zero of="$cli/tcase/b" bs=4096 count=32 conv=fsync status=noxfer
cmp "$cli/tcase/b" "$exp/tcase/b"

echo "== rename within dir + across dirs =="
mv "$cli/tcase/a" "$cli/tcase/a2"
mkdir -p "$cli/tcase/dir2"
mv "$cli/tcase/a2" "$cli/tcase/dir2/a"
test -f "$exp/tcase/dir2/a"

echo "== rename over existing (atomic replace) =="
echo old >"$cli/tcase/old"
echo new >"$cli/tcase/new"
mv -f "$cli/tcase/new" "$cli/tcase/old"
assert_eq "$(cat "$exp/tcase/old")" "new"
test ! -e "$exp/tcase/new"

echo "== symlink + readlink =="
ln -s "dir2/a" "$cli/tcase/syma"
assert_eq "$(readlink "$exp/tcase/syma")" "dir2/a"

echo "== hardlink =="
ln "$cli/tcase/dir2/a" "$cli/tcase/a_hard"
assert_eq "$(stat -c %h "$exp/tcase/dir2/a")" "$(stat -c %h "$exp/tcase/a_hard")"

echo "== truncate and sparse writes =="
# Write a single byte at a large offset. This should expand apparent size without
# requiring the backend to report real allocation semantics.
dd if=/dev/zero of="$cli/tcase/sparse" bs=1 count=1 seek=$((1024*1024)) conv=notrunc status=noxfer
sz_cli="$(stat -c %s "$cli/tcase/sparse")"
sz_exp="$(stat -c %s "$exp/tcase/sparse")"
test "$sz_cli" -ge $((1024*1024 + 1)) || fail "expected sparse size >= 1MiB+1 on client view, got $sz_cli (exp=$sz_exp)"
: >"$cli/tcase/sparse"
assert_eq "$(stat -c %s "$exp/tcase/sparse")" "0"

echo "== unlink while open =="
echo hold >"$cli/tcase/open_unlink"
(
exec 3<"$cli/tcase/open_unlink"
rm -f "$cli/tcase/open_unlink"
cat <&3 >/dev/null
exec 3<&-
)
test ! -e "$exp/tcase/open_unlink"

echo "== long filename and deep pathwalk =="
longname="$(printf 'a%.0s' $(seq 1 200))"
touch "$cli/tcase/$longname"
test -f "$exp/tcase/$longname"
mkdir -p "$cli/tcase/deep/1/2/3/4/5/6/7/8/9"
echo deep >"$cli/tcase/deep/1/2/3/4/5/6/7/8/9/file"
assert_eq "$(cat "$exp/tcase/deep/1/2/3/4/5/6/7/8/9/file")" "deep"

echo "== chmod propagation =="
chmod 640 "$cli/tcase/dir2/a"
assert_eq "$(stat -c %a "$exp/tcase/dir2/a")" "640"
chmod 600 "$exp/tcase/dir2/a"
assert_eq "$(stat -c %a "$cli/tcase/dir2/a")" "600"

echo "== chown/chgrp (best-effort) =="
if chown 0:0 "$cli/tcase/dir2/a" 2>/dev/null; then
assert_eq "$(stat -c %u:%g "$exp/tcase/dir2/a")" "0:0"
else
echo "SKIP: chown not permitted"
fi

echo "== utimensat/timestamps (best-effort) =="
if touch -m -t 202001010101.01 "$cli/tcase/dir2/a" 2>/dev/null; then
mt_cli="$(stat -c %Y "$cli/tcase/dir2/a")"
mt_exp="$(stat -c %Y "$exp/tcase/dir2/a")"
assert_eq "$mt_cli" "$mt_exp"
else
echo "SKIP: touch -t failed"
fi

echo "== statfs sanity =="
stat -f "$mnt" >/dev/null 2>&1 || df "$mnt"

echo "== unlink, rmdir =="
rm -f "$cli/tcase/b"
test ! -e "$exp/tcase/b"
rmdir "$cli/tcase/deep/1/2/3/4/5/6/7/8/9" 2>/dev/null || true
rm -f "$cli/tcase/deep/1/2/3/4/5/6/7/8/9/file" 2>/dev/null || true
rm -rf "$cli/tcase/deep" 2>/dev/null || true
rm -f "$cli/tcase/$longname" 2>/dev/null || true
rm -f "$cli/tcase/old" "$cli/tcase/sparse" 2>/dev/null || true
rmdir "$cli/tcase/dir2" 2>/dev/null || true
rm -f "$cli/tcase/dir2/a" "$cli/tcase/a_hard" "$cli/tcase/syma" 2>/dev/null || true
rmdir "$cli/tcase/dir2"
rmdir "$cli/tcase"

echo "PASS: 9p-proto server=${server} dialect=${dialect}"

17 changes: 15 additions & 2 deletions scripts/v9fs-build-initrd
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mount -t proc proc /proc || true
mount -t sysfs sysfs /sys || true
mount -t devtmpfs devtmpfs /dev || true

# Best-effort: start cpud in background (not used for smoke path).
# Best-effort: start cpud in background (not used for primary test path).
if [ -x /bbin/cpud ]; then
echo "init: starting cpud in background"
/bbin/cpud >/dev/console 2>&1 &
Expand All @@ -36,7 +36,10 @@ mkdir -p /run /tmp /mnt/9
tests="__V9FS_TESTS__"
echo "init: tests=${tests}"

if mount -t 9p -o trans=virtio,version=9p2000.L,cache=loose hostshare /mnt/9; then
bootver="9p2000.L"
echo "init: bootver=${bootver}"

if mount -t 9p -o "trans=virtio,version=${bootver},cache=loose" hostshare /mnt/9; then
echo "init: mounted hostshare at /mnt/9"
if [ -x /mnt/9/usr/sbin/chroot ] && [ -x /mnt/9/usr/bin/bash ]; then
echo "init: chroot -> /mnt/9, running container bash+ls"
Expand All @@ -54,6 +57,16 @@ if mount -t 9p -o trans=virtio,version=9p2000.L,cache=loose hostshare /mnt/9; th
dbench)
/mnt/9/usr/sbin/chroot /mnt/9 /bin/bash -lc 'set -e; mkdir -p /workspaces/tmpdir/dbench; /workspaces/tmp/testbin/dbench/dbench -t 30 -c /workspaces/tmp/testbin/dbench/client.txt -D /workspaces/tmpdir/dbench 16' || rc=$?
;;
qemu-9p2000.L|diod-9p2000.L)
/mnt/9/usr/sbin/chroot /mnt/9 /bin/bash -lc '
set -euo pipefail
tests="__V9FS_TESTS__"
server="${tests%%-*}"
dialect="${tests#*-}"
mkdir -p /home/v9fs-test/test/logs
/home/v9fs-test/test/scripts/v9fs-9p-protocol-tests "$server" "$dialect" 2>&1 | tee -a /home/v9fs-test/test/logs/9p-proto.log
' || rc=$?
;;
diod-regression)
# Run a subset of diod's kernel-client regression tests (sharness) inside the guest.
# Build artifacts are prepared on the host side under /workspaces/tmp/diod-build.
Expand Down
15 changes: 15 additions & 0 deletions scripts/v9fs-prepare-diod-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail

# Ensure diod server binary is available inside the chrooted container FS.
# Installs from distro packages (fast) rather than building from source.

export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y --no-install-recommends diod

command -v diod >/dev/null 2>&1 || { echo "ERROR: diod not found after install"; exit 2; }
diod --help >/dev/null 2>&1 || true

echo "Prepared diod server: $(command -v diod)"

7 changes: 6 additions & 1 deletion scripts/v9fs-run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ case "${tests}" in
diod-regression)
./scripts/v9fs-prepare-diod-regression
;;
qemu-9p2000.L)
;;
diod-9p2000.L)
./scripts/v9fs-prepare-diod-server
;;
*)
echo "ERROR: unknown tests=${tests} (expected smoke|fsx|postmark|dbench|diod-regression)"
echo "ERROR: unknown tests=${tests} (expected smoke|fsx|postmark|dbench|diod-regression|qemu-9p2000.L|diod-9p2000.L)"
exit 2
;;
esac
Expand Down
Loading