diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index bc417e8..e1781d0 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -4085,7 +4085,9 @@ GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symli ec.clear(); auto to = read_symlink(existing_symlink, ec); if (!ec) { - if (exists(to, ec) && is_directory(to, ec)) { + // Relative targets are relative to the source link parent (#211). + path lookup = to.is_absolute() ? to : (existing_symlink.parent_path() / to); + if (exists(lookup, ec) && is_directory(lookup, ec)) { create_directory_symlink(to, new_symlink, ec); } else { diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index 649f1d7..d1c1188 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -1918,6 +1918,18 @@ TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.c CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc2", ec)); CHECK(fs::exists("sdirc2")); CHECK(!ec); + + // Relative targets must be resolved against the source link parent + // (not cwd) when classifying directory vs file links on Windows. + fs::create_directories("nested/dir"); + fs::create_directory_symlink("dir", "nested/slink"); + CHECK_NOTHROW(fs::copy_symlink("nested/slink", "nested/slink_copy")); + CHECK(fs::read_symlink("nested/slink_copy") == "dir"); + CHECK(fs::is_directory("nested/slink_copy")); + CHECK_NOTHROW(fs::copy_symlink("nested/slink", "nested/slink_copy2", ec)); + CHECK(!ec); + CHECK(fs::read_symlink("nested/slink_copy2") == "dir"); + CHECK(fs::is_directory("nested/slink_copy2")); } CHECK_THROWS_AS(fs::copy_symlink("bar", "barc"), fs::filesystem_error); CHECK_NOTHROW(fs::copy_symlink("bar", "barc", ec));