use anyhow::{Context, Result, bail};
use std::{
path::Path,
process::{Child, Command, Stdio},
sync::mpsc,
};
use crate::{PackerProgress, image::LayerBlob, overlay::merge_layers_into_streaming};
pub fn write_squashfs(
receiver: mpsc::Receiver<Result<LayerBlob>>,
total_layers: usize,
output: &Path,
squashfs_binpath: Option<&Path>,
) -> Result<()> {
write_squashfs_with_progress(receiver, total_layers, output, squashfs_binpath, None)
}
pub fn write_squashfs_with_progress(
receiver: mpsc::Receiver<Result<LayerBlob>>,
total_layers: usize,
output: &Path,
squashfs_binpath: Option<&Path>,
progress_tx: Option<std::sync::mpsc::SyncSender<PackerProgress>>,
) -> Result<()> {
if output.exists() {
std::fs::remove_file(output)
.with_context(|| format!("removing existing {}", output.display()))?;
}
let mut child = spawn_mksquashfs(output, squashfs_binpath)?;
let stdin = child.stdin.take().context("child stdin")?;
let merge_result =
merge_layers_into_streaming(receiver, total_layers, stdin, progress_tx.as_ref());
let exit = child.wait_with_output().context("waiting for mksquashfs")?;
if merge_result.is_err() {
let _ = std::fs::remove_file(output);
return merge_result.context("merging layers into mksquashfs stdin");
}
if !exit.status.success() {
let _ = std::fs::remove_file(output);
let stderr = String::from_utf8_lossy(&exit.stderr);
bail!("mksquashfs failed:\n{stderr}");
}
Ok(())
}
fn spawn_mksquashfs(output: &Path, binpath: Option<&Path>) -> Result<Child> {
let mut cmd = match binpath {
Some(p) => Command::new(p),
None => Command::new("mksquashfs"),
};
cmd.args([
"-",
output.to_str().context("output path is not UTF-8")?,
"-tar",
"-noappend",
"-no-fragments",
"-comp",
"zstd",
"-Xcompression-level",
"2",
"-quiet",
"-default-mode",
"0755",
"-default-uid",
"0",
"-default-gid",
"0",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.context("spawning mksquashfs — is it installed?")
}