diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-17 19:56:24 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-17 19:56:24 +0200 |
| commit | 126d5b2e55dca93e024f5654fed6fae37a012369 (patch) | |
| tree | a74ad41c98c121a7bc37299b8a4d1d336e0cd2c5 /rsnix/src/fat.rs | |
| parent | wm: keep windows in place when the console scrolls beneath them (diff) | |
| download | rs-dos-126d5b2e55dca93e024f5654fed6fae37a012369.tar.gz rs-dos-126d5b2e55dca93e024f5654fed6fae37a012369.zip | |
Terminal-app design (ADR 0005) + disk-write controller
- ADR 0005: persistent home terminal (full-screen, chrome-less,
bottom-pinned), F1 terminal windows, per-instance vt/tty/shell,
queue-based input for all terminals, and the disk controller
- Glossary: add the Terminal term; update Task/App for the new model
- Add a re-entrant disk lock (DiskGuard over the preemption-depth
counter) and wrap all fs/fat mutating operations per-call
Diffstat (limited to 'rsnix/src/fat.rs')
| -rw-r--r-- | rsnix/src/fat.rs | 41 |
1 files changed, 33 insertions, 8 deletions
diff --git a/rsnix/src/fat.rs b/rsnix/src/fat.rs index b7e0da1..03b9550 100644 --- a/rsnix/src/fat.rs +++ b/rsnix/src/fat.rs | |||
| @@ -62,6 +62,26 @@ fn active_vol() -> &'static mut usize { | |||
| 62 | unsafe { &mut *core::ptr::addr_of_mut!(ACTIVE_VOL) } | 62 | unsafe { &mut *core::ptr::addr_of_mut!(ACTIVE_VOL) } |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | /// Re-entrant disk lock: serializes filesystem/disk access across tasks. | ||
| 66 | /// Multiple terminals may touch the disk concurrently, so every fat/fs | ||
| 67 | /// operation that touches the shared volume state, the FAT cache, or the | ||
| 68 | /// VFS must hold this. Re-entrant (the preemption-depth counter), so an | ||
| 69 | /// `fs` op can call a `fat` op without deadlocking. | ||
| 70 | pub(crate) struct DiskGuard; | ||
| 71 | |||
| 72 | impl DiskGuard { | ||
| 73 | pub(crate) fn acquire() -> Self { | ||
| 74 | crate::sched::lock(); | ||
| 75 | DiskGuard | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl Drop for DiskGuard { | ||
| 80 | fn drop(&mut self) { | ||
| 81 | crate::sched::unlock(); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 65 | pub fn volume_count() -> usize { | 85 | pub fn volume_count() -> usize { |
| 66 | vols().len() | 86 | vols().len() |
| 67 | } | 87 | } |
| @@ -89,9 +109,10 @@ pub fn crash_vol() -> Option<usize> { | |||
| 89 | } | 109 | } |
| 90 | for vol in 0..n { | 110 | for vol in 0..n { |
| 91 | if let Some(label) = volume_label(vol) | 111 | if let Some(label) = volume_label(vol) |
| 92 | && label.eq_ignore_ascii_case("CRASHLOG") { | 112 | && label.eq_ignore_ascii_case("CRASHLOG") |
| 93 | return Some(vol); | 113 | { |
| 94 | } | 114 | return Some(vol); |
| 115 | } | ||
| 95 | } | 116 | } |
| 96 | Some(0) | 117 | Some(0) |
| 97 | } | 118 | } |
| @@ -102,6 +123,7 @@ pub fn crash_vol() -> Option<usize> { | |||
| 102 | /// removed, so VFS disk handles keep their indices. Returns | 123 | /// removed, so VFS disk handles keep their indices. Returns |
| 103 | /// (mounted, unmounted). | 124 | /// (mounted, unmounted). |
| 104 | pub fn rescan() -> (usize, usize) { | 125 | pub fn rescan() -> (usize, usize) { |
| 126 | let _lock = DiskGuard::acquire(); | ||
| 105 | let disks = crate::ahci::disk_count(); | 127 | let disks = crate::ahci::disk_count(); |
| 106 | let mut mounted = 0usize; | 128 | let mut mounted = 0usize; |
| 107 | let mut unmounted = 0usize; | 129 | let mut unmounted = 0usize; |
| @@ -256,11 +278,7 @@ fn is_eoc(c: u32) -> bool { | |||
| 256 | 278 | ||
| 257 | fn next_cluster(c: u32) -> Result<u32, ()> { | 279 | fn next_cluster(c: u32) -> Result<u32, ()> { |
| 258 | let n = read_fat_entry(c); | 280 | let n = read_fat_entry(c); |
| 259 | if is_eoc(n) { | 281 | if is_eoc(n) { Err(()) } else { Ok(n) } |
| 260 | Err(()) | ||
| 261 | } else { | ||
| 262 | Ok(n) | ||
| 263 | } | ||
| 264 | } | 282 | } |
| 265 | 283 | ||
| 266 | /// Allocate a fresh cluster: mark it EOC in the FAT and zero its data. | 284 | /// Allocate a fresh cluster: mark it EOC in the FAT and zero its data. |
| @@ -532,6 +550,7 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> { | |||
| 532 | 550 | ||
| 533 | /// Public listing: no `.` / `..`. | 551 | /// Public listing: no `.` / `..`. |
| 534 | pub fn list_dir(vol: usize, dir_cluster: u32) -> Result<Vec<Entry>, ()> { | 552 | pub fn list_dir(vol: usize, dir_cluster: u32) -> Result<Vec<Entry>, ()> { |
| 553 | let _lock = DiskGuard::acquire(); | ||
| 535 | select_vol(vol)?; | 554 | select_vol(vol)?; |
| 536 | Ok(scan_dir(dir_cluster)? | 555 | Ok(scan_dir(dir_cluster)? |
| 537 | .into_iter() | 556 | .into_iter() |
| @@ -558,6 +577,7 @@ fn find_item(parent_dir_cluster: u32, name: &str) -> Result<DirItem, ()> { | |||
| 558 | 577 | ||
| 559 | /// Find an entry by name (case-insensitive). | 578 | /// Find an entry by name (case-insensitive). |
| 560 | pub fn find(vol: usize, parent_dir_cluster: u32, name: &str) -> Result<Entry, ()> { | 579 | pub fn find(vol: usize, parent_dir_cluster: u32, name: &str) -> Result<Entry, ()> { |
| 580 | let _lock = DiskGuard::acquire(); | ||
| 561 | select_vol(vol)?; | 581 | select_vol(vol)?; |
| 562 | find_item(parent_dir_cluster, name).map(|i| Entry { | 582 | find_item(parent_dir_cluster, name).map(|i| Entry { |
| 563 | name: i.name, | 583 | name: i.name, |
| @@ -576,6 +596,7 @@ pub fn read_file( | |||
| 576 | buf: &mut [u8], | 596 | buf: &mut [u8], |
| 577 | pos: u64, | 597 | pos: u64, |
| 578 | ) -> Result<usize, ()> { | 598 | ) -> Result<usize, ()> { |
| 599 | let _lock = DiskGuard::acquire(); | ||
| 579 | select_vol(vol)?; | 600 | select_vol(vol)?; |
| 580 | if buf.is_empty() { | 601 | if buf.is_empty() { |
| 581 | return Ok(0); | 602 | return Ok(0); |
| @@ -622,6 +643,7 @@ pub fn write_file( | |||
| 622 | buf: &[u8], | 643 | buf: &[u8], |
| 623 | pos: u64, | 644 | pos: u64, |
| 624 | ) -> Result<usize, ()> { | 645 | ) -> Result<usize, ()> { |
| 646 | let _lock = DiskGuard::acquire(); | ||
| 625 | select_vol(vol)?; | 647 | select_vol(vol)?; |
| 626 | if buf.is_empty() { | 648 | if buf.is_empty() { |
| 627 | return Ok(0); | 649 | return Ok(0); |
| @@ -685,6 +707,7 @@ pub fn write_file( | |||
| 685 | } | 707 | } |
| 686 | 708 | ||
| 687 | pub fn set_len(vol: usize, parent_dir_cluster: u32, name: &str, len: u64) -> Result<(), ()> { | 709 | pub fn set_len(vol: usize, parent_dir_cluster: u32, name: &str, len: u64) -> Result<(), ()> { |
| 710 | let _lock = DiskGuard::acquire(); | ||
| 688 | select_vol(vol)?; | 711 | select_vol(vol)?; |
| 689 | let it = find_item(parent_dir_cluster, name)?; | 712 | let it = find_item(parent_dir_cluster, name)?; |
| 690 | if it.attr & 0x10 != 0 { | 713 | if it.attr & 0x10 != 0 { |
| @@ -1009,6 +1032,7 @@ fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result< | |||
| 1009 | } | 1032 | } |
| 1010 | 1033 | ||
| 1011 | pub fn create(vol: usize, parent_dir_cluster: u32, name: &str, is_dir: bool) -> Result<(), ()> { | 1034 | pub fn create(vol: usize, parent_dir_cluster: u32, name: &str, is_dir: bool) -> Result<(), ()> { |
| 1035 | let _lock = DiskGuard::acquire(); | ||
| 1012 | select_vol(vol)?; | 1036 | select_vol(vol)?; |
| 1013 | if find(vol, parent_dir_cluster, name).is_ok() { | 1037 | if find(vol, parent_dir_cluster, name).is_ok() { |
| 1014 | return Err(()); | 1038 | return Err(()); |
| @@ -1048,6 +1072,7 @@ pub fn create(vol: usize, parent_dir_cluster: u32, name: &str, is_dir: bool) -> | |||
| 1048 | } | 1072 | } |
| 1049 | 1073 | ||
| 1050 | pub fn remove(vol: usize, parent_dir_cluster: u32, name: &str) -> Result<(), ()> { | 1074 | pub fn remove(vol: usize, parent_dir_cluster: u32, name: &str) -> Result<(), ()> { |
| 1075 | let _lock = DiskGuard::acquire(); | ||
| 1051 | select_vol(vol)?; | 1076 | select_vol(vol)?; |
| 1052 | let it = find_item(parent_dir_cluster, name)?; | 1077 | let it = find_item(parent_dir_cluster, name)?; |
| 1053 | if it.attr & 0x10 != 0 { | 1078 | if it.attr & 0x10 != 0 { |
