On Sun, 21 Sept 2025 at 23:09, Adam Wood wrote:
>
> I have implemented the feedback from Jonathan Wakely in this patch.
>
> 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.

Thanks for the update.

If I understand correctly, this only adds support for symlinks, but
not junctions, right? I don't really know what junctions are, only
that they exist on Windows and behave a bit like directory symlinks.
It's not a problem if they're unsupported, as this is still a big
improvement on what we have now. I'm just wondering if we should
document somewhere that we support symlinks and not junctions.

I was going to ask if filesystem::copy_symlink works correctly with
your changes, but I realized we don't have any tests for copy_symlink,
at all. I've filed a bug for that. Once we add those tests, we can
make sure that they work on Windows, but the lack of any tests
verifying that function is not your problem.

I've just noticed that in our email for the first version of the patch
you said you wanted to assign copyright to the FSF. Have you submitted
a copyright assignment form and signed the paperwork with the FSF?
If not, we'll need to get that process started (sorry, I should have
done that much earlier so it would be finished by now).


>
> libstdc++-v3/Changelog:
>
>         * src/c++17/fs_ops.cc:
>         Include <winioctl.h> for FSCTL_GET_REPARSE_POINT.
>         Include <ntdef.h> for REPARSE_DATA_BUFFER.
>         (path_find_dotdot_component): 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.
>         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.
>         * 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              | 267 +++++++++++++++++-
>  libstdc++-v3/src/filesystem/ops-common.h      |  81 +++++-
>  .../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, 341 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..97cdc5ab2dc 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 {
> @@ -78,6 +80,24 @@ fs::absolute(const path& p)
>    return ret;
>  }
>
> +#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> +namespace
> +{
> +  fs::path::iterator
> +  path_find_dotdot_component(const fs::path& p)
> +  {
> +    for (auto it = p.begin(); it != p.end(); ++it)
> +      {
> +       if (it->native() == L"..")
> +         {
> +           return it;
> +         }
> +      }
> +    return p.end();
> +  }
> +}
> +#endif
> +
>  fs::path
>  fs::absolute(const path& p, error_code& ec)
>  {
> @@ -97,6 +117,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 +130,24 @@ 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.
> +  auto dotdot_it = path_find_dotdot_component(p);
> +  if (dotdot_it != p.end() && dotdot_it != p.begin())
> +    {
> +      for (auto it = p.begin(); it != dotdot_it; ++it)
> +       {
> +         before_dotdot_p /= *it;
> +       }
> +      for (auto it = dotdot_it; it != p.end(); ++it)
> +       {
> +         after_dotdot_p /= *it;
> +       }
> +      s = before_dotdot_p.native();
> +    }
> +
>    uint32_t len = 1024;
>    wstring buf;
>    do
> @@ -123,6 +163,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 +205,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 +231,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 (path_find_dotdot_component(pa) == pa.end() && !exists(pa, ec))
> +#else
>    if (!exists(pa, ec))
> +#endif
>      {
>        if (!ec)
>         ec = make_error_code(std::errc::no_such_file_or_directory);
> @@ -234,7 +283,17 @@ fs::canonical(const path& p, error_code& ec)
>         {
>           result /= f;
>
> -         if (is_symlink(result, ec))
> +         auto st = symlink_status(result, ec);
> +#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> +         if (!exists(st))
> +           {
> +             if (!ec)
> +               ec.assign(ENOENT, std::generic_category());
> +             result.clear();
> +             return result;
> +           }
> +#endif
> +         if (is_symlink(st))
>             {
>               path link = read_symlink(result, ec);
>               if (!ec)
> @@ -644,6 +703,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;

In the mingw-w64 version of winbase.h I see that
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is defined conditionally
depending on the Windows version:

#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
#if _WIN32_WINNT >= 0x0600

#define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)

 WINBASEAPI BOOLEAN APIENTRY CreateSymbolicLinkA (LPCSTR
lpSymlinkFileName, LPCSTR lpTargetFileName, D
WORD dwFlags);
 WINBASEAPI BOOLEAN APIENTRY CreateSymbolicLinkW (LPCWSTR
lpSymlinkFileName, LPCWSTR lpTargetFileName,
DWORD dwFlags);

Does >= 0x0600 mean Windows Server 2008 and later? I guess that's
probably OK as a minimum supported version, but we could check #ifdef
SYMBOLIC_LINK_FLAG_DIRECTORY in windows_create_symlink
windows_read_symlink_handle (and other new functions?) and just report
an error if we're on an older version.

Although the mingw-w64 headers seem to define it unconditionally, it
looks like SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE will only work
on Windows 10 and later. What happens if we pass that flag to
CreateSymbolicLinkW on older versions of Windows? Is it just ignored?


> +    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 +757,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 +812,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 +928,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 +1296,73 @@ fs::proximate(const path& p, const path& base, 
> error_code& ec)
>    return result;
>  }
>
> +#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
> +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 +1418,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 = __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 +1480,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 +1515,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 +1582,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 4feacfdb932..36cc99b7e0b 100644
> --- a/libstdc++-v3/src/filesystem/ops-common.h
> +++ b/libstdc++-v3/src/filesystem/ops-common.h
> @@ -105,6 +105,80 @@ _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)
> +  {
> +    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;
> +  }
> +}
> +#endif
>  namespace __gnu_posix
>  {
>  #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> @@ -121,13 +195,10 @@ 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);
> -  }
> +  { return __detail::__stat_windows(path, buffer, false); }
>
>    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 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