Skip to content

Commit daa828b

Browse files
authored
Merge pull request uutils#8864 from Alonely0/fix_cp
cp: fix crash on -T/--no-preserve-target
2 parents 1c29075 + 9a8c8a5 commit daa828b

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

src/uu/cp/src/copydir.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
#[cfg(windows)]
1010
use std::borrow::Cow;
1111
use std::collections::{HashMap, HashSet};
12+
use std::convert::identity;
1213
use std::env;
13-
use std::fs;
14+
use std::fs::{self, exists};
1415
use std::io;
1516
use std::path::{Path, PathBuf, StripPrefixError};
1617

@@ -20,10 +21,9 @@ use uucore::error::UIoError;
2021
use uucore::fs::{
2122
FileInformation, MissingHandling, ResolveMode, canonicalize, path_ends_with_terminator,
2223
};
23-
use uucore::translate;
24-
2524
use uucore::show;
2625
use uucore::show_error;
26+
use uucore::translate;
2727
use uucore::uio_error;
2828
use walkdir::{DirEntry, WalkDir};
2929

@@ -194,15 +194,23 @@ impl Entry {
194194
get_local_to_root_parent(&source_absolute, context.root_parent.as_deref())?;
195195
if no_target_dir {
196196
let source_is_dir = source.is_dir();
197-
if path_ends_with_terminator(context.target) && source_is_dir {
197+
if path_ends_with_terminator(context.target)
198+
&& source_is_dir
199+
&& !exists(context.target).is_ok_and(identity)
200+
{
198201
if let Err(e) = fs::create_dir_all(context.target) {
199202
eprintln!(
200203
"{}",
201204
translate!("cp-error-failed-to-create-directory", "error" => e)
202205
);
203206
}
204-
} else {
205-
descendant = descendant.strip_prefix(context.root)?.to_path_buf();
207+
} else if let Some(stripped) = context
208+
.root
209+
.components()
210+
.next_back()
211+
.and_then(|stripped| descendant.strip_prefix(stripped).ok())
212+
{
213+
descendant = stripped.to_path_buf();
206214
}
207215
} else if context.root == Path::new(".") && context.target.is_dir() {
208216
// Special case: when copying current directory (.) to an existing directory,

tests/by-util/test_cp.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7090,3 +7090,49 @@ fn test_cp_recursive_files_ending_in_backslash() {
70907090
ts.ucmd().args(&["-r", "a", "b"]).succeeds();
70917091
assert!(at.file_exists("b/foo\\"));
70927092
}
7093+
7094+
#[test]
7095+
fn test_cp_no_preserve_target_directory() {
7096+
/* Expected result:
7097+
├── a
7098+
│ └── b
7099+
│ └── c
7100+
│ └── d
7101+
│ └── f1
7102+
├── d
7103+
│ └── f1
7104+
└── e
7105+
├── b
7106+
│ └── c
7107+
│ └── d
7108+
│ ├── c
7109+
│ │ └── d
7110+
│ │ └── f1
7111+
│ └── f1
7112+
├── d
7113+
│ └── f1
7114+
├── f2
7115+
└── f3
7116+
*/
7117+
7118+
let ts = TestScenario::new(util_name!());
7119+
let at = &ts.fixtures;
7120+
at.mkdir_all("a/b/c/d");
7121+
at.touch("a/b/c/d/f1");
7122+
ts.ucmd().args(&["-rT", "a", "e"]).succeeds();
7123+
at.touch("e/f2");
7124+
ts.ucmd().args(&["-rT", "a/", "e/"]).succeeds();
7125+
at.touch("e/f3");
7126+
ts.ucmd().args(&["-rvT", "a/b/c", "e/"]).succeeds();
7127+
ts.ucmd().args(&["-rvT", "a/b/", "e/b/c/d/"]).succeeds();
7128+
ts.ucmd().args(&["-rT", "a/b/c", "."]).succeeds();
7129+
assert!(!at.dir_exists("e/a"));
7130+
assert!(at.file_exists("e/b/c/d/f1"));
7131+
assert!(at.file_exists("e/b/c/d/c/d/f1"));
7132+
assert!(!at.dir_exists("e/c"));
7133+
assert!(!at.dir_exists("e/c/d/b"));
7134+
assert!(at.file_exists("e/d/f1"));
7135+
assert!(at.file_exists("./d/f1"));
7136+
assert!(at.file_exists("e/f2"));
7137+
assert!(at.file_exists("e/f3"));
7138+
}

0 commit comments

Comments
 (0)