@@ -4838,9 +4838,9 @@ CpError CopyDirRecursive(const std::filesystem::path& src_path,
48384838
48394839 std::function<CpError (std::filesystem::path, std::filesystem::path)>
48404840 copy_dir_contents;
4841- copy_dir_contents = [&options, ©_dir_contents, file_copy_opts](
4842- std::filesystem::path src,
4843- std::filesystem::path dest) -> CpError {
4841+ copy_dir_contents =
4842+ [&options, ©_dir_contents, &dest_path, file_copy_opts](
4843+ std::filesystem::path src, std::filesystem::path dest) -> CpError {
48444844 std::error_code error;
48454845 // Only the error_code overloads are used from here on: this runs on a
48464846 // thread pool thread and exceptions are disabled.
@@ -4888,6 +4888,28 @@ CpError CopyDirRecursive(const std::filesystem::path& src_path,
48884888 return CpError::Std (error, dest_str);
48894889 }
48904890
4891+ std::filesystem::path symlink_target_absolute;
4892+ if (options.fresh_destination ) {
4893+ // As path.resolve() does: lexical only, absolute targets verbatim.
4894+ symlink_target_absolute =
4895+ symlink_target.is_absolute ()
4896+ ? symlink_target
4897+ : std::filesystem::absolute (src / symlink_target, error)
4898+ .lexically_normal ();
4899+ } else {
4900+ symlink_target_absolute = std::filesystem::weakly_canonical (
4901+ std::filesystem::absolute (src / symlink_target, error), error);
4902+ }
4903+ if (error) {
4904+ return CpError::Std (error, dest_str);
4905+ }
4906+ #ifdef _WIN32
4907+ auto wstr = symlink_target_absolute.wstring ();
4908+ if (wstr.starts_with (L" \\\\ ?\\ " )) {
4909+ symlink_target_absolute = std::filesystem::path (wstr.substr (4 ));
4910+ }
4911+ #endif
4912+
48914913 if (std::filesystem::exists (dest_file_path, error)) {
48924914 if (std::filesystem::is_symlink (dest_file_path, error)) {
48934915 auto current_dest_symlink_target =
@@ -4896,9 +4918,33 @@ CpError CopyDirRecursive(const std::filesystem::path& src_path,
48964918 return CpError::Std (error, dest_str);
48974919 }
48984920
4921+ auto current_dest_symlink_target_absolute =
4922+ std::filesystem::weakly_canonical (
4923+ std::filesystem::absolute (dest_file_path.parent_path () /
4924+ current_dest_symlink_target,
4925+ error),
4926+ error);
4927+ if (error) {
4928+ return CpError::Std (error, dest_str);
4929+ }
4930+ #ifdef _WIN32
4931+ auto wstr2 = current_dest_symlink_target_absolute.wstring ();
4932+ if (wstr2.starts_with (L" \\\\ ?\\ " )) {
4933+ current_dest_symlink_target_absolute =
4934+ std::filesystem::path (wstr2.substr (4 ));
4935+ }
4936+ #endif
4937+ // Equal targets are safe unless they point to the destination
4938+ // root or one of its ancestors.
4939+ bool same_target =
4940+ symlink_target_absolute ==
4941+ current_dest_symlink_target_absolute &&
4942+ !isInsideDir (symlink_target_absolute, dest_path);
4943+
48994944 if (!options.dereference &&
49004945 std::filesystem::is_directory (symlink_target, error) &&
4901- isInsideDir (symlink_target, current_dest_symlink_target)) {
4946+ isInsideDir (symlink_target, current_dest_symlink_target) &&
4947+ !same_target) {
49024948 return {CpError::kEinval ,
49034949 0 ,
49044950 " cp" ,
@@ -4912,7 +4958,8 @@ CpError CopyDirRecursive(const std::filesystem::path& src_path,
49124958 // dest in this case would result in removing src contents
49134959 // and therefore a broken symlink would be created.
49144960 if (std::filesystem::is_directory (dest_file_path, error) &&
4915- isInsideDir (current_dest_symlink_target, symlink_target)) {
4961+ isInsideDir (current_dest_symlink_target, symlink_target) &&
4962+ !same_target) {
49164963 return {CpError::kSymlinkToSubdirectory ,
49174964 0 ,
49184965 " cp" ,
@@ -4939,27 +4986,6 @@ CpError CopyDirRecursive(const std::filesystem::path& src_path,
49394986 }
49404987 }
49414988 }
4942- std::filesystem::path symlink_target_absolute;
4943- if (options.fresh_destination ) {
4944- // As path.resolve() does: lexical only, absolute targets verbatim.
4945- symlink_target_absolute =
4946- symlink_target.is_absolute ()
4947- ? symlink_target
4948- : std::filesystem::absolute (src / symlink_target, error)
4949- .lexically_normal ();
4950- } else {
4951- symlink_target_absolute = std::filesystem::weakly_canonical (
4952- std::filesystem::absolute (src / symlink_target, error), error);
4953- }
4954- if (error) {
4955- return CpError::Std (error, dest_str);
4956- }
4957- #ifdef _WIN32
4958- auto wstr = symlink_target_absolute.wstring ();
4959- if (wstr.starts_with (L" \\\\ ?\\ " )) {
4960- symlink_target_absolute = std::filesystem::path (wstr.substr (4 ));
4961- }
4962- #endif
49634989 if (dir_entry.is_directory (error)) {
49644990 std::filesystem::create_directory_symlink (
49654991 symlink_target_absolute, dest_file_path, error);
0 commit comments