#!/usr/bin/env bash # After `make test-v2`: check, on the host, the disk image the run wrote to. # * every file system is still consistent: e2fsck (A:), fsck.fat (B:), # fsck.exfat (C:), all read-only (-n); # * the share is a plain single-FAT exFAT volume, and the file the # self-test left there (dash's /mnt/share/from-foundry.txt) reads back # on the host -- through scripts/exfat.py, not the kernel's driver; # * the files the self-test created and deleted are really gone. # Nothing is mounted (no root needed). Usage: scripts/check-v2-image.sh IMAGE set -euo pipefail IMAGE=$1 PATH=$PATH:/usr/sbin:/sbin HERE=$(cd "$(dirname "$0")" && pwd) WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT fail=0 ok() { echo "[check-v2] ok $*"; } bad() { echo "[check-v2] FAIL $*"; fail=1; } # Partition start/size in sectors, from the GPT. read -r S1 N1 S2 N2 S3 N3 < <(sfdisk --dump "$IMAGE" | sed -n 's/.*start= *\([0-9]*\), size= *\([0-9]*\).*/\1 \2/p' | tr '\n' ' '; echo) for i in 1 2 3; do eval "dd if=\"\$IMAGE\" of=\"\$WORK/p$i\" bs=512 skip=\$S$i count=\$N$i status=none" done e2fsck -fn "$WORK/p1" > "$WORK/e2fsck.txt" 2>&1 && ok "e2fsck -fn: A: (ext2 root) is clean: $(tail -1 "$WORK/e2fsck.txt")" \ || { bad "e2fsck on A:"; cat "$WORK/e2fsck.txt"; } # The FAT "dirty" bit (boot sector byte 65, bit 0): the kernel's FAT driver # (the fatfs crate) sets it on the first write. Since Rustbelt 0.21.1 the # kernel unmounts every file system when pid 1 exits (the "[shutdown] # unmounted ..." lines), which clears it again. A set bit means the volume # was not cleanly unmounted (K12, docs/kernel-requests.md): a hard failure. if [ $(( $(od -An -tu1 -j65 -N1 "$WORK/p2") & 1 )) = 1 ]; then bad "B: has the FAT dirty bit set: it was not cleanly unmounted (K12)" else ok "B: FAT dirty bit is clear (the kernel unmounted it at shutdown)" fi fsck.fat -n "$WORK/p2" > "$WORK/fsck-fat.txt" 2>&1 && ok "fsck.fat -n: B: (FAT32) is clean: $(tail -1 "$WORK/fsck-fat.txt")" \ || { bad "fsck.fat on B:"; cat "$WORK/fsck-fat.txt"; } fsck.exfat -n "$WORK/p3" > "$WORK/fsck-exfat.txt" 2>&1 && ok "fsck.exfat -n: C: (exFAT share) is clean: $(tail -1 "$WORK/fsck-exfat.txt")" \ || { bad "fsck.exfat on C:"; cat "$WORK/fsck-exfat.txt"; } info=$(python3 "$HERE/exfat.py" "$WORK/p3" info) case $info in *"1 FAT(s)"*) ok "share: $info" ;; *) bad "share is not single-FAT: $info" ;; esac got=$(python3 "$HERE/exfat.py" "$WORK/p3" cat /from-foundry.txt 2>&1 || true) [ "$got" = "written on Foundry OS by dash" ] && ok "host reads C:/from-foundry.txt: \"$got\"" \ || bad "C:/from-foundry.txt on the host: '$got'" listing=$(python3 "$HERE/exfat.py" "$WORK/p3" ls) echo "$listing" | sed 's/^/[check-v2] C:\//' for gone in v2-test.txt v2dir todo.txt made-here seq.txt; do echo "$listing" | grep -qw -- "$gone" && bad "C:/$gone should have been deleted" || true done echo "$listing" | grep -q "README.TXT" || bad "C:/README.TXT is missing" # A: and B: through e2fsprogs/mtools: the test files are gone, /etc is there. debugfs -R "ls -l /" "$WORK/p1" 2>/dev/null | grep -q "v2-test-root.txt" && bad "A:/v2-test-root.txt should be gone" || true [ "$(debugfs -R 'cat /etc/motd' "$WORK/p1" 2>/dev/null)" = "Welcome to Foundry OS, running on the Rustbelt kernel." ] \ && ok "A:/etc/motd reads back through debugfs" || bad "A:/etc/motd" MTOOLS_SKIP_CHECK=1 mdir -b -i "$WORK/p2" ::/ 2>/dev/null | grep -qi "v2-test-b" && bad "B:/v2-test-b.txt should be gone" || true [ $fail = 0 ] && echo "[check-v2] all host checks passed" || { echo "[check-v2] FAILED"; exit 1; }