From ac3fcca6c04f324d5de97efc92712f9960fef5aa Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Thu, 1 Sep 2022 22:12:16 -0400 Subject: [PATCH] cp: correctly copy ancestor dirs in --parents mode Fix a bug where `cp` failed to copy ancestor directories when using the `--parents` option. For example, before this commit: $ mkdir -p a/b/c d $ cp --parents a/b/c d $ find d d d/c After this commit $ mkdir -p a/b/c d $ cp --parents a/b/c d $ find d d d/a d/a/b d/a/b/c This commit also adds the correct messages for `--verbose` mode: $ cp -r --parents --verbose a/b/c d a -> d/a a/b -> d/a/b 'a/b/c' -> 'd/a/b/c' Fixes #3332. --- src/uu/cp/src/copydir.rs | 21 +++++++++++++++++++++ tests/by-util/test_cp.rs | 13 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index e0d6e96dbc4..8930282fb02 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -303,6 +303,27 @@ pub(crate) fn copy_directory( .into()); } + // If in `--parents` mode, create all the necessary ancestor directories. + // + // For example, if the command is `cp --parents a/b/c d`, that + // means we need to copy the two ancestor directories first: + // + // a -> d/a + // a/b -> d/a/b + // + let tmp = if options.parents { + if let Some(parent) = root.parent() { + let new_target = target.join(parent); + std::fs::create_dir_all(&new_target)?; + new_target + } else { + target.to_path_buf() + } + } else { + target.to_path_buf() + }; + let target = tmp.as_path(); + let mut hard_links: Vec<(String, u64)> = vec![]; let preserve_hard_links = options.preserve_hard_links(); diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index ecba6addd13..8285905c542 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1999,6 +1999,19 @@ fn test_copy_same_symlink_no_dereference_dangling() { ucmd.args(&["-d", "a", "b"]).succeeds(); } +#[cfg(not(windows))] +#[test] +fn test_cp_parents_2_dirs() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir_all("a/b/c"); + at.mkdir("d"); + ucmd.args(&["-a", "--parents", "a/b/c", "d"]) + .succeeds() + .no_stderr() + .no_stdout(); + assert!(at.dir_exists("d/a/b/c")); +} + #[test] #[ignore = "issue #3332"] fn test_cp_parents_2() {