I forgot to wrap windows_create_symlink in a #ifdef in the first version,
so I added the #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS. That is the only change.

This patch adds symlink support on Windows.
I tested it on x86_64-w64-mingw32 with msys2.
A few notes about this patch:

1. Symlinks can only be created while running as admin, or if Developer Mode
   is enabled on Windows 10/11.
2. Symlinks on Windows are either file symlinks or directory symlinks.
   I decided that fs::create_symlink should only be used for creating file
   symlinks, unlike the implementation on Linux. I am not sure if this
   was the correct decision.
3. Windows seems to process dotdots in paths before it resolves symlinks.
   This results in errors.
   For example, in test03 for fs::canonical, there is a directory setup with
   dir, dir/foo, and dir/bar. dir/foo contains a symlink, baz,
   which points to ../bar. The test checks that
   fs::canonical(dir/foo/baz/../bar) equals dir/foo/../bar/../bar or dir/bar.
   However, Windows interprets the path as dir/foo/bar.
   This is the reason my patch treats paths with a dotdot carefully
   in fs::absolute and fs::canonical.

This is my first contribution to libstdc++ and I would prefer the FSF hold
copyright of the code rather than me.

libstdc++-v3/Changelog:

        * src/c++17/fs_ops.cc:
        Include <winioctl.h> for FSCTL_GET_REPARSE_POINT.
        (path_starts_with_dotdot): New helper function for fs::absolute.
        (fs::absolute): Call GetFullPathNameW on part of path before ".."
        and then append the rest to the result.
        (fs::canonical): Don't use lexically_normal in call to fs::absolute.
        Don't check if path exists if it contains "..".
        Check that component of path exists if it isn't empty or "." or "..".
        (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.
        (__gnu_posix::__open_for_stat): New helper function for stat and lstat.
        (__gnu_posix::__is_handle_symlink): New helper function for
        stat, lstat, and fs::read_symlink.
        (__gnu_posix::__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.
        * 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.
        * testsuite/util/testsuite_fs.h: Do not define NO_SYMLINKS on Windows.
---
 libstdc++-v3/src/c++17/fs_ops.cc              | 291 +++++++++++++++++-
 libstdc++-v3/src/filesystem/ops-common.h      |  75 ++++-
 .../27_io/filesystem/operations/canonical.cc  |   2 +-
 .../27_io/filesystem/operations/copy.cc       |   7 +-
 .../filesystem/operations/weakly_canonical.cc |   2 +-
 libstdc++-v3/testsuite/util/testsuite_fs.h    |   2 +
 6 files changed, 359 insertions(+), 20 deletions(-)

diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc
index 4f188153ae3..a8a2f19d1c2 100644
--- a/libstdc++-v3/src/c++17/fs_ops.cc
+++ b/libstdc++-v3/src/c++17/fs_ops.cc
@@ -56,6 +56,7 @@
 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
+# include <winioctl.h> // FSCTL_GET_REPARSE_POINT
 #endif
 
 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
@@ -78,6 +79,23 @@ fs::absolute(const path& p)
   return ret;
 }
 
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+namespace
+{
+  bool
+  path_starts_with_dotdot(std::wstring_view p)
+  {
+    if (p.size() < 2)
+      return false;
+    if (p[0] == L'.' && p[1] == L'.')
+      return true;
+    if (p.size() < 3 || !(p[0] == L'/' || p[0] == L'\\'))
+      return false;
+    return p[1] == L'.' && p[2] == L'.';
+  }
+}
+#endif
+
 fs::path
 fs::absolute(const path& p, error_code& ec)
 {
@@ -97,6 +115,8 @@ fs::absolute(const path& p, error_code& ec)
 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
   // s must remain null-terminated
   wstring_view s = p.native();
+  path after_dotdot_p;
+  path before_dotdot_p;
 
   if (p.has_root_directory()) // implies !p.has_root_name()
     {
@@ -108,6 +128,25 @@ fs::absolute(const path& p, error_code& ec)
       s.remove_prefix(std::min(s.length(), pos) - 1);
     }
 
+
+  // GetFullPathNameW does not work correctly with a .. right after a symlink,
+  // so if we have a .. in the path, run GetFullPathNameW on the part before
+  // the dotdot, and then append the rest.
+  if (!path_starts_with_dotdot(s) && s.find(L"..") != wstring_view::npos)
+    {
+      bool before_dotdot = true;
+      for (const auto& component : p)
+       {
+         if (before_dotdot && !wcscmp(component.c_str(), L".."))
+               before_dotdot = false;
+         if (before_dotdot)
+           before_dotdot_p /= component;
+         else
+           after_dotdot_p /= component;
+       }
+      s = before_dotdot_p.native();
+    }
+
   uint32_t len = 1024;
   wstring buf;
   do
@@ -123,6 +162,9 @@ fs::absolute(const path& p, error_code& ec)
     ec = __last_system_error();
   else
     ret = std::move(buf);
+
+  if (!after_dotdot_p.empty())
+    ret /= after_dotdot_p;
 #else
   ret = current_path(ec);
   ret /= p;
@@ -162,11 +204,7 @@ fs::path
 fs::canonical(const path& p, error_code& ec)
 {
   path result;
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-  const path pa = absolute(p.lexically_normal(), ec);
-#else
   const path pa = absolute(p, ec);
-#endif
   if (ec)
     return result;
 
@@ -192,7 +230,17 @@ fs::canonical(const path& p, error_code& ec)
     }
 #endif
 
+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  // Windows handles relative paths after symlinks incorrectly.
+  // If we have a .. after a symlink, the .. will be cancelled before the
+  // symlink is resolved.  For example, if we have baz -> ../bar,
+  // and we have the path dir/foo/baz/../bar, Linux would interpret this as
+  // dir/foo/../bar/../bar = dir/bar, but Windows thinks the path is
+  // dir/foo/bar.  This leads to possible false negatives using exists.
+  if (pa.native().find(L"..") == std::wstring::npos && !exists(pa, ec))
+#else
   if (!exists(pa, ec))
+#endif
     {
       if (!ec)
        ec = make_error_code(std::errc::no_such_file_or_directory);
@@ -234,7 +282,15 @@ fs::canonical(const path& p, error_code& ec)
        {
          result /= f;
 
-         if (is_symlink(result, ec))
+         auto st = symlink_status(result, ec);
+         if (!exists(st))
+           {
+             if (!ec)
+               ec.assign(ENOENT, std::generic_category());
+             result.clear();
+             return result;
+           }
+         if (is_symlink(st))
            {
              path link = read_symlink(result, ec);
              if (!ec)
@@ -644,6 +700,44 @@ 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
+  {
+    auto symlink_flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+    if (target_type == fs::file_type::directory)
+      symlink_flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+    // 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_flags))
+      ec.clear();
+    else
+      ec = std::__last_system_error();
+  }
+}
+#endif
 
 void
 fs::create_directory_symlink(const path& to, const path& new_symlink)
@@ -660,7 +754,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 +809,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 +925,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
+                         | (FILE_FLAG_OPEN_REPARSE_POINT * !follow_symlink)),
+                        0)),
       ec(ec)
     {
       if (handle == INVALID_HANDLE_VALUE)
@@ -1193,6 +1293,100 @@ fs::proximate(const path& p, const path& base, 
error_code& ec)
   return result;
 }
 
+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
+namespace
+{
+  // This would be defined in <ntifs.h> if it were included.
+  typedef struct _REPARSE_DATA_BUFFER {
+    ULONG  ReparseTag;
+    USHORT ReparseDataLength;
+    USHORT Reserved;
+    union {
+      struct {
+       USHORT SubstituteNameOffset;
+       USHORT SubstituteNameLength;
+       USHORT PrintNameOffset;
+       USHORT PrintNameLength;
+       ULONG  Flags;
+       WCHAR  PathBuffer[1];
+      } SymbolicLinkReparseBuffer;
+      struct {
+       USHORT SubstituteNameOffset;
+       USHORT SubstituteNameLength;
+       USHORT PrintNameOffset;
+       USHORT PrintNameLength;
+       WCHAR  PathBuffer[1];
+      } MountPointReparseBuffer;
+      struct {
+       UCHAR DataBuffer[1];
+      } GenericReparseBuffer;
+    } DUMMYUNIONNAME;
+  } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+  void
+  windows_read_symlink_handle(auto_win_file_handle& link_handle,
+                             std::error_code& ec,
+                             fs::path& result)
+  {
+    REPARSE_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<REPARSE_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<REPARSE_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 +1442,25 @@ fs::path fs::read_symlink(const path& p, error_code& ec)
        bufsz *= 2;
     }
   while (true);
+#elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  auto_win_file_handle link_handle(p.c_str(), ec, false);
+  if (!link_handle)
+    return result;
+
+  int is_symlink = posix::__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 +1504,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 +1539,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.
+  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 +1606,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, then.
+  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 4feacfdb932..424bab9b55d 100644
--- a/libstdc++-v3/src/filesystem/ops-common.h
+++ b/libstdc++-v3/src/filesystem/ops-common.h
@@ -120,15 +120,80 @@ namespace __gnu_posix
 
   using stat_type = struct ::__stat64;
 
-  inline int stat(const wchar_t* path, stat_type* buffer)
-  { return ::_wstat64(path, buffer); }
+#define S_IFLNK 0xC000
+#define        S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)
 
-  inline int lstat(const wchar_t* path, stat_type* buffer)
+  inline HANDLE __open_for_stat(const wchar_t* path, bool following_symlinks)
   {
-    // FIXME: symlinks not currently supported
-    return stat(path, buffer);
+    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)
+  {
+    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;
+  }
+
+  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;
+  }
+
+  inline int stat(const wchar_t* path, stat_type* buffer)
+  { return __stat_windows(path, buffer, true); }
+
+  inline int lstat(const wchar_t* path, stat_type* buffer)
+  { return __stat_windows(path, buffer, false); }
+
   using ::mode_t;
 
   inline int chmod(const wchar_t* path, mode_t mode)
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc 
b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
index 6eb7ce28928..6005e423852 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
@@ -118,7 +118,7 @@ test03()
   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 289bef6160b..50c7b0d1577 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 6085d5568e6..1c63bf4834d 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc
@@ -40,7 +40,7 @@ test01()
   fs::path p;
 
 #ifndef NO_SYMLINKS
-  fs::create_symlink("../bar", foo/"bar");
+  fs::create_directory_symlink("../bar", foo/"bar");
 
   p = fs::weakly_canonical(dir/"foo//./bar///../biz/.");
   VERIFY( p == dirc/"biz/" );
diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h 
b/libstdc++-v3/testsuite/util/testsuite_fs.h
index 9cf400d87db..cc0264b2563 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.50.0

Reply via email to