#!/usr/bin/env bash # Check Rustbelt's exFAT writes with the host's own tools (exfatprogs). # # 1. fsck.exfat the pristine images build.rs made (this checks the # build-time populator, build/exfat_populate.rs). # 2. Copy them to target/fsck/ and boot Rustbelt on the *writable* copies # (no snapshot=on), so whatever it writes really lands in the files: # - the shell self-test (cp B:/C: -> D:, cp D: -> C:, md, echo >) # - the tests/exfat.rs kernel (create/write/append/truncate/unlink/ # rmdir, NoFatChain -> FAT chain, a directory grown to 200 files, # root directory growth; 4 KiB and 512-byte clusters) # 3. `fsck.exfat -n` every copy: -n = open read-only and answer "no" to # every question (report, don't fix). It checks the boot checksum, # bitmap vs. cluster chains, SetChecksums, name hashes... Any # complaint means the driver wrote something inconsistent. # 4. Show the self-test copy's root directory and one new file with # dump.exfat, and read that file's bytes straight off the image. # # Usage: ./scripts/exfat-check.sh (after `cargo build` and # `cargo test --test exfat`, which build the images and the kernels) set -euo pipefail cd "$(dirname "$0")/.." export PATH="$HOME/.cargo/bin:$PATH:/usr/sbin:/sbin" for tool in fsck.exfat dump.exfat qemu-system-x86_64; do command -v $tool >/dev/null || { echo "need $tool (apt install exfatprogs qemu-system-x86)"; exit 1; } done fsck() { # fsck echo "== fsck.exfat -n $1 ($2)" fsck.exfat -n "$1" || { echo "!! fsck.exfat found problems in $1"; status=1; } } status=0 for img in target/exfat.img target/exfat-512.img; do fsck $img "pristine, as built" done mkdir -p target/fsck cp target/exfat.img target/fsck/selftest-exfat.img for img in exfat exfat-512; do cp target/$img.img target/fsck/tests-$img.img; done echo "== boot the shell self-test on target/fsck/selftest-exfat.img" EXFAT_IMG=target/fsck/selftest-exfat.img ./scripts/run-headless.sh >/dev/null && echo "self-test OK" echo "== run the tests/exfat.rs kernel on target/fsck/tests-exfat*.img" KERNEL=$(ls -t target/x86_64-rust_kernel/debug/build/rustbelt/*/out/bootimage-exfat-*.bin 2>/dev/null | head -1 || true) [ -n "$KERNEL" ] || { echo "no test kernel: run 'cargo test --test exfat' first"; exit 1; } set +e timeout 120 qemu-system-x86_64 -drive format=raw,file="$KERNEL" -no-reboot \ -drive file=target/disk.img,format=raw,if=virtio,snapshot=on \ -drive file=target/ext2.img,format=raw,if=virtio,snapshot=on \ -drive file=target/fsck/tests-exfat.img,format=raw,if=virtio \ -drive file=target/btrfs.img,format=raw,if=virtio,snapshot=on \ -drive file=target/ext2-4k.img,format=raw,if=virtio,snapshot=on \ -drive file=target/ext2-rev0.img,format=raw,if=virtio,snapshot=on \ -drive file=target/fsck/tests-exfat-512.img,format=raw,if=virtio \ -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ -serial stdio -display none > target/fsck/tests-exfat.log code=$? set -e grep -c "\[ok\]" target/fsck/tests-exfat.log | sed 's/$/ exFAT tests ok/' [ $code = 33 ] || { echo "!! test kernel failed (exit $code), see target/fsck/tests-exfat.log"; exit 1; } for img in target/fsck/selftest-exfat.img target/fsck/tests-exfat.img target/fsck/tests-exfat-512.img; do fsck "$img" "after Rustbelt wrote to it" done IMG=target/fsck/selftest-exfat.img echo "== dump.exfat: what is in / on the self-test copy" dump.exfat -s / $IMG 2>/dev/null | sed -n 's/^Path: / /p' # (dump.exfat -s, a directory scan, rather than -d "/New Folder/Note.txt": # exfatprogs 1.2.9's -d can't find files inside some directories, e.g. # "NewFolder", even on images only mkfs + our populator ever touched.) echo "== dump.exfat -s \"/New Folder\": the entry set Rustbelt wrote for Note.txt" set_info=$(dump.exfat -s "/New Folder" $IMG 2>/dev/null) echo "$set_info" | grep -E "^Path|Entrytype|SetChecksum|GeneralSecondaryFlags|NameLength|NameHash|FirstCluster|DataLength" | sed 's/^ */ /' # Read the file's bytes straight from the image: cluster N starts at # ClusterHeapOffset + (N - 2) * cluster size. info=$(dump.exfat $IMG 2>/dev/null) heap=$(echo "$info" | sed -n 's/^Cluster Heap Offset (sector offset): *//p') spc=$(echo "$info" | sed -n 's/^Sectors per Cluster: *//p') first=$(echo "$set_info" | sed -n 's/^ *FirstCluster: *//p' | head -1) len=$(echo "$set_info" | sed -n 's/^ *DataLength: *//p' | head -1) echo "== its $len bytes at cluster $first, read with dd:" dd if=$IMG bs=512 skip=$((heap + (first - 2) * spc)) count=1 status=none | head -c "$len" [ $status = 0 ] && echo "== all images are clean" || echo "== fsck.exfat found problems" exit $status