Skip to content

Commit c2c9cc6

Browse files
committed
mkdir: do not ignore EEXIST on non-recursive creation
Fixes uutils#13970 Signed-off-by: Alex Chen <l46983284@gmail.com>
1 parent 1a36740 commit c2c9cc6

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

‎src/uu/mkdir/src/mkdir.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ fn create_single_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<(
353353
Ok(())
354354
}
355355

356-
Err(_) if path.is_dir() => {
356+
Err(_) if config.recursive && path.is_dir() => {
357357
// Directory already exists - check if this is a logical directory creation
358358
// (i.e., not just a parent reference like "test_dir/..")
359359
let ends_with_parent_dir = matches!(

‎tests/by-util/test_mkdir.rs‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,3 +1134,55 @@ mod diagnostics {
11341134
assert!(!stderr.contains(":1:"), "{stderr}");
11351135
}
11361136
}
1137+
1138+
#[test]
1139+
fn test_mkdir_concurrent_non_recursive() {
1140+
// Test concurrent mkdir operations without -p: exactly one process must succeed per round
1141+
use std::sync::Arc;
1142+
use std::sync::atomic::{AtomicUsize, Ordering};
1143+
use std::thread;
1144+
1145+
for round in 0..10 {
1146+
let scene = TestScenario::new(util_name!());
1147+
let target_dir = scene.fixtures.plus(format!("concurrent_target_{round}"));
1148+
let path_str = target_dir.to_string_lossy().to_string();
1149+
let bin_path = scene.bin_path.clone();
1150+
1151+
let winners = Arc::new(AtomicUsize::new(0));
1152+
let mut handles = vec![];
1153+
1154+
for _ in 0..16 {
1155+
let path_clone = path_str.clone();
1156+
let bin_path_clone = bin_path.clone();
1157+
let winners_clone = Arc::clone(&winners);
1158+
1159+
let handle = thread::spawn(move || {
1160+
let result = std::process::Command::new(&bin_path_clone)
1161+
.arg("mkdir")
1162+
.arg(&path_clone)
1163+
.current_dir(std::env::current_dir().unwrap())
1164+
.output()
1165+
.expect("failed to run binary");
1166+
if result.status.success() {
1167+
winners_clone.fetch_add(1, Ordering::SeqCst);
1168+
}
1169+
});
1170+
handles.push(handle);
1171+
}
1172+
1173+
for h in handles {
1174+
h.join().unwrap();
1175+
}
1176+
1177+
assert_eq!(
1178+
winners.load(Ordering::SeqCst),
1179+
1,
1180+
"round {round}: expected exactly 1 winner for concurrent non-recursive mkdir"
1181+
);
1182+
assert!(
1183+
scene
1184+
.fixtures
1185+
.dir_exists(format!("concurrent_target_{round}"))
1186+
);
1187+
}
1188+
}

0 commit comments

Comments
 (0)