[PATCH v4] libstdc++: Add symlink support on Windows

Jonathan Wakely jwakely@redhat.com
Fri Jun 19 17:11:29 GMT 2026


On Mon, 16 Feb 2026 at 14:36 -0800, Adam Wood wrote:
>Tested on x86_64-w64-mingw32 on Windows 11.
>
>The changes in v4 make sure the new symlink functions are only defined/enabled
>on versions of Windows with symlink support.
>
>Because path resolution may be different between Windows and POSIX when a
>dotdot follows a symlink, I also made some edits to tests involving that
>specific case. I removed my changes to fs::absolute and
>fs::canonical because I was misplaced and was trying to force Windows to
>act like POSIX in that specific case.
>
>Finally, I dealt with the issue that creating symlinks with the unprivileged
>flag returns an error in earlier versions of Windows by just trying again
>if ERROR_INVALID_PARAMETER occurs without the unpriviledged flag. This
>is how MSVC STL does it as well.
>
>The patch does not support junctions or mount points.
>
>Changes in v4:
>  * Remove my changes to fs::absolute and fs::canonical.
>  * Set error code in windows_create_symlink if SYMBOLIC_LINK_FLAG_DIRECTORY
>    is not defined.
>  * Attempt symlink creation with SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
>    and if that causes a ERROR_INVALID_PARAMETER try again without the
>    unprivileged flag.
>  * Don't define windows_read_symlink_handle if SYMBOLIC_LINK_FLAG_DIRECTORY
>    is not defined.
>  * Set error code in fs::read_symlink on Windows
>    if SYMBOLIC_LINK_FLAG_DIRECTORY is not defined.
>  * Return 0 unconditionally in __detail::__is_handle_symlink if
>    SYMBOLIC_LINK_FLAG_DIRECTORY is not defined.
>  * Use stat for lstat if SYMBOLIC_LINK_FLAG_DIRECTORY is not defined.
>  * Changed an ifdef NO_SYMLINKS to defined(NO_SYMLINKS) or
>    defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) in test03 for fs::canonical.
>  * Disabled, on Windows, some sections of test01 for fs::weakly_canonical
>    that tested a dotdot immediately after a symlink.
>
>Changes in v3:
>  * Include <ntdef.h> for REPARSE_DATA_BUFFER and removed
>    the manual definition of REPARSE_DATA_BUFFER.
>  * Replace path_starts_with_dotdot with path_find_dotdot_component
>    which returns an iterator to the first component that is ".."
>    or the end iterator.
>  * Wrap the inner fs::exists call inside a Windows-specific ifdef.
>  * Use PRREPARSE_DATA_BUFFER instead of REPARSE_DATA_BUFFER*.
>  * Move the helper functions for __gnu_posix::stat into a new
>    namespace called __detail.
>
>Changes in v2:
>  * Wrapped Windows specific code in an ifdef.
>
>libstdc++-v3/Changelog:
>
>	* src/c++17/fs_ops.cc:
>	Include <winioctl.h> for FSCTL_GET_REPARSE_POINT.
>	Include <ntdef.h> for REPARSE_DATA_BUFFER.
>	(windows_create_symlink): New helper function for fs::create_symlink
>	and fs::create_directory_symlink.
>	(fs::create_directory_symlink): Call windows_create_symlink on Windows.
>	(fs::create_symlink): Call windows_create_symlink on Windows.
>	(auto_win_file_handle::auto_win_file_handle): Add follow_symlink
>	parameter to control whether the handle should open the symlink
>	or the target, with a default value of true.
>	(windows_read_symlink_handle): New helper function
>	for fs::read_symlink.
>	(fs::read_symlink): Call windows_read_symlink_handle on Windows.
>	(fs::remove): Call RemoveDirectoryW only for directories, and
>	DeleteFileW for regular files, but attempt both for symlinks.
>	(fs::remove_all): Return immediately if path is empty.
>	Check if path points to a symlink, and if so, remove the
>	symlink using fs::remove.
>	* src/filesystem/ops-common.h:
>	Define S_IFLNK and S_ISLNK.
>	Create helper namespace __detail.
>	(__detail::__open_for_stat): New helper function for stat and lstat.
>	(__detail::__is_handle_symlink): New helper function for
>	stat, lstat, and fs::read_symlink.
>	(__detail::__stat_windows): New helper function for stat and lstat.
>	(__gnu_posix::stat, __gnu_posix::lstat): Use __stat_windows to properly
>	follow or not follow symlinks, and check if file is a symlink.
>	* testsuite/27_io/filesystem/operations/canonical.cc (test03):
>	Use fs::create_directory_symlink instead of fs::create_symlink.
>	Check if NO_SYMLINKS or _GLIBCXX_FILESYSTEM_IS_WINDOWS instead of just
>	checking NO_SYMLINKS when defining baz.
>	* testsuite/27_io/filesystem/operations/copy.cc (test02):
>	Create a symlink to temporary file instead of ".".
>	Use fs::exists(symlink_status()) instead of fs::exists for symlinks.
>	* testsuite/27_io/filesystem/operations/weakly_canonical.cc (test01):
>	Use fs::create_directory_symlink instead of fs::create_symlink.
>	Wrap statements that test a dotdot after a symlink in an ifndef
>	_GLIBCXX_FILESYSTEM_IS_WINDOWS.
>	* testsuite/util/testsuite_fs.h: Do not define NO_SYMLINKS on Windows.
>---
> libstdc++-v3/src/c++17/fs_ops.cc              | 210 +++++++++++++++++-
> libstdc++-v3/src/filesystem/ops-common.h      |  89 +++++++-
> .../27_io/filesystem/operations/canonical.cc  |   4 +-
> .../27_io/filesystem/operations/copy.cc       |   7 +-
> .../filesystem/operations/weakly_canonical.cc |   6 +-
> libstdc++-v3/testsuite/util/testsuite_fs.h    |   2 +
> 6 files changed, 302 insertions(+), 16 deletions(-)
>
>diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc
>index 454962c7521..4d8153d3f64 100644
>--- a/libstdc++-v3/src/c++17/fs_ops.cc
>+++ b/libstdc++-v3/src/c++17/fs_ops.cc
>@@ -56,6 +56,8 @@
> #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> # define WIN32_LEAN_AND_MEAN
> # include <windows.h>
>+# include <winioctl.h> // FSCTL_GET_REPARSE_POINT
>+# include <ntdef.h> // REPARSE_DATA_BUFFER
> #endif
>
> #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
>@@ -644,6 +646,52 @@ fs::create_directory(const path& p, const path& attributes,
> #endif
> }
>
>+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
>+namespace
>+{
>+  void
>+  windows_create_symlink(const fs::path& to, const fs::path& new_symlink,
>+			 const fs::file_type target_type,
>+			 std::error_code& ec) noexcept
>+  {
>+#ifdef SYMBOLIC_LINK_FLAG_DIRECTORY
>+    DWORD symlink_type = target_type == fs::file_type::directory
>+			 ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
>+    // Windows can't handle relative symlinks with non-preferred slashes.
>+    // Creating the symlink will succeed, but the symlink won't resolve
>+    // correctly in later operations.
>+    const fs::path* preferred_to = &to;
>+    fs::path to2;
>+    if (to.native().find(L'/') != std::string::npos)
>+      {
>+	__try
>+	  {
>+	    to2 = to;
>+	    to2.make_preferred();
>+	    preferred_to = &to2;
>+	  }
>+	__catch (const std::bad_alloc&)
>+	  {
>+	    ec = std::make_error_code(std::errc::not_enough_memory);
>+	    return;
>+	  }
>+      }
>+    if (CreateSymbolicLinkW(new_symlink.c_str(), preferred_to->c_str(),
>+			    symlink_type
>+			    | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
>+      ec.clear();
>+    else if (GetLastError() == ERROR_INVALID_PARAMETER
>+	     && CreateSymbolicLinkW(new_symlink.c_str(), preferred_to->c_str(),
>+				    symlink_type))
>+      ec.clear();
>+    else
>+      ec = std::__last_system_error();
>+#else
>+    ec = std::make_error_code(std::errc::function_not_supported);
>+#endif
>+  }
>+}
>+#endif
>
> void
> fs::create_directory_symlink(const path& to, const path& new_symlink)
>@@ -660,7 +708,7 @@ fs::create_directory_symlink(const path& to, const path& new_symlink,
> 			     error_code& ec) noexcept
> {
> #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
>-  ec = std::make_error_code(std::errc::function_not_supported);
>+  windows_create_symlink(to, new_symlink, file_type::directory, ec);
> #else
>   create_symlink(to, new_symlink, ec);
> #endif
>@@ -715,6 +763,8 @@ fs::create_symlink(const path& to, const path& new_symlink,
>     ec.assign(errno, std::generic_category());
>   else
>     ec.clear();
>+#elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
>+  windows_create_symlink(to, new_symlink, file_type::regular, ec);
> #else
>   ec = std::make_error_code(std::errc::function_not_supported);
> #endif
>@@ -829,10 +879,14 @@ namespace
>   struct auto_win_file_handle
>   {
>     explicit
>-    auto_win_file_handle(const wchar_t* p, std::error_code& ec) noexcept
>+    auto_win_file_handle(const wchar_t* p, std::error_code& ec,
>+			 const bool follow_symlink = true) noexcept
>     : handle(CreateFileW(p, 0,
> 			 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
>-			 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)),
>+			 0, OPEN_EXISTING,
>+			 (FILE_FLAG_BACKUP_SEMANTICS
>+			  | (follow_symlink ? 0 : FILE_FLAG_OPEN_REPARSE_POINT)),
>+			 0)),
>       ec(ec)
>     {
>       if (handle == INVALID_HANDLE_VALUE)
>@@ -1193,6 +1247,74 @@ fs::proximate(const path& p, const path& base, error_code& ec)
>   return result;
> }
>
>+#if defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) \
>+    && defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
>+namespace
>+{
>+  void
>+  windows_read_symlink_handle(auto_win_file_handle& link_handle,
>+			      std::error_code& ec,
>+			      fs::path& result)
>+  {
>+    PREPARSE_DATA_BUFFER reparse_buffer = nullptr;
>+    std::unique_ptr<char[]> big_buffer;
>+
>+    // Allocate enough memory on the stack to get the reparse data
>+    // plus a 260 character path.  Should be sufficient in most cases.
>+    // Allocate an extra wchar_t to ensure we can manually null terminate.
>+    static constexpr size_t small_buffer_size = sizeof(REPARSE_DATA_BUFFER)
>+						+ 260 * sizeof(wchar_t);
>+    char small_buffer[small_buffer_size + sizeof(wchar_t)];
>+    reparse_buffer = reinterpret_cast<PREPARSE_DATA_BUFFER>(&small_buffer[0]);
>+    long unsigned int bytes_returned, big_buffer_size;
>+
>+    // Attempt to get the reparse data with the buffer on the stack
>+    // before allocating the exact amount needed on the heap.
>+    bool got_reparse_data = DeviceIoControl(link_handle.handle,
>+					    FSCTL_GET_REPARSE_POINT,
>+					    nullptr, 0,
>+					    &small_buffer, small_buffer_size,
>+					    &bytes_returned, nullptr);
>+
>+    int last_error = GetLastError();
>+    if (!got_reparse_data && last_error == ERROR_MORE_DATA)
>+      {
>+	big_buffer_size = bytes_returned;
>+	big_buffer.reset(new char[big_buffer_size + sizeof(wchar_t)]);
>+	got_reparse_data = DeviceIoControl(link_handle.handle,
>+					   FSCTL_GET_REPARSE_POINT,
>+					   nullptr, 0,
>+					   big_buffer.get(), big_buffer_size,
>+					   &bytes_returned, nullptr);
>+	if (!got_reparse_data)
>+	  {
>+	    ec = std::__last_system_error();
>+	    return;
>+	  }
>+
>+	reparse_buffer
>+	  = reinterpret_cast<PREPARSE_DATA_BUFFER>(big_buffer.get());
>+
>+      }
>+    else
>+      {
>+	if (!got_reparse_data)
>+	  {
>+	    ec = std::__last_system_error();
>+	    return;
>+	  }
>+      }
>+
>+    ec.clear();
>+    auto& symlink_buffer = reparse_buffer->SymbolicLinkReparseBuffer;
>+    wchar_t* target_name = &symlink_buffer.PathBuffer[0];
>+    target_name += symlink_buffer.PrintNameOffset / sizeof(wchar_t);
>+    target_name[symlink_buffer.PrintNameLength / sizeof(wchar_t)] = L'\0';
>+    result = target_name;
>+  }
>+};
>+#endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
>+
> fs::path
> fs::read_symlink(const path& p)
> {
>@@ -1248,6 +1370,26 @@ fs::path fs::read_symlink(const path& p, error_code& ec)
> 	bufsz *= 2;
>     }
>   while (true);
>+#elif defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) \
>+      && defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
>+  auto_win_file_handle link_handle(p.c_str(), ec, false);
>+  if (!link_handle)
>+    return result;
>+
>+  int is_symlink = __detail::__is_handle_symlink(link_handle.handle);
>+  if (is_symlink == -1)
>+    {
>+      ec = __last_system_error();
>+      return result;
>+    }
>+
>+  if (!is_symlink)
>+    {
>+      ec.assign(EINVAL, std::generic_category());
>+      return result;
>+    }
>+
>+  windows_read_symlink_handle(link_handle, ec, result);
> #else
>   ec = std::make_error_code(std::errc::function_not_supported);
> #endif
>@@ -1291,8 +1433,14 @@ fs::remove(const path& p, error_code& ec) noexcept
>   auto st = symlink_status(p, ec);
>   if (exists(st))
>     {
>-      if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
>-	  || DeleteFileW(p.c_str()))
>+      if ((is_directory(st) || is_symlink(st))
>+	  && RemoveDirectoryW(p.c_str()))
>+	{
>+	  ec.clear();
>+	  return true;
>+	}
>+      else if ((is_regular_file(st) || is_symlink(st))
>+	       && DeleteFileW(p.c_str()))
> 	{
> 	  ec.clear();
> 	  return true;
>@@ -1320,6 +1468,30 @@ std::uintmax_t
> fs::remove_all(const path& p)
> {
>   error_code ec;
>+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
>+  if (p.empty())
>+    return 0;
>+  // The current opendir implementation on Windows always follows an initial
>+  // symlink.  Therefore, if remove_all is called on a symlink,
>+  // the target is removed.  Call remove if we have a symlink.

Hi Adam,

I think this patch looks good now and I plan to push it after some
testing, so the following is just me musing, not a request for a new
patch.

I wonder if it makes sense to just call fs::remove(p, ec) and if it
succeeds, we're done (either it was an empty directory, or a symlink,
or a regular file). If it fails with ERROR_DIR_NOT_EMPTY in ec then
we do the recursive delete.

That way we wouldn't need symlink_status and the error handling here:

>+  auto p_status = symlink_status(p, ec);
>+  if (!exists(p_status))
>+    {
>+      int err = ec.default_error_condition().value();
>+      bool not_found = !ec || is_not_found_errno(err);
>+      if (!not_found)
>+	_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all",
>+						 p, ec));
>+      return 0;
>+    }
>+  if (is_symlink(p_status))
>+    {
>+      if (!remove(p, ec))
>+	_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all",
>+						 p, ec));
>+      return 1;
>+    }
>+#endif
>   uintmax_t count = 0;
>   recursive_directory_iterator dir(p, directory_options{64|128}, ec);
>   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
>@@ -1363,6 +1535,34 @@ fs::remove_all(const path& p)
> std::uintmax_t
> fs::remove_all(const path& p, error_code& ec)
> {
>+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
>+  if (p.empty())
>+    {
>+      ec.clear();
>+      return 0;
>+    }
>+  // The current opendir implementation on Windows always follows an initial
>+  // symlink.  Therefore, if remove_all is called on a symlink,
>+  // the target is removed.  Call remove if we have a symlink.
>+  auto p_status = symlink_status(p, ec);
>+  if (!exists(p_status))
>+    {
>+      int err = ec.default_error_condition().value();
>+      bool not_found = !ec || is_not_found_errno(err);
>+      if (not_found)
>+	{
>+	  ec.clear();
>+	  return 0;
>+	}
>+      return -1;
>+    }
>+  if (is_symlink(p_status))
>+    {
>+      if (remove(p, ec))
>+	return 1;
>+      return ec ? -1 : 0;
>+    }
>+#endif
>   uintmax_t count = 0;
>   recursive_directory_iterator dir(p, directory_options{64|128}, ec);
>   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
>diff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h
>index 304d5896d02..5203a65443f 100644
>--- a/libstdc++-v3/src/filesystem/ops-common.h
>+++ b/libstdc++-v3/src/filesystem/ops-common.h
>@@ -105,6 +105,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> namespace filesystem
> {
>+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
>+namespace __detail
>+{
>+#define S_IFLNK 0xC000
>+#define	S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
>+
>+  using stat_type = struct ::__stat64;
>+
>+  inline HANDLE __open_for_stat(const wchar_t* path, bool following_symlinks)
>+  {
>+    constexpr auto share_flags
>+      = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
>+    auto file_flags = FILE_FLAG_BACKUP_SEMANTICS;
>+    if (!following_symlinks)
>+      file_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
>+    HANDLE handle;
>+    handle = CreateFileW(path, 0, share_flags, 0, OPEN_EXISTING, file_flags, 0);
>+
>+    if (handle == INVALID_HANDLE_VALUE)
>+      {
>+	// CreateFileW does not set errno.
>+	std::error_condition generic_error
>+	  = std::__last_system_error().default_error_condition();
>+	errno = generic_error.value();
>+      }
>+
>+    return handle;
>+  }
>+
>+  // -1 error, 0 not a symlink, 1 a symlink
>+  inline int __is_handle_symlink(HANDLE handle)
>+  {
>+#ifdef SYMBOLIC_LINK_FLAG_DIRECTORY
>+    FILE_ATTRIBUTE_TAG_INFO type_info;
>+    if (!GetFileInformationByHandleEx(handle, FileAttributeTagInfo,
>+				      &type_info, sizeof(type_info)))
>+      {
>+	errno = std::__last_system_error().default_error_condition().value();
>+	return -1;
>+      }
>+    return type_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT
>+	   && type_info.ReparseTag == IO_REPARSE_TAG_SYMLINK;
>+#else
>+    return 0;
>+#endif
>+  }
>+
>+  inline int __stat_windows(const wchar_t* path, stat_type* buffer,
>+			    bool following_symlinks)
>+  {
>+    HANDLE handle = __open_for_stat(path, following_symlinks);
>+    if (handle == INVALID_HANDLE_VALUE)
>+      return -1;
>+    // Manually check for symlink, because _fstat does not.
>+    int is_symlink = __is_handle_symlink(handle);
>+    if (is_symlink == -1)
>+      {
>+	CloseHandle(handle);
>+	return -1;
>+      }
>+    int fd = ::_open_osfhandle((intptr_t)handle, _O_RDONLY);
>+    if (fd == -1)
>+      {
>+	CloseHandle(handle);
>+	return -1;
>+      }
>+    int stat_result = ::_fstat64(fd, buffer);
>+    if (is_symlink)
>+      {
>+	// Clear the previous file type.
>+	buffer->st_mode &= ~S_IFMT;
>+	buffer->st_mode |= S_IFLNK;
>+      }
>+    ::_close(fd);
>+    return stat_result;
>+  }
>+}
>+#endif
> namespace __gnu_posix
> {
> #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
>@@ -121,13 +199,14 @@ namespace __gnu_posix
>   using stat_type = struct ::__stat64;
>
>   inline int stat(const wchar_t* path, stat_type* buffer)
>-  { return ::_wstat64(path, buffer); }
>+  { return __detail::__stat_windows(path, buffer, true); }
>
>   inline int lstat(const wchar_t* path, stat_type* buffer)
>-  {
>-    // FIXME: symlinks not currently supported
>-    return stat(path, buffer);
>-  }
>+#ifdef SYMBOLIC_LINK_FLAG_DIRECTORY
>+  { return __detail::__stat_windows(path, buffer, false); }
>+#else
>+  { return stat(path, buffer); }
>+#endif
>
>   using ::mode_t;
>
>diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
>index a915b291c37..277fcc8b5ce 100644
>--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
>+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
>@@ -111,14 +111,14 @@ test03()
>   fs::path foo = dir/"foo", bar = dir/"bar";
>   fs::create_directory(foo);
>   fs::create_directory(bar);
>-#ifdef NO_SYMLINKS
>+#if defined(NO_SYMLINKS) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS)
> #if defined(__MINGW32__) || defined(__MINGW64__)
>   const fs::path baz = dir/"foo\\\\..\\bar///";
> #else
>   const fs::path baz = dir/"foo//../bar///";
> #endif
> #else
>-  fs::create_symlink("../bar", foo/"baz");
>+  fs::create_directory_symlink("../bar", foo/"baz");
>   const fs::path baz = dir/"foo//./baz///";
> #endif
>
>diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc
>index 1ca4a44da59..03960fa90f2 100644
>--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc
>+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc
>@@ -68,13 +68,14 @@ test02()
> {
> #ifndef NO_SYMLINKS
>   const std::error_code bad_ec = make_error_code(std::errc::invalid_argument);
>+  __gnu_test::scoped_file tmp_file;
>   auto from = __gnu_test::nonexistent_path();
>   std::error_code ec;
>
>   ec = bad_ec;
>-  fs::create_symlink(".", from, ec);
>+  fs::create_symlink(tmp_file.path, from, ec);
>   VERIFY( !ec );
>-  VERIFY( fs::exists(from) );
>+  VERIFY( fs::exists(symlink_status(from)) );
>
>   auto to = __gnu_test::nonexistent_path();
>   ec = bad_ec;
>@@ -97,7 +98,7 @@ test02()
>   ec = bad_ec;
>   fs::copy(from, to, fs::copy_options::copy_symlinks, ec);
>   VERIFY( !ec );
>-  VERIFY( fs::exists(to) );
>+  VERIFY( fs::exists(symlink_status(to)) );
>   VERIFY( is_symlink(to) );
>
>   ec.clear();
>diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc
>index 6c187c73b79..dc93b4467fc 100644
>--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc
>+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc
>@@ -40,19 +40,23 @@ test01()
>   fs::path p;
>
> #ifndef NO_SYMLINKS
>-  fs::create_symlink("../bar", foo/"bar");
>+  fs::create_directory_symlink("../bar", foo/"bar");
>
>+#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
>   p = fs::weakly_canonical(dir/"foo//./bar///../biz/.");
>   VERIFY( p == dirc/"biz/" );
>+#endif
>   p = fs::weakly_canonical(dir/"foo/.//bar/././baz/.");
>   VERIFY( p == dirc/"bar/baz" );
>   p = fs::weakly_canonical(fs::current_path()/dir/"bar//../foo/bar/baz");
>   VERIFY( p == dirc/"bar/baz" );
>
>+#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
>   ec = bad_ec;
>   p = fs::weakly_canonical(dir/"foo//./bar///../biz/.", ec);
>   VERIFY( !ec );
>   VERIFY( p == dirc/"biz/" );
>+#endif
>   ec = bad_ec;
>   p = fs::weakly_canonical(dir/"foo/.//bar/././baz/.", ec);
>   VERIFY( !ec );
>diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h
>index 6940ae51aa6..aee56c3dbf7 100644
>--- a/libstdc++-v3/testsuite/util/testsuite_fs.h
>+++ b/libstdc++-v3/testsuite/util/testsuite_fs.h
>@@ -43,8 +43,10 @@ namespace test_fs = std::experimental::filesystem;
> #endif
>
> #ifndef _GLIBCXX_HAVE_SYMLINK
>+#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> #define NO_SYMLINKS
> #endif
>+#endif
>
> #if !defined (_GLIBCXX_HAVE_SYS_STATVFS_H) \
>   && !defined (_GLIBCXX_FILESYSTEM_IS_WINDOWS)
>-- 
>2.52.0
>
>



More information about the Libstdc++ mailing list