From f037641e70bbdd1e9037a4b07c3826af027808a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Schu=CC=88mann?= Date: Sun, 19 Jul 2026 11:19:46 +0200 Subject: [PATCH] Work on handling of relative symlinks on Windows, refs #211 --- include/ghc/filesystem.hpp | 22 +++++++++++++++------- test/filesystem_test.cpp | 12 ++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index c10f810..696d2e4 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -2199,7 +2199,7 @@ GHC_INLINE std::unique_ptrSymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { + if (resolve_relative && reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { result = p.parent_path() / result; } break; @@ -2233,6 +2233,7 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) } return result; #else + (void)resolve_relative; size_t bufferSize = 256; while (true) { std::vector buffer(bufferSize, static_cast(0)); @@ -2250,6 +2251,11 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) #endif } +GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) +{ + return resolveSymlink(p, ec, true); +} + #ifdef GHC_OS_WINDOWS GHC_INLINE file_time_type timeFromFILETIME(const FILETIME& ft) { @@ -4088,16 +4094,18 @@ GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symli GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept { ec.clear(); - auto to = read_symlink(existing_symlink, ec); + auto resolved_to = read_symlink(existing_symlink, ec); if (!ec) { #ifdef GHC_OS_WINDOWS - auto resolved_to = to.is_absolute() ? to : existing_symlink.parent_path() / to; - auto to_directory = exists(resolved_to, ec) && is_directory(resolved_to, ec); + auto to = detail::resolveSymlink(existing_symlink, ec, false); if (!ec) { - detail::create_symlink_unchecked(to, new_symlink, to_directory, ec); + auto to_directory = exists(resolved_to, ec) && is_directory(resolved_to, ec); + if (!ec) { + detail::create_symlink_unchecked(to, new_symlink, to_directory, ec); + } } #else - create_symlink(to, new_symlink, ec); + create_symlink(resolved_to, new_symlink, ec); #endif } } diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index eb3ad37..0d1b940 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -1927,19 +1927,31 @@ TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.c CHECK_NOTHROW(fs::copy_symlink("links/relative-dir-link", "links/relative-dir-copy")); CHECK(fs::is_directory("links/relative-dir-copy")); +#ifdef GHC_OS_WINDOWS + CHECK(fs::read_symlink("links/relative-dir-copy") == fs::path("links/dir-target")); +#else CHECK(fs::read_symlink("links/relative-dir-copy") == fs::path("dir-target")); +#endif ec = std::make_error_code(std::errc::invalid_argument); CHECK_NOTHROW(fs::copy_symlink("links/relative-file-link", "links/relative-file-copy", ec)); CHECK(!ec); CHECK(fs::is_regular_file("links/relative-file-copy")); +#ifdef GHC_OS_WINDOWS + CHECK(fs::read_symlink("links/relative-file-copy") == fs::path("links/file-target")); +#else CHECK(fs::read_symlink("links/relative-file-copy") == fs::path("file-target")); +#endif fs::create_symlink("missing-target", "links/dangling-link"); CHECK_NOTHROW(fs::copy_symlink("links/dangling-link", "links/dangling-copy", ec)); CHECK(!ec); CHECK(fs::is_symlink("links/dangling-copy")); +#ifdef GHC_OS_WINDOWS + CHECK(fs::read_symlink("links/dangling-copy") == fs::path("links/missing-target")); +#else CHECK(fs::read_symlink("links/dangling-copy") == fs::path("missing-target")); +#endif fs::create_symlink(fs::absolute("foo"), "links/absolute-link"); CHECK_NOTHROW(fs::copy_symlink("links/absolute-link", "links/absolute-copy"));