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);

Reply via email to