use eyre::Result; use log::info; use std::path::Path; use tokio::fs; use crate::base64_decode; pub async fn files(files: &[crate::File], root: &str, dry_run: bool) -> Result<()> { for file in files { let path = chroot(root, &file.path); let path = Path::new(&path); if !dry_run && let Some(parent) = path.parent() { fs::create_dir_all(parent).await?; } use crate::FileKind as K; match &file.kind { K::Content(content) => { if dry_run { info!( "would create {} ({} bytes from content)", file.path, content.len() ); } else { fs::write(path, content.as_bytes()).await?; } } K::Content64(content) => { let content = base64_decode(content)?; if dry_run { info!( "would create {} ({} bytes from content64)", file.path, content.len() ); } else { fs::write(path, content).await? } } K::Dir(true) => { if dry_run { info!("would create {} (directory)", file.path); } else { fs::create_dir(path).await?; } } K::Dir(false) => {} // shouldn't happen, but semantic is to ignore K::Symlink(tgt) => { if dry_run { info!("would create {} (symlink to {})", file.path, tgt); } else { fs::symlink(tgt, path).await?; } } } if dry_run { continue; } 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 == '/')) }