use std::path::Path;
use serde::{Deserialize, Serialize};
pub const ROOTFS_METADATA_PATH: &str = "/.a3s_rootfs_metadata_v1.json";
pub const IMAGE_ROOTFS_METADATA_PATH: &str = "/.a3s_image_metadata_v1.json";
pub const RUNTIME_ENV_PATH: &str = "/.a3s-box-env";
pub const ROOTFS_METADATA_SCHEMA: &str = "a3s.box.rootfs-metadata.v1";
pub fn runtime_managed_rootfs_mode(path: &Path) -> Option<u32> {
match path.to_str() {
Some("etc/hostname" | "etc/hosts" | "etc/resolv.conf") => Some(0o644),
Some("sbin/init" | "usr/sbin/init") => Some(0o755),
Some(".a3s-box-env") => Some(0o600),
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RootfsEntryKind {
Directory,
Regular,
Symlink,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RootfsMetadataEntry {
pub path_base64: String,
pub kind: RootfsEntryKind,
pub mode: u32,
pub uid: u64,
pub gid: u64,
pub mtime: u64,
pub size: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub link_target_base64: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RootfsMetadataManifest {
pub schema: String,
pub entries: Vec<RootfsMetadataEntry>,
}
impl RootfsMetadataManifest {
pub fn new(entries: Vec<RootfsMetadataEntry>) -> Self {
Self {
schema: ROOTFS_METADATA_SCHEMA.to_string(),
entries,
}
}
pub fn validate(&self) -> Result<(), String> {
if self.schema != ROOTFS_METADATA_SCHEMA {
return Err(format!(
"unsupported rootfs metadata schema: {}",
self.schema
));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn runtime_managed_rootfs_files_have_canonical_modes() {
for path in ["etc/hostname", "etc/hosts", "etc/resolv.conf"] {
assert_eq!(runtime_managed_rootfs_mode(Path::new(path)), Some(0o644));
}
for path in ["sbin/init", "usr/sbin/init"] {
assert_eq!(runtime_managed_rootfs_mode(Path::new(path)), Some(0o755));
}
assert_eq!(
runtime_managed_rootfs_mode(Path::new(".a3s-box-env")),
Some(0o600)
);
assert_eq!(runtime_managed_rootfs_mode(Path::new("etc/passwd")), None);
}
}