On Fri, Jul 10, 2026 at 1:34 PM Jonathan Wakely <[email protected]> wrote:
> 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.
>
This simplifies a lot. Thanks.
>
> 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.
> ---
>
> Tested x86_64-linux and x86_64-w64-mingw32 cross with Wine testing.
>
> 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);
> --
> 2.55.0
>
>