diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 02:19:07 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 02:19:07 +0200 |
| commit | 92ed4f6668efccf6af73f45654405d333e6a318c (patch) | |
| tree | bb9b651dc36edcc90ae0289d7c4223d983454440 /scripts | |
| parent | scripts: automate the data-disk workflow (mkdisk / sendkeys / test-disk) (diff) | |
| download | rs-dos-92ed4f6668efccf6af73f45654405d333e6a318c.tar.gz rs-dos-92ed4f6668efccf6af73f45654405d333e6a318c.zip | |
fat: mount every FAT32 volume under /disk/<UID>/, fix multi-entry LFN
- fat.rs: VOL: Option<Volume> → VOLS: Vec<Volume>; 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)
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/mkdisk.sh | 8 | ||||
| -rwxr-xr-x | scripts/test-disk.sh | 35 |
2 files changed, 30 insertions, 13 deletions
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 | |||
| 55 | 55 | ||
| 56 | # mkfs.fat sizes are 1024-byte blocks; accept M/G/K suffixes too. | 56 | # mkfs.fat sizes are 1024-byte blocks; accept M/G/K suffixes too. |
| 57 | case "$SIZE" in | 57 | case "$SIZE" in |
| 58 | *G) SIZE_BLOCKS=$(( ${SIZE%G} * 1024 * 1024 )) ;; | 58 | *G) SIZE_BLOCKS=$((${SIZE%G} * 1024 * 1024)) ;; |
| 59 | *M) SIZE_BLOCKS=$(( ${SIZE%M} * 1024 )) ;; | 59 | *M) SIZE_BLOCKS=$((${SIZE%M} * 1024)) ;; |
| 60 | *K) SIZE_BLOCKS=$(( ${SIZE%K} )) ;; | 60 | *K) SIZE_BLOCKS=$((${SIZE%K})) ;; |
| 61 | *) SIZE_BLOCKS="$SIZE" ;; | 61 | *) SIZE_BLOCKS="$SIZE" ;; |
| 62 | esac | 62 | esac |
| 63 | if [ -z "$SIZE_BLOCKS" ] || [ "$SIZE_BLOCKS" -le 0 ] 2>/dev/null; then | 63 | if [ -z "$SIZE_BLOCKS" ] || [ "$SIZE_BLOCKS" -le 0 ] 2>/dev/null; then |
| 64 | echo "mkdisk: bad size '$SIZE' (use e.g. 64M or 65536)" >&2 | 64 | 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" | |||
| 31 | QPID=/tmp/rsdos-test-qpid | 31 | QPID=/tmp/rsdos-test-qpid |
| 32 | SOCK=/tmp/rsdos-test-qmon.sock | 32 | SOCK=/tmp/rsdos-test-qmon.sock |
| 33 | LOG=/tmp/rsdos-test-serial.log | 33 | LOG=/tmp/rsdos-test-serial.log |
| 34 | QLOG=/tmp/rsdos-test-qemu.log | ||
| 34 | STAMP="$(date +%s)" | 35 | STAMP="$(date +%s)" |
| 35 | 36 | ||
| 37 | # Never leave a QEMU behind (an orphan holding our stdout would hang any | ||
| 38 | # caller that pipes our output). | ||
| 39 | trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT | ||
| 40 | |||
| 36 | fail() { | 41 | fail() { |
| 37 | echo "FAIL: $1" | 42 | echo "FAIL: $1" |
| 38 | [ -f "$LOG" ] && tail -20 "$LOG" | 43 | [ -f "$LOG" ] && tail -20 "$LOG" |
| @@ -42,7 +47,7 @@ fail() { | |||
| 42 | # wait_log FILE PATTERN TIMEOUT-SECONDS — poll a file for a regex. | 47 | # wait_log FILE PATTERN TIMEOUT-SECONDS — poll a file for a regex. |
| 43 | wait_log() { | 48 | wait_log() { |
| 44 | local i=0 | 49 | local i=0 |
| 45 | while [ "$i" -lt $(( ${3:-60} * 10 )) ]; do | 50 | while [ "$i" -lt $((${3:-60} * 10)) ]; do |
| 46 | grep -q "$2" "$1" 2>/dev/null && return 0 | 51 | grep -q "$2" "$1" 2>/dev/null && return 0 |
| 47 | sleep 0.1 | 52 | sleep 0.1 |
| 48 | i=$((i + 1)) | 53 | i=$((i + 1)) |
| @@ -53,7 +58,7 @@ wait_log() { | |||
| 53 | # wait_qemu_exit TIMEOUT-SECONDS — poll until the pid is gone. | 58 | # wait_qemu_exit TIMEOUT-SECONDS — poll until the pid is gone. |
| 54 | wait_qemu_exit() { | 59 | wait_qemu_exit() { |
| 55 | local i=0 | 60 | local i=0 |
| 56 | while [ "$i" -lt $(( ${1:-30} * 10 )) ]; do | 61 | while [ "$i" -lt $((${1:-30} * 10)) ]; do |
| 57 | kill -0 "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || return 0 | 62 | kill -0 "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || return 0 |
| 58 | sleep 0.1 | 63 | sleep 0.1 |
| 59 | i=$((i + 1)) | 64 | i=$((i + 1)) |
| @@ -80,18 +85,29 @@ rm -f "$SOCK" "$LOG" | |||
| 80 | -device ide-hd,drive=esp,bus=ide.1 \ | 85 | -device ide-hd,drive=esp,bus=ide.1 \ |
| 81 | -serial file:"$LOG" \ | 86 | -serial file:"$LOG" \ |
| 82 | -monitor unix:"$SOCK",server,nowait \ | 87 | -monitor unix:"$SOCK",server,nowait \ |
| 83 | -m 256M -machine q35 -cpu qemu64 -no-reboot -display none & | 88 | -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ |
| 84 | echo $! > "$QPID" | 89 | >"$QLOG" 2>&1 & |
| 90 | echo $! >"$QPID" | ||
| 85 | 91 | ||
| 86 | wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" | 92 | wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" |
| 87 | sleep 1 | 93 | sleep 1 |
| 88 | 94 | ||
| 95 | # Volumes mount under /disk/<UID>/ where UID = "LABEL-SERIAL" (e.g. | ||
| 96 | # RSDOS-5BD1E55F). Discover it from the boot log so the test works on | ||
| 97 | # any image without knowing the serial in advance. | ||
| 98 | VOL_UID="$(grep -a 'fat: mounted' "$LOG" | sed -n 's/.*at \/disk\/\([^ )]*\).*/\1/p' | head -1)" | ||
| 99 | [ -n "$VOL_UID" ] || fail "no FAT32 volume was mounted (look for 'fat: mounted' in the log)" | ||
| 100 | echo " mounted volume UID: $VOL_UID" | ||
| 101 | |||
| 89 | echo "==> 4/6 drive the shell (ls / mkdir / echo > / cat / poweroff)" | 102 | echo "==> 4/6 drive the shell (ls / mkdir / echo > / cat / poweroff)" |
| 103 | # The filename is deliberately >13 chars so the FAT LFN path gets | ||
| 104 | # multi-entry entries (they're stored last-first on disk). | ||
| 105 | TESTFILE="disk-test-$STAMP.txt" | ||
| 90 | python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ | 106 | python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ |
| 91 | "ls /disk" \ | 107 | "ls /disk" \ |
| 92 | "mkdir /disk/sub" \ | 108 | "mkdir /disk/$VOL_UID/sub" \ |
| 93 | "echo disk-test-$STAMP > /disk/sub/hello.txt" \ | 109 | "echo disk-test-$STAMP > /disk/$VOL_UID/sub/$TESTFILE" \ |
| 94 | "cat /disk/sub/hello.txt" \ | 110 | "cat /disk/$VOL_UID/sub/$TESTFILE" \ |
| 95 | "poweroff" | 111 | "poweroff" |
| 96 | 112 | ||
| 97 | echo "==> 5/6 wait for shutdown" | 113 | echo "==> 5/6 wait for shutdown" |
| @@ -102,10 +118,11 @@ grep -a "disk-test-$STAMP" "$LOG" >/dev/null || fail "cat output missing from se | |||
| 102 | if ! fsck.fat -n "$IMG" >/tmp/rsdos-fsck.out 2>&1; then | 118 | if ! fsck.fat -n "$IMG" >/tmp/rsdos-fsck.out 2>&1; then |
| 103 | fail "fsck.fat reports problems: $(cat /tmp/rsdos-fsck.out | tail -3)" | 119 | fail "fsck.fat reports problems: $(cat /tmp/rsdos-fsck.out | tail -3)" |
| 104 | fi | 120 | fi |
| 105 | MT="$(mtype -i "$IMG" "::/sub/hello.txt" 2>/dev/null || true)" | 121 | MT="$(mtype -i "$IMG" "::/sub/$TESTFILE" 2>/dev/null || true)" |
| 106 | [ "$MT" = "disk-test-$STAMP" ] || fail "mtype mismatch: got '$MT'" | 122 | [ "$MT" = "disk-test-$STAMP" ] || fail "mtype mismatch: got '$MT'" |
| 107 | 123 | ||
| 108 | echo "" | 124 | echo "" |
| 109 | echo "PASS — disk round trip OK (fsck clean, file survived poweroff)" | 125 | echo "PASS — disk round trip OK (fsck clean, file survived poweroff)" |
| 110 | echo " image: $IMG" | 126 | echo " image: $IMG" |
| 111 | echo " written: /disk/sub/hello.txt = \"disk-test-$STAMP\"" | 127 | echo " volume: /disk/$VOL_UID" |
| 128 | echo " written: /disk/$VOL_UID/sub/$TESTFILE = \"disk-test-$STAMP\"" | ||
