aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennox Shou Hao Ho <lennoxhoe@gmail.com>2024-07-29 21:09:27 +0100
committerJonathan Wakely <redi@gcc.gnu.org>2024-07-30 12:55:10 +0100
commit658193658f05e9a8ebf0bce8bab15555f43bfee1 (patch)
tree02c263cdfe6138da48e6ec350c7a98f4f5246e1c
parent0c382da0943dc7d14455ba2ada2f620a25bd1366 (diff)
downloadgcc-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.cc59
-rw-r--r--libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc37
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();
+}