Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 49 additions & 17 deletions stl/src/filesys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,33 +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
_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);

bool _Ok1 = false;
bool _Ok2 = false;
int _Result = -1; // negative indicates error

{
// If we can get FILE_ID_INFO, use that as the source of truth.
_FILE_ID_INFO _Info1 = {0};
_FILE_ID_INFO _Info2 = {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 = GetFileInformationByHandleEx(_Handle2, FileIdInfo, &_Info2, sizeof(_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<int>(_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 memcmp(&_Info1, &_Info2, sizeof(_FILE_ID_INFO)) == 0 ? 1 : 0;
if (_Handle2 != INVALID_HANDLE_VALUE) {
CloseHandle(_Handle2);
}

return _Result;
}

extern "C" _CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Link(const wchar_t* _Fname1, const wchar_t* _Fname2) noexcept {
Expand Down
25 changes: 24 additions & 1 deletion stl/src/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,30 @@ namespace {
return __std_win_error::_Success;
}

return __std_win_error{GetLastError()};
__std_win_error _Last_error{GetLastError()};

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
}

// 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;
_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()};

return _Last_error;
}

[[nodiscard]] _Success_(return == __std_win_error::_Success) __std_win_error
Expand Down