summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2021-11-01 00:16:41 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2021-11-01 00:19:11 +0100
commite3ff2872701d9f1d6f4f5223641092ec19c10a7a (patch)
treef8c68fab29e7bbefee2e163cdc393491a5bab735
parentdab7a68951b29077b3ed43612e48f1bad92d37cd (diff)
downloadrebel-e3ff2872701d9f1d6f4f5223641092ec19c10a7a.tar
rebel-e3ff2872701d9f1d6f4f5223641092ec19c10a7a.zip
runner: detect file conflicts in dependencies again
The new code will also detect conflicts between the rootfs and individual dependencies.
-rw-r--r--Cargo.lock1
-rw-r--r--crates/runner/Cargo.toml3
-rw-r--r--crates/runner/src/task.rs67
3 files changed, 68 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b252090..3a8373c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -538,6 +538,7 @@ dependencies = [
"tar",
"tee_readwrite",
"uds",
+ "walkdir",
]
[[package]]
diff --git a/crates/runner/Cargo.toml b/crates/runner/Cargo.toml
index d8fe562..19ad124 100644
--- a/crates/runner/Cargo.toml
+++ b/crates/runner/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2021"
[dependencies]
common = { path = "../common", package = "rebel-common" }
+bincode = "1.3.3"
blake3 = { version = "1.0.0", features = ["traits-preview"] }
capctl = "0.2.0"
digest = "0.9.0"
@@ -21,4 +22,4 @@ serde_json = "1.0.62"
tar = "0.4.32"
tee_readwrite = "0.1.0"
uds = "0.2.6"
-bincode = "1.3.3"
+walkdir = "2.3.2"
diff --git a/crates/runner/src/task.rs b/crates/runner/src/task.rs
index 941e5f1..174768c 100644
--- a/crates/runner/src/task.rs
+++ b/crates/runner/src/task.rs
@@ -1,8 +1,8 @@
use std::{
- collections::{BTreeMap, HashMap},
+ collections::{BTreeMap, HashMap, HashSet},
io::{self, BufWriter},
os::unix::prelude::CommandExt,
- path::Path,
+ path::{Path, PathBuf},
process::{self, Command, Stdio},
time::Instant,
};
@@ -18,6 +18,7 @@ use serde::Serialize;
use tee_readwrite::{TeeReader, TeeWriter};
use common::{error::*, string_hash::*, types::*};
+use walkdir::WalkDir;
use super::{
jobserver::Jobserver,
@@ -126,6 +127,59 @@ fn init_task(input_hash: &InputHash, task: &Task) -> Result<fs::Mount> {
Ok(mount)
}
+fn get_contents<P1: AsRef<Path>, P2: AsRef<Path>>(
+ path: P1,
+ prefix: P2,
+) -> Result<(HashSet<PathBuf>, HashSet<PathBuf>)> {
+ let mut dirs = HashSet::new();
+ let mut files = HashSet::new();
+
+ let root: PathBuf = Path::new("/").join(prefix.as_ref());
+
+ for result in WalkDir::new(path.as_ref()).min_depth(1).into_iter() {
+ let entry = result
+ .with_context(|| format!("Failed to list contents of directory {:?}", path.as_ref()))?;
+ let is_dir = entry.file_type().is_dir();
+ let entry_path = root.join(entry.into_path().strip_prefix(path.as_ref()).unwrap());
+ if is_dir {
+ dirs.insert(entry_path);
+ } else {
+ files.insert(entry_path);
+ }
+ }
+
+ dirs.insert(root);
+
+ Ok((dirs, files))
+}
+
+fn check_conflicts(
+ dirs1: &HashSet<PathBuf>,
+ files1: &HashSet<PathBuf>,
+ dirs2: &HashSet<PathBuf>,
+ files2: &HashSet<PathBuf>,
+) -> Result<()> {
+ let mut conflicts = Vec::new();
+
+ conflicts.extend(files1.intersection(files2));
+ conflicts.extend(dirs1.intersection(files2));
+ conflicts.extend(files1.intersection(dirs2));
+
+ if !conflicts.is_empty() {
+ let mut conflict_strings: Box<[_]> = conflicts
+ .into_iter()
+ .map(|path| path.to_string_lossy().to_string())
+ .collect();
+ conflict_strings.sort();
+ return Err(Error::new(format!(
+ "Found the following file conflicts in dependencies:\n{}",
+ conflict_strings.join("\n")
+ )));
+ }
+
+ Ok(())
+}
+
fn init_task_rootfs(input_hash: &InputHash, depends: &DependMap) -> Result<Stack<fs::Mount>> {
let task_tmp_dir = paths::task_tmp_dir(input_hash);
let mount_target = paths::join(&[&task_tmp_dir, paths::TASK_TMP_ROOTFS_SUBDIR]);
@@ -143,6 +197,8 @@ fn init_task_rootfs(input_hash: &InputHash, depends: &DependMap) -> Result<Stack
.with_context(|| format!("Failed to bind mount rootfs to {:?}", mount_target))?,
);
+ let (mut dirs, mut files) = get_contents(&mount_target, "")?;
+
for (path, dep_hashes) in depends {
assert!(!dep_hashes.is_empty());
@@ -156,6 +212,13 @@ fn init_task_rootfs(input_hash: &InputHash, depends: &DependMap) -> Result<Stack
let dep_target = mount_target.clone() + path;
let dep_paths: Box<[_]> = dep_hashes.iter().map(paths::depend_dir).collect();
+ for dep in dep_paths.iter() {
+ let (dep_dirs, dep_files) = get_contents(dep, path)?;
+ check_conflicts(&dirs, &files, &dep_dirs, &dep_files)?;
+ dirs.extend(dep_dirs);
+ files.extend(dep_files);
+ }
+
let options = format!(
"xino=off,index=off,metacopy=off,lowerdir={lower}:{base}",
lower = dep_paths.join(":"),