Skip to content

Commit

Permalink
tests: ensure Root::mkdir_all races don't return EEXIST
Browse files Browse the repository at this point in the history
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
  • Loading branch information
cyphar committed Dec 8, 2024
1 parent 47d117b commit 2aa9d4d
Showing 1 changed file with 95 additions and 2 deletions.
97 changes: 95 additions & 2 deletions src/tests/test_root_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,66 @@ macro_rules! root_op_tests {
}
};


($(#[cfg($ignore_meta:meta)])* @impl mkdir_all_racing [#$num_threads:expr] $test_name:ident ($path:expr, $mode:expr) => $expected_result:expr) => {
paste::paste! {
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
fn [<$test_name _ $num_threads threads>](root) {
utils::check_root_mkdir_all_racing($num_threads, &root, $path, Permissions::from_mode($mode), $expected_result)
}
}
}
};

($(#[cfg($ignore_meta:meta)])* @impl mkdir_all_racing $test_name:ident ( $($args:tt)* ) => $expected_result:expr) => {
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#2] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#4] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#8] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#16] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#32] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#64] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#128] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#256] $test_name( $($args)* ) => $expected_result
}
root_op_tests! {
$(#[cfg_attr(not($ignore_meta), ignore)])*
@impl mkdir_all_racing [#512] $test_name( $($args)* ) => $expected_result
}
};

// root_tests!{
// ...
// }
($($(#[cfg($ignore_meta:meta)])* $test_name:ident: $file_type:ident ( $($args:expr),* ) => $expected_result:expr );+ $(;)?) => {
($($(#[cfg($ignore_meta:meta)])* $test_name:ident: $file_type:ident ( $($args:tt)* ) => $expected_result:expr );+ $(;)?) => {
paste::paste! {
$(
root_op_tests!{
$(#[cfg($ignore_meta)])*
@impl $file_type [<$file_type _ $test_name>]( $($args),* ) => $expected_result
@impl $file_type [<$file_type _ $test_name>]( $($args)* ) => $expected_result
}
)*
}
Expand Down Expand Up @@ -437,6 +488,10 @@ root_op_tests! {
setgid_selfdir: mkdir_all("setgid-self/a/b/c/d", 0o711) => Ok(());
#[cfg(feature = "_test_as_root")]
setgid_otherdir: mkdir_all("setgid-other/a/b/c/d", 0o711) => Ok(());

// Check that multiple mkdir_alls racing against each other will not result
// in a spurious error. <https://github.com/opencontainers/runc/issues/4543>
plain: mkdir_all_racing("a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z", 0o711) => Ok(());
}

mod utils {
Expand All @@ -460,6 +515,8 @@ mod utils {
io::{AsFd, OwnedFd},
},
path::{Path, PathBuf},
sync::{Arc, Barrier},
thread,
};

use anyhow::{Context, Error};
Expand Down Expand Up @@ -920,4 +977,40 @@ mod utils {

Ok(())
}

pub(super) fn check_root_mkdir_all_racing<R: RootImpl + Sync, P: AsRef<Path>>(
num_threads: usize,
root: R,
unsafe_path: P,
perm: Permissions,
expected_result: Result<(), ErrorKind>,
) -> Result<(), Error> {
let root = &root;
let unsafe_path = unsafe_path.as_ref();

// Do lots of runs to try to catch any possible races.
let num_retries = if num_threads > 32 {
2_000 / (num_threads >> 5)
} else {
20_000 / num_threads
};
for _ in 0..num_retries {
thread::scope(|s| {
let start_barrier = Arc::new(Barrier::new(num_threads));
for _ in 0..num_threads {
let barrier = Arc::clone(&start_barrier);
let perm = perm.clone();
s.spawn(move || {
barrier.wait();
let res = root
.mkdir_all(unsafe_path, &perm)
.with_wrap(|| format!("mkdir_all {unsafe_path:?}"));
tests_common::check_err(&res, &expected_result).expect("unexpected result");
});
}
});
}

Ok(())
}
}

0 comments on commit 2aa9d4d

Please sign in to comment.