summaryrefslogtreecommitdiffstats
path: root/rsnix/src/fs.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 15:52:44 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 15:52:44 +0100
commit316a2a239446fe927e35b029b651cbeb08ff3f48 (patch)
treec16bcc30c25c9eaad99445d2a30b703f43c0d091 /rsnix/src/fs.rs
parentbins: read user args from argv[1..]; fix date year digit (diff)
downloadrs-dos-316a2a239446fe927e35b029b651cbeb08ff3f48.tar.gz
rs-dos-316a2a239446fe927e35b029b651cbeb08ff3f48.zip
fs: /bin alias for the system volume
ADR 0006 deferred root-path unification because it "needs VFS bind/alias machinery". Implement the /bin half: paths whose first component is `bin` are rewritten to the first mounted volume whose root has a \bin directory, so `ls /bin/cli`, `cat /bin/cli/x.efi` and a binary's own fs_resolve("/bin/cli/x.efi") work without knowing the volume UID. The alias is transparent — it creates no node, so `ls /` keeps its old listing and /bin simply does not exist when no system volume is mounted — and allocates nothing for non-matching paths. exec::resolve keeps its per-file volume scan (stricter for multiple volumes). Update ADR 0006 to record the decision and what remains deferred (\etc and friends at the root).
Diffstat (limited to 'rsnix/src/fs.rs')
-rw-r--r--rsnix/src/fs.rs41
1 files changed, 41 insertions, 0 deletions
diff --git a/rsnix/src/fs.rs b/rsnix/src/fs.rs
index 3585c00..a3bae6d 100644
--- a/rsnix/src/fs.rs
+++ b/rsnix/src/fs.rs
@@ -274,12 +274,53 @@ fn lookup(dir: usize, name: &str) -> Option<usize> {
274 None 274 None
275} 275}
276 276
277/// `/bin` alias for the system volume (ADR 0006): the binaries actually
278/// live on the FAT volume at `\<vol>\bin\<kind>\`, but `/bin` is the
279/// stable user-facing path. When the first path component is `bin`, the
280/// path is rewritten to the first mounted volume whose root has a `\bin`
281/// directory — so `ls /bin/cli` and a binary's own
282/// `fs_resolve("/bin/cli/x.efi")` work without knowing the volume UID.
283/// Every other path returns `None` here without allocating. (The alias is
284/// transparent: `ls /` does not list `bin`, and with no system volume
285/// mounted `/bin` simply does not exist.)
286fn alias_bin(path: &str) -> Option<String> {
287 let p = path.trim_start_matches('/');
288 let p = p.trim_start_matches("./"); // tolerate a leading ./ prefix
289 let first = match p.find('/') {
290 Some(i) => &p[..i],
291 None => p,
292 };
293 if first != "bin" {
294 return None;
295 }
296 for vol in 0..crate::fat::volume_count() {
297 if !crate::fat::is_mounted(vol) {
298 continue;
299 }
300 if crate::fat::find(vol, crate::fat::root_cluster(vol), "bin").is_err() {
301 continue; // volume has no \bin root — not the system volume
302 }
303 let uid = String::from(crate::fat::uid(vol));
304 return Some(alloc::format!("/disk/{}/{}", uid, p));
305 }
306 None
307}
308
277/// Resolve an absolute path (supports `.` and `..`). Paths under 309/// Resolve an absolute path (supports `.` and `..`). Paths under
278/// `/disk` walk the mounted FAT32 volume through ephemeral Disk nodes. 310/// `/disk` walk the mounted FAT32 volume through ephemeral Disk nodes.
311/// A leading `bin` component goes through the `/bin` alias first.
279pub fn resolve(path: &str) -> Option<usize> { 312pub fn resolve(path: &str) -> Option<usize> {
280 if nodes().is_empty() { 313 if nodes().is_empty() {
281 return None; 314 return None;
282 } 315 }
316 let aliased;
317 let path = match alias_bin(path) {
318 Some(p) => {
319 aliased = p;
320 aliased.as_str()
321 }
322 None => path,
323 };
283 let mut cur = 0usize; // root 324 let mut cur = 0usize; // root
284 // (vol, cluster, parent, name) of directories descended into under /disk. 325 // (vol, cluster, parent, name) of directories descended into under /disk.
285 let mut dstack: Vec<(usize, u32, u32, String)> = Vec::new(); 326 let mut dstack: Vec<(usize, u32, u32, String)> = Vec::new();