zoi_core/sysroot.rs
1/// Manages the "Sysroot" (System Root) for Zoi operations.
2///
3/// A Sysroot allows Zoi to operate on an external directory as if it were
4/// the primary system root (`/`). This is a powerful feature for:
5/// - Bootstrapping: Creating a bootable OS image or container from the
6/// outside.
7/// - Cross-Installation: Installing packages for an embedded system or
8/// another partition.
9/// - Isolation: Testing installations without affecting the current host
10/// system.
11///
12/// When a sysroot is set, Zoi transparently redirects all absolute and
13/// relative paths to reside within this target directory.
14use std::path::{Component, PathBuf};
15use std::sync::{OnceLock, RwLock};
16
17/// Provides thread-safe access to the optional global sysroot path.
18fn sysroot_store() -> &'static RwLock<Option<PathBuf>> {
19 static SYSROOT: OnceLock<RwLock<Option<PathBuf>>> = OnceLock::new();
20 SYSROOT.get_or_init(|| RwLock::new(None))
21}
22
23/// Sets or replaces the global sysroot path.
24pub fn set_sysroot(path: PathBuf) {
25 if let Ok(mut guard) = sysroot_store().write() {
26 *guard = Some(path);
27 }
28}
29
30/// Clears the global sysroot path.
31pub fn clear_sysroot() {
32 if let Ok(mut guard) = sysroot_store().write() {
33 *guard = None;
34 }
35}
36
37/// Returns the global sysroot path if it has been set.
38pub fn get_sysroot() -> Option<PathBuf> {
39 sysroot_store().read().ok().and_then(|g| g.clone())
40}
41
42/// Prepends the sysroot to the given path if a sysroot is set.
43/// If the path is absolute, it is made relative to the current root before
44/// joining.
45pub fn apply_sysroot(path: impl Into<PathBuf>) -> PathBuf {
46 let path = path.into();
47 if let Some(root) = get_sysroot() {
48 let mut applied = root.clone();
49 for component in path.components() {
50 match component {
51 Component::Prefix(_)
52 | Component::RootDir
53 | Component::CurDir => {}
54 Component::ParentDir => {
55 // Never allow a relative path to pop above the configured
56 // sysroot. This keeps callers safe even when metadata
57 // contains `..` components.
58 if applied != root {
59 applied.pop();
60 }
61 }
62 Component::Normal(name) => applied.push(name)
63 }
64 }
65 applied
66 } else {
67 path
68 }
69}