Skip to content

Commit 76815a4

Browse files
committed
cp: avoid resolving cwd for absolute recursive copies
Recursive copies unconditionally resolve cwd. This causes errors when the working directory has been removed, even if the source and destination paths were absolute. This fix is to stop resolving the cwd if the source path is absolute. Fixes #9105.
1 parent c98b19c commit 76815a4

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

src/uu/cp/src/copydir.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,11 @@ struct Context<'a> {
119119

120120
impl<'a> Context<'a> {
121121
fn new(root: &'a Path, target: &'a Path) -> io::Result<Self> {
122-
let current_dir = env::current_dir()?;
122+
let current_dir = if root.is_absolute() {
123+
PathBuf::new()
124+
} else {
125+
env::current_dir()?
126+
};
123127
let root_path = current_dir.join(root);
124128
let target_is_file = target.is_file();
125129
let root_parent =

tests/by-util/test_cp.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7834,6 +7834,52 @@ fn test_cp_preserve_context_root() {
78347834
}
78357835
}
78367836

7837+
// Regression test for https://github.com/uutils/coreutils/issues/9105.
7838+
// Absolute operands must not require resolving the current working directory.
7839+
#[cfg(unix)]
7840+
#[rstest]
7841+
#[case::existing_target(true)]
7842+
#[case::new_target(false)]
7843+
fn test_cp_absolute_paths_from_deleted_cwd(#[case] target_exists: bool) {
7844+
use std::process::Command;
7845+
7846+
let ts = TestScenario::new(util_name!());
7847+
let at = &ts.fixtures;
7848+
at.mkdir_all("src/sub");
7849+
at.write("src/sub/file", "contents");
7850+
at.mkdir("deleted-cwd");
7851+
if target_exists {
7852+
at.mkdir("dst");
7853+
}
7854+
7855+
let source = at.plus("src");
7856+
let target = at.plus("dst");
7857+
let deleted_cwd = at.plus("deleted-cwd");
7858+
let output = Command::new("sh")
7859+
.arg("-c")
7860+
.arg("cd \"$1\" && rmdir \"$1\" && exec \"$2\" \"$3\" -Ra --no-preserve=ownership \"$4\" \"$5\"")
7861+
.arg("sh")
7862+
.arg(&deleted_cwd)
7863+
.arg(&ts.bin_path)
7864+
.arg(&ts.util_name)
7865+
.arg(&source)
7866+
.arg(&target)
7867+
.output()
7868+
.unwrap();
7869+
7870+
assert!(
7871+
output.status.success(),
7872+
"cp failed from a deleted cwd: {}",
7873+
String::from_utf8_lossy(&output.stderr)
7874+
);
7875+
let copied_file = if target_exists {
7876+
target.join("src/sub/file")
7877+
} else {
7878+
target.join("sub/file")
7879+
};
7880+
assert_eq!(std::fs::read_to_string(copied_file).unwrap(), "contents");
7881+
}
7882+
78377883
// Test copying current directory (.) to an existing directory.
78387884
// This tests the special case where we copy the current directory
78397885
// to an existing directory, ensuring the directory name is properly

0 commit comments

Comments
 (0)