diff options
author | Lennox Shou Hao Ho <lennoxhoe@gmail.com> | 2024-07-29 21:09:27 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-07-30 12:55:10 +0100 |
commit | 658193658f05e9a8ebf0bce8bab15555f43bfee1 (patch) | |
tree | 02c263cdfe6138da48e6ec350c7a98f4f5246e1c | |
parent | 0c382da0943dc7d14455ba2ada2f620a25bd1366 (diff) | |
download | gcc-658193658f05e9a8ebf0bce8bab15555f43bfee1.zip gcc-658193658f05e9a8ebf0bce8bab15555f43bfee1.tar.gz gcc-658193658f05e9a8ebf0bce8bab15555f43bfee1.tar.bz2 |
libstdc++: Fix fs::hard_link_count behaviour on MinGW [PR113663]
std::filesystem::hard_link_count() always returns 1 on
mingw-w64ucrt-11.0.1-r3 on Windows 10 19045
hard_link_count() queries _wstat64() on MinGW-w64
The MSFT documentation claims _wstat64() will always return 1 *non*-NTFS volumes
https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/14h5k7ff(v=vs.120)
My tests suggest that is not always true -
hard_link_count()/_wstat64() still returns 1 on NTFS.
GetFileInformationByHandle does return the correct result of 2.
Please see the PR for a minimal repro.
This patch changes the Windows implementation to always call
GetFileInformationByHandle.
PR libstdc++/113663
libstdc++-v3/ChangeLog:
* src/c++17/fs_ops.cc (fs::equivalent): Moved helper class
auto_handle to anonymous namespace as auto_win_file_handle.
(fs::hard_link_count): Changed Windows implementation to use
information provided by GetFileInformationByHandle which is more
reliable.
* testsuite/27_io/filesystem/operations/hard_link_count.cc: New
test.
Signed-off-by: "Lennox" Shou Hao Ho <lennoxhoe@gmail.com>
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
-rw-r--r-- | libstdc++-v3/src/c++17/fs_ops.cc | 59 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc | 37 |
2 files changed, 74 insertions, 22 deletions
diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc index 07bc2a0..81227c4 100644 --- a/libstdc++-v3/src/c++17/fs_ops.cc +++ b/libstdc++-v3/src/c++17/fs_ops.cc @@ -822,6 +822,34 @@ fs::equivalent(const path& p1, const path& p2) return result; } +#if _GLIBCXX_FILESYSTEM_IS_WINDOWS +namespace +{ + // An RAII type that opens a handle for an existing file. + struct auto_win_file_handle + { + explicit + auto_win_file_handle(const fs::path& p_) + : handle(CreateFileW(p_.c_str(), 0, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)) + { } + + ~auto_win_file_handle() + { if (*this) CloseHandle(handle); } + + explicit operator bool() const + { return handle != INVALID_HANDLE_VALUE; } + + bool get_info() + { return GetFileInformationByHandle(handle, &info); } + + HANDLE handle; + BY_HANDLE_FILE_INFORMATION info; + }; +} +#endif + bool fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept { @@ -858,27 +886,8 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev) return false; - struct auto_handle { - explicit auto_handle(const path& p_) - : handle(CreateFileW(p_.c_str(), 0, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)) - { } - - ~auto_handle() - { if (*this) CloseHandle(handle); } - - explicit operator bool() const - { return handle != INVALID_HANDLE_VALUE; } - - bool get_info() - { return GetFileInformationByHandle(handle, &info); } - - HANDLE handle; - BY_HANDLE_FILE_INFORMATION info; - }; - auto_handle h1(p1); - auto_handle h2(p2); + auto_win_file_handle h1(p1); + auto_win_file_handle h2(p2); if (!h1 || !h2) { if (!h1 && !h2) @@ -982,7 +991,13 @@ fs::hard_link_count(const path& p) std::uintmax_t fs::hard_link_count(const path& p, error_code& ec) noexcept { -#ifdef _GLIBCXX_HAVE_SYS_STAT_H +#if _GLIBCXX_FILESYSTEM_IS_WINDOWS + auto_win_file_handle h(p); + if (h && h.get_info()) + return static_cast<uintmax_t>(h.info.nNumberOfLinks); + ec = __last_system_error(); + return static_cast<uintmax_t>(-1); +#elif defined _GLIBCXX_HAVE_SYS_STAT_H return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), static_cast<uintmax_t>(-1)); #else diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc new file mode 100644 index 0000000..8b2fb4f --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc @@ -0,0 +1,37 @@ +// { dg-do run { target c++17 } } +// { dg-require-filesystem-ts "" } + +#include <filesystem> +#include <testsuite_hooks.h> +#include <testsuite_fs.h> + +namespace fs = std::filesystem; + +void test01() +{ + // PR libstdc++/113663 + + fs::path p1 = __gnu_test::nonexistent_path(); + VERIFY( !fs::exists(p1) ); + + __gnu_test::scoped_file f1(p1); + VERIFY( fs::exists(p1) ); + + VERIFY( fs::hard_link_count(p1) == 1 ); + + fs::path p2 = __gnu_test::nonexistent_path(); + VERIFY( !fs::exists(p2) ); + + fs::create_hard_link(p1, p2); + __gnu_test::scoped_file f2(p2, __gnu_test::scoped_file::adopt_file); + VERIFY( fs::exists(p2) ); + + VERIFY( fs::hard_link_count(p1) == 2 ); + VERIFY( fs::hard_link_count(p2) == 2 ); +} + +int +main() +{ + test01(); +} |