From 6400cded54ba436ad72f0758dfafe174491e378e Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 19 Jun 2021 17:45:45 +0200 Subject: [PATCH 1/4] cp: fix order of checks in `copy_helper` --- src/uu/cp/src/cp.rs | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 851117bde..cf723e4ee 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1218,28 +1218,38 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { /// Copy the file from `source` to `dest` either using the normal `fs::copy` or a /// copy-on-write scheme if --reflink is specified and the filesystem supports it. fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { - if options.reflink_mode != ReflinkMode::Never { - #[cfg(not(any(target_os = "linux", target_os = "macos")))] - return Err("--reflink is only supported on linux and macOS" - .to_string() - .into()); - - #[cfg(target_os = "macos")] - copy_on_write_macos(source, dest, options.reflink_mode)?; - #[cfg(target_os = "linux")] - copy_on_write_linux(source, dest, options.reflink_mode)?; - } else if !options.dereference && fs::symlink_metadata(&source)?.file_type().is_symlink() { - copy_link(source, dest)?; - } else if source.to_string_lossy() == "/dev/null" { + if options.parents { + let parent = dest.parent().unwrap_or(dest); + fs::create_dir_all(parent)?; + } + let is_symlink = fs::symlink_metadata(&source)?.file_type().is_symlink(); + if source.to_string_lossy() == "/dev/null" { /* workaround a limitation of fs::copy * https://github.com/rust-lang/rust/issues/79390 */ File::create(dest)?; - } else { - if options.parents { - let parent = dest.parent().unwrap_or(dest); - fs::create_dir_all(parent)?; + } else if !options.dereference && is_symlink { + copy_link(source, dest)?; + } else if options.reflink_mode != ReflinkMode::Never { + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + return Err("--reflink is only supported on linux and macOS" + .to_string() + .into()); + if is_symlink { + assert!(options.dereference); + let real_path = std::fs::read_link(source)?; + + #[cfg(target_os = "macos")] + copy_on_write_macos(&real_path, dest, options.reflink_mode)?; + #[cfg(target_os = "linux")] + copy_on_write_linux(&real_path, dest, options.reflink_mode)?; + } else { + #[cfg(target_os = "macos")] + copy_on_write_macos(source, dest, options.reflink_mode)?; + #[cfg(target_os = "linux")] + copy_on_write_linux(source, dest, options.reflink_mode)?; } + } else { fs::copy(source, dest).context(&*context_for(source, dest))?; } From 9fb927aa856eec92a58b6cfbf53438e4a48f71e2 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 19 Jun 2021 17:49:04 +0200 Subject: [PATCH 2/4] cp: always delete the destination for symlinks --- src/uu/cp/src/cp.rs | 5 +++++ tests/by-util/test_cp.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index cf723e4ee..840035e4a 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1269,6 +1269,11 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> { ), } } else { + // we always need to remove the file to be able to create a symlink, + // even if it is writeable. + if dest.exists() { + fs::remove_file(dest)?; + } dest.into() }; symlink_file(&link, &dest, &*context_for(&link, &dest)) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 4ce587e02..19f93e499 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1325,3 +1325,16 @@ fn test_copy_dir_with_symlinks() { ucmd.args(&["-r", "dir", "copy"]).succeeds(); assert_eq!(at.resolve_link("copy/file-link"), "file"); } + +#[test] +#[cfg(not(windows))] +fn test_copy_symlink_force() { + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("file"); + at.symlink_file("file", "file-link"); + at.touch("copy"); + + ucmd.args(&["file-link", "copy", "-f", "--no-dereference"]) + .succeeds(); + assert_eq!(at.resolve_link("copy"), "file"); +} From 076c7fa501d43a190ab4356dfaa583677c80dc3b Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 19 Jun 2021 17:49:41 +0200 Subject: [PATCH 3/4] cp: default to --reflink=auto on linux and macos --- src/uu/cp/src/cp.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 840035e4a..7cf6a1d9b 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -667,7 +667,14 @@ impl Options { } } } else { - ReflinkMode::Never + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + ReflinkMode::Auto + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + ReflinkMode::Never + } } }, backup: backup_mode, From a91369bbff9f6587bfd23d76f043be25bfdbd294 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sun, 20 Jun 2021 19:10:51 +0200 Subject: [PATCH 4/4] cp: fix dead code warnings on windows --- src/uu/cp/src/cp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 7cf6a1d9b..9186ec259 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1242,6 +1242,7 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> return Err("--reflink is only supported on linux and macOS" .to_string() .into()); + #[cfg(any(target_os = "linux", target_os = "macos"))] if is_symlink { assert!(options.dereference); let real_path = std::fs::read_link(source)?;