#[cfg(target_os = "linux")]
pub mod overlayfs;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;
use anyhow::Result;
pub struct Sandbox {
pub target: PathBuf,
pub upper: PathBuf,
pub work: PathBuf,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChangeKind {
Added,
Modified,
Deleted,
}
impl ChangeKind {
pub fn letter(self) -> char {
match self {
ChangeKind::Added => 'A',
ChangeKind::Modified => 'M',
ChangeKind::Deleted => 'D',
}
}
}
#[derive(Debug)]
pub struct Change {
pub kind: ChangeKind,
pub path: PathBuf,
pub is_dir: bool,
}
pub trait SnapshotBackend {
fn exec(&self, sandbox: &Sandbox, command: &str) -> Result<ExitStatus>;
fn changes(&self, sandbox: &Sandbox) -> Result<Vec<Change>>;
fn merge(&self, sandbox: &Sandbox) -> Result<()>;
}
pub fn select() -> Result<Box<dyn SnapshotBackend>> {
#[cfg(target_os = "linux")]
{
Ok(Box::new(overlayfs::OverlayFs))
}
#[cfg(not(target_os = "linux"))]
{
anyhow::bail!(
"no snapshot backend supports this platform yet (OverlayFS is Linux-only; \
an APFS backend is planned).\n\
Refusing to run the command unsandboxed. On macOS, use the Linux dev \
container: `make shell-linux`."
)
}
}
pub fn in_test_container() -> bool {
std::env::var_os("OOPS_TEST_CONTAINER").is_some()
}
pub fn sort_changes(changes: &mut [Change]) {
changes.sort_by(|a, b| {
a.path
.as_os_str()
.as_encoded_bytes()
.cmp(b.path.as_os_str().as_encoded_bytes())
});
}
pub fn marker_path(sandbox: &Sandbox) -> PathBuf {
sandbox
.upper
.parent()
.unwrap_or(&sandbox.upper)
.join("started")
}
pub fn validate_mount_path(path: &Path) -> Result<()> {
let s = path.to_string_lossy();
if s.contains(':') || s.contains(',') || s.contains('\\') {
anyhow::bail!(
"path {} contains characters unsupported in overlay mount options (: , \\)",
path.display()
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sort_is_raw_byte_order_not_component_order() {
let mut changes = vec![
Change {
kind: ChangeKind::Added,
path: PathBuf::from("a/c"),
is_dir: false,
},
Change {
kind: ChangeKind::Added,
path: PathBuf::from("a-b"),
is_dir: false,
},
];
sort_changes(&mut changes);
assert_eq!(changes[0].path, PathBuf::from("a-b"));
assert_eq!(changes[1].path, PathBuf::from("a/c"));
}
}