summaryrefslogtreecommitdiffstats
path: root/crates/rebel-runner/src/tar.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/rebel-runner/src/tar.rs')
-rw-r--r--crates/rebel-runner/src/tar.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/crates/rebel-runner/src/tar.rs b/crates/rebel-runner/src/tar.rs
new file mode 100644
index 0000000..891c603
--- /dev/null
+++ b/crates/rebel-runner/src/tar.rs
@@ -0,0 +1,105 @@
+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 rebel_common::{error::*, string_hash::ArchiveHash};
+
+use super::{
+ ns,
+ util::{checkable::Checkable, fs},
+};
+use crate::paths;
+
+pub fn pack<W: Write, P: AsRef<Path>>(
+ 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::<str, str, str, str>(
+ 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<R: Read, P: AsRef<Path>>(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")
+}