use std::{ io::{self, Read, Write}, os::unix::prelude::CommandExt, path::Path, process::{self, Command, Stdio}, }; use nix::{ mount::{self, MsFlags}, sched::CloneFlags, sys::wait, }; use common::error::*; use super::{ ns, util::{checkable::Checkable, fs}, }; use crate::paths; pub fn pack>(archive: &mut W, source: P) -> Result<()> { let (mut piper, pipew) = fs::pipe()?; let exec_tar = || -> Result<()> { // We are in our own mount namespace, so mounting into the shared ROOTFS_DIR is fine let dev_target = paths::join(&[paths::ROOTFS_DIR, "dev"]); mount::mount::<_, _, str, str>( Some(paths::DEV_DIR), dev_target.as_str(), None, MsFlags::MS_BIND | MsFlags::MS_REC, None, )?; let mount_target = paths::join(&[paths::ROOTFS_DIR, paths::TASK_BUILDDIR]); mount::mount::<_, _, str, str>( Some(source.as_ref()), mount_target.as_str(), None, MsFlags::MS_BIND, None, )?; ns::pivot_root(paths::ROOTFS_DIR); let err = Command::new("tar") .args(&[ "-c", "--sort=name", "--numeric-owner", "--owner=0", "--group=0", "--mtime=@0", ".", ]) .stdin(Stdio::null()) .stdout(pipew) .current_dir(paths::TASK_BUILDDIR) .env_clear() .env("PATH", "/usr/sbin:/usr/bin:/sbin:/bin") .exec(); eprintln!("{}", err); process::exit(127); }; let pid = unsafe { ns::spawn(CloneFlags::CLONE_NEWNS, || exec_tar().unwrap()) } .context("Failed to run tar")?; let result = io::copy(&mut piper, archive).context("Failed to write TAR archive"); wait::waitpid(pid, None)? .check() .context("tar did not exit successfully")?; result?; Ok(()) } pub fn unpack>(archive: R, dest: P) -> Result<()> { fs::mkdir(&dest)?; let mut ar = tar::Archive::new(archive); ar.set_preserve_permissions(true); ar.set_preserve_mtime(true); ar.set_unpack_xattrs(true); ar.set_overwrite(false); ar.unpack(dest).context("Failed to unpack TAR archive") }