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.
---
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