diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 7426529e26..6d58077ba8 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -2412,6 +2412,13 @@ fs::file fs::make_gather(std::vector files) return result; } +std::string fs::generate_neighboring_path(std::string_view source, [[maybe_unused]] u64 seed) +{ + // Seed is currently not used + + return fmt::format(u8"%s/$%s.%s.tmp", get_parent_dir(source), source.substr(source.find_last_of(fs::delim) + 1), fmt::base57(utils::get_unique_tsc())); +} + bool fs::pending_file::open(std::string_view path) { file.close(); @@ -2430,7 +2437,7 @@ bool fs::pending_file::open(std::string_view path) do { - m_path = fmt::format(u8"%s/$%s.%s.tmp", get_parent_dir(path), path.substr(path.find_last_of(fs::delim) + 1), fmt::base57(utils::get_unique_tsc())); + m_path = fs::generate_neighboring_path(path, 0); if (file.open(m_path, fs::create + fs::write + fs::read + fs::excl)) { @@ -2563,21 +2570,52 @@ bool fs::pending_file::commit(bool overwrite) file.close(); #ifdef _WIN32 - const auto ws2 = to_wchar(m_dest); + const auto wdest = to_wchar(m_dest); bool ok = false; if (hardlink_paths.empty()) { - ok = MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH : MOVEFILE_WRITE_THROUGH); + ok = MoveFileExW(ws1.get(), wdest.get(), overwrite ? MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH : MOVEFILE_WRITE_THROUGH); } else { - ok = ReplaceFileW(ws1.get(), ws2.get(), nullptr, 0, nullptr, nullptr); + ok = ReplaceFileW(ws1.get(), wdest.get(), nullptr, 0, nullptr, nullptr); } if (ok) { + for (const std::wstring& link_name : hardlink_paths) + { + std::unique_ptr write_temp_path; + + do + { + write_temp_path = to_wchar(fs::generate_neighboring_path(m_dest, 0)); + + // Generate a temporary hard linke + if (CreateHardLinkW(wdest.get(), write_temp_path.get(), nullptr)) + { + if (MoveFileExW(write_temp_path.get(), link_name.data(), MOVEFILE_REPLACE_EXISTING)) + { + // Success + write_temp_path.reset(); + break; + } + + break; + } + } + while (fs::g_tls_error == fs::error::exist); // Only retry if failed due to existing file + + if (write_temp_path) + { + // Failure + g_tls_error = to_error(GetLastError()); + return false; + } + } + // Disable the destructor m_path.clear(); return true; diff --git a/Utilities/File.h b/Utilities/File.h index c95bcbfc02..4c8a085131 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -601,6 +601,8 @@ namespace fs // Temporary directory const std::string& get_temp_dir(); + std::string generate_neighboring_path(std::string_view source, u64 seed); + // Unique pending file creation destined to be renamed to the destination file struct pending_file {