From 92ed4f6668efccf6af73f45654405d333e6a318c Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 1 Aug 2026 02:19:07 +0200 Subject: fat: mount every FAT32 volume under /disk//, fix multi-entry LFN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fat.rs: VOL: Option → VOLS: Vec; try_mount() returns a Volume per disk and init() mounts every valid FAT32 volume found. Volume UID = sanitized uppercase label + 8-hex volume serial (e.g. RSDOS-5BD1E55F): unique per format, stable across reboots, human-recognizable, machine-parseable (^[A-Z0-9_]+-[0-9A-F]{8}$); cloned volumes get a -N suffix. Public ops take a vol index and select it (select_vol also picks the backing AHCI disk), matching the ahci::select pattern - fs.rs: /disk is now a plain directory whose children are one Disk node per mounted volume, named by UID; DiskInfo carries vol - LFN fix: entries are stored last-first on disk — both the writer (head-entry first) and reader (append in disk order) were reversed and cancelled out for ≤13-char names; multi-entry names (>13 chars) read back mangled (e.g. 'from-kernel.txt' as 'xt'). Writer now places seq=1 closest to the SFN and seq=N|0x40 first; reader places chunks by sequence number and trims at the first NUL. Verified with a 25-char filename round trip: kernel ls/cat correct, mtools shows the full LFN, fsck clean on both volumes - test-disk.sh: test file renamed to disk-test-.txt (>13 chars, exercises multi-entry LFN); UID discovered from the boot log; QEMU stdio redirected to a log file + EXIT trap so an orphaned QEMU can never hang a piped caller (it previously did, 400s) --- scripts/mkdisk.sh | 8 ++++---- scripts/test-disk.sh | 35 ++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/mkdisk.sh b/scripts/mkdisk.sh index f9a8c09..6bdd50c 100755 --- a/scripts/mkdisk.sh +++ b/scripts/mkdisk.sh @@ -55,10 +55,10 @@ fi # mkfs.fat sizes are 1024-byte blocks; accept M/G/K suffixes too. case "$SIZE" in -*G) SIZE_BLOCKS=$(( ${SIZE%G} * 1024 * 1024 )) ;; -*M) SIZE_BLOCKS=$(( ${SIZE%M} * 1024 )) ;; -*K) SIZE_BLOCKS=$(( ${SIZE%K} )) ;; -*) SIZE_BLOCKS="$SIZE" ;; +*G) SIZE_BLOCKS=$((${SIZE%G} * 1024 * 1024)) ;; +*M) SIZE_BLOCKS=$((${SIZE%M} * 1024)) ;; +*K) SIZE_BLOCKS=$((${SIZE%K})) ;; +*) SIZE_BLOCKS="$SIZE" ;; esac if [ -z "$SIZE_BLOCKS" ] || [ "$SIZE_BLOCKS" -le 0 ] 2>/dev/null; then echo "mkdisk: bad size '$SIZE' (use e.g. 64M or 65536)" >&2 diff --git a/scripts/test-disk.sh b/scripts/test-disk.sh index f7be7c6..f7337ef 100755 --- a/scripts/test-disk.sh +++ b/scripts/test-disk.sh @@ -31,8 +31,13 @@ ESP_DIR="$PROJECT_DIR/esp" QPID=/tmp/rsdos-test-qpid SOCK=/tmp/rsdos-test-qmon.sock LOG=/tmp/rsdos-test-serial.log +QLOG=/tmp/rsdos-test-qemu.log STAMP="$(date +%s)" +# Never leave a QEMU behind (an orphan holding our stdout would hang any +# caller that pipes our output). +trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT + fail() { echo "FAIL: $1" [ -f "$LOG" ] && tail -20 "$LOG" @@ -42,7 +47,7 @@ fail() { # wait_log FILE PATTERN TIMEOUT-SECONDS — poll a file for a regex. wait_log() { local i=0 - while [ "$i" -lt $(( ${3:-60} * 10 )) ]; do + while [ "$i" -lt $((${3:-60} * 10)) ]; do grep -q "$2" "$1" 2>/dev/null && return 0 sleep 0.1 i=$((i + 1)) @@ -53,7 +58,7 @@ wait_log() { # wait_qemu_exit TIMEOUT-SECONDS — poll until the pid is gone. wait_qemu_exit() { local i=0 - while [ "$i" -lt $(( ${1:-30} * 10 )) ]; do + while [ "$i" -lt $((${1:-30} * 10)) ]; do kill -0 "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || return 0 sleep 0.1 i=$((i + 1)) @@ -80,18 +85,29 @@ rm -f "$SOCK" "$LOG" -device ide-hd,drive=esp,bus=ide.1 \ -serial file:"$LOG" \ -monitor unix:"$SOCK",server,nowait \ - -m 256M -machine q35 -cpu qemu64 -no-reboot -display none & -echo $! > "$QPID" + -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ + >"$QLOG" 2>&1 & +echo $! >"$QPID" wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" sleep 1 +# Volumes mount under /disk// where UID = "LABEL-SERIAL" (e.g. +# RSDOS-5BD1E55F). Discover it from the boot log so the test works on +# any image without knowing the serial in advance. +VOL_UID="$(grep -a 'fat: mounted' "$LOG" | sed -n 's/.*at \/disk\/\([^ )]*\).*/\1/p' | head -1)" +[ -n "$VOL_UID" ] || fail "no FAT32 volume was mounted (look for 'fat: mounted' in the log)" +echo " mounted volume UID: $VOL_UID" + echo "==> 4/6 drive the shell (ls / mkdir / echo > / cat / poweroff)" +# The filename is deliberately >13 chars so the FAT LFN path gets +# multi-entry entries (they're stored last-first on disk). +TESTFILE="disk-test-$STAMP.txt" python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ "ls /disk" \ - "mkdir /disk/sub" \ - "echo disk-test-$STAMP > /disk/sub/hello.txt" \ - "cat /disk/sub/hello.txt" \ + "mkdir /disk/$VOL_UID/sub" \ + "echo disk-test-$STAMP > /disk/$VOL_UID/sub/$TESTFILE" \ + "cat /disk/$VOL_UID/sub/$TESTFILE" \ "poweroff" echo "==> 5/6 wait for shutdown" @@ -102,10 +118,11 @@ grep -a "disk-test-$STAMP" "$LOG" >/dev/null || fail "cat output missing from se if ! fsck.fat -n "$IMG" >/tmp/rsdos-fsck.out 2>&1; then fail "fsck.fat reports problems: $(cat /tmp/rsdos-fsck.out | tail -3)" fi -MT="$(mtype -i "$IMG" "::/sub/hello.txt" 2>/dev/null || true)" +MT="$(mtype -i "$IMG" "::/sub/$TESTFILE" 2>/dev/null || true)" [ "$MT" = "disk-test-$STAMP" ] || fail "mtype mismatch: got '$MT'" echo "" echo "PASS — disk round trip OK (fsck clean, file survived poweroff)" echo " image: $IMG" -echo " written: /disk/sub/hello.txt = \"disk-test-$STAMP\"" +echo " volume: /disk/$VOL_UID" +echo " written: /disk/$VOL_UID/sub/$TESTFILE = \"disk-test-$STAMP\"" -- cgit v1.2.3