Skip to content

Commit 6dd653f

Browse files
committed
same: windows: use BY_HANDLE_FILE_INFORMATION to accurately compare paths
.st_ino is always zero on Windows
1 parent 26f0d63 commit 6dd653f

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

src/equivalent.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ namespace Filesystem = std::filesystem;
1919
#include <fcntl.h> // AT_* constants for statx()
2020
#endif
2121

22+
#if defined(_WIN32)
23+
#define WIN32_LEAN_AND_MEAN
24+
#include <Windows.h>
25+
#endif
26+
2227
#endif
2328

2429

@@ -38,6 +43,43 @@ bool fs_equivalent(std::string_view path1, std::string_view path2)
3843

3944
#else
4045

46+
#if defined(_WIN32)
47+
// FUTURE: GetFileInformationByName Windows ~24H2
48+
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyname
49+
// https://github.com/rust-lang/rust/issues/130169
50+
//
51+
// for now use GetFileInformationByHandle
52+
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileinformationbyhandle#remarks
53+
// FILE_FLAG_BACKUP_SEMANTICS to allow opening directories
54+
55+
HANDLE h1 = CreateFileW(fs_win32_to_wide(path1).data(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr,
56+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
57+
if(h1 == INVALID_HANDLE_VALUE) {
58+
fs_print_error(path1, __func__);
59+
return false;
60+
}
61+
62+
HANDLE h2 = CreateFileW(fs_win32_to_wide(path2).data(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr,
63+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
64+
if(h2 == INVALID_HANDLE_VALUE) {
65+
fs_print_error(path2, __func__);
66+
CloseHandle(h1);
67+
return false;
68+
}
69+
70+
BY_HANDLE_FILE_INFORMATION f1, f2;
71+
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information
72+
BOOL ok1 = GetFileInformationByHandle(h1, &f1);
73+
BOOL ok2 = GetFileInformationByHandle(h2, &f2);
74+
CloseHandle(h1);
75+
CloseHandle(h2);
76+
if(ok1 && ok2) {
77+
return f1.dwVolumeSerialNumber == f2.dwVolumeSerialNumber &&
78+
f1.nFileIndexHigh == f2.nFileIndexHigh &&
79+
f1.nFileIndexLow == f2.nFileIndexLow;
80+
}
81+
82+
#else
4183
int r1 = 0;
4284
int r2 = 0;
4385

@@ -65,6 +107,7 @@ bool fs_equivalent(std::string_view path1, std::string_view path2)
65107
return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino;
66108
}
67109

110+
#endif
68111
#endif
69112

70113
fs_print_error(path1, path2, __func__, ec);

src/inquire.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ int fs_st_dev(std::string_view path)
119119

120120
#endif
121121

122-
if((r == 0) || errno == ENOSYS){
122+
if(r == 0 || errno == ENOSYS){
123123
if(struct stat s; !stat(path.data(), &s))
124124
return s.st_dev;
125125
}
@@ -132,6 +132,10 @@ int fs_st_dev(std::string_view path)
132132
int fs_inode(std::string_view path)
133133
{
134134
// inode number of the file or directory
135+
//
136+
// Windows: .st_ino is always zero.
137+
// See source code for fs_equivalent() for how to use BY_HANDLE_FILE_INFORMATION
138+
// with GetFileInformationByHandle().
135139
int r = 0;
136140

137141
#if defined(STATX_INO) && defined(USE_STATX)

0 commit comments

Comments
 (0)