summaryrefslogtreecommitdiffstats
path: root/crates/runner/src/tar.rs
blob: 10cac9264b50a5d561a189123c3e2afa56d673a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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 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")
}