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::*, string_hash::ArchiveHash}; use super::{ ns, util::{checkable::Checkable, fs}, }; use crate::paths; pub fn pack>( rootfs_hash: &ArchiveHash, archive: &mut W, source: P, ) -> Result<()> { let rootfs = paths::depend_dir(rootfs_hash); let _rootfs_mount = fs::mount(&rootfs, &rootfs, None, MsFlags::MS_BIND, None) .with_context(|| format!("Failed to bind mount rootfs to {:?}", rootfs))?; mount::mount::( None, &rootfs, None, MsFlags::MS_REMOUNT | MsFlags::MS_BIND | MsFlags::MS_RDONLY, None, ) .context("Failed to mount container rootfs read-only")?; let (mut piper, pipew) = fs::pipe()?; let exec_tar = || -> Result<()> { // We are in our own mount namespace, so mounting into the shared rootfs is fine let dev_target = paths::join(&[&rootfs, "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(&[&rootfs, paths::TASK_BUILDDIR]); mount::mount::<_, _, str, str>( Some(source.as_ref()), mount_target.as_str(), None, MsFlags::MS_BIND, None, )?; ns::pivot_root(&rootfs); 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") }