Skip to content
Snippets Groups Projects
Commit cbb763e1 authored by Apertis package maintainers's avatar Apertis package maintainers
Browse files

d/patches: backport upstream fixes for cp


In some cases, `cp` doesn't handle symlinks due to the lack of checks,
especially with symlinks pointing to directories. This has been fixed
upstream so we backport the corresponding patches in this commit.

Signed-off-by: default avatarArnaud Ferraris <arnaud.ferraris@collabora.com>
parent 06f5219f
No related branches found
No related tags found
1 merge request!4Backport upstream fixes for cp
From: Michael Debertol <michael.debertol@gmail.com>
Date: Thu, 17 Jun 2021 22:26:13 +0200
Subject: cp: improve symlink handling
Origin: upstream, https://github.com/uutils/coreutils/commit/12a1c87cb8e72f0df8b03594f425f8c98268ec1c
---
src/uu/cp/src/cp.rs | 79 +++++++++++++++++++++++++++---------------------
tests/by-util/test_cp.rs | 26 ++++++++++++++++
2 files changed, 70 insertions(+), 35 deletions(-)
diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs
index 569ee78..d6420fb 100644
--- a/src/uu/cp/src/cp.rs
+++ b/src/uu/cp/src/cp.rs
@@ -47,7 +47,6 @@ use std::os::windows::ffi::OsStrExt;
use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr;
use std::string::ToString;
-use uucore::fs::resolve_relative_path;
use uucore::fs::{canonicalize, CanonicalizeMode};
use walkdir::WalkDir;
@@ -202,7 +201,6 @@ pub struct Options {
copy_contents: bool,
copy_mode: CopyMode,
dereference: bool,
- no_dereference: bool,
no_target_dir: bool,
one_file_system: bool,
overwrite: OverwriteMode,
@@ -620,11 +618,12 @@ impl Options {
attributes_only: matches.is_present(OPT_ATTRIBUTES_ONLY),
copy_contents: matches.is_present(OPT_COPY_CONTENTS),
copy_mode: CopyMode::from_matches(matches),
- dereference: matches.is_present(OPT_DEREFERENCE),
// No dereference is set with -p, -d and --archive
- no_dereference: matches.is_present(OPT_NO_DEREFERENCE)
+ dereference: !(matches.is_present(OPT_NO_DEREFERENCE)
|| matches.is_present(OPT_NO_DEREFERENCE_PRESERVE_LINKS)
- || matches.is_present(OPT_ARCHIVE),
+ || matches.is_present(OPT_ARCHIVE)
+ || recursive)
+ || matches.is_present(OPT_DEREFERENCE),
one_file_system: matches.is_present(OPT_ONE_FILE_SYSTEM),
overwrite: OverwriteMode::from_matches(matches),
parents: matches.is_present(OPT_PARENTS),
@@ -876,7 +875,14 @@ fn copy_source(
options: &Options,
) -> CopyResult<()> {
let source_path = Path::new(&source);
- if source_path.is_dir() {
+ // if no-dereference is enabled and this is a symlink, don't treat it as a directory
+ if source_path.is_dir()
+ && !(!options.dereference
+ && fs::symlink_metadata(source_path)
+ .unwrap()
+ .file_type()
+ .is_symlink())
+ {
// Copy as directory
copy_directory(source, target, options)
} else {
@@ -917,7 +923,7 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
return Err(format!("omitting directory '{}'", root.display()).into());
}
- let root_path = Path::new(&root).canonicalize()?;
+ let root_path = env::current_dir().unwrap().join(root);
let root_parent = if target.exists() {
root_path.parent()
@@ -938,17 +944,15 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
#[cfg(any(windows, target_os = "redox"))]
let mut hard_links: Vec<(String, u64)> = vec![];
- for path in WalkDir::new(root).same_file_system(options.one_file_system) {
+ for path in WalkDir::new(root)
+ .same_file_system(options.one_file_system)
+ .follow_links(options.dereference)
+ {
let p = or_continue!(path);
let is_symlink = fs::symlink_metadata(p.path())?.file_type().is_symlink();
- let path = if (options.no_dereference || options.dereference) && is_symlink {
- // we are dealing with a symlink. Don't follow it
- match env::current_dir() {
- Ok(cwd) => cwd.join(resolve_relative_path(p.path())),
- Err(e) => crash!(1, "failed to get current directory {}", e),
- }
- } else {
- or_continue!(p.path().canonicalize())
+ let path = match env::current_dir() {
+ Ok(cwd) => cwd.join(&p.path()),
+ Err(e) => crash!(1, "failed to get current directory {}", e),
};
let local_to_root_parent = match root_parent {
@@ -972,9 +976,10 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
};
let local_to_target = target.join(&local_to_root_parent);
-
- if path.is_dir() && !local_to_target.exists() {
- or_continue!(fs::create_dir_all(local_to_target.clone()));
+ if is_symlink && !options.dereference {
+ copy_link(&path, &local_to_target)?;
+ } else if path.is_dir() && !local_to_target.exists() {
+ or_continue!(fs::create_dir_all(local_to_target));
} else if !path.is_dir() {
if preserve_hard_links {
let mut found_hard_link = false;
@@ -1232,22 +1237,8 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()>
ReflinkMode::Never => {}
}
}
- } else if options.no_dereference && fs::symlink_metadata(&source)?.file_type().is_symlink() {
- // Here, we will copy the symlink itself (actually, just recreate it)
- let link = fs::read_link(&source)?;
- let dest: Cow<'_, Path> = if dest.is_dir() {
- match source.file_name() {
- Some(name) => dest.join(name).into(),
- None => crash!(
- EXIT_ERR,
- "cannot stat ‘{}’: No such file or directory",
- source.display()
- ),
- }
- } else {
- dest.into()
- };
- symlink_file(&link, &dest, &*context_for(&link, &dest))?;
+ } else if !options.dereference && fs::symlink_metadata(&source)?.file_type().is_symlink() {
+ copy_link(source, dest)?;
} else if source.to_string_lossy() == "/dev/null" {
/* workaround a limitation of fs::copy
* https://github.com/rust-lang/rust/issues/79390
@@ -1264,6 +1255,24 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()>
Ok(())
}
+fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> {
+ // Here, we will copy the symlink itself (actually, just recreate it)
+ let link = fs::read_link(&source)?;
+ let dest: Cow<'_, Path> = if dest.is_dir() {
+ match source.file_name() {
+ Some(name) => dest.join(name).into(),
+ None => crash!(
+ EXIT_ERR,
+ "cannot stat ‘{}’: No such file or directory",
+ source.display()
+ ),
+ }
+ } else {
+ dest.into()
+ };
+ symlink_file(&link, &dest, &*context_for(&link, &dest))
+}
+
/// Generate an error message if `target` is not the correct `target_type`
pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> {
match (target_type, target.is_dir()) {
diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs
index 1fa8212..bac7e2a 100644
--- a/tests/by-util/test_cp.rs
+++ b/tests/by-util/test_cp.rs
@@ -1074,3 +1074,29 @@ fn test_cp_one_file_system() {
}
}
}
+
+#[test]
+fn test_copy_dir_symlink() {
+ let (at, mut ucmd) = at_and_ucmd!();
+ at.mkdir("dir");
+ at.symlink_dir("dir", "dir-link");
+ ucmd.args(&["-r", "dir-link", "copy"]).succeeds();
+ assert_eq!(at.resolve_link("copy"), "dir");
+}
+
+#[test]
+fn test_copy_dir_with_symlinks() {
+ let (at, mut ucmd) = at_and_ucmd!();
+ at.mkdir("dir");
+ at.make_file("dir/file");
+
+ TestScenario::new("ln")
+ .ucmd()
+ .arg("-sr")
+ .arg(at.subdir.join("dir/file"))
+ .arg(at.subdir.join("dir/file-link"))
+ .succeeds();
+
+ ucmd.args(&["-r", "dir", "copy"]).succeeds();
+ assert_eq!(at.resolve_link("copy/file-link"), "file");
+}
From: Michael Debertol <michael.debertol@gmail.com>
Date: Fri, 18 Jun 2021 11:42:37 +0200
Subject: cp: move symlink check to the right place
Origin: upstream, https://github.com/uutils/coreutils/commit/315bfd65a3f07221abdb262ba8731d364441e581
---
src/uu/cp/src/cp.rs | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs
index d6420fb..4b93b3a 100644
--- a/src/uu/cp/src/cp.rs
+++ b/src/uu/cp/src/cp.rs
@@ -875,14 +875,7 @@ fn copy_source(
options: &Options,
) -> CopyResult<()> {
let source_path = Path::new(&source);
- // if no-dereference is enabled and this is a symlink, don't treat it as a directory
- if source_path.is_dir()
- && !(!options.dereference
- && fs::symlink_metadata(source_path)
- .unwrap()
- .file_type()
- .is_symlink())
- {
+ if source_path.is_dir() {
// Copy as directory
copy_directory(source, target, options)
} else {
@@ -923,6 +916,11 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
return Err(format!("omitting directory '{}'", root.display()).into());
}
+ // if no-dereference is enabled and this is a symlink, copy it as a file
+ if !options.dereference && fs::symlink_metadata(root).unwrap().file_type().is_symlink() {
+ return copy_file(root, target, options);
+ }
+
let root_path = env::current_dir().unwrap().join(root);
let root_parent = if target.exists() {
......@@ -14,6 +14,8 @@ Sort-Implement-stable-sort-ignore-non-printing-month.patch
Ignore-a-test.patch
Sort-Various-fixes-and-performance-improvements.patch
sort-implement-k-and-t-support.patch
cp-improve-symlink-handling.patch
cp-move-symlink-check-to-the-right-place.patch
# Apertis patches
sort-fix-dependencies-versions.patch
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment