浏览代码

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.
Richard Stanway 4 年之前
父节点
当前提交
9201390a46
共有 1 个文件被更改,包括 48 次插入2 次删除
  1. 48 2
      UI/win-update/updater/updater.cpp

+ 48 - 2
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;