Skip to content

Commit

Permalink
cp: allow removing symbolic link loop destination
Browse files Browse the repository at this point in the history
Allow `cp --remove-destination` to remove a symbolic link loop (or a
symbolic link that initiates a chain of too many symbolic
links). Before this commit, if `loop` were a symbolic link to itself,
then

    cp --remove-destination file loop

would fail with an error message. After this commit, it succeeds. This
matches the behaviotr of GNU cp.
  • Loading branch information
jfinkels committed Sep 27, 2022
1 parent b43bbe9 commit aad3202
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,15 @@ fn handle_existing_dest(
Ok(())
}

/// Decide whether the given path exists.
fn file_or_link_exists(path: &Path) -> bool {
// Using `Path.exists()` or `Path.try_exists()` is not sufficient,
// because if `path` is a symbolic link and there are too many
// levels of symbolic link, then those methods will return false
// or an OS error.
path.symlink_metadata().is_ok()
}

/// Copy the a file from `source` to `dest`. `source` will be dereferenced if
/// `options.dereference` is set to true. `dest` will be dereferenced only if
/// the source was not a symlink.
Expand All @@ -1392,7 +1401,7 @@ fn copy_file(
symlinked_files: &mut HashSet<FileInformation>,
source_in_command_line: bool,
) -> CopyResult<()> {
if dest.exists() {
if file_or_link_exists(dest) {
handle_existing_dest(source, dest, options, source_in_command_line)?;
}

Expand Down
13 changes: 13 additions & 0 deletions tests/by-util/test_cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2077,3 +2077,16 @@ fn test_cp_mode_hardlink_no_dereference() {
assert!(at.symlink_exists("z"));
assert_eq!(at.read_symlink("z"), "slink");
}

#[test]
fn test_remove_destination_symbolic_link_loop() {
let (at, mut ucmd) = at_and_ucmd!();
at.symlink_file("loop", "loop");
at.plus("loop");
at.touch("f");
ucmd.args(&["--remove-destination", "f", "loop"])
.succeeds()
.no_stdout()
.no_stderr();
assert!(at.file_exists("loop"));
}

0 comments on commit aad3202

Please sign in to comment.