From 316a2a239446fe927e35b029b651cbeb08ff3f48 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 15:52:44 +0100 Subject: fs: /bin alias for the system volume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- rsnix/src/fs.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'rsnix/src') 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 { None } +/// `/bin` alias for the system volume (ADR 0006): the binaries actually +/// live on the FAT volume at `\\bin\\`, but `/bin` is the +/// stable user-facing path. When the first path component is `bin`, the +/// path is rewritten to the first mounted volume whose root has a `\bin` +/// directory — so `ls /bin/cli` and a binary's own +/// `fs_resolve("/bin/cli/x.efi")` work without knowing the volume UID. +/// Every other path returns `None` here without allocating. (The alias is +/// transparent: `ls /` does not list `bin`, and with no system volume +/// mounted `/bin` simply does not exist.) +fn alias_bin(path: &str) -> Option { + let p = path.trim_start_matches('/'); + let p = p.trim_start_matches("./"); // tolerate a leading ./ prefix + let first = match p.find('/') { + Some(i) => &p[..i], + None => p, + }; + if first != "bin" { + return None; + } + for vol in 0..crate::fat::volume_count() { + if !crate::fat::is_mounted(vol) { + continue; + } + if crate::fat::find(vol, crate::fat::root_cluster(vol), "bin").is_err() { + continue; // volume has no \bin root — not the system volume + } + let uid = String::from(crate::fat::uid(vol)); + return Some(alloc::format!("/disk/{}/{}", uid, p)); + } + None +} + /// Resolve an absolute path (supports `.` and `..`). Paths under /// `/disk` walk the mounted FAT32 volume through ephemeral Disk nodes. +/// A leading `bin` component goes through the `/bin` alias first. pub fn resolve(path: &str) -> Option { if nodes().is_empty() { return None; } + let aliased; + let path = match alias_bin(path) { + Some(p) => { + aliased = p; + aliased.as_str() + } + None => path, + }; let mut cur = 0usize; // root // (vol, cluster, parent, name) of directories descended into under /disk. let mut dstack: Vec<(usize, u32, u32, String)> = Vec::new(); -- cgit v1.2.3