[gcc r17-2309] libstdc++: Adjust error handling for filesystem::read_symlink on Windows

Jonathan Wakely via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <20260710135930.ECB0A4BA2E21__15055.6203139544$1783691976$gmane$org@sourceware.org>
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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.