Merge pull request #8864 from Alonely0/fix_cp

cp: fix crash on -T/--no-preserve-target
This commit is contained in:
Daniel Hofstetter 2025-10-10 16:37:16 +02:00 committed by GitHub
commit daa828b270
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 60 additions and 6 deletions

View file

@ -9,8 +9,9 @@
#[cfg(windows)]
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::convert::identity;
use std::env;
use std::fs;
use std::fs::{self, exists};
use std::io;
use std::path::{Path, PathBuf, StripPrefixError};
@ -20,10 +21,9 @@ use uucore::error::UIoError;
use uucore::fs::{
FileInformation, MissingHandling, ResolveMode, canonicalize, path_ends_with_terminator,
};
use uucore::translate;
use uucore::show;
use uucore::show_error;
use uucore::translate;
use uucore::uio_error;
use walkdir::{DirEntry, WalkDir};
@ -194,15 +194,23 @@ impl Entry {
get_local_to_root_parent(&source_absolute, context.root_parent.as_deref())?;
if no_target_dir {
let source_is_dir = source.is_dir();
if path_ends_with_terminator(context.target) && source_is_dir {
if path_ends_with_terminator(context.target)
&& source_is_dir
&& !exists(context.target).is_ok_and(identity)
{
if let Err(e) = fs::create_dir_all(context.target) {
eprintln!(
"{}",
translate!("cp-error-failed-to-create-directory", "error" => e)
);
}
} else {
descendant = descendant.strip_prefix(context.root)?.to_path_buf();
} else if let Some(stripped) = context
.root
.components()
.next_back()
.and_then(|stripped| descendant.strip_prefix(stripped).ok())
{
descendant = stripped.to_path_buf();
}
} else if context.root == Path::new(".") && context.target.is_dir() {
// Special case: when copying current directory (.) to an existing directory,

View file

@ -7090,3 +7090,49 @@ fn test_cp_recursive_files_ending_in_backslash() {
ts.ucmd().args(&["-r", "a", "b"]).succeeds();
assert!(at.file_exists("b/foo\\"));
}
#[test]
fn test_cp_no_preserve_target_directory() {
/* Expected result:
a
b
c
d
f1
d
f1
e
b
c
d
c
d
f1
f1
d
f1
f2
f3
*/
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.mkdir_all("a/b/c/d");
at.touch("a/b/c/d/f1");
ts.ucmd().args(&["-rT", "a", "e"]).succeeds();
at.touch("e/f2");
ts.ucmd().args(&["-rT", "a/", "e/"]).succeeds();
at.touch("e/f3");
ts.ucmd().args(&["-rvT", "a/b/c", "e/"]).succeeds();
ts.ucmd().args(&["-rvT", "a/b/", "e/b/c/d/"]).succeeds();
ts.ucmd().args(&["-rT", "a/b/c", "."]).succeeds();
assert!(!at.dir_exists("e/a"));
assert!(at.file_exists("e/b/c/d/f1"));
assert!(at.file_exists("e/b/c/d/c/d/f1"));
assert!(!at.dir_exists("e/c"));
assert!(!at.dir_exists("e/c/d/b"));
assert!(at.file_exists("e/d/f1"));
assert!(at.file_exists("./d/f1"));
assert!(at.file_exists("e/f2"));
assert!(at.file_exists("e/f3"));
}