diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 32168b09009..eb739e4678d 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -7,6 +7,7 @@ use quick_error::quick_error; use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; +use std::env; #[cfg(not(windows))] use std::ffi::CString; use std::ffi::OsString; @@ -1979,6 +1980,22 @@ fn handle_copy_mode( if dest.exists() && options.overwrite == OverwriteMode::Clobber(ClobberMode::Force) { fs::remove_file(dest)?; } + if source.is_relative() { + let current_dir = env::current_dir()?; + let abs_dest = + canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap(); + if let Some(parent) = abs_dest.parent() { + if parent != current_dir { + { + return Err(format!( + "{}: can make relative symbolic links only in current directory", + dest.maybe_quote() + ) + .into()); + } + } + } + } symlink_file(source, dest, symlinked_files)?; } CopyMode::Update => { diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 7a0889b0fa6..2fa2e5ce5a8 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -5745,6 +5745,34 @@ fn test_preserve_attrs_overriding_2() { } } +#[test] +fn test_symlink_to_subdir() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.touch("file"); + at.mkdir("dir"); + + ucmd.args(&["--symbolic", "file", "dir"]) + .fails() + .no_stdout() + .stderr_contains("can make relative symbolic links only in current directory\n"); + + assert!(at.dir_exists("dir")); + assert!(!at.file_exists("dir/file")); +} + +#[test] +fn test_symlink_from_subdir() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.mkdir("dir"); + at.touch("dir/file"); + + ucmd.args(&["--symbolic", "dir/file", "."]).succeeds(); + + assert!(at.symlink_exists("file")); +} + /// Test the behavior of preserving permissions when copying through a symlink #[test] #[cfg(unix)]