diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-02 15:21:34 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-02 15:21:34 +0200 |
| commit | c7d92071e1dd11c45999457b16554b4abaa73b28 (patch) | |
| tree | aff7c7f19adcffe1dd91e1c2514af02c77fd524e /rsnix/src/fat.rs | |
| parent | v0.5.5: fix real-hardware boot crash — shadow back in heap as first allocation (diff) | |
| download | rs-dos-c7d92071e1dd11c45999457b16554b4abaa73b28.tar.gz rs-dos-c7d92071e1dd11c45999457b16554b4abaa73b28.zip | |
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/<uid>/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
Diffstat (limited to 'rsnix/src/fat.rs')
| -rw-r--r-- | rsnix/src/fat.rs | 31 |
1 files changed, 30 insertions, 1 deletions
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 { | |||
| 71 | vols().get(vol).map(|v| v.mounted).unwrap_or(false) | 71 | vols().get(vol).map(|v| v.mounted).unwrap_or(false) |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | /// The volume's label — the part of its uid before the serial suffix | ||
| 75 | /// (uid is "LABEL-XXXXXXXX", label sanitized to [A-Z0-9_], so the first | ||
| 76 | /// '-' always terminates it). No allocation: borrows the uid string. | ||
| 77 | pub fn volume_label(vol: usize) -> Option<&'static str> { | ||
| 78 | let uid = &vols().get(vol)?.uid; | ||
| 79 | Some(uid.split_once('-').map(|(l, _)| l).unwrap_or(uid)) | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Index of the crash-log volume: the first volume whose label is | ||
| 83 | /// "CRASHLOG" (a dedicated stick survives reboot writes that a boot | ||
| 84 | /// device may not accept), else slot 0 (first mounted) as before. | ||
| 85 | pub fn crash_vol() -> Option<usize> { | ||
| 86 | let n = volume_count(); | ||
| 87 | if n == 0 { | ||
| 88 | return None; | ||
| 89 | } | ||
| 90 | for vol in 0..n { | ||
| 91 | if let Some(label) = volume_label(vol) | ||
| 92 | && label.eq_ignore_ascii_case("CRASHLOG") { | ||
| 93 | return Some(vol); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | Some(0) | ||
| 97 | } | ||
| 98 | |||
| 74 | /// Re-scan after a disk arrival/removal: unmount volumes whose disk left, | 99 | /// Re-scan after a disk arrival/removal: unmount volumes whose disk left, |
| 75 | /// mount every disk that has no mounted volume (which also re-tries | 100 | /// mount every disk that has no mounted volume (which also re-tries |
| 76 | /// mounts that failed at boot). Volume slots are tombstoned, never | 101 | /// mounts that failed at boot). Volume slots are tombstoned, never |
| @@ -175,7 +200,11 @@ fn v() -> &'static mut Volume { | |||
| 175 | match vols().get_mut(i) { | 200 | match vols().get_mut(i) { |
| 176 | Some(v) => v, | 201 | Some(v) => v, |
| 177 | None => { | 202 | None => { |
| 178 | crate::serial_println!("fat: internal error — no volume at slot {}", i); | 203 | crate::serial_println!( |
| 204 | "fat: internal error — no volume at slot {} (len={})", | ||
| 205 | i, | ||
| 206 | vols().len() | ||
| 207 | ); | ||
| 179 | crate::interrupts::halt(); | 208 | crate::interrupts::halt(); |
| 180 | } | 209 | } |
| 181 | } | 210 | } |
