From 9201390a46ae14ec3838fb43437bce2f74558363 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Tue, 15 Dec 2020 20:59:04 +0100 Subject: [PATCH] UI/updater: Move in-use files away before writing On a modern Windows OS, you can rename an in-use file despite not being able to write to it. With the introduction of the virtual camera, it is now quite common that users will have in-use files when updating. This commit renames in-use files, allowing the new version to be installed. Upon a reboot, the previously in-use file will be deleted. --- UI/win-update/updater/updater.cpp | 50 +++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index 1bda84a13..ced9b8e76 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -786,6 +786,41 @@ static void UpdateWithPatchIfAvailable(const char *name, const char *hash, } } +static bool MoveInUseFileAway(update_t &file) +{ + _TCHAR deleteMeName[MAX_PATH]; + _TCHAR randomStr[MAX_PATH]; + + BYTE junk[40]; + BYTE hash[BLAKE2_HASH_LENGTH]; + + CryptGenRandom(hProvider, sizeof(junk), junk); + blake2b(hash, sizeof(hash), junk, sizeof(junk), NULL, 0); + HashToString(hash, randomStr); + randomStr[8] = 0; + + StringCbCopy(deleteMeName, sizeof(deleteMeName), + file.outputPath.c_str()); + + StringCbCat(deleteMeName, sizeof(deleteMeName), L"."); + StringCbCat(deleteMeName, sizeof(deleteMeName), randomStr); + StringCbCat(deleteMeName, sizeof(deleteMeName), L".deleteme"); + + if (MoveFile(file.outputPath.c_str(), deleteMeName)) { + + if (MyCopyFile(deleteMeName, file.outputPath.c_str())) { + MoveFileEx(deleteMeName, NULL, + MOVEFILE_DELAY_UNTIL_REBOOT); + + return true; + } else { + MoveFile(deleteMeName, file.outputPath.c_str()); + } + } + + return false; +} + static bool UpdateFile(update_t &file) { wchar_t oldFileRenamedPath[MAX_PATH]; @@ -838,6 +873,9 @@ static bool UpdateFile(update_t &file) int error_code; bool installed_ok; + bool already_tried_to_move = false; + + retryAfterMovingFile: if (file.patchable) { error_code = ApplyPatch(file.tempPath.c_str(), @@ -877,15 +915,23 @@ static bool UpdateFile(update_t &file) int is_sharing_violation = (error_code == ERROR_SHARING_VIOLATION); - if (is_sharing_violation) + if (is_sharing_violation) { + if (!already_tried_to_move) { + already_tried_to_move = true; + + if (MoveInUseFileAway(file)) + goto retryAfterMovingFile; + } + Status(L"Update failed: %s is still in use. " L"Close all " L"programs and try again.", curFileName); - else + } else { Status(L"Update failed: Couldn't update %s " L"(error %d)", curFileName, GetLastError()); + } file.state = STATE_INSTALL_FAILED; return false;