diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index 5c92ebfef74..bef87e6aa90 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -403,6 +403,20 @@ pub(crate) fn copy_directory( if let Some(parent) = root.parent() { let new_target = target.join(parent); build_dir(&new_target, true, options, None)?; + if root + .components() + .next_back() + .is_some_and(|component| matches!(component, std::path::Component::ParentDir)) + { + let dest = target.join(root); + build_dir(&dest, false, options, Some(root)).map_err(|err| match err { + CpError::IoErr(io_err) => CpError::IoErrContext( + io_err, + format!("cannot create directory {}", dest.quote()), + ), + err => err, + })?; + } if options.verbose { // For example, if copying file `a/b/c` and its parents // to directory `d/`, then print @@ -572,7 +586,9 @@ pub(crate) fn copy_directory( // Also fix permissions for parent directories, // if we were asked to create them. if options.parents { - let dest = target.join(root.file_name().unwrap()); + let dest = root + .file_name() + .map_or_else(|| target.to_path_buf(), |name| target.join(name)); for (x, y) in aligned_ancestors(root, dest.as_path()) { if let Ok(src) = canonicalize(x, MissingHandling::Normal, ResolveMode::Physical) { copy_attributes( diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 1b3fde67814..3ca1ea4fae1 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -3608,6 +3608,23 @@ fn test_cp_parents_2_deep_dir() { assert!(at.dir_exists("d/e/a/b/c")); } +#[cfg(not(windows))] +#[test] +fn test_cp_parents_recursive_source_ending_in_parent_dir() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir_all("src/sub"); + at.write("src/sub/f", "x\n"); + at.mkdir("d"); + + ucmd.args(&["--parents", "-r", "src/sub/..", "d"]) + .fails() + .stderr_contains("cannot create directory 'd/src/sub/..'") + .stderr_contains("File exists"); + + assert!(at.dir_exists("d/src/sub")); + assert!(!at.file_exists("d/src/sub/f")); +} + #[test] fn test_cp_copy_symlink_contents_recursive() { let (at, mut ucmd) = at_and_ucmd!();