//! A minimal `newc` cpio writer for the per-job initramfs: regular files //! at the archive root, owned by root. pub struct Entry<'a> { pub name: &'a str, pub mode: u32, pub data: &'a [u8], } fn header(out: &mut Vec, ino: u32, mode: u32, size: usize, name: &str) { let fields = [ino, mode, 0, 0, 1, 0, size as u32, 0, 0, 0, 0, (name.len() + 1) as u32, 0]; out.extend_from_slice(b"070701"); for f in fields { out.extend_from_slice(format!("{f:08X}").as_bytes()); } out.extend_from_slice(name.as_bytes()); out.push(0); pad(out); } fn pad(out: &mut Vec) { while !out.len().is_multiple_of(4) { out.push(0); } } pub fn newc(entries: &[Entry]) -> Vec { let mut out = Vec::new(); for (i, e) in entries.iter().enumerate() { header(&mut out, i as u32 + 1, 0o100000 | e.mode, e.data.len(), e.name); out.extend_from_slice(e.data); pad(&mut out); } header(&mut out, 0, 0, 0, "TRAILER!!!"); out } #[cfg(test)] mod tests { use super::*; #[test] fn system_cpio_reads_it() { let dir = tempfile::tempdir().unwrap(); let archive = newc(&[Entry { name: "init", mode: 0o755, data: b"#!x\n" }, Entry { name: "job.json", mode: 0o644, data: b"{}" }]); std::fs::write(dir.path().join("a.cpio"), &archive).unwrap(); let listed = std::process::Command::new("cpio").args(["-it", "--quiet", "-F"]).arg(dir.path().join("a.cpio")).output(); if let Ok(o) = listed { if o.status.success() { assert_eq!(String::from_utf8_lossy(&o.stdout), "init\njob.json\n"); } } assert!(archive.len().is_multiple_of(4)); assert!(archive.windows(10).any(|w| w == b"TRAILER!!!")); } }