use std::sync::OnceLock;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DaemonMode {
Full,
NestedAdaptive,
Degraded,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DaemonCapabilities {
pub is_root: bool,
pub is_nested: bool,
pub cgroup_parent: Option<String>,
pub can_write_cgroup_root: bool,
pub has_cap_net_admin: bool,
pub tun_device_available: bool,
pub overlayfs_rootfs_available: bool,
pub fuse_overlayfs_rootfs_available: bool,
pub effective_mode: DaemonMode,
}
static CAPS: OnceLock<DaemonCapabilities> = OnceLock::new();
impl DaemonCapabilities {
pub fn get() -> &'static Self {
CAPS.get_or_init(Self::probe)
}
pub fn seed(caps: Self) -> &'static Self {
let _ = CAPS.set(caps);
CAPS.get()
.expect("CAPS is filled after set or was already filled")
}
#[must_use]
pub fn probe() -> Self {
let is_root = zlayer_paths::is_root();
let cgroup_parent = current_cgroup_v2_path();
let is_nested = cgroup_parent.is_some();
let can_write_cgroup_root = probe_can_write_cgroup_root();
let has_cap_net_admin = probe_has_cap_net_admin();
let tun_device_available = probe_tun_device_available();
let overlayfs_rootfs_available = probe_overlayfs_rootfs_available(is_root);
let fuse_overlayfs_rootfs_available = probe_fuse_overlayfs_rootfs_available();
let effective_mode =
if !is_nested && can_write_cgroup_root && has_cap_net_admin && tun_device_available {
DaemonMode::Full
} else if can_write_cgroup_root || cgroup_parent.is_some() {
DaemonMode::NestedAdaptive
} else {
DaemonMode::Degraded
};
Self {
is_root,
is_nested,
cgroup_parent,
can_write_cgroup_root,
has_cap_net_admin,
tun_device_available,
overlayfs_rootfs_available,
fuse_overlayfs_rootfs_available,
effective_mode,
}
}
}
#[must_use]
pub fn capability_overlay_fallback(
has_cap_net_admin: bool,
tun_device_available: bool,
) -> Option<String> {
match (has_cap_net_admin, tun_device_available) {
(true, true) => None,
(false, false) => Some(
"CAP_NET_ADMIN is not in the daemon's effective set and /dev/net/tun is not available"
.to_string(),
),
(false, true) => Some("CAP_NET_ADMIN is not in the daemon's effective set".to_string()),
(true, false) => Some("/dev/net/tun is not available".to_string()),
}
}
#[must_use]
#[allow(clippy::fn_params_excessive_bools)] pub fn can_rootless_overlay(
is_root: bool,
has_cap_net_admin: bool,
tun_device_available: bool,
pasta_available: bool,
) -> bool {
!is_root && !has_cap_net_admin && tun_device_available && pasta_available
}
#[cfg(target_os = "linux")]
fn parse_cgroup_v2_line(content: &str) -> Option<String> {
for line in content.lines() {
if let Some(rest) = line.strip_prefix("0::") {
let trimmed = rest.trim();
if trimmed.is_empty() || trimmed == "/" {
return None;
}
return Some(trimmed.to_string());
}
}
None
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn current_cgroup_v2_path() -> Option<String> {
let content = std::fs::read_to_string("/proc/self/cgroup").ok()?;
parse_cgroup_v2_line(&content)
}
#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn current_cgroup_v2_path() -> Option<String> {
None
}
#[cfg(target_os = "linux")]
fn compute_target_parent(scope: &str) -> String {
let base = scope.strip_suffix("/init").unwrap_or(scope);
let base = base.trim_end_matches('/');
format!("{base}/containers")
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn ensure_daemon_leaf_and_container_parent() -> Option<String> {
let scope = current_cgroup_v2_path()?;
let containers = compute_target_parent(&scope);
if scope.ends_with("/init") {
let containers_fs = format!("/sys/fs/cgroup{containers}");
match std::fs::create_dir_all(&containers_fs) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(_) => return None,
}
return Some(containers);
}
let scope = scope.trim_end_matches('/').to_string();
let mount = "/sys/fs/cgroup";
let init_dir = format!("{mount}{scope}/init");
match std::fs::create_dir_all(&init_dir) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(_) => return None,
}
let pid_path = format!("{init_dir}/cgroup.procs");
let pid_str = format!("{}", std::process::id());
if std::fs::write(&pid_path, &pid_str).is_err() {
let now = current_cgroup_v2_path()?;
if now != format!("{scope}/init") {
return None;
}
}
let after = current_cgroup_v2_path()?;
if after != format!("{scope}/init") {
return None;
}
let containers_dir = format!("{mount}{containers}");
match std::fs::create_dir_all(&containers_dir) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(_) => return None,
}
Some(containers)
}
#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn ensure_daemon_leaf_and_container_parent() -> Option<String> {
None
}
#[cfg(target_os = "linux")]
const HOST_CONTAINER_ROOT: &str = "/zlayer";
#[cfg(target_os = "linux")]
const HOST_CGROUP_CONTROLLERS: &[&str] = &["cpu", "cpuset", "io", "memory", "pids"];
#[cfg(target_os = "linux")]
#[must_use]
fn compute_host_container_parent() -> String {
format!("{HOST_CONTAINER_ROOT}/containers")
}
#[cfg(target_os = "linux")]
fn enable_available_controllers(dir: &str) {
let available =
std::fs::read_to_string(format!("{dir}/cgroup.controllers")).unwrap_or_default();
let tokens: Vec<String> = HOST_CGROUP_CONTROLLERS
.iter()
.filter(|c| available.split_whitespace().any(|a| a == **c))
.map(|c| format!("+{c}"))
.collect();
if tokens.is_empty() {
return;
}
let _ = std::fs::write(format!("{dir}/cgroup.subtree_control"), tokens.join(" "));
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn ensure_host_container_parent() -> Option<String> {
let mount = "/sys/fs/cgroup";
let containers = compute_host_container_parent();
let root_fs = format!("{mount}{HOST_CONTAINER_ROOT}");
let containers_fs = format!("{mount}{containers}");
for dir in [&root_fs, &containers_fs] {
match std::fs::create_dir_all(dir) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(_) => return None,
}
}
enable_available_controllers(&root_fs);
enable_available_controllers(&containers_fs);
Some(containers)
}
#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn ensure_host_container_parent() -> Option<String> {
None
}
#[cfg(target_os = "linux")]
fn remove_cgroup_tree(dir: &std::path::Path) {
let _ = std::fs::write(dir.join("cgroup.kill"), "1");
match std::fs::read_dir(dir) {
Ok(entries) => {
for entry in entries.flatten() {
let path = entry.path();
if entry.file_type().is_ok_and(|t| t.is_dir()) {
remove_cgroup_tree(&path);
} else {
let _ = std::fs::remove_file(&path);
}
}
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return,
Err(e) => {
tracing::warn!(cgroup = %dir.display(), error = %e, "cgroup read_dir failed");
}
}
match std::fs::remove_dir(dir) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => {
tracing::warn!(cgroup = %dir.display(), error = %e, "cgroup rmdir failed");
}
}
}
#[cfg(target_os = "linux")]
fn remove_host_container_cgroup_at(base: &str, container_id: &str) {
let leaf = std::path::PathBuf::from(base).join(format!("zlayer/containers/{container_id}"));
if !leaf.exists() {
return;
}
remove_cgroup_tree(&leaf);
}
#[cfg(target_os = "linux")]
pub fn remove_host_container_cgroup(container_id: &str) {
remove_host_container_cgroup_at("/sys/fs/cgroup", container_id);
}
#[cfg(not(target_os = "linux"))]
pub fn remove_host_container_cgroup(_container_id: &str) {}
#[cfg(target_os = "linux")]
fn probe_can_write_cgroup_root() -> bool {
use std::ffi::CString;
let Ok(path) = CString::new("/sys/fs/cgroup/cgroup.subtree_control") else {
return false;
};
#[allow(unsafe_code)]
let rc = unsafe { libc::access(path.as_ptr(), libc::W_OK) };
rc == 0
}
#[cfg(not(target_os = "linux"))]
fn probe_can_write_cgroup_root() -> bool {
false
}
#[cfg(target_os = "linux")]
fn probe_has_cap_net_admin() -> bool {
const CAP_NET_ADMIN_BIT: u64 = 1 << 12;
let Ok(status) = std::fs::read_to_string("/proc/self/status") else {
return false;
};
for line in status.lines() {
if let Some(hex) = line.strip_prefix("CapEff:") {
let trimmed = hex.trim();
if let Ok(eff) = u64::from_str_radix(trimmed, 16) {
return eff & CAP_NET_ADMIN_BIT != 0;
}
return false;
}
}
false
}
#[cfg(not(target_os = "linux"))]
fn probe_has_cap_net_admin() -> bool {
false
}
#[cfg(target_os = "linux")]
fn probe_tun_device_available() -> bool {
use std::os::unix::fs::OpenOptionsExt;
std::fs::OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_NONBLOCK)
.open("/dev/net/tun")
.is_ok()
}
#[cfg(not(target_os = "linux"))]
fn probe_tun_device_available() -> bool {
false
}
#[cfg(target_os = "linux")]
const CAP_SYS_ADMIN_BIT: u64 = 1 << 21;
#[cfg(target_os = "linux")]
fn probe_has_cap_sys_admin() -> bool {
let Ok(status) = std::fs::read_to_string("/proc/self/status") else {
return false;
};
for line in status.lines() {
if let Some(hex) = line.strip_prefix("CapEff:") {
if let Ok(eff) = u64::from_str_radix(hex.trim(), 16) {
return eff & CAP_SYS_ADMIN_BIT != 0;
}
return false;
}
}
false
}
#[cfg(target_os = "linux")]
fn proc_filesystems_has_overlay(content: &str) -> bool {
content
.lines()
.any(|line| line.split_whitespace().next_back() == Some("overlay"))
}
#[cfg(target_os = "linux")]
fn probe_overlayfs_rootfs_available(is_root: bool) -> bool {
if !is_root && !probe_has_cap_sys_admin() {
return false;
}
let Ok(content) = std::fs::read_to_string("/proc/filesystems") else {
return false;
};
if !proc_filesystems_has_overlay(&content) {
return false;
}
probe_overlay_mount_roundtrip()
}
#[cfg(not(target_os = "linux"))]
fn probe_overlayfs_rootfs_available(_is_root: bool) -> bool {
false
}
#[cfg(target_os = "linux")]
fn probe_overlay_mount_roundtrip() -> bool {
use nix::mount::{mount, umount2, MntFlags, MsFlags};
let Ok(base) = tempfile::Builder::new()
.prefix("zlayer-ovl-probe-")
.tempdir()
else {
return false;
};
let lower = base.path().join("lower");
let upper = base.path().join("upper");
let work = base.path().join("work");
let merged = base.path().join("merged");
for d in [&lower, &upper, &work, &merged] {
if std::fs::create_dir_all(d).is_err() {
return false;
}
}
let opts = format!(
"lowerdir={},upperdir={},workdir={}",
lower.display(),
upper.display(),
work.display()
);
let mounted = mount(
Some("overlay"),
&merged,
Some("overlay"),
MsFlags::empty(),
Some(opts.as_str()),
)
.is_ok();
if !mounted {
return false;
}
umount2(&merged, MntFlags::empty()).is_ok() || umount2(&merged, MntFlags::MNT_DETACH).is_ok()
}
#[cfg(target_os = "linux")]
fn which_in(name: &str, path_var: &str) -> Option<std::path::PathBuf> {
if name.is_empty() {
return None;
}
for dir in path_var.split(':').filter(|d| !d.is_empty()) {
let candidate = std::path::Path::new(dir).join(name);
if candidate.exists() {
return Some(candidate);
}
}
None
}
#[cfg(target_os = "linux")]
fn fuse_overlayfs_binary() -> Option<std::path::PathBuf> {
let path_var = std::env::var("PATH").ok()?;
which_in("fuse-overlayfs", &path_var)
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn fusermount_binary() -> Option<std::path::PathBuf> {
let path_var = std::env::var("PATH").ok()?;
which_in("fusermount3", &path_var).or_else(|| which_in("fusermount", &path_var))
}
#[cfg(not(target_os = "linux"))]
#[must_use]
pub fn fusermount_binary() -> Option<std::path::PathBuf> {
None
}
#[cfg(target_os = "linux")]
fn probe_dev_fuse_available() -> bool {
std::fs::OpenOptions::new()
.read(true)
.write(true)
.open("/dev/fuse")
.is_ok()
}
#[cfg(target_os = "linux")]
fn probe_fuse_overlayfs_rootfs_available() -> bool {
let Some(bin) = fuse_overlayfs_binary() else {
return false;
};
if !probe_dev_fuse_available() {
return false;
}
let Some(fusermount) = fusermount_binary() else {
return false;
};
probe_fuse_overlay_mount_roundtrip(&bin, &fusermount)
}
#[cfg(not(target_os = "linux"))]
fn probe_fuse_overlayfs_rootfs_available() -> bool {
false
}
#[cfg(target_os = "linux")]
fn probe_fuse_overlay_mount_roundtrip(bin: &std::path::Path, fusermount: &std::path::Path) -> bool {
let Ok(base) = tempfile::Builder::new()
.prefix("zlayer-fuse-ovl-probe-")
.tempdir()
else {
return false;
};
let lower = base.path().join("lower");
let upper = base.path().join("upper");
let work = base.path().join("work");
let merged = base.path().join("merged");
for d in [&lower, &upper, &work, &merged] {
if std::fs::create_dir_all(d).is_err() {
return false;
}
}
let opts = format!(
"lowerdir={},upperdir={},workdir={}",
lower.display(),
upper.display(),
work.display()
);
let mounted = std::process::Command::new(bin)
.arg("-o")
.arg(&opts)
.arg(&merged)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success());
if !mounted {
return false;
}
std::process::Command::new(fusermount)
.arg("-u")
.arg(&merged)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probe_does_not_panic_and_is_nested_agrees_with_cgroup_parent() {
let caps = DaemonCapabilities::probe();
assert_eq!(caps.is_nested, caps.cgroup_parent.is_some());
}
#[cfg(target_os = "linux")]
#[test]
fn probe_has_cap_net_admin_matches_cap_eff() {
let status = std::fs::read_to_string("/proc/self/status").unwrap();
let cap_eff_line = status
.lines()
.find(|l| l.starts_with("CapEff:"))
.expect("CapEff: present in /proc/self/status");
let hex = cap_eff_line.trim_start_matches("CapEff:").trim();
let eff: u64 = u64::from_str_radix(hex, 16).unwrap();
let expected = (eff & (1u64 << 12)) != 0;
assert_eq!(super::probe_has_cap_net_admin(), expected);
}
#[allow(clippy::fn_params_excessive_bools)]
fn classify(
is_nested: bool,
can_write_cgroup_root: bool,
has_cap_net_admin: bool,
tun_device_available: bool,
cgroup_parent_is_some: bool,
) -> DaemonMode {
if !is_nested && can_write_cgroup_root && has_cap_net_admin && tun_device_available {
DaemonMode::Full
} else if can_write_cgroup_root || cgroup_parent_is_some {
DaemonMode::NestedAdaptive
} else {
DaemonMode::Degraded
}
}
#[test]
fn effective_mode_full_requires_all_four_signals() {
assert_eq!(
classify(false, true, true, true, false),
DaemonMode::Full,
"all four signals set should be Full"
);
assert_ne!(classify(true, true, true, true, true), DaemonMode::Full);
assert_ne!(classify(false, false, true, true, false), DaemonMode::Full);
assert_ne!(classify(false, true, false, true, false), DaemonMode::Full);
assert_ne!(classify(false, true, true, false, false), DaemonMode::Full);
}
#[test]
fn effective_mode_nested_adaptive_when_writable_or_has_parent() {
assert_eq!(
classify(false, true, false, false, false),
DaemonMode::NestedAdaptive
);
assert_eq!(
classify(true, false, false, false, true),
DaemonMode::NestedAdaptive
);
}
#[test]
fn effective_mode_degraded_when_no_writable_path() {
assert_eq!(
classify(false, false, false, false, false),
DaemonMode::Degraded
);
assert_eq!(
classify(true, false, false, false, false),
DaemonMode::Degraded
);
}
#[test]
fn overlay_fallback_none_only_when_both_present() {
assert!(super::capability_overlay_fallback(true, true).is_none());
}
#[test]
fn overlay_fallback_reports_missing_cap_net_admin() {
let reason = super::capability_overlay_fallback(false, true).expect("should fall back");
assert!(reason.contains("CAP_NET_ADMIN"));
}
#[test]
fn overlay_fallback_reports_missing_tun() {
let reason = super::capability_overlay_fallback(true, false).expect("should fall back");
assert!(reason.contains("/dev/net/tun"));
}
#[test]
fn overlay_fallback_reports_both_missing() {
let reason = super::capability_overlay_fallback(false, false).expect("should fall back");
assert!(reason.contains("CAP_NET_ADMIN"));
assert!(reason.contains("/dev/net/tun"));
}
#[test]
fn rootless_overlay_requires_nonroot_no_cap_tun_and_pasta() {
assert!(super::can_rootless_overlay(false, false, true, true));
}
#[test]
fn rootless_overlay_rejected_when_root() {
assert!(!super::can_rootless_overlay(true, false, true, true));
}
#[test]
fn rootless_overlay_rejected_when_already_has_cap_net_admin() {
assert!(!super::can_rootless_overlay(false, true, true, true));
}
#[test]
fn rootless_overlay_rejected_without_tun() {
assert!(!super::can_rootless_overlay(false, false, false, true));
}
#[test]
fn rootless_overlay_rejected_without_pasta() {
assert!(!super::can_rootless_overlay(false, false, true, false));
}
#[test]
fn serializes_round_trip_via_serde_json() {
let caps = DaemonCapabilities::probe();
let json = serde_json::to_string(&caps).expect("serialize");
let parsed: DaemonCapabilities = serde_json::from_str(&json).expect("deserialize");
assert_eq!(parsed.is_root, caps.is_root);
assert_eq!(parsed.is_nested, caps.is_nested);
assert_eq!(parsed.cgroup_parent, caps.cgroup_parent);
assert_eq!(parsed.can_write_cgroup_root, caps.can_write_cgroup_root);
assert_eq!(parsed.has_cap_net_admin, caps.has_cap_net_admin);
assert_eq!(parsed.tun_device_available, caps.tun_device_available);
assert_eq!(
parsed.overlayfs_rootfs_available,
caps.overlayfs_rootfs_available
);
assert_eq!(
parsed.fuse_overlayfs_rootfs_available,
caps.fuse_overlayfs_rootfs_available
);
assert_eq!(parsed.effective_mode, caps.effective_mode);
}
#[cfg(target_os = "linux")]
mod fuse_probe {
use super::super::{fusermount_binary, which_in};
#[test]
fn which_in_finds_binary_in_first_matching_dir() {
let tmp = tempfile::tempdir().unwrap();
let bin = tmp.path().join("fuse-overlayfs");
std::fs::write(&bin, b"#!/bin/sh\n").unwrap();
let other = tempfile::tempdir().unwrap();
let path_var = format!("{}:{}", other.path().display(), tmp.path().display());
assert_eq!(
which_in("fuse-overlayfs", &path_var).as_deref(),
Some(bin.as_path())
);
}
#[test]
fn which_in_none_when_absent_and_ignores_empty_segments() {
let tmp = tempfile::tempdir().unwrap();
let path_var = format!(":{}:", tmp.path().display());
assert!(which_in("fuse-overlayfs", &path_var).is_none());
assert!(which_in("", &path_var).is_none());
}
#[test]
fn fusermount_binary_resolves_or_none_without_panic() {
if let Some(p) = fusermount_binary() {
assert!(
p.exists(),
"resolved fusermount must exist: {}",
p.display()
);
}
}
}
#[cfg(target_os = "linux")]
mod proc_filesystems {
use super::super::proc_filesystems_has_overlay;
#[test]
fn detects_overlay_as_nodev_filesystem() {
let content = "nodev\tsysfs\nnodev\ttmpfs\nnodev\toverlay\n\text4\n";
assert!(proc_filesystems_has_overlay(content));
}
#[test]
fn absent_when_overlay_not_listed() {
let content = "nodev\tsysfs\nnodev\ttmpfs\n\text4\n\txfs\n";
assert!(!proc_filesystems_has_overlay(content));
}
#[test]
fn does_not_match_substring_overlayfs() {
let content = "nodev\toverlayfs2\n\text4\n";
assert!(!proc_filesystems_has_overlay(content));
}
#[test]
fn empty_input_is_false() {
assert!(!proc_filesystems_has_overlay(""));
}
}
#[cfg(target_os = "linux")]
#[test]
fn overlay_probe_consistent_with_privilege() {
let caps = DaemonCapabilities::probe();
if caps.overlayfs_rootfs_available {
assert!(
caps.is_root || super::probe_has_cap_sys_admin(),
"overlay availability must imply root or CAP_SYS_ADMIN"
);
}
}
#[test]
fn daemon_mode_serde_uses_snake_case() {
assert_eq!(
serde_json::to_string(&DaemonMode::Full).unwrap(),
"\"full\""
);
assert_eq!(
serde_json::to_string(&DaemonMode::NestedAdaptive).unwrap(),
"\"nested_adaptive\""
);
assert_eq!(
serde_json::to_string(&DaemonMode::Degraded).unwrap(),
"\"degraded\""
);
}
#[cfg(target_os = "linux")]
mod target_parent {
use super::super::compute_target_parent;
#[test]
fn idempotent_when_already_under_init() {
assert_eq!(
compute_target_parent(
"/user.slice/user-1000.slice/user@1000.service/app.slice/run-p123.scope"
),
"/user.slice/user-1000.slice/user@1000.service/app.slice/run-p123.scope/containers"
);
assert_eq!(
compute_target_parent(
"/user.slice/user-1000.slice/user@1000.service/app.slice/run-p123.scope/init"
),
"/user.slice/user-1000.slice/user@1000.service/app.slice/run-p123.scope/containers"
);
assert_eq!(compute_target_parent("/foo/bar/"), "/foo/bar/containers");
assert_eq!(
compute_target_parent("/foo/bar/init"),
"/foo/bar/containers"
);
}
}
#[cfg(target_os = "linux")]
mod host_parent {
use super::super::{compute_host_container_parent, HOST_CONTAINER_ROOT};
#[test]
fn host_parent_is_top_level_and_outside_any_unit() {
assert_eq!(compute_host_container_parent(), "/zlayer/containers");
assert!(compute_host_container_parent().starts_with(HOST_CONTAINER_ROOT));
assert!(!compute_host_container_parent().contains("zlayer.service"));
assert!(!compute_host_container_parent().contains(".slice"));
}
}
#[cfg(target_os = "linux")]
mod host_cgroup_reap {
use super::super::remove_host_container_cgroup_at;
use std::fs;
#[test]
fn reaps_stale_empty_cgroup_tree_depth_first() {
let base = tempfile::tempdir().expect("tempdir");
let base_path = base.path().to_str().unwrap();
let id = "zata-storage-rep-1";
let leaf = base.path().join(format!("zlayer/containers/{id}"));
let child = leaf.join("child-scope");
fs::create_dir_all(&child).expect("create nested cgroup tree");
fs::write(leaf.join("cgroup.procs"), "").unwrap();
fs::write(child.join("cgroup.procs"), "").unwrap();
assert!(leaf.exists(), "precondition: stale leaf exists");
remove_host_container_cgroup_at(base_path, id);
assert!(
!leaf.exists(),
"stale cgroup leaf must be reaped (depth-first removal of children + leaf)"
);
assert!(
base.path().join("zlayer/containers").exists(),
"shared containers parent must survive"
);
}
#[test]
fn idempotent_when_leaf_absent() {
let base = tempfile::tempdir().expect("tempdir");
let base_path = base.path().to_str().unwrap();
remove_host_container_cgroup_at(base_path, "never-existed");
}
}
#[cfg(target_os = "linux")]
mod cgroup_parser {
use super::super::parse_cgroup_v2_line;
#[test]
fn parse_cgroup_v2_root_returns_none() {
assert_eq!(parse_cgroup_v2_line("0::/\n"), None);
}
#[test]
fn parse_cgroup_v2_path_returns_some() {
assert_eq!(
parse_cgroup_v2_line("0::/system.slice/forgejo-runner.service\n"),
Some("/system.slice/forgejo-runner.service".to_string())
);
}
#[test]
fn parse_cgroup_v2_hybrid_finds_v2_line() {
let input = "12:devices:/user.slice\n11:memory:/user.slice\n0::/foo\n";
assert_eq!(parse_cgroup_v2_line(input), Some("/foo".to_string()));
}
#[test]
fn parse_cgroup_v2_no_newline() {
assert_eq!(parse_cgroup_v2_line("0::/bar"), Some("/bar".to_string()));
}
#[test]
fn parse_cgroup_v2_missing_returns_none() {
assert_eq!(parse_cgroup_v2_line(""), None);
}
}
}