diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 85e6c1cddfba..ce7d55916326 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -2475,7 +2475,6 @@ bool fs::pending_file::commit(bool overwrite) { file.sync(); } - #endif #ifdef _WIN32 @@ -2486,16 +2485,62 @@ bool fs::pending_file::commit(bool overwrite) disp.DeleteFileW = false; ensure(SetFileInformationByHandle(file.get_handle(), FileDispositionInfo, &disp, sizeof(disp))); } + + std::vector hardlinkPaths; + + const auto ws1 = to_wchar(m_path); + + const HANDLE fileHandle = CreateFileW(ws1.get(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + while (fileHandle != INVALID_HANDLE_VALUE) + { + // Get file ID (used to check for hardlinks) + BY_HANDLE_FILE_INFORMATION fileInfo; + + if (!GetFileInformationByHandle(fileHandle, &fileInfo) || fileInfo.nNumberOfLinks == 1) + { + CloseHandle(fileHandle); + break; + } + + // Buffer for holding link name + WCHAR linkNameBuffer[MAX_PATH]; + HANDLE findHandle = FindFirstFileNameW(ws1.get(), 0, nullptr, linkNameBuffer); + + if (findHandle != INVALID_HANDLE_VALUE) + { + const std::wstring_view ws1_sv = ws1.get(); + + do + { + if (linkNameBuffer != ws1_sv) + { + hardlinkPaths.push_back(linkNameBuffer); + } + } + while (FindNextFileNameW(findHandle, nullptr, linkNameBuffer)); + } + + // Clean up + FindClose(findHandle); + CloseHandle(fileHandle); + break; + } #endif file.close(); #ifdef _WIN32 - const auto ws1 = to_wchar(m_path); const auto ws2 = to_wchar(m_dest); if (MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH : MOVEFILE_WRITE_THROUGH)) { + for (auto&& path : hardlinkPaths) + { + // Recreate hard links + createHardLinkW(m_dest.c_str(), path.c_str()); + } + // Disable the destructor m_path.clear(); return true;