summaryrefslogtreecommitdiffstats
path: root/rsnix
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-01 02:19:07 +0200
committersalmonik <salmonik@tuta.io>2026-08-01 02:19:07 +0200
commit92ed4f6668efccf6af73f45654405d333e6a318c (patch)
treebb9b651dc36edcc90ae0289d7c4223d983454440 /rsnix
parentscripts: automate the data-disk workflow (mkdisk / sendkeys / test-disk) (diff)
downloadrs-dos-92ed4f6668efccf6af73f45654405d333e6a318c.tar.gz
rs-dos-92ed4f6668efccf6af73f45654405d333e6a318c.zip
fat: mount every FAT32 volume under /disk/<UID>/, fix multi-entry LFN
- fat.rs: VOL: Option<Volume> → VOLS: Vec<Volume>; try_mount() returns a Volume per disk and init() mounts every valid FAT32 volume found. Volume UID = sanitized uppercase label + 8-hex volume serial (e.g. RSDOS-5BD1E55F): unique per format, stable across reboots, human-recognizable, machine-parseable (^[A-Z0-9_]+-[0-9A-F]{8}$); cloned volumes get a -N suffix. Public ops take a vol index and select it (select_vol also picks the backing AHCI disk), matching the ahci::select pattern - fs.rs: /disk is now a plain directory whose children are one Disk node per mounted volume, named by UID; DiskInfo carries vol - LFN fix: entries are stored last-first on disk — both the writer (head-entry first) and reader (append in disk order) were reversed and cancelled out for ≤13-char names; multi-entry names (>13 chars) read back mangled (e.g. 'from-kernel.txt' as 'xt'). Writer now places seq=1 closest to the SFN and seq=N|0x40 first; reader places chunks by sequence number and trims at the first NUL. Verified with a 25-char filename round trip: kernel ls/cat correct, mtools shows the full LFN, fsck clean on both volumes - test-disk.sh: test file renamed to disk-test-.txt (>13 chars, exercises multi-entry LFN); UID discovered from the boot log; QEMU stdio redirected to a log file + EXIT trap so an orphaned QEMU can never hang a piped caller (it previously did, 400s)
Diffstat (limited to 'rsnix')
-rw-r--r--rsnix/src/fat.rs201
-rw-r--r--rsnix/src/fs.rs76
2 files changed, 188 insertions, 89 deletions
diff --git a/rsnix/src/fat.rs b/rsnix/src/fat.rs
index 616a08f..c486dfe 100644
--- a/rsnix/src/fat.rs
+++ b/rsnix/src/fat.rs
@@ -34,6 +34,8 @@ pub struct Entry {
34} 34}
35 35
36struct Volume { 36struct Volume {
37 disk: usize, // AHCI disk index this volume was mounted from
38 uid: String, // unique mount name: "LABEL-XXXXXXXX"
37 sectors_per_cluster: u8, 39 sectors_per_cluster: u8,
38 reserved: u16, 40 reserved: u16,
39 fat_count: u8, 41 fat_count: u8,
@@ -45,14 +47,34 @@ struct Volume {
45 fat: Vec<u8>, // FAT 1 cached in RAM 47 fat: Vec<u8>, // FAT 1 cached in RAM
46} 48}
47 49
48static mut VOL: Option<Volume> = None; 50static mut VOLS: Vec<Volume> = Vec::new();
51static mut ACTIVE_VOL: usize = 0;
49 52
50pub fn is_mounted() -> bool { 53pub fn volume_count() -> usize {
51 unsafe { VOL.is_some() } 54 unsafe { VOLS.len() }
52} 55}
53 56
54pub fn root_cluster() -> u32 { 57/// Unique mount name of volume `vol` ("RSDOS-5BD1E55F").
55 unsafe { VOL.as_ref() }.map(|v| v.root_cluster).unwrap_or(0) 58pub fn uid(vol: usize) -> &'static str {
59 unsafe { VOLS.get(vol) }.map(|v| v.uid.as_str()).unwrap_or("")
60}
61
62pub fn root_cluster(vol: usize) -> u32 {
63 unsafe { VOLS.get(vol) }.map(|v| v.root_cluster).unwrap_or(0)
64}
65
66/// Point all FAT operations at volume `vol`; also selects the backing
67/// AHCI disk. Called by every public entry point (single-threaded
68/// kernel, so a process-wide "current volume" is fine).
69fn select_vol(vol: usize) -> Result<(), ()> {
70 let disk = unsafe { VOLS.get(vol) }.map(|v| v.disk).ok_or(())?;
71 unsafe {
72 ACTIVE_VOL = vol;
73 }
74 if !crate::ahci::select(disk) {
75 return Err(());
76 }
77 Ok(())
56} 78}
57 79
58// --- sector I/O ----------------------------------------------------------- 80// --- sector I/O -----------------------------------------------------------
@@ -71,7 +93,7 @@ fn write_sector(lba: u64, data: &[u8; SECTOR]) -> Result<(), ()> {
71// --- cluster math ---------------------------------------------------------- 93// --- cluster math ----------------------------------------------------------
72 94
73fn v() -> &'static mut Volume { 95fn v() -> &'static mut Volume {
74 unsafe { VOL.as_mut() }.unwrap() 96 unsafe { VOLS.get_mut(ACTIVE_VOL) }.unwrap()
75} 97}
76 98
77fn spc() -> usize { 99fn spc() -> usize {
@@ -336,7 +358,19 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> {
336 _ => { 358 _ => {
337 let attr = e[11]; 359 let attr = e[11];
338 if attr == 0x0F { 360 if attr == 0x0F {
339 // LFN entry: 13 UTF-16 chars (bytes 1-10, 14-25, 28-31). 361 // LFN entry: 13 UTF-16 chars (bytes 1-10, 14-25,
362 // 28-31). Entries are stored last-first on disk;
363 // place each chunk by its sequence number so
364 // multi-entry names reassemble in the right order.
365 let seq = (e[0] & 0x1F) as usize;
366 if seq == 0 || seq > 20 {
367 continue;
368 }
369 let need = seq * 13;
370 if lfn.len() < need {
371 lfn.resize(need, 0);
372 }
373 let base = (seq - 1) * 13;
340 for k in 0..13 { 374 for k in 0..13 {
341 let pos = if k < 5 { 375 let pos = if k < 5 {
342 1 + k * 2 376 1 + k * 2
@@ -347,11 +381,9 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> {
347 }; 381 };
348 let ch = u16::from_le_bytes([e[pos], e[pos + 1]]); 382 let ch = u16::from_le_bytes([e[pos], e[pos + 1]]);
349 if ch == 0x0000 { 383 if ch == 0x0000 {
350 break; 384 break; // end of the name; rest is 0xFFFF padding
351 }
352 if ch != 0xFFFF {
353 lfn.push(ch);
354 } 385 }
386 lfn[base + k] = ch;
355 } 387 }
356 lfn_sum = e[13]; 388 lfn_sum = e[13];
357 } else { 389 } else {
@@ -359,7 +391,8 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> {
359 let mut sfn = [0u8; 11]; 391 let mut sfn = [0u8; 11];
360 sfn.copy_from_slice(&e[0..11]); 392 sfn.copy_from_slice(&e[0..11]);
361 let name = if !lfn.is_empty() && lfn_checksum(&sfn) == lfn_sum { 393 let name = if !lfn.is_empty() && lfn_checksum(&sfn) == lfn_sum {
362 utf16_to_string(&lfn) 394 let end = lfn.iter().position(|&c| c == 0).unwrap_or(lfn.len());
395 utf16_to_string(&lfn[..end])
363 } else { 396 } else {
364 sfn_decode(e) 397 sfn_decode(e)
365 }; 398 };
@@ -387,7 +420,8 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> {
387} 420}
388 421
389/// Public listing: no `.` / `..`. 422/// Public listing: no `.` / `..`.
390pub fn list_dir(dir_cluster: u32) -> Result<Vec<Entry>, ()> { 423pub fn list_dir(vol: usize, dir_cluster: u32) -> Result<Vec<Entry>, ()> {
424 select_vol(vol)?;
391 Ok(scan_dir(dir_cluster)? 425 Ok(scan_dir(dir_cluster)?
392 .into_iter() 426 .into_iter()
393 .filter(|i| i.name != "." && i.name != "..") 427 .filter(|i| i.name != "." && i.name != "..")
@@ -412,7 +446,8 @@ fn find_item(parent_dir_cluster: u32, name: &str) -> Result<DirItem, ()> {
412} 446}
413 447
414/// Find an entry by name (case-insensitive). 448/// Find an entry by name (case-insensitive).
415pub fn find(parent_dir_cluster: u32, name: &str) -> Result<Entry, ()> { 449pub fn find(vol: usize, parent_dir_cluster: u32, name: &str) -> Result<Entry, ()> {
450 select_vol(vol)?;
416 find_item(parent_dir_cluster, name).map(|i| Entry { 451 find_item(parent_dir_cluster, name).map(|i| Entry {
417 name: i.name, 452 name: i.name,
418 is_dir: i.attr & 0x10 != 0, 453 is_dir: i.attr & 0x10 != 0,
@@ -424,15 +459,17 @@ pub fn find(parent_dir_cluster: u32, name: &str) -> Result<Entry, ()> {
424// --- file data -------------------------------------------------------------- 459// --- file data --------------------------------------------------------------
425 460
426pub fn read_file( 461pub fn read_file(
462 vol: usize,
427 parent_dir_cluster: u32, 463 parent_dir_cluster: u32,
428 name: &str, 464 name: &str,
429 buf: &mut [u8], 465 buf: &mut [u8],
430 pos: u64, 466 pos: u64,
431) -> Result<usize, ()> { 467) -> Result<usize, ()> {
468 select_vol(vol)?;
432 if buf.is_empty() { 469 if buf.is_empty() {
433 return Ok(0); 470 return Ok(0);
434 } 471 }
435 let it = find(parent_dir_cluster, name)?; 472 let it = find(vol, parent_dir_cluster, name)?;
436 if it.is_dir || it.cluster == 0 { 473 if it.is_dir || it.cluster == 0 {
437 return Err(()); 474 return Err(());
438 } 475 }
@@ -467,7 +504,8 @@ pub fn read_file(
467 Ok(out) 504 Ok(out)
468} 505}
469 506
470pub fn write_file(parent_dir_cluster: u32, name: &str, buf: &[u8], pos: u64) -> Result<usize, ()> { 507pub fn write_file(vol: usize, parent_dir_cluster: u32, name: &str, buf: &[u8], pos: u64) -> Result<usize, ()> {
508 select_vol(vol)?;
471 if buf.is_empty() { 509 if buf.is_empty() {
472 return Ok(0); 510 return Ok(0);
473 } 511 }
@@ -529,7 +567,8 @@ pub fn write_file(parent_dir_cluster: u32, name: &str, buf: &[u8], pos: u64) ->
529 Ok(buf.len()) 567 Ok(buf.len())
530} 568}
531 569
532pub fn set_len(parent_dir_cluster: u32, name: &str, len: u64) -> Result<(), ()> { 570pub fn set_len(vol: usize, parent_dir_cluster: u32, name: &str, len: u64) -> Result<(), ()> {
571 select_vol(vol)?;
533 let it = find_item(parent_dir_cluster, name)?; 572 let it = find_item(parent_dir_cluster, name)?;
534 if it.attr & 0x10 != 0 { 573 if it.attr & 0x10 != 0 {
535 return Err(()); 574 return Err(());
@@ -721,8 +760,11 @@ fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<
721 if run >= needed { 760 if run >= needed {
722 if groups > 0 { 761 if groups > 0 {
723 for g in 0..groups { 762 for g in 0..groups {
724 let seq = (groups - g) as u8 | if g == groups - 1 { 0x40 } else { 0 }; 763 // LFN entries are stored last-first on disk: the tail
725 let e = &mut buf[(start + g) * 32..(start + g) * 32 + 32]; 764 // chunk (highest seq, LAST flag) goes first, and the
765 // head chunk (seq 1) lands immediately before the SFN.
766 let seq = (g + 1) as u8 | if g == groups - 1 { 0x40 } else { 0 };
767 let e = &mut buf[(start + groups - 1 - g) * 32..(start + groups - 1 - g) * 32 + 32];
726 e.fill(0); 768 e.fill(0);
727 e[0] = seq; 769 e[0] = seq;
728 e[11] = 0x0F; 770 e[11] = 0x0F;
@@ -769,8 +811,9 @@ fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<
769 } 811 }
770} 812}
771 813
772pub fn create(parent_dir_cluster: u32, name: &str, is_dir: bool) -> Result<(), ()> { 814pub fn create(vol: usize, parent_dir_cluster: u32, name: &str, is_dir: bool) -> Result<(), ()> {
773 if find(parent_dir_cluster, name).is_ok() { 815 select_vol(vol)?;
816 if find(vol, parent_dir_cluster, name).is_ok() {
774 return Err(()); 817 return Err(());
775 } 818 }
776 let cluster = if is_dir { 819 let cluster = if is_dir {
@@ -807,11 +850,12 @@ pub fn create(parent_dir_cluster: u32, name: &str, is_dir: bool) -> Result<(), (
807 add_dir_entry(parent_dir_cluster, name, is_dir, cluster) 850 add_dir_entry(parent_dir_cluster, name, is_dir, cluster)
808} 851}
809 852
810pub fn remove(parent_dir_cluster: u32, name: &str) -> Result<(), ()> { 853pub fn remove(vol: usize, parent_dir_cluster: u32, name: &str) -> Result<(), ()> {
854 select_vol(vol)?;
811 let it = find_item(parent_dir_cluster, name)?; 855 let it = find_item(parent_dir_cluster, name)?;
812 if it.attr & 0x10 != 0 { 856 if it.attr & 0x10 != 0 {
813 // Only empty directories may be removed. 857 // Only empty directories may be removed.
814 let kids = list_dir(it.cluster)?; 858 let kids = list_dir(vol, it.cluster)?;
815 if !kids.is_empty() { 859 if !kids.is_empty() {
816 return Err(()); 860 return Err(());
817 } 861 }
@@ -847,35 +891,59 @@ pub fn init() -> bool {
847 crate::serial_println!("fat: no disk"); 891 crate::serial_println!("fat: no disk");
848 return false; 892 return false;
849 } 893 }
894 let mut mounted = false;
850 for d in 0..disks { 895 for d in 0..disks {
851 if !crate::ahci::select(d) { 896 if !crate::ahci::select(d) {
852 continue; 897 continue;
853 } 898 }
854 if try_mount() { 899 match try_mount(d) {
855 return true; 900 Some(volume) => {
901 let uid = volume.uid.clone();
902 let vol = unsafe {
903 VOLS.push(volume);
904 VOLS.len() - 1
905 };
906 // Re-sync the FSInfo free-cluster count (may be stale
907 // from earlier sessions).
908 if select_vol(vol).is_ok() {
909 fsinfo_recount();
910 }
911 let count = list_dir(vol, root_cluster(vol))
912 .map(|es| es.len())
913 .unwrap_or(0);
914 crate::serial_println!(
915 "fat: mounted \"{}\" at /disk/{} ({} clusters, {} B/cluster, root: {} entries)",
916 uid,
917 uid,
918 v().cluster_count,
919 cluster_size(),
920 count
921 );
922 mounted = true;
923 }
924 None => crate::serial_println!("fat: no FAT32 volume on disk {}", d),
856 } 925 }
857 crate::serial_println!("fat: no FAT32 volume on disk {}", d);
858 } 926 }
859 false 927 mounted
860} 928}
861 929
862/// Parse the BPB of the selected disk, cache the FAT and validate the 930/// Parse the BPB of the selected disk, cache the FAT and validate the
863/// volume. 931/// volume. Returns the ready-to-mount volume or None.
864fn try_mount() -> bool { 932fn try_mount(d: usize) -> Option<Volume> {
865 let mut bpb = [0u8; SECTOR]; 933 let mut bpb = [0u8; SECTOR];
866 if read_sector(0, &mut bpb).is_err() { 934 if read_sector(0, &mut bpb).is_err() {
867 crate::serial_println!("fat: no disk"); 935 crate::serial_println!("fat: no disk");
868 return false; 936 return None;
869 } 937 }
870 let bytes_per_sector = u16::from_le_bytes([bpb[11], bpb[12]]); 938 let bytes_per_sector = u16::from_le_bytes([bpb[11], bpb[12]]);
871 if bytes_per_sector != 512 { 939 if bytes_per_sector != 512 {
872 crate::serial_println!("fat: unsupported sector size {}", bytes_per_sector); 940 crate::serial_println!("fat: unsupported sector size {}", bytes_per_sector);
873 return false; 941 return None;
874 } 942 }
875 let spc = bpb[13]; 943 let spc = bpb[13];
876 if spc == 0 || spc & (spc - 1) != 0 { 944 if spc == 0 || spc & (spc - 1) != 0 {
877 crate::serial_println!("fat: bad sectors/cluster {}", spc); 945 crate::serial_println!("fat: bad sectors/cluster {}", spc);
878 return false; 946 return None;
879 } 947 }
880 let reserved = u16::from_le_bytes([bpb[14], bpb[15]]); 948 let reserved = u16::from_le_bytes([bpb[14], bpb[15]]);
881 let fat_count = bpb[16]; 949 let fat_count = bpb[16];
@@ -891,12 +959,12 @@ fn try_mount() -> bool {
891 let fsinfo_sec = u16::from_le_bytes([bpb[48], bpb[49]]) as u32; 959 let fsinfo_sec = u16::from_le_bytes([bpb[48], bpb[49]]) as u32;
892 if fat_size == 0 || root_cluster < 2 || reserved == 0 || fat_count == 0 { 960 if fat_size == 0 || root_cluster < 2 || reserved == 0 || fat_count == 0 {
893 crate::serial_println!("fat: not FAT32 (root={} fat={})", root_cluster, fat_size); 961 crate::serial_println!("fat: not FAT32 (root={} fat={})", root_cluster, fat_size);
894 return false; 962 return None;
895 } 963 }
896 let first_data = reserved as u32 + fat_count as u32 * fat_size; 964 let first_data = reserved as u32 + fat_count as u32 * fat_size;
897 if first_data >= total { 965 if first_data >= total {
898 crate::serial_println!("fat: bad geometry"); 966 crate::serial_println!("fat: bad geometry");
899 return false; 967 return None;
900 } 968 }
901 let cluster_count = (total - first_data) / spc as u32; 969 let cluster_count = (total - first_data) / spc as u32;
902 970
@@ -909,7 +977,7 @@ fn try_mount() -> bool {
909 let n = ((fat.len() - done) / SECTOR).min(8) as u32; 977 let n = ((fat.len() - done) / SECTOR).min(8) as u32;
910 if ahci::read_sectors(lba, n).is_err() { 978 if ahci::read_sectors(lba, n).is_err() {
911 crate::serial_println!("fat: FAT read failed at lba {}", lba); 979 crate::serial_println!("fat: FAT read failed at lba {}", lba);
912 return false; 980 return None;
913 } 981 }
914 unsafe { 982 unsafe {
915 core::ptr::copy(ahci::buf(), fat.as_mut_ptr().add(done), n as usize * SECTOR); 983 core::ptr::copy(ahci::buf(), fat.as_mut_ptr().add(done), n as usize * SECTOR);
@@ -926,30 +994,45 @@ fn try_mount() -> bool {
926 e0 & 0xFF, 994 e0 & 0xFF,
927 e1 & EOC_MASK 995 e1 & EOC_MASK
928 ); 996 );
929 return false; 997 return None;
930 } 998 }
931 unsafe { 999 // Volume identity: sanitized label + volume serial → "RSDOS-5BD1E55F".
932 VOL = Some(Volume { 1000 // Unique per format, stable across reboots, human-recognizable, and
933 sectors_per_cluster: spc, 1001 // machine-parseable (^[A-Z0-9_]+-[0-9A-F]{8}$).
934 reserved, 1002 let serial = u32::from_le_bytes([bpb[67], bpb[68], bpb[69], bpb[70]]);
935 fat_count, 1003 let mut label = String::new();
936 fat_size, 1004 for &c in &bpb[71..82] {
937 root_cluster, 1005 if c == 0 || c == b' ' {
938 first_data, 1006 continue; // strip padding/NULs
939 cluster_count, 1007 }
940 fsinfo: (fsinfo_sec != 0 && fsinfo_sec < total).then_some(fsinfo_sec), 1008 let ch = (c as char).to_ascii_uppercase();
941 fat, 1009 label.push(if ch.is_ascii_alphanumeric() { ch } else { '_' });
942 }); 1010 }
943 } 1011 if label.is_empty() {
944 // Re-sync the FSInfo free-cluster count (may be stale from earlier 1012 label = String::from("DISK");
945 // sessions that predate FSInfo maintenance). 1013 }
946 fsinfo_recount(); 1014 // Deduplicate against already-mounted volumes (e.g. cloned images).
947 let count = list_dir(root_cluster).map(|v| v.len()).unwrap_or(0); 1015 let mut uid = alloc::format!("{}-{:08X}", label, serial);
948 crate::serial_println!( 1016 let mut n = 2u32;
949 "fat: FAT32 mounted — {} clusters ({} bytes each), root: {} entries", 1017 loop {
1018 let taken = unsafe { VOLS.iter() }.any(|v| v.uid == uid);
1019 if !taken {
1020 break;
1021 }
1022 uid = alloc::format!("{}-{:08X}-{}", label, serial, n);
1023 n += 1;
1024 }
1025 Some(Volume {
1026 disk: d,
1027 uid,
1028 sectors_per_cluster: spc,
1029 reserved,
1030 fat_count,
1031 fat_size,
1032 root_cluster,
1033 first_data,
950 cluster_count, 1034 cluster_count,
951 cluster_size(), 1035 fsinfo: (fsinfo_sec != 0 && fsinfo_sec < total).then_some(fsinfo_sec),
952 count 1036 fat,
953 ); 1037 })
954 true
955} 1038}
diff --git a/rsnix/src/fs.rs b/rsnix/src/fs.rs
index 6950171..4b76e07 100644
--- a/rsnix/src/fs.rs
+++ b/rsnix/src/fs.rs
@@ -59,6 +59,7 @@ enum NodeData {
59/// op, so it stays correct as the volume changes), `cluster` the entry's 59/// op, so it stays correct as the volume changes), `cluster` the entry's
60/// own first cluster. 60/// own first cluster.
61struct DiskInfo { 61struct DiskInfo {
62 vol: usize, // index into fat::VOLS (which FAT32 volume this lives on)
62 parent: u32, 63 parent: u32,
63 name: String, 64 name: String,
64 is_dir: bool, 65 is_dir: bool,
@@ -174,21 +175,32 @@ pub fn init() -> (usize, usize) {
174 ), 175 ),
175 ); 176 );
176 177
177 // Mount the FAT32 volume at /disk when one is present. 178 // Mount every FAT32 volume under /disk/<UID>/ (UID = label-serial).
178 if crate::fat::is_mounted() { 179 let disks = crate::fat::volume_count();
179 let root_cl = crate::fat::root_cluster(); 180 if disks > 0 {
180 add( 181 let disk_dir = add(
181 "disk", 182 "disk",
182 Some(root), 183 Some(root),
183 FileType::Dir, 184 FileType::Dir,
184 NodeData::Disk(DiskInfo { 185 NodeData::Dir(Vec::new()),
185 parent: root_cl,
186 name: String::new(),
187 is_dir: true,
188 size: 0,
189 cluster: root_cl,
190 }),
191 ); 186 );
187 for vol in 0..disks {
188 let uid = String::from(crate::fat::uid(vol));
189 let root_cl = crate::fat::root_cluster(vol);
190 add(
191 &uid,
192 Some(disk_dir),
193 FileType::Dir,
194 NodeData::Disk(DiskInfo {
195 vol,
196 parent: root_cl,
197 name: String::new(),
198 is_dir: true,
199 size: 0,
200 cluster: root_cl,
201 }),
202 );
203 }
192 } 204 }
193 205
194 let dirs = nodes().iter().filter(|n| n.kind == FileType::Dir).count(); 206 let dirs = nodes().iter().filter(|n| n.kind == FileType::Dir).count();
@@ -217,14 +229,15 @@ pub fn resolve(path: &str) -> Option<usize> {
217 return None; 229 return None;
218 } 230 }
219 let mut cur = 0usize; // root 231 let mut cur = 0usize; // root
220 // (cluster, parent, name) of directories descended into under /disk. 232 // (vol, cluster, parent, name) of directories descended into under /disk.
221 let mut dstack: Vec<(u32, u32, String)> = Vec::new(); 233 let mut dstack: Vec<(usize, u32, u32, String)> = Vec::new();
222 for seg in path.split('/') { 234 for seg in path.split('/') {
223 match seg { 235 match seg {
224 "" | "." => {} 236 "" | "." => {}
225 ".." => { 237 ".." => {
226 if let Some((cl, p, n)) = dstack.pop() { 238 if let Some((vol, cl, p, n)) = dstack.pop() {
227 cur = materialize_disk(DiskInfo { 239 cur = materialize_disk(DiskInfo {
240 vol,
228 parent: p, 241 parent: p,
229 name: n, 242 name: n,
230 is_dir: true, 243 is_dir: true,
@@ -236,13 +249,14 @@ pub fn resolve(path: &str) -> Option<usize> {
236 } 249 }
237 } 250 }
238 name => { 251 name => {
239 let disk_dir: Option<u32> = match &nodes()[cur].data { 252 let disk_ctx: Option<(usize, u32)> = match &nodes()[cur].data {
240 NodeData::Disk(info) => Some(info.cluster), 253 NodeData::Disk(info) => Some((info.vol, info.cluster)),
241 _ => None, 254 _ => None,
242 }; 255 };
243 if let Some(dir_cluster) = disk_dir { 256 if let Some((vol, dir_cluster)) = disk_ctx {
244 let e = crate::fat::find(dir_cluster, name).ok()?; 257 let e = crate::fat::find(vol, dir_cluster, name).ok()?;
245 let d = DiskInfo { 258 let d = DiskInfo {
259 vol,
246 parent: dir_cluster, 260 parent: dir_cluster,
247 name: e.name.clone(), 261 name: e.name.clone(),
248 is_dir: e.is_dir, 262 is_dir: e.is_dir,
@@ -250,7 +264,7 @@ pub fn resolve(path: &str) -> Option<usize> {
250 cluster: e.cluster, 264 cluster: e.cluster,
251 }; 265 };
252 if e.is_dir { 266 if e.is_dir {
253 dstack.push((e.cluster, dir_cluster, e.name)); 267 dstack.push((vol, e.cluster, dir_cluster, e.name));
254 } 268 }
255 cur = materialize_disk(d); 269 cur = materialize_disk(d);
256 } else { 270 } else {
@@ -279,7 +293,7 @@ pub fn size(h: usize) -> u64 {
279pub fn children(h: usize) -> Vec<(String, FileType, u64)> { 293pub fn children(h: usize) -> Vec<(String, FileType, u64)> {
280 if let NodeData::Disk(i) = &nodes()[h].data { 294 if let NodeData::Disk(i) = &nodes()[h].data {
281 let cl = i.cluster; 295 let cl = i.cluster;
282 return crate::fat::list_dir(cl) 296 return crate::fat::list_dir(i.vol, cl)
283 .map(|es| { 297 .map(|es| {
284 es.into_iter() 298 es.into_iter()
285 .map(|e| { 299 .map(|e| {
@@ -357,7 +371,7 @@ pub fn read(h: usize, buf: &mut [u8], pos: u64) -> Result<usize, ()> {
357 buf[..n].copy_from_slice(&bytes[p..p + n]); 371 buf[..n].copy_from_slice(&bytes[p..p + n]);
358 Ok(n) 372 Ok(n)
359 } 373 }
360 NodeData::Disk(i) => crate::fat::read_file(i.parent, &i.name, buf, pos), 374 NodeData::Disk(i) => crate::fat::read_file(i.vol, i.parent, &i.name, buf, pos),
361 NodeData::Dir(_) => Err(()), 375 NodeData::Dir(_) => Err(()),
362 } 376 }
363} 377}
@@ -394,7 +408,7 @@ pub fn write(h: usize, buf: &[u8], pos: u64) -> Result<usize, ()> {
394 } 408 }
395 _ => Err(()), // read-only devices 409 _ => Err(()), // read-only devices
396 }, 410 },
397 NodeData::Disk(i) => crate::fat::write_file(i.parent, &i.name, buf, pos), 411 NodeData::Disk(i) => crate::fat::write_file(i.vol, i.parent, &i.name, buf, pos),
398 NodeData::Dir(_) => Err(()), 412 NodeData::Dir(_) => Err(()),
399 } 413 }
400} 414}
@@ -406,7 +420,7 @@ pub fn set_len(h: usize, len: u64) -> Result<(), ()> {
406 v.resize(len as usize, 0); 420 v.resize(len as usize, 0);
407 Ok(()) 421 Ok(())
408 } 422 }
409 NodeData::Disk(i) => crate::fat::set_len(i.parent, &i.name, len), 423 NodeData::Disk(i) => crate::fat::set_len(i.vol, i.parent, &i.name, len),
410 _ => Err(()), 424 _ => Err(()),
411 } 425 }
412} 426}
@@ -417,22 +431,23 @@ pub fn create(parent: usize, name: &str, kind: FileType) -> Result<usize, ()> {
417 return Err(()); 431 return Err(());
418 } 432 }
419 // Disk directory: create through the FAT volume. 433 // Disk directory: create through the FAT volume.
420 let disk_parent: Option<u32> = match &nodes()[parent].data { 434 let disk_parent: Option<(usize, u32)> = match &nodes()[parent].data {
421 NodeData::Disk(i) => { 435 NodeData::Disk(i) => {
422 if !i.is_dir { 436 if !i.is_dir {
423 return Err(()); 437 return Err(());
424 } 438 }
425 Some(i.cluster) 439 Some((i.vol, i.cluster))
426 } 440 }
427 _ => None, 441 _ => None,
428 }; 442 };
429 if let Some(p) = disk_parent { 443 if let Some((vol, p)) = disk_parent {
430 if crate::fat::find(p, name).is_ok() { 444 if crate::fat::find(vol, p, name).is_ok() {
431 return Err(()); 445 return Err(());
432 } 446 }
433 crate::fat::create(p, name, kind == FileType::Dir)?; 447 crate::fat::create(vol, p, name, kind == FileType::Dir)?;
434 let e = crate::fat::find(p, name)?; 448 let e = crate::fat::find(vol, p, name)?;
435 return Ok(materialize_disk(DiskInfo { 449 return Ok(materialize_disk(DiskInfo {
450 vol,
436 parent: p, 451 parent: p,
437 name: e.name, 452 name: e.name,
438 is_dir: e.is_dir, 453 is_dir: e.is_dir,
@@ -458,9 +473,10 @@ pub fn remove(h: usize) -> Result<(), ()> {
458 if i.name.is_empty() { 473 if i.name.is_empty() {
459 return Err(()); // the /disk mount itself 474 return Err(()); // the /disk mount itself
460 } 475 }
476 let vol = i.vol;
461 let p = i.parent; 477 let p = i.parent;
462 let n = i.name.clone(); 478 let n = i.name.clone();
463 return crate::fat::remove(p, &n); 479 return crate::fat::remove(vol, p, &n);
464 } 480 }
465 let parent = match nodes()[h].parent { 481 let parent = match nodes()[h].parent {
466 Some(p) => p, 482 Some(p) => p,