use eyre::Result; use log::info; use std::path::Path; use tokio::fs; pub async fn files(files: &[crate::File], root: &str) -> Result<()> { for file in files { let path = chroot(root, &file.path); let path = Path::new(&path); if let Some(parent) = path.parent() { fs::create_dir_all(parent).await?; } use crate::FileKind as K; match &file.kind { K::Content(content) => fs::write(path, content.as_bytes()).await?, K::Dir(true) => fs::create_dir(path).await?, K::Dir(false) => {} // shouldn't happen, but semantic is to ignore K::Symlink(tgt) => fs::symlink(tgt, path).await?, } match file.kind { K::Symlink(_) => {} _ => set_perms(path, file.mode).await?, } info!("created {}", file.path); } Ok(()) } pub async fn set_perms(path: impl AsRef, mode: Option) -> std::io::Result<()> { if let Some(mode) = mode.filter(|m| *m != 0) { use std::os::unix::fs::PermissionsExt; let mode = std::fs::Permissions::from_mode(mode); fs::set_permissions(path, mode).await?; } Ok(()) } pub fn chroot(root: &str, path: &str) -> String { format!("{root}/{}", path.trim_start_matches(|c| c == '/')) }