From 3246b4b0c2a25ad565a1f8b1225e55e242cb4d14 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 27 Mar 2026 12:57:33 -0700 Subject: [PATCH 1/3] Partially revert GH 5434 "Win8 baseline: Simplify `` API calls". This reverts only the alternatives to GetFileInformationByHandleEx() with FileIdInfo. All other changes, including the unconditional use of GetFileInformationByHandleEx() with FileStandardInfo to get NumberOfLinks in legacy filesys.cpp _Hard_links(), are being allowed to stand as correct. Note that that case matches what modern filesystem.cpp __std_fs_get_stats() has always done. --- stl/src/filesys.cpp | 33 +++++++++++++++++++++++++++++++++ stl/src/filesystem.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/stl/src/filesys.cpp b/stl/src/filesys.cpp index eff04e50ff6..cd4fa12bcfa 100644 --- a/stl/src/filesys.cpp +++ b/stl/src/filesys.cpp @@ -374,6 +374,7 @@ extern "C" _CRTIMP2_PURE space_info __CLRCALL_PURE_OR_CDECL _Statvfs(const wchar extern "C" _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Equivalent( const wchar_t* _Fname1, const wchar_t* _Fname2) noexcept { // test for equivalent file names // See GH-3571: File IDs are only guaranteed to be unique and stable while handles remain open +#ifdef _CRT_APP _FILE_ID_INFO _Info1 = {0}; _FILE_ID_INFO _Info2 = {0}; bool _Ok1 = false; @@ -401,6 +402,38 @@ extern "C" _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Equivalent( } else { // test existing files for equivalence return memcmp(&_Info1, &_Info2, sizeof(_FILE_ID_INFO)) == 0 ? 1 : 0; } +#else // ^^^ defined(_CRT_APP) / !defined(_CRT_APP) vvv + BY_HANDLE_FILE_INFORMATION _Info1 = {0}; + BY_HANDLE_FILE_INFORMATION _Info2 = {0}; + bool _Ok1 = false; + bool _Ok2 = false; + + HANDLE _Handle1 = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); + if (_Handle1 != INVALID_HANDLE_VALUE) { // get file1 info + _Ok1 = GetFileInformationByHandle(_Handle1, &_Info1) != 0; + } + + HANDLE _Handle2 = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); + if (_Handle2 != INVALID_HANDLE_VALUE) { // get file2 info + _Ok2 = GetFileInformationByHandle(_Handle2, &_Info2) != 0; + CloseHandle(_Handle2); + } + + if (_Handle1 != INVALID_HANDLE_VALUE) { + CloseHandle(_Handle1); + } + + if (!_Ok1 && !_Ok2) { + return -1; + } else if (!_Ok1 || !_Ok2) { + return 0; + } else { // test existing files for equivalence + return _Info1.dwVolumeSerialNumber == _Info2.dwVolumeSerialNumber + && _Info1.nFileIndexHigh == _Info2.nFileIndexHigh && _Info1.nFileIndexLow == _Info2.nFileIndexLow + ? 1 + : 0; + } +#endif // ^^^ !defined(_CRT_APP) ^^^ } extern "C" _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Link(const wchar_t* _Fname1, const wchar_t* _Fname2) noexcept { diff --git a/stl/src/filesystem.cpp b/stl/src/filesystem.cpp index 8abbc2d5fd0..75953890338 100644 --- a/stl/src/filesystem.cpp +++ b/stl/src/filesystem.cpp @@ -114,7 +114,31 @@ namespace { return __std_win_error::_Success; } - return __std_win_error{GetLastError()}; + __std_win_error _Last_error{GetLastError()}; + +#ifndef _CRT_APP + switch (_Last_error) { + case __std_win_error::_Not_supported: + case __std_win_error::_Invalid_parameter: + break; // try more things + default: + return _Last_error; // real error, bail to the caller + } + + // try GetFileInformationByHandle as a fallback + BY_HANDLE_FILE_INFORMATION _Info; + if (GetFileInformationByHandle(_Handle, &_Info)) { + _Id->VolumeSerialNumber = _Info.dwVolumeSerialNumber; + _CSTD memcpy(&_Id->FileId.Identifier[0], &_Info.nFileIndexHigh, 4); + _CSTD memcpy(&_Id->FileId.Identifier[4], &_Info.nFileIndexLow, 4); + _CSTD memset(&_Id->FileId.Identifier[8], 0, 8); + return __std_win_error::_Success; + } + + _Last_error = __std_win_error{GetLastError()}; +#endif // !defined(_CRT_APP) + + return _Last_error; } [[nodiscard]] _Success_(return == __std_win_error::_Success) __std_win_error From dca554e7a9aab020ab92303f9b1c39995be51427 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 27 Mar 2026 14:53:18 -0700 Subject: [PATCH 2/3] filesystem.cpp: Always build the fallback, improve comment. --- stl/src/filesystem.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stl/src/filesystem.cpp b/stl/src/filesystem.cpp index 75953890338..6df9e9650ac 100644 --- a/stl/src/filesystem.cpp +++ b/stl/src/filesystem.cpp @@ -116,7 +116,6 @@ namespace { __std_win_error _Last_error{GetLastError()}; -#ifndef _CRT_APP switch (_Last_error) { case __std_win_error::_Not_supported: case __std_win_error::_Invalid_parameter: @@ -125,7 +124,8 @@ namespace { return _Last_error; // real error, bail to the caller } - // try GetFileInformationByHandle as a fallback + // Some filesystems don't support FILE_ID_INFO's 128-bit file identifiers. + // Try GetFileInformationByHandle() as a fallback. BY_HANDLE_FILE_INFORMATION _Info; if (GetFileInformationByHandle(_Handle, &_Info)) { _Id->VolumeSerialNumber = _Info.dwVolumeSerialNumber; @@ -136,7 +136,6 @@ namespace { } _Last_error = __std_win_error{GetLastError()}; -#endif // !defined(_CRT_APP) return _Last_error; } From 625d039769996177320a883f7a46aea33d1a13d2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 27 Mar 2026 15:21:42 -0700 Subject: [PATCH 3/3] filesys.cpp: Add fallback behavior, improve comments. Previously, the legacy implementation had NO fallback behavior. For `_CRT_APP`, 128-bit file IDs were the only codepath. That might have been okay, depending on what filesystems were available, but it might have been a mistake. For non-App, 64-bit file indexes were the only codepath, so we weren't handling modern filesystems in an ideal way. I'm bringing the logic closer to the modern implementation. It isn't an exact match (we don't look at the error codes, so if neither file exists, we try the fallback only to discover again that neither file exists). However, it should be good enough for the legacy implementation, and a strict improvement over the status quo. --- stl/src/filesys.cpp | 91 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/stl/src/filesys.cpp b/stl/src/filesys.cpp index cd4fa12bcfa..d743268553b 100644 --- a/stl/src/filesys.cpp +++ b/stl/src/filesys.cpp @@ -374,66 +374,65 @@ extern "C" _CRTIMP2_PURE space_info __CLRCALL_PURE_OR_CDECL _Statvfs(const wchar extern "C" _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Equivalent( const wchar_t* _Fname1, const wchar_t* _Fname2) noexcept { // test for equivalent file names // See GH-3571: File IDs are only guaranteed to be unique and stable while handles remain open -#ifdef _CRT_APP - _FILE_ID_INFO _Info1 = {0}; - _FILE_ID_INFO _Info2 = {0}; - bool _Ok1 = false; - bool _Ok2 = false; - HANDLE _Handle1 = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle1 != INVALID_HANDLE_VALUE) { // get file1 info - _Ok1 = GetFileInformationByHandleEx(_Handle1, FileIdInfo, &_Info1, sizeof(_Info1)) != 0; - } - HANDLE _Handle2 = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle2 != INVALID_HANDLE_VALUE) { // get file2 info - _Ok2 = GetFileInformationByHandleEx(_Handle2, FileIdInfo, &_Info2, sizeof(_Info2)) != 0; - CloseHandle(_Handle2); - } - if (_Handle1 != INVALID_HANDLE_VALUE) { - CloseHandle(_Handle1); - } + bool _Ok1 = false; + bool _Ok2 = false; + int _Result = -1; // negative indicates error - if (!_Ok1 && !_Ok2) { - return -1; - } else if (!_Ok1 || !_Ok2) { - return 0; - } else { // test existing files for equivalence - return memcmp(&_Info1, &_Info2, sizeof(_FILE_ID_INFO)) == 0 ? 1 : 0; - } -#else // ^^^ defined(_CRT_APP) / !defined(_CRT_APP) vvv - BY_HANDLE_FILE_INFORMATION _Info1 = {0}; - BY_HANDLE_FILE_INFORMATION _Info2 = {0}; - bool _Ok1 = false; - bool _Ok2 = false; + { + // If we can get FILE_ID_INFO, use that as the source of truth. + _FILE_ID_INFO _Info1 = {0}; + _FILE_ID_INFO _Info2 = {0}; - HANDLE _Handle1 = _FilesysOpenFile(_Fname1, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle1 != INVALID_HANDLE_VALUE) { // get file1 info - _Ok1 = GetFileInformationByHandle(_Handle1, &_Info1) != 0; + if (_Handle1 != INVALID_HANDLE_VALUE) { + _Ok1 = GetFileInformationByHandleEx(_Handle1, FileIdInfo, &_Info1, sizeof(_Info1)) != 0; + } + + if (_Handle2 != INVALID_HANDLE_VALUE) { + _Ok2 = GetFileInformationByHandleEx(_Handle2, FileIdInfo, &_Info2, sizeof(_Info2)) != 0; + } + + if (_Ok1 && _Ok2) { // test existing files for equivalence + _Result = memcmp(&_Info1, &_Info2, sizeof(_FILE_ID_INFO)) == 0 ? 1 : 0; + } else if (_Ok1 || _Ok2) { // one file exists, the other doesn't + _Result = 0; + } } - HANDLE _Handle2 = _FilesysOpenFile(_Fname2, FILE_READ_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS); - if (_Handle2 != INVALID_HANDLE_VALUE) { // get file2 info - _Ok2 = GetFileInformationByHandle(_Handle2, &_Info2) != 0; - CloseHandle(_Handle2); + if (_Result < 0) { + // Some filesystems don't support FILE_ID_INFO's 128-bit file identifiers. + // Try GetFileInformationByHandle() as a fallback. + BY_HANDLE_FILE_INFORMATION _Info1 = {0}; + BY_HANDLE_FILE_INFORMATION _Info2 = {0}; + + if (_Handle1 != INVALID_HANDLE_VALUE) { + _Ok1 = GetFileInformationByHandle(_Handle1, &_Info1) != 0; + } + + if (_Handle2 != INVALID_HANDLE_VALUE) { + _Ok2 = GetFileInformationByHandle(_Handle2, &_Info2) != 0; + } + + if (_Ok1 && _Ok2) { // test existing files for equivalence + _Result = static_cast(_Info1.dwVolumeSerialNumber == _Info2.dwVolumeSerialNumber + && _Info1.nFileIndexHigh == _Info2.nFileIndexHigh + && _Info1.nFileIndexLow == _Info2.nFileIndexLow); + } else if (_Ok1 || _Ok2) { // one file exists, the other doesn't + _Result = 0; + } } if (_Handle1 != INVALID_HANDLE_VALUE) { CloseHandle(_Handle1); } - if (!_Ok1 && !_Ok2) { - return -1; - } else if (!_Ok1 || !_Ok2) { - return 0; - } else { // test existing files for equivalence - return _Info1.dwVolumeSerialNumber == _Info2.dwVolumeSerialNumber - && _Info1.nFileIndexHigh == _Info2.nFileIndexHigh && _Info1.nFileIndexLow == _Info2.nFileIndexLow - ? 1 - : 0; + if (_Handle2 != INVALID_HANDLE_VALUE) { + CloseHandle(_Handle2); } -#endif // ^^^ !defined(_CRT_APP) ^^^ + + return _Result; } extern "C" _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Link(const wchar_t* _Fname1, const wchar_t* _Fname2) noexcept {