Skip to main content

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}