pelagos 0.59.2

Fast Linux container runtime — OCI-compatible, namespaces, cgroups v2, seccomp, networking, image management
Documentation
//! `pelagos rootfs` — manage the local rootfs store.

use super::rootfs_store;
use std::os::unix::fs::symlink;

pub fn cmd_rootfs_import(name: &str, path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let store = rootfs_store();
    std::fs::create_dir_all(&store)?;

    // Resolve path to absolute.
    let src =
        std::fs::canonicalize(path).map_err(|e| format!("cannot resolve '{}': {}", path, e))?;

    let link = store.join(name);
    if link.exists() || link.is_symlink() {
        std::fs::remove_file(&link)
            .map_err(|e| format!("remove existing '{}': {}", link.display(), e))?;
    }

    symlink(&src, &link)
        .map_err(|e| format!("symlink {} -> {}: {}", link.display(), src.display(), e))?;

    println!("Imported rootfs '{}' → {}", name, src.display());
    Ok(())
}

pub fn cmd_rootfs_ls(json: bool) -> Result<(), Box<dyn std::error::Error>> {
    let store = rootfs_store();
    let entries = match std::fs::read_dir(&store) {
        Ok(e) => e,
        Err(_) => {
            if json {
                println!("[]");
            } else {
                println!("No rootfs store found at {}", store.display());
            }
            return Ok(());
        }
    };

    let mut items: Vec<(String, String)> = Vec::new();
    for entry in entries.flatten() {
        let name = entry.file_name().to_string_lossy().into_owned();
        let target = std::fs::read_link(entry.path())
            .map(|p| p.to_string_lossy().into_owned())
            .unwrap_or_else(|_| entry.path().to_string_lossy().into_owned());
        items.push((name, target));
    }
    items.sort_by(|a, b| a.0.cmp(&b.0));

    if json {
        #[derive(serde::Serialize)]
        struct RootfsInfo {
            name: String,
            path: String,
        }
        let infos: Vec<RootfsInfo> = items
            .iter()
            .map(|(n, p)| RootfsInfo {
                name: n.clone(),
                path: p.clone(),
            })
            .collect();
        println!("{}", serde_json::to_string_pretty(&infos)?);
        return Ok(());
    }

    if items.is_empty() {
        println!("No rootfs images. Use: pelagos rootfs import <name> <path>");
        return Ok(());
    }

    let name_w = items.iter().map(|(n, _)| n.len()).max().unwrap_or(4).max(4);
    println!("{:<name_w$}  PATH", "NAME", name_w = name_w);
    for (name, target) in &items {
        println!("{:<name_w$}  {}", name, target, name_w = name_w);
    }
    Ok(())
}

pub fn cmd_rootfs_rm(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    let link = rootfs_store().join(name);
    if !link.exists() && !link.is_symlink() {
        return Err(format!("rootfs '{}' not found", name).into());
    }
    std::fs::remove_file(&link).map_err(|e| format!("remove '{}': {}", link.display(), e))?;
    println!("Removed rootfs '{}'", name);
    Ok(())
}