#![allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
#[cfg(not(windows))]
use libc::mode_t;
#[cfg(not(windows))]
use std::os::unix::fs::PermissionsExt;
#[cfg(feature = "feat_selinux")]
use uucore::selinux::get_getfattr_output;
#[cfg(not(windows))]
use uutests::at_and_ucmd;
use uutests::new_ucmd;
use uutests::util::TestScenario;
use uutests::util_name;
#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
}
#[test]
fn test_version_no_path() {
use std::process::Command;
use uutests::get_tests_binary;
let tests_binary = get_tests_binary!();
let mkdir_binary_path = std::path::Path::new(tests_binary)
.parent()
.unwrap()
.join("mkdir");
let output = if mkdir_binary_path.exists() {
Command::new(&mkdir_binary_path)
.arg("--version")
.output()
.expect("Failed to execute mkdir binary")
} else {
Command::new(tests_binary)
.args(["mkdir", "--version"])
.output()
.expect("Failed to execute mkdir via multicall binary")
};
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.starts_with("mkdir (uutils coreutils)"));
}
#[test]
fn test_no_arg() {
new_ucmd!()
.fails_with_code(1)
.stderr_contains("error: the following required arguments were not provided:");
}
#[test]
fn test_mkdir_mkdir() {
new_ucmd!().arg("test_dir").succeeds();
}
#[cfg(feature = "test_risky_names")]
#[test]
fn test_mkdir_non_unicode() {
let (at, mut ucmd) = at_and_ucmd!();
let target = uucore::os_str_from_bytes(b"some-\xc0-dir-\xf3")
.expect("Only unix platforms can test non-unicode names");
ucmd.arg(&target).succeeds();
assert!(at.dir_exists(target));
}
#[test]
fn test_mkdir_verbose() {
let expected = "mkdir: created directory 'test_dir'\n";
new_ucmd!()
.arg("test_dir")
.arg("-v")
.succeeds()
.stdout_is(expected);
}
#[test]
fn test_mkdir_dup_dir() {
let scene = TestScenario::new(util_name!());
let test_dir = "test_dir";
scene.ucmd().arg(test_dir).succeeds();
scene.ucmd().arg(test_dir).fails();
}
#[test]
fn test_mkdir_mode() {
new_ucmd!().arg("-m").arg("755").arg("test_dir").succeeds();
}
#[test]
fn test_mkdir_parent() {
let scene = TestScenario::new(util_name!());
let test_dir = "parent_dir/child_dir";
scene.ucmd().arg("-p").arg(test_dir).succeeds();
scene.ucmd().arg("-p").arg("-p").arg(test_dir).succeeds();
scene.ucmd().arg("--parent").arg(test_dir).succeeds();
scene
.ucmd()
.arg("--parent")
.arg("--parent")
.arg(test_dir)
.succeeds();
scene.ucmd().arg("--parents").arg(test_dir).succeeds();
scene
.ucmd()
.arg("--parents")
.arg("--parents")
.arg(test_dir)
.succeeds();
}
#[test]
fn test_mkdir_no_parent() {
new_ucmd!().arg("parent_dir/child_dir").fails();
}
#[test]
fn test_mkdir_dup_dir_parent() {
let scene = TestScenario::new(util_name!());
let test_dir = "test_dir";
scene.ucmd().arg(test_dir).succeeds();
scene.ucmd().arg("-p").arg(test_dir).succeeds();
}
#[cfg(not(windows))]
#[test]
fn test_mkdir_parent_mode() {
let (at, mut ucmd) = at_and_ucmd!();
let default_umask: mode_t = 0o160;
ucmd.arg("-p")
.arg("a/b")
.umask(default_umask)
.succeeds()
.no_stderr()
.no_stdout();
assert!(at.dir_exists("a"));
assert_eq!(
at.metadata("a").permissions().mode() as mode_t,
((!default_umask & 0o777) | 0o300) + 0o40000
);
assert!(at.dir_exists("a/b"));
assert_eq!(
at.metadata("a/b").permissions().mode() as mode_t,
(!default_umask & 0o777) + 0o40000
);
}
#[cfg(not(windows))]
#[test]
fn test_mkdir_parent_mode_check_existing_parent() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
let parent_a_perms = at.metadata("a").permissions().mode();
let default_umask: mode_t = 0o160;
ucmd.arg("-p")
.arg("a/b/c")
.umask(default_umask)
.succeeds()
.no_stderr()
.no_stdout();
assert!(at.dir_exists("a"));
assert_eq!(at.metadata("a").permissions().mode(), parent_a_perms);
assert!(at.dir_exists("a/b"));
assert_eq!(
at.metadata("a/b").permissions().mode() as mode_t,
((!default_umask & 0o777) | 0o300) + 0o40000
);
assert!(at.dir_exists("a/b/c"));
assert_eq!(
at.metadata("a/b/c").permissions().mode() as mode_t,
(!default_umask & 0o777) + 0o40000
);
}
#[cfg(not(windows))]
#[test]
fn test_mkdir_parent_mode_skip_existing_last_component_chmod() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
at.mkdir("a/b");
at.set_mode("a/b", 0);
let default_umask: mode_t = 0o160;
ucmd.arg("-p")
.arg("a/b")
.umask(default_umask)
.succeeds()
.no_stderr()
.no_stdout();
assert_eq!(at.metadata("a/b").permissions().mode() as mode_t, 0o40000);
}
#[test]
fn test_mkdir_dup_file() {
let scene = TestScenario::new(util_name!());
let test_file = "test_file.txt";
scene.fixtures.touch(test_file);
scene.ucmd().arg(test_file).fails();
scene.ucmd().arg("-p").arg(test_file).fails();
}
#[test]
#[cfg(not(windows))]
fn test_symbolic_mode() {
let (at, mut ucmd) = at_and_ucmd!();
let test_dir = "test_dir";
ucmd.arg("-m").arg("a=rwx").arg(test_dir).succeeds();
let perms = at.metadata(test_dir).permissions().mode();
assert_eq!(perms, 0o40777);
}
#[test]
#[cfg(not(windows))]
fn test_symbolic_alteration() {
let (at, mut ucmd) = at_and_ucmd!();
let test_dir = "test_dir";
let default_umask = 0o022;
ucmd.arg("-m")
.arg("-w")
.arg(test_dir)
.umask(default_umask)
.succeeds();
let perms = at.metadata(test_dir).permissions().mode();
assert_eq!(perms, 0o40577);
}
#[test]
#[cfg(not(windows))]
fn test_multi_symbolic() {
let (at, mut ucmd) = at_and_ucmd!();
let test_dir = "test_dir";
ucmd.arg("-m").arg("u=rwx,g=rx,o=").arg(test_dir).succeeds();
let perms = at.metadata(test_dir).permissions().mode();
assert_eq!(perms, 0o40750);
}
#[test]
fn test_recursive_reporting() {
let test_dir = "test_dir/test_dir_a/test_dir_b";
new_ucmd!()
.arg("-p")
.arg("-v")
.arg(test_dir)
.succeeds()
.stdout_contains("created directory 'test_dir'")
.stdout_contains("created directory 'test_dir/test_dir_a'")
.stdout_contains("created directory 'test_dir/test_dir_a/test_dir_b'");
new_ucmd!().arg("-v").arg(test_dir).fails().no_stdout();
let test_dir = "test_dir/../test_dir_a/../test_dir_b";
new_ucmd!()
.arg("-p")
.arg("-v")
.arg(test_dir)
.succeeds()
.stdout_contains("created directory 'test_dir'")
.stdout_contains("created directory 'test_dir/../test_dir_a'")
.stdout_contains("created directory 'test_dir/../test_dir_a/../test_dir_b'");
}
#[test]
#[cfg(target_os = "linux")]
fn test_mkdir_acl() {
use std::{collections::HashMap, ffi::OsString};
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
let mut map: HashMap<OsString, Vec<u8>> = HashMap::new();
let xattr_val: Vec<u8> = vec![
2, 0, 0, 0, 1, 0, 7, 0, 255, 255, 255, 255, 4, 0, 7, 0, 255, 255, 255, 255, 32, 0, 5, 0,
255, 255, 255, 255,
];
map.insert(OsString::from("system.posix_acl_default"), xattr_val);
uucore::fsxattr::apply_xattrs(at.plus("a"), map).unwrap();
ucmd.arg("-p").arg("a/b").umask(0x077).succeeds();
let perms = at.metadata("a/b").permissions().mode();
assert_eq!(perms, 16893);
}
#[test]
fn test_mkdir_trailing_dot() {
new_ucmd!().arg("-p").arg("-v").arg("test_dir").succeeds();
new_ucmd!()
.arg("-p")
.arg("-v")
.arg("test_dir_a/.")
.succeeds()
.stdout_contains("created directory 'test_dir_a'");
new_ucmd!()
.arg("-p")
.arg("-v")
.arg("test_dir_b/..")
.succeeds()
.stdout_contains("created directory 'test_dir_b'");
let scene = TestScenario::new("ls");
let result = scene.ucmd().arg("-al").run();
println!("ls dest {}", result.stdout_str());
}
#[test]
fn test_mkdir_trailing_dot_and_slash() {
new_ucmd!().arg("-p").arg("-v").arg("test_dir").succeeds();
new_ucmd!()
.arg("-p")
.arg("-v")
.arg("test_dir_a/./")
.succeeds()
.stdout_contains("created directory 'test_dir_a'");
let scene = TestScenario::new("ls");
let result = scene.ucmd().arg("-al").run();
println!("ls dest {}", result.stdout_str());
}
#[test]
fn test_mkdir_trailing_spaces_and_dots() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene.ucmd().arg("-p").arg("test ").succeeds();
assert!(at.dir_exists("test "));
scene.ucmd().arg("-p").arg("test ").succeeds();
assert!(at.dir_exists("test "));
scene.ucmd().arg("-p").arg(".hidden").succeeds();
assert!(at.dir_exists(".hidden"));
scene.ucmd().arg("-p").arg("test.").succeeds();
assert!(at.dir_exists("test."));
scene.ucmd().arg("-p").arg("...test").succeeds();
assert!(at.dir_exists("...test"));
}
#[test]
#[cfg(not(windows))]
fn test_umask_compliance() {
fn test_single_case(umask_set: mode_t) {
let test_dir = "test_dir";
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(test_dir).umask(umask_set).succeeds();
let perms = at.metadata(test_dir).permissions().mode() as mode_t;
assert_eq!(perms, (!umask_set & 0o0777) + 0o40000); }
for i in 0o0..0o027 {
test_single_case(i as mode_t);
}
}
#[test]
fn test_empty_argument() {
new_ucmd!()
.arg("")
.fails()
.stderr_only("mkdir: cannot create directory '': No such file or directory\n");
}
#[test]
#[cfg(feature = "feat_selinux")]
fn test_selinux() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dest = "test_dir_a";
let args = ["-Z", "--context=unconfined_u:object_r:user_tmp_t:s0"];
for arg in args {
new_ucmd!()
.arg(arg)
.arg("-v")
.arg(at.plus_as_string(dest))
.succeeds()
.stdout_contains("created directory");
let context_value = get_getfattr_output(&at.plus_as_string(dest));
assert!(
context_value.contains("unconfined_u"),
"Expected '{}' not found in getfattr output:\n{}",
"unconfined_u",
context_value
);
at.rmdir(dest);
}
}
#[test]
#[cfg(feature = "feat_selinux")]
fn test_selinux_invalid() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dest = "test_dir_a";
new_ucmd!()
.arg("--context=testtest")
.arg(at.plus_as_string(dest))
.fails()
.no_stdout()
.stderr_contains("failed to set default file creation context to 'testtest':");
assert!(!at.dir_exists(dest));
}
#[test]
fn test_mkdir_deep_nesting() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let depth = 350;
let dir_name = "d";
let mut path = std::path::PathBuf::new();
for _ in 0..depth {
path.push(dir_name);
}
scene.ucmd().arg("-p").arg(&path).succeeds();
assert!(at.dir_exists(&path));
}
#[test]
fn test_mkdir_dot_components() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let path = "test/././test2/././test3/././test4";
scene.ucmd().arg("-p").arg(path).succeeds();
assert!(at.dir_exists("test/test2/test3/test4"));
scene
.ucmd()
.arg("-p")
.arg("./test_dot/test_dot2")
.succeeds();
assert!(at.dir_exists("test_dot/test_dot2"));
scene
.ucmd()
.arg("-p")
.arg("mixed/./normal/./path")
.succeeds();
assert!(at.dir_exists("mixed/normal/path"));
}
#[test]
fn test_mkdir_parent_dir_components() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir("base");
at.mkdir("base/child");
scene
.ucmd()
.arg("-p")
.arg("base/child/../sibling")
.succeeds();
assert!(at.dir_exists("base/sibling"));
scene
.ucmd()
.arg("-p")
.arg("base/child/../../other")
.succeeds();
assert!(at.dir_exists("other"));
scene
.ucmd()
.arg("-p")
.arg("base/child/../sibling")
.succeeds();
assert!(at.dir_exists("base/sibling"));
}
#[test]
fn test_mkdir_mixed_special_components() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene
.ucmd()
.arg("-p")
.arg("./start/./middle/../end/./final")
.succeeds();
assert!(at.dir_exists("start/end/final"));
}
#[test]
fn test_mkdir_control_characters() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.ucmd().arg("-p").arg("test\nname").run();
if result.succeeded() {
assert!(at.dir_exists("test\nname"));
}
let result = scene.ucmd().arg("-p").arg("test\tname").run();
if result.succeeded() {
assert!(at.dir_exists("test\tname"));
}
scene.ucmd().arg("-p").arg("test name").succeeds();
assert!(at.dir_exists("test name"));
#[cfg(unix)]
{
scene.ucmd().arg("-pv").arg("a/\"\"/b/c").succeeds();
assert!(at.dir_exists("a/\"\"/b/c"));
}
#[cfg(windows)]
{
let result = scene.ucmd().arg("-pv").arg("a/\"\"/b/c").run();
assert!(at.dir_exists("a"));
assert!(!at.dir_exists("a/\"\""));
assert!(!result.succeeded());
}
scene.ucmd().arg("-p").arg("a/''/b/c").succeeds();
assert!(at.dir_exists("a/''/b/c"));
}
#[test]
fn test_mkdir_maximum_path_length() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let long_path = "a".repeat(50) + "/" + &"b".repeat(50) + "/" + &"c".repeat(50);
scene.ucmd().arg("-p").arg(&long_path).succeeds();
assert!(at.dir_exists(&long_path));
let longer_path = "x".repeat(100) + "/" + &"y".repeat(50) + "/" + &"z".repeat(30);
scene.ucmd().arg("-p").arg(&longer_path).succeeds();
assert!(at.dir_exists(&longer_path));
let very_long_path = "very_long_directory_name_".repeat(20) + "/final";
let result = scene.ucmd().arg("-p").arg(&very_long_path).run();
if result.succeeded() {
assert!(at.dir_exists(&very_long_path));
}
}
#[test]
fn test_mkdir_reserved_device_names() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.ucmd().arg("-p").arg("CON").run();
if result.succeeded() {
assert!(at.dir_exists("CON"));
}
let result = scene.ucmd().arg("-p").arg("PRN").run();
if result.succeeded() {
assert!(at.dir_exists("PRN"));
}
let result = scene.ucmd().arg("-p").arg("AUX").run();
if result.succeeded() {
assert!(at.dir_exists("AUX"));
}
let result = scene.ucmd().arg("-p").arg("COM1").run();
if result.succeeded() {
assert!(at.dir_exists("COM1"));
}
let result = scene.ucmd().arg("-p").arg("LPT1").run();
if result.succeeded() {
assert!(at.dir_exists("LPT1"));
}
}
#[test]
fn test_mkdir_case_sensitivity() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene.ucmd().arg("-p").arg("CaseTest").succeeds();
assert!(at.dir_exists("CaseTest"));
let result = scene.ucmd().arg("-p").arg("casetest").run();
if result.succeeded() {
assert!(at.dir_exists("CaseTest"));
assert!(at.dir_exists("casetest"));
} else {
assert!(at.dir_exists("CaseTest"));
}
scene.ucmd().arg("-p").arg("CASETEST").succeeds();
scene.ucmd().arg("-p").arg("caseTEST").succeeds();
}
#[test]
fn test_mkdir_network_paths() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.ucmd().arg("-p").arg("//server/share/test").run();
if result.succeeded() {
assert!(at.dir_exists("//server/share/test"));
}
scene.ucmd().arg("-p").arg("server_share_test").succeeds();
assert!(at.dir_exists("server_share_test"));
scene.ucmd().arg("-p").arg("test//double//slash").succeeds();
assert!(at.dir_exists("test//double//slash"));
}
#[test]
fn test_mkdir_environment_expansion() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
unsafe {
std::env::set_var("TEST_VAR", "expanded_value");
}
scene.ucmd().arg("-p").arg("$TEST_VAR/dir").succeeds();
assert!(at.dir_exists("$TEST_VAR/dir"));
assert!(!at.dir_exists("expanded_value/dir"));
scene
.ucmd()
.arg("-p")
.arg("${TEST_VAR}_braced/dir")
.succeeds();
assert!(at.dir_exists("${TEST_VAR}_braced/dir"));
scene.ucmd().arg("-p").arg("~/test_dir").succeeds();
assert!(at.dir_exists("~/test_dir"));
unsafe {
std::env::remove_var("TEST_VAR");
}
}
#[test]
fn test_mkdir_concurrent_creation() {
use std::thread;
for _ in 0..10 {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let mut dir = at.plus("concurrent_test");
dir.push("a");
for _ in 0..40 {
dir.push("a");
}
let path_str = dir.to_string_lossy().to_string();
let bin_path = scene.bin_path.clone();
let mut handles = vec![];
for _ in 0..8 {
let path_clone = path_str.clone();
let bin_path_clone = bin_path.clone();
let handle = thread::spawn(move || {
let result = std::process::Command::new(&bin_path_clone)
.arg("mkdir")
.arg("-p")
.arg(&path_clone)
.current_dir(std::env::current_dir().unwrap())
.output();
match result {
Ok(output) => {
assert!(
output.status.success(),
"mkdir failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Err(e) => panic!("Failed to execute mkdir: {e}"),
}
});
handles.push(handle);
}
handles
.drain(..)
.map(|handle| handle.join().unwrap())
.count();
assert!(at.dir_exists(&path_str));
}
}