From c7d92071e1dd11c45999457b16554b4abaa73b28 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 2 Aug 2026 15:21:34 +0200 Subject: v0.5.5: crash reports go to a dedicated CRASHLOG USB stick The fault handler now writes crash.log to the volume labeled CRASHLOG (label-selected via fat::crash_vol), falling back to slot 0. On real hardware the boot USB may be unwritable, so a second stick labeled CRASHLOG survives the write; the shell auto-prints the previous crash report from it at next boot (read_crash_log resolves /disk//crash.log through the FAT path, since Disk nodes hold no cached children). - fat.rs: volume_label() + crash_vol() (no allocation) - fs.rs: write_crash_log targets crash_vol(); read_crash_log prefers the CRASHLOG disk node; drop now-unused first_disk() - mkdisk.sh: --label option - test-fault.sh: boots with a dedicated CRASHLOG stick (mkfs -F 32 -n CRASHLOG), verifies crash.log lands there and NOT on the ESP --- rsnix/src/fat.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'rsnix/src/fat.rs') diff --git a/rsnix/src/fat.rs b/rsnix/src/fat.rs index eda81ed..b7e0da1 100644 --- a/rsnix/src/fat.rs +++ b/rsnix/src/fat.rs @@ -71,6 +71,31 @@ pub fn is_mounted(vol: usize) -> bool { vols().get(vol).map(|v| v.mounted).unwrap_or(false) } +/// The volume's label — the part of its uid before the serial suffix +/// (uid is "LABEL-XXXXXXXX", label sanitized to [A-Z0-9_], so the first +/// '-' always terminates it). No allocation: borrows the uid string. +pub fn volume_label(vol: usize) -> Option<&'static str> { + let uid = &vols().get(vol)?.uid; + Some(uid.split_once('-').map(|(l, _)| l).unwrap_or(uid)) +} + +/// Index of the crash-log volume: the first volume whose label is +/// "CRASHLOG" (a dedicated stick survives reboot writes that a boot +/// device may not accept), else slot 0 (first mounted) as before. +pub fn crash_vol() -> Option { + let n = volume_count(); + if n == 0 { + return None; + } + for vol in 0..n { + if let Some(label) = volume_label(vol) + && label.eq_ignore_ascii_case("CRASHLOG") { + return Some(vol); + } + } + Some(0) +} + /// Re-scan after a disk arrival/removal: unmount volumes whose disk left, /// mount every disk that has no mounted volume (which also re-tries /// mounts that failed at boot). Volume slots are tombstoned, never @@ -175,7 +200,11 @@ fn v() -> &'static mut Volume { match vols().get_mut(i) { Some(v) => v, None => { - crate::serial_println!("fat: internal error — no volume at slot {}", i); + crate::serial_println!( + "fat: internal error — no volume at slot {} (len={})", + i, + vols().len() + ); crate::interrupts::halt(); } } -- cgit v1.2.3