[PATCH v5] libstdc++: Add symlink support on Windows
Jonathan Wakely
jwakely@redhat.com
Fri Jun 26 18:15:42 GMT 2026
On Fri, 26 Jun 2026 at 17:59, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> From: Adam Wood <adam.wood@mines.sdsmt.edu>
>
> Tested on x86_64-w64-mingw32 on Windows 11.
>
> The 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.
>
> 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
> unprivileged flag. This is how MSVC STL does it as well.
>
> The patch does not support junctions or mount points.
>
> 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 [_GLIBCXX_FILESYSTEM_IS_WINDOWS]
> (S_IFLNK, S_ISLNK): Define.
> (__detail::__open_for_stat): New helper function for stat and
> lstat.
> (__detail::FileType): New enum type.
> (__detail::__check_handle_type): New helper function for stat
> and lstat.
> (__detail::__is_handle_symlink): New helper function for
> 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.
>
> Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
> ---
>
> Tested x86_64-linux.
>
> Tested x86_64-w64-mingw32 running on Wine 11.0 and Windows 11.
>
> v5: Fix stat and lstat emulation for mingw-w64 13.0.0 which does not set
> S_IFDIR for directories. Adjust weakly_canonical.cc test for different
> behaviour on Windows.
>
> libstdc++-v3/src/c++17/fs_ops.cc | 210 +++++++++++++++++-
> libstdc++-v3/src/filesystem/ops-common.h | 106 ++++++++-
> .../27_io/filesystem/operations/canonical.cc | 4 +-
> .../27_io/filesystem/operations/copy.cc | 7 +-
> .../filesystem/operations/weakly_canonical.cc | 17 +-
> libstdc++-v3/testsuite/util/testsuite_fs.h | 2 +-
> 6 files changed, 328 insertions(+), 18 deletions(-)
>
> diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc
> index 454962c75219..387869751c58 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 // Implies CreateSymbolicLinkW support.
> + 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.
> + 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 304d5896d026..cdb4a1ca0ca8 100644
> --- a/libstdc++-v3/src/filesystem/ops-common.h
> +++ b/libstdc++-v3/src/filesystem/ops-common.h
> @@ -105,6 +105,101 @@ _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
> + = CreateFileW(path, 0, share_flags, 0, OPEN_EXISTING, file_flags, 0);
> +
> + if (handle == INVALID_HANDLE_VALUE)
> + {
> + // CreateFileW does not set errno.
> + errno = std::__last_system_error().default_error_condition().value();
> + }
> +
> + return handle;
> + }
> +
> + // _fstat64 in mingw-w64 does not know about symlinks and before v14.0.0
> + // it does not know about directories.
> + // We use GetFileInformationByHandleEx to check whether the HANDLE refers
> + // to a symlink or directory, then fix the result of _fstat64 accordingly.
> + enum class FileType { Err = -1, Dir = S_IFDIR, Link = S_IFLNK, Other = 0 };
> +
> + inline FileType __check_handle_type(HANDLE handle, bool following_symlinks)
> + {
> +#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 FileType::Err;
> + }
> + // A directory symlink has both DIRECTORY and REPARSE_POINT set,
> + // so to detect a symlink we need to check for REPARSE_POINT first.
> + if (!following_symlinks)
> + if (type_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT
> + && type_info.ReparseTag == IO_REPARSE_TAG_SYMLINK)
> + return FileType::Link;
> + if (type_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
> + return FileType::Dir;
> +#endif
> + return FileType::Other;
> + }
> +
> + // -1 error, 0 not a symlink, 1 a symlink
> + inline int __is_handle_symlink(HANDLE handle)
> + {
> + FileType type = __check_handle_type(handle, false);
> + if (type == FileType::Err)
> + return -1;
> + return type == FileType::Link;
> + }
> +
> + 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 directory or symlink, because _fstat does not.
> + FileType type = __check_handle_type(handle, following_symlinks);
> + if (type == FileType::Err)
> + {
> + 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 (stat_result != -1 && type != FileType::Other)
> + {
> + // Clear the previous file type.
> + buffer->st_mode &= ~S_IFMT;
> + buffer->st_mode |= (::mode_t)type;
> + }
> + ::_close(fd);
> + return stat_result;
> + }
> +}
> +#endif
> namespace __gnu_posix
> {
> #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> @@ -121,13 +216,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 884b6da438a3..74d6fd16e21f 100644
> --- a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
> +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc
> @@ -113,14 +113,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 1ca4a44da59e..03960fa90f24 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 6c187c73b79f..9cfdf6231a8b 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,32 @@ test01()
> fs::path p;
>
> #ifndef NO_SYMLINKS
> - fs::create_symlink("../bar", foo/"bar");
> + fs::create_directory_symlink(fs::path("..")/"bar", foo/"bar");
> +
> + // This fails when under under Wine:
> + p = fs::canonical(dir/"foo//./bar/.");
This seems to be a Wine bug, reported as
https://bugs.winehq.org/show_bug.cgi?id=59922
> + VERIFY( p == dirc/"bar" );
>
> p = fs::weakly_canonical(dir/"foo//./bar///../biz/.");
> +#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> VERIFY( p == dirc/"biz/" );
> +#else
> + VERIFY( p == dirc/"foo\\biz\\" ); // XXX is this right? check MSVC
> +#endif
> +
> p = fs::weakly_canonical(dir/"foo/.//bar/././baz/.");
> - VERIFY( p == dirc/"bar/baz" );
> + VERIFY( p == dirc/"bar"/"baz" );
> p = fs::weakly_canonical(fs::current_path()/dir/"bar//../foo/bar/baz");
> VERIFY( p == dirc/"bar/baz" );
>
> ec = bad_ec;
> p = fs::weakly_canonical(dir/"foo//./bar///../biz/.", ec);
> VERIFY( !ec );
> +#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
> VERIFY( p == dirc/"biz/" );
> +#else
> + VERIFY( p == dirc/"foo\\biz\\" ); // XXX is this right? check MSVC
> +#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 fa099b0986bc..f1cf28ce4dd9 100644
> --- a/libstdc++-v3/testsuite/util/testsuite_fs.h
> +++ b/libstdc++-v3/testsuite/util/testsuite_fs.h
> @@ -42,7 +42,7 @@ namespace test_fs = std::experimental::filesystem;
> #include <stdlib.h> // mkstemp
> #endif
>
> -#ifndef _GLIBCXX_HAVE_SYMLINK
> +#if !defined _GLIBCXX_HAVE_SYMLINK && !defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
> #define NO_SYMLINKS
> #endif
>
> --
> 2.54.0
>
More information about the Libstdc++
mailing list