https://gcc.gnu.org/g:8663c3db6e6177b96b7770b7d13ec7b74e1bac1c
commit r17-2309-g8663c3db6e6177b96b7770b7d13ec7b74e1bac1c Author: Jonathan Wakely <[email protected]> Date: Tue Jul 7 18:59:30 2026 +0100 libstdc++: Adjust error handling for filesystem::read_symlink on Windows Do not set errno in the helper functions __open_for_stat and __check_handle_type, because those are used from std::filesystem APIs which should set a std::error_code instead of changing errno. Move setting errno into the Windows-specific __stat_windows function and change the helper functions to use a std::error_code parameter instead. Also simplify the Windows implementation of fs::read_symlink by moving the error handling for non-symlinks into windows_read_symlink_handle. libstdc++-v3/ChangeLog: * src/c++17/fs_ops.cc (windows_read_symlink_handle): Use __detail::__is_handle_symlink and report an error for non-symlinks. (filesystem::read_symlink): Remove error handling for non-symlinks. * src/filesystem/ops-common.h (__detail::__open_for_stat): Use std::error_code parameter to report errors instead of setting errno. (__detail::__check_handle_type): Likewise. (__detail::__is_handle_symlink): Likewise. (__detail::__stat_windows): Pass std::error_code to helper functions and set errno to report errors. Reviewed-by: Tomasz KamiĆski <[email protected]> Diff: --- libstdc++-v3/src/c++17/fs_ops.cc | 26 +++++++------------- libstdc++-v3/src/filesystem/ops-common.h | 41 ++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc index 81bffc7b1513..31f5abfa6fc7 100644 --- a/libstdc++-v3/src/c++17/fs_ops.cc +++ b/libstdc++-v3/src/c++17/fs_ops.cc @@ -1257,6 +1257,13 @@ namespace std::error_code& ec, fs::path& result) { + if (!fs::__detail::__is_handle_symlink(link_handle.handle, ec)) + { + if (!ec) + ec.assign(EINVAL, std::generic_category()); // not a symlink + return; + } + PREPARSE_DATA_BUFFER reparse_buffer = nullptr; std::unique_ptr<char[]> big_buffer; @@ -1374,23 +1381,8 @@ fs::path fs::read_symlink(const path& p, error_code& ec) #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); + if (link_handle) + windows_read_symlink_handle(link_handle, ec, result); #else ec = std::make_error_code(std::errc::function_not_supported); #endif diff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h index cdb4a1ca0ca8..df610b74e402 100644 --- a/libstdc++-v3/src/filesystem/ops-common.h +++ b/libstdc++-v3/src/filesystem/ops-common.h @@ -113,7 +113,9 @@ namespace __detail using stat_type = struct ::__stat64; - inline HANDLE __open_for_stat(const wchar_t* path, bool following_symlinks) + inline HANDLE + __open_for_stat(const wchar_t* path, bool following_symlinks, + std::error_code& ec) { constexpr auto share_flags = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; @@ -124,10 +126,7 @@ namespace __detail = 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(); - } + ec = std::__last_system_error(); return handle; } @@ -138,14 +137,15 @@ namespace __detail // 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) + inline FileType + __check_handle_type(HANDLE handle, bool following_symlinks, error_code& ec) { #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(); + ec = std::__last_system_error(); return FileType::Err; } // A directory symlink has both DIRECTORY and REPARSE_POINT set, @@ -160,32 +160,37 @@ namespace __detail return FileType::Other; } - // -1 error, 0 not a symlink, 1 a symlink - inline int __is_handle_symlink(HANDLE handle) + // If no error occurs and `handle` represents a symlink, returns true. + // Otherwise, returns false. Sets `ec` if an error occurred. + inline bool __is_handle_symlink(HANDLE handle, std::error_code& ec) { - FileType type = __check_handle_type(handle, false); - if (type == FileType::Err) - return -1; - return type == FileType::Link; + return __check_handle_type(handle, false, ec) == FileType::Link; } - inline int __stat_windows(const wchar_t* path, stat_type* buffer, - bool following_symlinks) + inline int + __stat_windows(const wchar_t* path, stat_type* buffer, + bool following_symlinks) { - HANDLE handle = __open_for_stat(path, following_symlinks); + std::error_code ec; + HANDLE handle = __open_for_stat(path, following_symlinks, ec); if (handle == INVALID_HANDLE_VALUE) - return -1; + { + errno = ec.default_error_condition().value(); + return -1; + } // Manually check for directory or symlink, because _fstat does not. - FileType type = __check_handle_type(handle, following_symlinks); + FileType type = __check_handle_type(handle, following_symlinks, ec); if (type == FileType::Err) { CloseHandle(handle); + errno = ec.default_error_condition().value(); return -1; } int fd = ::_open_osfhandle((intptr_t)handle, _O_RDONLY); if (fd == -1) { CloseHandle(handle); + errno = ec.default_error_condition().value(); return -1; } int stat_result = ::_fstat64(fd, buffer);
