diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
commit | 61847dddd4fd497057c780658e383c4627de19ec (patch) | |
tree | f85a845182922538ab9aa6ee85b0db3ab40c1f6e /src/engine/apply.cpp | |
parent | 8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff) | |
download | wix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2 wix-61847dddd4fd497057c780658e383c4627de19ec.zip |
Import code from old v4 repo
Diffstat (limited to 'src/engine/apply.cpp')
-rw-r--r-- | src/engine/apply.cpp | 2516 |
1 files changed, 2516 insertions, 0 deletions
diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp new file mode 100644 index 00000000..0cef9ac8 --- /dev/null +++ b/src/engine/apply.cpp | |||
@@ -0,0 +1,2516 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
6 | const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; | ||
7 | |||
8 | // structs | ||
9 | |||
10 | struct BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT | ||
11 | { | ||
12 | BURN_USER_EXPERIENCE* pUX; | ||
13 | BURN_CONTAINER* pContainer; | ||
14 | BURN_PACKAGE* pPackage; | ||
15 | BURN_PAYLOAD* pPayload; | ||
16 | DWORD64 qwCacheProgress; | ||
17 | DWORD64 qwTotalCacheSize; | ||
18 | |||
19 | BOOL fCancel; | ||
20 | BOOL fError; | ||
21 | }; | ||
22 | |||
23 | typedef struct _BURN_EXECUTE_CONTEXT | ||
24 | { | ||
25 | BURN_USER_EXPERIENCE* pUX; | ||
26 | BOOL fRollback; | ||
27 | BURN_PACKAGE* pExecutingPackage; | ||
28 | DWORD cExecutedPackages; | ||
29 | DWORD cExecutePackagesTotal; | ||
30 | DWORD* pcOverallProgressTicks; | ||
31 | } BURN_EXECUTE_CONTEXT; | ||
32 | |||
33 | |||
34 | // internal function declarations | ||
35 | static HRESULT WINAPI AuthenticationRequired( | ||
36 | __in LPVOID pData, | ||
37 | __in HINTERNET hUrl, | ||
38 | __in long lHttpCode, | ||
39 | __out BOOL* pfRetrySend, | ||
40 | __out BOOL* pfRetry | ||
41 | ); | ||
42 | |||
43 | static HRESULT ExecuteDependentRegistrationActions( | ||
44 | __in HANDLE hPipe, | ||
45 | __in const BURN_REGISTRATION* pRegistration, | ||
46 | __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, | ||
47 | __in DWORD cActions | ||
48 | ); | ||
49 | static HRESULT ExtractContainer( | ||
50 | __in HANDLE hSourceEngineFile, | ||
51 | __in BURN_CONTAINER* pContainer, | ||
52 | __in_z LPCWSTR wzContainerPath, | ||
53 | __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, | ||
54 | __in DWORD cExtractPayloads | ||
55 | ); | ||
56 | static void UpdateCacheSuccessProgress( | ||
57 | __in BURN_PLAN* pPlan, | ||
58 | __in BURN_CACHE_ACTION* pCacheAction, | ||
59 | __inout DWORD64* pqwSuccessfulCachedProgress | ||
60 | ); | ||
61 | static HRESULT LayoutBundle( | ||
62 | __in BURN_USER_EXPERIENCE* pUX, | ||
63 | __in BURN_VARIABLES* pVariables, | ||
64 | __in HANDLE hPipe, | ||
65 | __in_z LPCWSTR wzExecutableName, | ||
66 | __in_z LPCWSTR wzLayoutDirectory, | ||
67 | __in_z LPCWSTR wzUnverifiedPath, | ||
68 | __in DWORD64 qwSuccessfulCacheProgress, | ||
69 | __in DWORD64 qwTotalCacheSize | ||
70 | ); | ||
71 | static HRESULT AcquireContainerOrPayload( | ||
72 | __in BURN_USER_EXPERIENCE* pUX, | ||
73 | __in BURN_VARIABLES* pVariables, | ||
74 | __in_opt BURN_CONTAINER* pContainer, | ||
75 | __in_opt BURN_PACKAGE* pPackage, | ||
76 | __in_opt BURN_PAYLOAD* pPayload, | ||
77 | __in LPCWSTR wzDestinationPath, | ||
78 | __in DWORD64 qwSuccessfulCacheProgress, | ||
79 | __in DWORD64 qwTotalCacheSize | ||
80 | ); | ||
81 | static HRESULT LayoutOrCacheContainerOrPayload( | ||
82 | __in BURN_USER_EXPERIENCE* pUX, | ||
83 | __in HANDLE hPipe, | ||
84 | __in_opt BURN_CONTAINER* pContainer, | ||
85 | __in_opt BURN_PACKAGE* pPackage, | ||
86 | __in_opt BURN_PAYLOAD* pPayload, | ||
87 | __in BOOL fAlreadyProvidedProgress, | ||
88 | __in DWORD64 qwSuccessfullyCacheProgress, | ||
89 | __in DWORD64 qwTotalCacheSize, | ||
90 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
91 | __in_z LPCWSTR wzUnverifiedPath, | ||
92 | __in BOOL fMove, | ||
93 | __in DWORD cTryAgainAttempts, | ||
94 | __out BOOL* pfRetry | ||
95 | ); | ||
96 | static HRESULT PromptForSource( | ||
97 | __in BURN_USER_EXPERIENCE* pUX, | ||
98 | __in_z LPCWSTR wzPackageOrContainerId, | ||
99 | __in_z_opt LPCWSTR wzPayloadId, | ||
100 | __in_z LPCWSTR wzLocalSource, | ||
101 | __in_z_opt LPCWSTR wzDownloadSource, | ||
102 | __out BOOL* pfRetry, | ||
103 | __out BOOL* pfDownload | ||
104 | ); | ||
105 | static HRESULT CopyPayload( | ||
106 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
107 | __in_z LPCWSTR wzSourcePath, | ||
108 | __in_z LPCWSTR wzDestinationPath | ||
109 | ); | ||
110 | static HRESULT DownloadPayload( | ||
111 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
112 | __in_z LPCWSTR wzDestinationPath | ||
113 | ); | ||
114 | static DWORD CALLBACK CacheProgressRoutine( | ||
115 | __in LARGE_INTEGER TotalFileSize, | ||
116 | __in LARGE_INTEGER TotalBytesTransferred, | ||
117 | __in LARGE_INTEGER StreamSize, | ||
118 | __in LARGE_INTEGER StreamBytesTransferred, | ||
119 | __in DWORD dwStreamNumber, | ||
120 | __in DWORD dwCallbackReason, | ||
121 | __in HANDLE hSourceFile, | ||
122 | __in HANDLE hDestinationFile, | ||
123 | __in_opt LPVOID lpData | ||
124 | ); | ||
125 | static void DoRollbackCache( | ||
126 | __in BURN_USER_EXPERIENCE* pUX, | ||
127 | __in BURN_PLAN* pPlan, | ||
128 | __in HANDLE hPipe, | ||
129 | __in DWORD dwCheckpoint | ||
130 | ); | ||
131 | static HRESULT DoExecuteAction( | ||
132 | __in BURN_ENGINE_STATE* pEngineState, | ||
133 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
134 | __in_opt HANDLE hCacheThread, | ||
135 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
136 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
137 | __out DWORD* pdwCheckpoint, | ||
138 | __out BOOL* pfKeepRegistration, | ||
139 | __out BOOL* pfSuspend, | ||
140 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
141 | ); | ||
142 | static HRESULT DoRollbackActions( | ||
143 | __in BURN_ENGINE_STATE* pEngineState, | ||
144 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
145 | __in DWORD dwCheckpoint, | ||
146 | __in BOOL fInTransaction, | ||
147 | __out BOOL* pfKeepRegistration, | ||
148 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
149 | ); | ||
150 | static HRESULT ExecuteExePackage( | ||
151 | __in BURN_ENGINE_STATE* pEngineState, | ||
152 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
153 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
154 | __in BOOL fRollback, | ||
155 | __out BOOL* pfRetry, | ||
156 | __out BOOL* pfSuspend, | ||
157 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
158 | ); | ||
159 | static HRESULT ExecuteMsiPackage( | ||
160 | __in BURN_ENGINE_STATE* pEngineState, | ||
161 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
162 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
163 | __in BOOL fRollback, | ||
164 | __out BOOL* pfRetry, | ||
165 | __out BOOL* pfSuspend, | ||
166 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
167 | ); | ||
168 | static HRESULT ExecuteMspPackage( | ||
169 | __in BURN_ENGINE_STATE* pEngineState, | ||
170 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
171 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
172 | __in BOOL fRollback, | ||
173 | __out BOOL* pfRetry, | ||
174 | __out BOOL* pfSuspend, | ||
175 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
176 | ); | ||
177 | static HRESULT ExecuteMsuPackage( | ||
178 | __in BURN_ENGINE_STATE* pEngineState, | ||
179 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
180 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
181 | __in BOOL fRollback, | ||
182 | __in BOOL fStopWusaService, | ||
183 | __out BOOL* pfRetry, | ||
184 | __out BOOL* pfSuspend, | ||
185 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
186 | ); | ||
187 | static HRESULT ExecutePackageProviderAction( | ||
188 | __in BURN_ENGINE_STATE* pEngineState, | ||
189 | __in BURN_EXECUTE_ACTION* pAction, | ||
190 | __in BURN_EXECUTE_CONTEXT* pContext | ||
191 | ); | ||
192 | static HRESULT ExecuteDependencyAction( | ||
193 | __in BURN_ENGINE_STATE* pEngineState, | ||
194 | __in BURN_EXECUTE_ACTION* pAction, | ||
195 | __in BURN_EXECUTE_CONTEXT* pContext | ||
196 | ); | ||
197 | static HRESULT ExecuteCompatiblePackageAction( | ||
198 | __in BURN_ENGINE_STATE* pEngineState, | ||
199 | __in BURN_EXECUTE_ACTION* pAction | ||
200 | ); | ||
201 | static HRESULT CleanPackage( | ||
202 | __in HANDLE hElevatedPipe, | ||
203 | __in BURN_PACKAGE* pPackage | ||
204 | ); | ||
205 | static int GenericExecuteMessageHandler( | ||
206 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
207 | __in LPVOID pvContext | ||
208 | ); | ||
209 | static int MsiExecuteMessageHandler( | ||
210 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
211 | __in_opt LPVOID pvContext | ||
212 | ); | ||
213 | static HRESULT ReportOverallProgressTicks( | ||
214 | __in BURN_USER_EXPERIENCE* pUX, | ||
215 | __in BOOL fRollback, | ||
216 | __in DWORD cOverallProgressTicksTotal, | ||
217 | __in DWORD cOverallProgressTicks | ||
218 | ); | ||
219 | static HRESULT ExecutePackageComplete( | ||
220 | __in BURN_USER_EXPERIENCE* pUX, | ||
221 | __in BURN_VARIABLES* pVariables, | ||
222 | __in BURN_PACKAGE* pPackage, | ||
223 | __in HRESULT hrOverall, | ||
224 | __in HRESULT hrExecute, | ||
225 | __in BOOL fRollback, | ||
226 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart, | ||
227 | __out BOOL* pfRetry, | ||
228 | __out BOOL* pfSuspend | ||
229 | ); | ||
230 | |||
231 | static HRESULT DoMsiBeginTransaction( | ||
232 | __in BURN_EXECUTE_CONTEXT *context | ||
233 | , __in BURN_ENGINE_STATE* pEngineState | ||
234 | ); | ||
235 | static HRESULT DoMsiCommitTransaction( | ||
236 | __in BURN_EXECUTE_CONTEXT *context | ||
237 | , __in BURN_ENGINE_STATE* pEngineState | ||
238 | ); | ||
239 | static HRESULT DoMsiRollbackTransaction( | ||
240 | __in BURN_EXECUTE_CONTEXT *context | ||
241 | , __in BURN_ENGINE_STATE* pEngineState | ||
242 | ); | ||
243 | static HRESULT ExecuteMsiBeginTransaction( | ||
244 | __in BURN_EXECUTE_CONTEXT* pContext | ||
245 | , __in BURN_ENGINE_STATE* pEngineState | ||
246 | ); | ||
247 | static HRESULT ExecuteMsiCommitTransaction( | ||
248 | __in BURN_EXECUTE_CONTEXT* pContext | ||
249 | , __in BURN_ENGINE_STATE* pEngineState | ||
250 | ); | ||
251 | static HRESULT ExecuteMsiRollbackTransaction( | ||
252 | __in BURN_EXECUTE_CONTEXT* pContext | ||
253 | , __in BURN_ENGINE_STATE* pEngineState | ||
254 | ); | ||
255 | |||
256 | // function definitions | ||
257 | |||
258 | extern "C" void ApplyInitialize() | ||
259 | { | ||
260 | // Prevent the system from sleeping. | ||
261 | ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); | ||
262 | } | ||
263 | |||
264 | extern "C" void ApplyUninitialize() | ||
265 | { | ||
266 | ::SetThreadExecutionState(ES_CONTINUOUS); | ||
267 | } | ||
268 | |||
269 | extern "C" HRESULT ApplySetVariables( | ||
270 | __in BURN_VARIABLES* pVariables | ||
271 | ) | ||
272 | { | ||
273 | HRESULT hr = S_OK; | ||
274 | |||
275 | hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE); | ||
276 | ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); | ||
277 | |||
278 | LExit: | ||
279 | return hr; | ||
280 | } | ||
281 | |||
282 | extern "C" void ApplyReset( | ||
283 | __in BURN_USER_EXPERIENCE* pUX, | ||
284 | __in BURN_PACKAGES* pPackages | ||
285 | ) | ||
286 | { | ||
287 | UserExperienceExecuteReset(pUX); | ||
288 | |||
289 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
290 | { | ||
291 | BURN_PACKAGE* pPackage = pPackages->rgPackages + i; | ||
292 | pPackage->hrCacheResult = S_OK; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | extern "C" HRESULT ApplyLock( | ||
297 | __in BOOL /*fPerMachine*/, | ||
298 | __out HANDLE* /*phLock*/ | ||
299 | ) | ||
300 | { | ||
301 | HRESULT hr = S_OK; | ||
302 | #if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. | ||
303 | DWORD er = ERROR_SUCCESS; | ||
304 | HANDLE hLock = NULL; | ||
305 | |||
306 | hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock"); | ||
307 | ExitOnNullWithLastError(hLock, hr, "Failed to create lock."); | ||
308 | |||
309 | er = ::GetLastError(); | ||
310 | if (ERROR_ALREADY_EXISTS == er) | ||
311 | { | ||
312 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING)); | ||
313 | } | ||
314 | |||
315 | *phLock = hLock; | ||
316 | hLock = NULL; | ||
317 | |||
318 | LExit: | ||
319 | ReleaseHandle(hLock); | ||
320 | #endif | ||
321 | return hr; | ||
322 | } | ||
323 | |||
324 | extern "C" HRESULT ApplyRegister( | ||
325 | __in BURN_ENGINE_STATE* pEngineState | ||
326 | ) | ||
327 | { | ||
328 | HRESULT hr = S_OK; | ||
329 | LPWSTR sczEngineWorkingPath = NULL; | ||
330 | |||
331 | hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience); | ||
332 | ExitOnRootFailure(hr, "BA aborted register begin."); | ||
333 | |||
334 | // If we have a resume mode that suggests the bundle is on the machine. | ||
335 | if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType) | ||
336 | { | ||
337 | // resume previous session | ||
338 | if (pEngineState->registration.fPerMachine) | ||
339 | { | ||
340 | hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables); | ||
341 | ExitOnFailure(hr, "Failed to resume registration session in per-machine process."); | ||
342 | } | ||
343 | else | ||
344 | { | ||
345 | hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables); | ||
346 | ExitOnFailure(hr, "Failed to resume registration session."); | ||
347 | } | ||
348 | } | ||
349 | else // need to complete registration on the machine. | ||
350 | { | ||
351 | hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath); | ||
352 | ExitOnFailure(hr, "Failed to calculate working path for engine."); | ||
353 | |||
354 | // begin new session | ||
355 | if (pEngineState->registration.fPerMachine) | ||
356 | { | ||
357 | hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); | ||
358 | ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); | ||
359 | } | ||
360 | else | ||
361 | { | ||
362 | hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, &pEngineState->userExperience, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); | ||
363 | ExitOnFailure(hr, "Failed to begin registration session."); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | // Apply any registration actions. | ||
368 | HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions); | ||
369 | UNREFERENCED_PARAMETER(hrExecuteRegistration); | ||
370 | |||
371 | // Try to save engine state. | ||
372 | hr = CoreSaveEngineState(pEngineState); | ||
373 | if (FAILED(hr)) | ||
374 | { | ||
375 | LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); | ||
376 | hr = S_OK; | ||
377 | } | ||
378 | |||
379 | LExit: | ||
380 | UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); | ||
381 | ReleaseStr(sczEngineWorkingPath); | ||
382 | |||
383 | return hr; | ||
384 | } | ||
385 | |||
386 | extern "C" HRESULT ApplyUnregister( | ||
387 | __in BURN_ENGINE_STATE* pEngineState, | ||
388 | __in BOOL fFailedOrRollback, | ||
389 | __in BOOL fKeepRegistration, | ||
390 | __in BOOL fSuspend, | ||
391 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
392 | ) | ||
393 | { | ||
394 | HRESULT hr = S_OK; | ||
395 | BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; | ||
396 | |||
397 | hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience); | ||
398 | ExitOnRootFailure(hr, "BA aborted unregister begin."); | ||
399 | |||
400 | // Calculate the correct resume mode. If a restart has been initiated, that trumps all other | ||
401 | // modes. If the user chose to suspend the install then we'll use that as the resume mode. | ||
402 | // Barring those special cases, if it was determined that we should keep the registration then | ||
403 | // do that, otherwise the resume mode was initialized to none and registration will be removed. | ||
404 | if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) | ||
405 | { | ||
406 | resumeMode = BURN_RESUME_MODE_REBOOT_PENDING; | ||
407 | } | ||
408 | else if (fSuspend) | ||
409 | { | ||
410 | resumeMode = BURN_RESUME_MODE_SUSPEND; | ||
411 | } | ||
412 | else if (fKeepRegistration) | ||
413 | { | ||
414 | resumeMode = BURN_RESUME_MODE_ARP; | ||
415 | } | ||
416 | |||
417 | // If apply failed in any way and we're going to be keeping the bundle registered then | ||
418 | // execute any rollback dependency registration actions. | ||
419 | if (fFailedOrRollback && fKeepRegistration) | ||
420 | { | ||
421 | // Execute any rollback registration actions. | ||
422 | HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions); | ||
423 | UNREFERENCED_PARAMETER(hrRegistrationRollback); | ||
424 | } | ||
425 | |||
426 | if (pEngineState->registration.fPerMachine) | ||
427 | { | ||
428 | hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); | ||
429 | ExitOnFailure(hr, "Failed to end session in per-machine process."); | ||
430 | } | ||
431 | else | ||
432 | { | ||
433 | hr = RegistrationSessionEnd(&pEngineState->registration, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); | ||
434 | ExitOnFailure(hr, "Failed to end session in per-user process."); | ||
435 | } | ||
436 | |||
437 | pEngineState->resumeMode = resumeMode; | ||
438 | |||
439 | LExit: | ||
440 | UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); | ||
441 | |||
442 | return hr; | ||
443 | } | ||
444 | |||
445 | extern "C" HRESULT ApplyCache( | ||
446 | __in HANDLE hSourceEngineFile, | ||
447 | __in BURN_USER_EXPERIENCE* pUX, | ||
448 | __in BURN_VARIABLES* pVariables, | ||
449 | __in BURN_PLAN* pPlan, | ||
450 | __in HANDLE hPipe, | ||
451 | __inout DWORD* pcOverallProgressTicks, | ||
452 | __out BOOL* pfRollback | ||
453 | ) | ||
454 | { | ||
455 | HRESULT hr = S_OK; | ||
456 | DWORD dwCheckpoint = 0; | ||
457 | BOOL fRetry = FALSE; | ||
458 | DWORD iRetryAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
459 | BURN_PACKAGE* pStartedPackage = NULL; | ||
460 | DWORD64 qwSuccessfulCachedProgress = 0; | ||
461 | |||
462 | // Allow us to retry and skip packages. | ||
463 | DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
464 | DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
465 | |||
466 | hr = UserExperienceOnCacheBegin(pUX); | ||
467 | ExitOnRootFailure(hr, "BA aborted cache."); | ||
468 | |||
469 | do | ||
470 | { | ||
471 | hr = S_OK; | ||
472 | fRetry = FALSE; | ||
473 | |||
474 | // Allow us to retry just a container or payload. | ||
475 | LPCWSTR wzRetryId = NULL; | ||
476 | DWORD iRetryContainerOrPayloadAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
477 | BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; | ||
478 | |||
479 | // cache actions | ||
480 | for (DWORD i = (BURN_PLAN_INVALID_ACTION_INDEX == iRetryAction) ? 0 : iRetryAction; SUCCEEDED(hr) && i < pPlan->cCacheActions; ++i) | ||
481 | { | ||
482 | BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; | ||
483 | BOOL fRetryContainerOrPayload = FALSE; | ||
484 | cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; | ||
485 | |||
486 | if (pCacheAction->fSkipUntilRetried) | ||
487 | { | ||
488 | // If this action was retried, let's make sure it will not be skipped any longer. | ||
489 | if (iRetryAction == i) | ||
490 | { | ||
491 | pCacheAction->fSkipUntilRetried = FALSE; | ||
492 | } | ||
493 | else // skip the action. | ||
494 | { | ||
495 | continue; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | switch (pCacheAction->type) | ||
500 | { | ||
501 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
502 | dwCheckpoint = pCacheAction->checkpoint.dwId; | ||
503 | break; | ||
504 | |||
505 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
506 | hr = LayoutBundle(pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); | ||
507 | if (SUCCEEDED(hr)) | ||
508 | { | ||
509 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
510 | ++(*pcOverallProgressTicks); | ||
511 | |||
512 | hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); | ||
513 | if (FAILED(hr)) | ||
514 | { | ||
515 | LogErrorId(hr, MSG_USER_CANCELED, L"layout bundle", NULL, NULL); | ||
516 | } | ||
517 | } | ||
518 | break; | ||
519 | |||
520 | case BURN_CACHE_ACTION_TYPE_PACKAGE_START: | ||
521 | iPackageStartAction = i; // if we retry this package, we'll start here in the plan. | ||
522 | iPackageCompleteAction = pCacheAction->packageStart.iPackageCompleteAction; // if we ignore this package, we'll start after the complete action in the plan. | ||
523 | pStartedPackage = pCacheAction->packageStart.pPackage; | ||
524 | |||
525 | hr = UserExperienceOnCachePackageBegin(pUX, pStartedPackage->sczId, pCacheAction->packageStart.cCachePayloads, pCacheAction->packageStart.qwCachePayloadSizeTotal); | ||
526 | if (FAILED(hr)) | ||
527 | { | ||
528 | LogErrorId(hr, MSG_USER_CANCELED, L"begin cache package", pStartedPackage->sczId, NULL); | ||
529 | } | ||
530 | break; | ||
531 | |||
532 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: | ||
533 | hr = AcquireContainerOrPayload(pUX, pVariables, pCacheAction->resolveContainer.pContainer, NULL, NULL, pCacheAction->resolveContainer.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); | ||
534 | if (SUCCEEDED(hr)) | ||
535 | { | ||
536 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
537 | } | ||
538 | else | ||
539 | { | ||
540 | LogErrorId(hr, MSG_FAILED_ACQUIRE_CONTAINER, pCacheAction->resolveContainer.pContainer->sczId, pCacheAction->resolveContainer.sczUnverifiedPath, NULL); | ||
541 | } | ||
542 | break; | ||
543 | |||
544 | case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: | ||
545 | // If this action is to be skipped until the acquire action is not skipped and the other | ||
546 | // action is still being skipped then skip this action. | ||
547 | if (BURN_PLAN_INVALID_ACTION_INDEX != pCacheAction->extractContainer.iSkipUntilAcquiredByAction && pPlan->rgCacheActions[pCacheAction->extractContainer.iSkipUntilAcquiredByAction].fSkipUntilRetried) | ||
548 | { | ||
549 | break; | ||
550 | } | ||
551 | |||
552 | hr = ExtractContainer(hSourceEngineFile, pCacheAction->extractContainer.pContainer, pCacheAction->extractContainer.sczContainerUnverifiedPath, pCacheAction->extractContainer.rgPayloads, pCacheAction->extractContainer.cPayloads); | ||
553 | if (FAILED(hr)) | ||
554 | { | ||
555 | LogErrorId(hr, MSG_FAILED_EXTRACT_CONTAINER, pCacheAction->extractContainer.pContainer->sczId, pCacheAction->extractContainer.sczContainerUnverifiedPath, NULL); | ||
556 | } | ||
557 | break; | ||
558 | |||
559 | case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: | ||
560 | hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, pCacheAction->layoutContainer.pContainer, pCacheAction->layoutContainer.pPackage, NULL, pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath, pCacheAction->layoutContainer.fMove, pCacheAction->layoutContainer.cTryAgainAttempts, &fRetryContainerOrPayload); | ||
561 | if (SUCCEEDED(hr)) | ||
562 | { | ||
563 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
564 | } | ||
565 | else | ||
566 | { | ||
567 | LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pCacheAction->layoutContainer.pContainer->sczId, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath); | ||
568 | |||
569 | if (fRetryContainerOrPayload) | ||
570 | { | ||
571 | wzRetryId = pCacheAction->layoutContainer.pContainer->sczId; | ||
572 | iRetryContainerOrPayloadAction = pCacheAction->layoutContainer.iTryAgainAction; | ||
573 | |||
574 | ++pCacheAction->layoutContainer.cTryAgainAttempts; | ||
575 | } | ||
576 | } | ||
577 | break; | ||
578 | |||
579 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: | ||
580 | hr = AcquireContainerOrPayload(pUX, pVariables, NULL, pCacheAction->resolvePayload.pPackage, pCacheAction->resolvePayload.pPayload, pCacheAction->resolvePayload.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); | ||
581 | if (SUCCEEDED(hr)) | ||
582 | { | ||
583 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
584 | } | ||
585 | else | ||
586 | { | ||
587 | LogErrorId(hr, MSG_FAILED_ACQUIRE_PAYLOAD, pCacheAction->resolvePayload.pPayload->sczKey, pCacheAction->resolvePayload.sczUnverifiedPath, NULL); | ||
588 | } | ||
589 | break; | ||
590 | |||
591 | case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: | ||
592 | hr = LayoutOrCacheContainerOrPayload(pUX, pCacheAction->cachePayload.pPackage->fPerMachine ? hPipe : INVALID_HANDLE_VALUE, NULL, pCacheAction->cachePayload.pPackage, pCacheAction->cachePayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, NULL, pCacheAction->cachePayload.sczUnverifiedPath, pCacheAction->cachePayload.fMove, pCacheAction->cachePayload.cTryAgainAttempts, &fRetryContainerOrPayload); | ||
593 | if (SUCCEEDED(hr)) | ||
594 | { | ||
595 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
596 | } | ||
597 | else | ||
598 | { | ||
599 | LogErrorId(hr, MSG_FAILED_CACHE_PAYLOAD, pCacheAction->cachePayload.pPayload->sczKey, pCacheAction->cachePayload.sczUnverifiedPath, NULL); | ||
600 | |||
601 | if (fRetryContainerOrPayload) | ||
602 | { | ||
603 | wzRetryId = pCacheAction->cachePayload.pPayload->sczKey; | ||
604 | iRetryContainerOrPayloadAction = pCacheAction->cachePayload.iTryAgainAction; | ||
605 | |||
606 | ++pCacheAction->cachePayload.cTryAgainAttempts; | ||
607 | } | ||
608 | } | ||
609 | break; | ||
610 | |||
611 | case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: | ||
612 | hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, NULL, pCacheAction->layoutPayload.pPackage, pCacheAction->layoutPayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath, pCacheAction->layoutPayload.fMove, pCacheAction->layoutPayload.cTryAgainAttempts, &fRetryContainerOrPayload); | ||
613 | if (SUCCEEDED(hr)) | ||
614 | { | ||
615 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
616 | } | ||
617 | else | ||
618 | { | ||
619 | LogErrorId(hr, MSG_FAILED_LAYOUT_PAYLOAD, pCacheAction->layoutPayload.pPayload->sczKey, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath); | ||
620 | |||
621 | if (fRetryContainerOrPayload) | ||
622 | { | ||
623 | wzRetryId = pCacheAction->layoutPayload.pPayload->sczKey; | ||
624 | iRetryContainerOrPayloadAction = pCacheAction->layoutPayload.iTryAgainAction; | ||
625 | |||
626 | ++pCacheAction->layoutPayload.cTryAgainAttempts; | ||
627 | } | ||
628 | } | ||
629 | break; | ||
630 | |||
631 | case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: | ||
632 | AssertSz(pStartedPackage == pCacheAction->packageStop.pPackage, "Expected package started cached to be the same as the package checkpointed."); | ||
633 | |||
634 | hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks + 1); | ||
635 | if (FAILED(hr)) | ||
636 | { | ||
637 | LogErrorId(hr, MSG_USER_CANCELED, L"end cache package", NULL, NULL); | ||
638 | } | ||
639 | else | ||
640 | { | ||
641 | ++(*pcOverallProgressTicks); | ||
642 | |||
643 | UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); | ||
644 | |||
645 | pStartedPackage->hrCacheResult = hr; | ||
646 | |||
647 | iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
648 | iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
649 | pStartedPackage = NULL; | ||
650 | } | ||
651 | break; | ||
652 | |||
653 | case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: | ||
654 | if (!::SetEvent(pCacheAction->syncpoint.hEvent)) | ||
655 | { | ||
656 | ExitWithLastError(hr, "Failed to set syncpoint event."); | ||
657 | } | ||
658 | break; | ||
659 | |||
660 | default: | ||
661 | AssertSz(FALSE, "Unknown cache action."); | ||
662 | break; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | if (BURN_PLAN_INVALID_ACTION_INDEX != iRetryContainerOrPayloadAction) | ||
667 | { | ||
668 | Assert(wzRetryId); | ||
669 | |||
670 | LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, wzRetryId, NULL, NULL); | ||
671 | |||
672 | iRetryAction = iRetryContainerOrPayloadAction; | ||
673 | fRetry = TRUE; | ||
674 | } | ||
675 | else if (pStartedPackage) | ||
676 | { | ||
677 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageStartAction); | ||
678 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageCompleteAction); | ||
679 | |||
680 | cachePackageCompleteAction = SUCCEEDED(hr) || pStartedPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; | ||
681 | UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); | ||
682 | if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) | ||
683 | { | ||
684 | LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pStartedPackage->sczId, NULL, NULL); | ||
685 | |||
686 | iRetryAction = iPackageStartAction; | ||
687 | fRetry = TRUE; | ||
688 | } | ||
689 | else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pStartedPackage->fVital) // ignore non-vital download failures. | ||
690 | { | ||
691 | LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pStartedPackage->sczId, hr); | ||
692 | |||
693 | ++(*pcOverallProgressTicks); // add progress even though we didn't fully cache the package. | ||
694 | |||
695 | iRetryAction = iPackageCompleteAction + 1; | ||
696 | fRetry = TRUE; | ||
697 | } | ||
698 | |||
699 | pStartedPackage->hrCacheResult = hr; | ||
700 | |||
701 | iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
702 | iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
703 | pStartedPackage = NULL; | ||
704 | } | ||
705 | else | ||
706 | { | ||
707 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); | ||
708 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); | ||
709 | } | ||
710 | } while (fRetry); | ||
711 | |||
712 | LExit: | ||
713 | Assert(NULL == pStartedPackage); | ||
714 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); | ||
715 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); | ||
716 | |||
717 | if (FAILED(hr)) | ||
718 | { | ||
719 | DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); | ||
720 | *pfRollback = TRUE; | ||
721 | } | ||
722 | |||
723 | // Clean up any remanents in the cache. | ||
724 | if (INVALID_HANDLE_VALUE != hPipe) | ||
725 | { | ||
726 | ElevationCacheCleanup(hPipe); | ||
727 | } | ||
728 | |||
729 | CacheCleanup(FALSE, pPlan->wzBundleId); | ||
730 | |||
731 | UserExperienceOnCacheComplete(pUX, hr); | ||
732 | return hr; | ||
733 | } | ||
734 | |||
735 | extern "C" HRESULT ApplyExecute( | ||
736 | __in BURN_ENGINE_STATE* pEngineState, | ||
737 | __in_opt HANDLE hCacheThread, | ||
738 | __inout DWORD* pcOverallProgressTicks, | ||
739 | __out BOOL* pfKeepRegistration, | ||
740 | __out BOOL* pfRollback, | ||
741 | __out BOOL* pfSuspend, | ||
742 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
743 | ) | ||
744 | { | ||
745 | HRESULT hr = S_OK; | ||
746 | DWORD dwCheckpoint = 0; | ||
747 | BURN_EXECUTE_CONTEXT context = { }; | ||
748 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
749 | BOOL fSeekNextRollbackBoundary = FALSE; | ||
750 | BOOL fInTransaction = FALSE; | ||
751 | |||
752 | context.pUX = &pEngineState->userExperience; | ||
753 | context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; | ||
754 | context.pcOverallProgressTicks = pcOverallProgressTicks; | ||
755 | |||
756 | // Send execute begin to BA. | ||
757 | hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); | ||
758 | ExitOnRootFailure(hr, "BA aborted execute begin."); | ||
759 | |||
760 | // Do execute actions. | ||
761 | for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i) | ||
762 | { | ||
763 | BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i]; | ||
764 | if (pExecuteAction->fDeleted) | ||
765 | { | ||
766 | continue; | ||
767 | } | ||
768 | |||
769 | // Transaction end/start | ||
770 | if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) | ||
771 | { | ||
772 | // End previous transaction | ||
773 | if (fInTransaction) | ||
774 | { | ||
775 | LogString(REPORT_STANDARD, "Committing MSI transaction\n"); | ||
776 | hr = DoMsiCommitTransaction(&context, pEngineState); | ||
777 | ExitOnFailure(hr, "Failed committing an MSI transaction"); | ||
778 | fInTransaction = FALSE; | ||
779 | } | ||
780 | |||
781 | // Start New transaction | ||
782 | if (!fInTransaction && pExecuteAction->rollbackBoundary.pRollbackBoundary && pExecuteAction->rollbackBoundary.pRollbackBoundary->fTransaction) | ||
783 | { | ||
784 | // Transactions don't go together with DisableRollback. | ||
785 | if (pEngineState->fDisableRollback) | ||
786 | { | ||
787 | LogString(REPORT_STANDARD, "Ignoring Transaction flag due to DisableRollback flag\n"); | ||
788 | } | ||
789 | else | ||
790 | { | ||
791 | LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); | ||
792 | hr = DoMsiBeginTransaction(&context, pEngineState); | ||
793 | ExitOnFailure(hr, "Failed beginning an MSI transaction"); | ||
794 | fInTransaction = TRUE; | ||
795 | } | ||
796 | } | ||
797 | } | ||
798 | |||
799 | // If we are seeking the next rollback boundary, skip if this action wasn't it. | ||
800 | if (fSeekNextRollbackBoundary) | ||
801 | { | ||
802 | if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) | ||
803 | { | ||
804 | continue; | ||
805 | } | ||
806 | else | ||
807 | { | ||
808 | fSeekNextRollbackBoundary = FALSE; | ||
809 | } | ||
810 | } | ||
811 | |||
812 | // Execute the action. | ||
813 | hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &dwCheckpoint, pfKeepRegistration, pfSuspend, pRestart); | ||
814 | |||
815 | if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) | ||
816 | { | ||
817 | if (fInTransaction) | ||
818 | { | ||
819 | hr = E_INVALIDSTATE; | ||
820 | LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback."); | ||
821 | } | ||
822 | else | ||
823 | { | ||
824 | ExitFunction(); | ||
825 | } | ||
826 | } | ||
827 | |||
828 | if (FAILED(hr)) | ||
829 | { | ||
830 | // If we failed, but rollback is disabled just bail with our error code. | ||
831 | if (pEngineState->fDisableRollback) | ||
832 | { | ||
833 | *pfRollback = TRUE; | ||
834 | break; | ||
835 | } | ||
836 | else // the action failed, roll back to previous rollback boundary. | ||
837 | { | ||
838 | HRESULT hrRollback = DoRollbackActions(pEngineState, &context, dwCheckpoint, fInTransaction, pfKeepRegistration, pRestart); | ||
839 | UNREFERENCED_PARAMETER(hrRollback); | ||
840 | fInTransaction = FALSE; | ||
841 | |||
842 | // If the rollback boundary is vital, end execution here. | ||
843 | if (pRollbackBoundary && pRollbackBoundary->fVital) | ||
844 | { | ||
845 | *pfRollback = TRUE; | ||
846 | break; | ||
847 | } | ||
848 | |||
849 | // Move forward to next rollback boundary. | ||
850 | fSeekNextRollbackBoundary = TRUE; | ||
851 | } | ||
852 | } | ||
853 | } | ||
854 | |||
855 | if (fInTransaction) | ||
856 | { | ||
857 | LogString(REPORT_STANDARD, "Committing an MSI transaction\n"); | ||
858 | hr = DoMsiCommitTransaction(&context, pEngineState); | ||
859 | ExitOnFailure(hr, "Failed committing an MSI transaction"); | ||
860 | fInTransaction = FALSE; | ||
861 | } | ||
862 | |||
863 | LExit: | ||
864 | // Send execute complete to BA. | ||
865 | UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); | ||
866 | |||
867 | return hr; | ||
868 | } | ||
869 | |||
870 | extern "C" void ApplyClean( | ||
871 | __in BURN_USER_EXPERIENCE* /*pUX*/, | ||
872 | __in BURN_PLAN* pPlan, | ||
873 | __in HANDLE hPipe | ||
874 | ) | ||
875 | { | ||
876 | HRESULT hr = S_OK; | ||
877 | |||
878 | for (DWORD i = 0; i < pPlan->cCleanActions; ++i) | ||
879 | { | ||
880 | BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; | ||
881 | |||
882 | hr = CleanPackage(hPipe, pCleanAction->pPackage); | ||
883 | } | ||
884 | } | ||
885 | |||
886 | |||
887 | // internal helper functions | ||
888 | |||
889 | static HRESULT ExecuteDependentRegistrationActions( | ||
890 | __in HANDLE hPipe, | ||
891 | __in const BURN_REGISTRATION* pRegistration, | ||
892 | __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, | ||
893 | __in DWORD cActions | ||
894 | ) | ||
895 | { | ||
896 | HRESULT hr = S_OK; | ||
897 | |||
898 | for (DWORD iAction = 0; iAction < cActions; ++iAction) | ||
899 | { | ||
900 | const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction; | ||
901 | |||
902 | if (pRegistration->fPerMachine) | ||
903 | { | ||
904 | hr = ElevationProcessDependentRegistration(hPipe, pAction); | ||
905 | ExitOnFailure(hr, "Failed to execute dependent registration action."); | ||
906 | } | ||
907 | else | ||
908 | { | ||
909 | hr = DependencyProcessDependentRegistration(pRegistration, pAction); | ||
910 | ExitOnFailure(hr, "Failed to process dependency registration action."); | ||
911 | } | ||
912 | } | ||
913 | |||
914 | LExit: | ||
915 | return hr; | ||
916 | } | ||
917 | |||
918 | static HRESULT ExtractContainer( | ||
919 | __in HANDLE hSourceEngineFile, | ||
920 | __in BURN_CONTAINER* pContainer, | ||
921 | __in_z LPCWSTR wzContainerPath, | ||
922 | __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, | ||
923 | __in DWORD cExtractPayloads | ||
924 | ) | ||
925 | { | ||
926 | HRESULT hr = S_OK; | ||
927 | BURN_CONTAINER_CONTEXT context = { }; | ||
928 | HANDLE hContainerHandle = INVALID_HANDLE_VALUE; | ||
929 | LPWSTR sczExtractPayloadId = NULL; | ||
930 | |||
931 | // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. | ||
932 | if (pContainer->fActuallyAttached) | ||
933 | { | ||
934 | hContainerHandle = hSourceEngineFile; | ||
935 | } | ||
936 | |||
937 | hr = ContainerOpen(&context, pContainer, hContainerHandle, wzContainerPath); | ||
938 | ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); | ||
939 | |||
940 | while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId))) | ||
941 | { | ||
942 | BOOL fExtracted = FALSE; | ||
943 | |||
944 | for (DWORD iExtract = 0; iExtract < cExtractPayloads; ++iExtract) | ||
945 | { | ||
946 | BURN_EXTRACT_PAYLOAD* pExtract = rgExtractPayloads + iExtract; | ||
947 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->pPayload->sczSourcePath, -1)) | ||
948 | { | ||
949 | // TODO: Send progress when extracting stream to file. | ||
950 | hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); | ||
951 | ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); | ||
952 | |||
953 | fExtracted = TRUE; | ||
954 | break; | ||
955 | } | ||
956 | } | ||
957 | |||
958 | if (!fExtracted) | ||
959 | { | ||
960 | hr = ContainerSkipStream(&context); | ||
961 | ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); | ||
962 | } | ||
963 | } | ||
964 | |||
965 | if (E_NOMOREITEMS == hr) | ||
966 | { | ||
967 | hr = S_OK; | ||
968 | } | ||
969 | ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); | ||
970 | |||
971 | LExit: | ||
972 | ReleaseStr(sczExtractPayloadId); | ||
973 | ContainerClose(&context); | ||
974 | |||
975 | return hr; | ||
976 | } | ||
977 | |||
978 | static void UpdateCacheSuccessProgress( | ||
979 | __in BURN_PLAN* pPlan, | ||
980 | __in BURN_CACHE_ACTION* pCacheAction, | ||
981 | __inout DWORD64* pqwSuccessfulCachedProgress | ||
982 | ) | ||
983 | { | ||
984 | switch (pCacheAction->type) | ||
985 | { | ||
986 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
987 | *pqwSuccessfulCachedProgress += pCacheAction->bundleLayout.qwBundleSize; | ||
988 | break; | ||
989 | |||
990 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: | ||
991 | if (!pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply) | ||
992 | { | ||
993 | pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply = TRUE; | ||
994 | *pqwSuccessfulCachedProgress += pCacheAction->resolveContainer.pContainer->qwFileSize; | ||
995 | } | ||
996 | break; | ||
997 | |||
998 | case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: | ||
999 | if (!pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply) | ||
1000 | { | ||
1001 | pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply = TRUE; | ||
1002 | *pqwSuccessfulCachedProgress += pCacheAction->layoutContainer.pContainer->qwFileSize; | ||
1003 | } | ||
1004 | break; | ||
1005 | |||
1006 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: | ||
1007 | if (!pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply) | ||
1008 | { | ||
1009 | pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply = TRUE; | ||
1010 | *pqwSuccessfulCachedProgress += pCacheAction->resolvePayload.pPayload->qwFileSize; | ||
1011 | } | ||
1012 | break; | ||
1013 | |||
1014 | case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: | ||
1015 | if (!pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply) | ||
1016 | { | ||
1017 | pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply = TRUE; | ||
1018 | *pqwSuccessfulCachedProgress += pCacheAction->cachePayload.pPayload->qwFileSize; | ||
1019 | } | ||
1020 | break; | ||
1021 | |||
1022 | case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: | ||
1023 | if (!pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply) | ||
1024 | { | ||
1025 | pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply = TRUE; | ||
1026 | *pqwSuccessfulCachedProgress += pCacheAction->layoutPayload.pPayload->qwFileSize; | ||
1027 | } | ||
1028 | break; | ||
1029 | |||
1030 | default: | ||
1031 | AssertSz(FALSE, "Unexpected cache action type."); | ||
1032 | break; | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | static HRESULT LayoutBundle( | ||
1037 | __in BURN_USER_EXPERIENCE* pUX, | ||
1038 | __in BURN_VARIABLES* pVariables, | ||
1039 | __in HANDLE hPipe, | ||
1040 | __in_z LPCWSTR wzExecutableName, | ||
1041 | __in_z LPCWSTR wzLayoutDirectory, | ||
1042 | __in_z LPCWSTR wzUnverifiedPath, | ||
1043 | __in DWORD64 qwSuccessfulCacheProgress, | ||
1044 | __in DWORD64 qwTotalCacheSize | ||
1045 | ) | ||
1046 | { | ||
1047 | HRESULT hr = S_OK; | ||
1048 | LPWSTR sczBundlePath = NULL; | ||
1049 | LPWSTR sczDestinationPath = NULL; | ||
1050 | int nEquivalentPaths = 0; | ||
1051 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; | ||
1052 | BOOL fRetry = FALSE; | ||
1053 | BOOL fRetryAcquire = FALSE; | ||
1054 | |||
1055 | hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); | ||
1056 | if (FAILED(hr)) | ||
1057 | { | ||
1058 | if (E_NOTFOUND != hr) | ||
1059 | { | ||
1060 | ExitOnFailure(hr, "Failed to get path to bundle source process path to layout."); | ||
1061 | } | ||
1062 | |||
1063 | hr = PathForCurrentProcess(&sczBundlePath, NULL); | ||
1064 | ExitOnFailure(hr, "Failed to get path to bundle to layout."); | ||
1065 | } | ||
1066 | |||
1067 | hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczDestinationPath); | ||
1068 | ExitOnFailure(hr, "Failed to concat layout path for bundle."); | ||
1069 | |||
1070 | // If the destination path is the currently running bundle, bail. | ||
1071 | hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); | ||
1072 | ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); | ||
1073 | |||
1074 | if (CSTR_EQUAL == nEquivalentPaths) | ||
1075 | { | ||
1076 | ExitFunction1(hr = S_OK); | ||
1077 | } | ||
1078 | |||
1079 | progress.pUX = pUX; | ||
1080 | progress.qwCacheProgress = qwSuccessfulCacheProgress; | ||
1081 | progress.qwTotalCacheSize = qwTotalCacheSize; | ||
1082 | |||
1083 | do | ||
1084 | { | ||
1085 | hr = S_OK; | ||
1086 | fRetry = FALSE; | ||
1087 | |||
1088 | for (;;) | ||
1089 | { | ||
1090 | fRetryAcquire = FALSE; | ||
1091 | progress.fCancel = FALSE; | ||
1092 | |||
1093 | hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); | ||
1094 | ExitOnRootFailure(hr, "BA aborted cache acquire begin."); | ||
1095 | |||
1096 | hr = CopyPayload(&progress, sczBundlePath, wzUnverifiedPath); | ||
1097 | // Error handling happens after sending complete message to BA. | ||
1098 | |||
1099 | UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire); | ||
1100 | if (fRetryAcquire) | ||
1101 | { | ||
1102 | continue; | ||
1103 | } | ||
1104 | |||
1105 | ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); | ||
1106 | break; | ||
1107 | } | ||
1108 | |||
1109 | do | ||
1110 | { | ||
1111 | hr = UserExperienceOnCacheVerifyBegin(pUX, NULL, NULL); | ||
1112 | ExitOnRootFailure(hr, "BA aborted cache verify begin."); | ||
1113 | |||
1114 | if (INVALID_HANDLE_VALUE != hPipe) | ||
1115 | { | ||
1116 | hr = ElevationLayoutBundle(hPipe, wzLayoutDirectory, wzUnverifiedPath); | ||
1117 | } | ||
1118 | else | ||
1119 | { | ||
1120 | hr = CacheLayoutBundle(wzExecutableName, wzLayoutDirectory, wzUnverifiedPath); | ||
1121 | } | ||
1122 | |||
1123 | BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; | ||
1124 | UserExperienceOnCacheVerifyComplete(pUX, NULL, NULL, hr, &action); | ||
1125 | if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) | ||
1126 | { | ||
1127 | hr = S_FALSE; // retry verify. | ||
1128 | } | ||
1129 | else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) | ||
1130 | { | ||
1131 | fRetry = TRUE; // go back and retry acquire. | ||
1132 | } | ||
1133 | } while (S_FALSE == hr); | ||
1134 | } while (fRetry); | ||
1135 | LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, wzLayoutDirectory); | ||
1136 | |||
1137 | LExit: | ||
1138 | ReleaseStr(sczDestinationPath); | ||
1139 | ReleaseStr(sczBundlePath); | ||
1140 | |||
1141 | return hr; | ||
1142 | } | ||
1143 | |||
1144 | static HRESULT AcquireContainerOrPayload( | ||
1145 | __in BURN_USER_EXPERIENCE* pUX, | ||
1146 | __in BURN_VARIABLES* pVariables, | ||
1147 | __in_opt BURN_CONTAINER* pContainer, | ||
1148 | __in_opt BURN_PACKAGE* pPackage, | ||
1149 | __in_opt BURN_PAYLOAD* pPayload, | ||
1150 | __in LPCWSTR wzDestinationPath, | ||
1151 | __in DWORD64 qwSuccessfulCacheProgress, | ||
1152 | __in DWORD64 qwTotalCacheSize | ||
1153 | ) | ||
1154 | { | ||
1155 | AssertSz(pContainer || pPayload, "Must provide a container or a payload."); | ||
1156 | |||
1157 | HRESULT hr = S_OK; | ||
1158 | int nEquivalentPaths = 0; | ||
1159 | LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; | ||
1160 | LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; | ||
1161 | LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; | ||
1162 | LPWSTR sczSourceFullPath = NULL; | ||
1163 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; | ||
1164 | BOOL fRetry = FALSE; | ||
1165 | |||
1166 | progress.pContainer = pContainer; | ||
1167 | progress.pPackage = pPackage; | ||
1168 | progress.pPayload = pPayload; | ||
1169 | progress.pUX = pUX; | ||
1170 | progress.qwCacheProgress = qwSuccessfulCacheProgress; | ||
1171 | progress.qwTotalCacheSize = qwTotalCacheSize; | ||
1172 | |||
1173 | do | ||
1174 | { | ||
1175 | LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; | ||
1176 | LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath; | ||
1177 | |||
1178 | BOOL fFoundLocal = FALSE; | ||
1179 | BOOL fCopy = FALSE; | ||
1180 | BOOL fDownload = FALSE; | ||
1181 | |||
1182 | fRetry = FALSE; | ||
1183 | progress.fCancel = FALSE; | ||
1184 | |||
1185 | hr = CacheFindLocalSource(wzSourcePath, pVariables, &fFoundLocal, &sczSourceFullPath); | ||
1186 | ExitOnFailure(hr, "Failed to search local source."); | ||
1187 | |||
1188 | if (fFoundLocal) // the file exists locally, so copy it. | ||
1189 | { | ||
1190 | // If the source path and destination path are different, do the copy (otherwise there's no point). | ||
1191 | hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths); | ||
1192 | ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path."); | ||
1193 | |||
1194 | fCopy = (CSTR_EQUAL != nEquivalentPaths); | ||
1195 | } | ||
1196 | else // can't find the file locally, so prompt for source. | ||
1197 | { | ||
1198 | DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE; | ||
1199 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath); | ||
1200 | |||
1201 | hr = PromptForSource(pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); | ||
1202 | |||
1203 | // If the BA requested download then ensure a download url is available (it may have been set | ||
1204 | // during PromptForSource so we need to check again). | ||
1205 | if (fDownload) | ||
1206 | { | ||
1207 | wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; | ||
1208 | if (!wzDownloadUrl || !*wzDownloadUrl) | ||
1209 | { | ||
1210 | hr = E_INVALIDARG; | ||
1211 | } | ||
1212 | } | ||
1213 | |||
1214 | // Log the error | ||
1215 | LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath); | ||
1216 | } | ||
1217 | |||
1218 | if (fCopy) | ||
1219 | { | ||
1220 | hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); | ||
1221 | ExitOnRootFailure(hr, "BA aborted cache acquire begin."); | ||
1222 | |||
1223 | hr = CopyPayload(&progress, sczSourceFullPath, wzDestinationPath); | ||
1224 | // Error handling happens after sending complete message to BA. | ||
1225 | |||
1226 | // We successfully copied from a source location, set that as the last used source. | ||
1227 | if (SUCCEEDED(hr)) | ||
1228 | { | ||
1229 | CacheSetLastUsedSource(pVariables, sczSourceFullPath, wzRelativePath); | ||
1230 | } | ||
1231 | } | ||
1232 | else if (fDownload) | ||
1233 | { | ||
1234 | hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl); | ||
1235 | ExitOnRootFailure(hr, "BA aborted cache download payload begin."); | ||
1236 | |||
1237 | hr = DownloadPayload(&progress, wzDestinationPath); | ||
1238 | // Error handling happens after sending complete message to BA. | ||
1239 | } | ||
1240 | |||
1241 | if (fCopy || fDownload) | ||
1242 | { | ||
1243 | UserExperienceOnCacheAcquireComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); | ||
1244 | if (fRetry) | ||
1245 | { | ||
1246 | hr = S_OK; | ||
1247 | } | ||
1248 | } | ||
1249 | ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath); | ||
1250 | } while (fRetry); | ||
1251 | ExitOnFailure(hr, "Failed to find external payload to cache."); | ||
1252 | |||
1253 | LExit: | ||
1254 | ReleaseStr(sczSourceFullPath); | ||
1255 | |||
1256 | return hr; | ||
1257 | } | ||
1258 | |||
1259 | static HRESULT LayoutOrCacheContainerOrPayload( | ||
1260 | __in BURN_USER_EXPERIENCE* pUX, | ||
1261 | __in HANDLE hPipe, | ||
1262 | __in_opt BURN_CONTAINER* pContainer, | ||
1263 | __in_opt BURN_PACKAGE* pPackage, | ||
1264 | __in_opt BURN_PAYLOAD* pPayload, | ||
1265 | __in BOOL fAlreadyProvidedProgress, | ||
1266 | __in DWORD64 qwSuccessfulCachedProgress, | ||
1267 | __in DWORD64 qwTotalCacheSize, | ||
1268 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
1269 | __in_z LPCWSTR wzUnverifiedPath, | ||
1270 | __in BOOL fMove, | ||
1271 | __in DWORD cTryAgainAttempts, | ||
1272 | __out BOOL* pfRetry | ||
1273 | ) | ||
1274 | { | ||
1275 | HRESULT hr = S_OK; | ||
1276 | LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; | ||
1277 | LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; | ||
1278 | LARGE_INTEGER liContainerOrPayloadSize = { }; | ||
1279 | LARGE_INTEGER liZero = { }; | ||
1280 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; | ||
1281 | |||
1282 | liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; | ||
1283 | |||
1284 | progress.pContainer = pContainer; | ||
1285 | progress.pPackage = pPackage; | ||
1286 | progress.pPayload = pPayload; | ||
1287 | progress.pUX = pUX; | ||
1288 | progress.qwTotalCacheSize = qwTotalCacheSize; | ||
1289 | if (fAlreadyProvidedProgress) | ||
1290 | { | ||
1291 | Assert(qwSuccessfulCachedProgress >= static_cast<DWORD64>(liContainerOrPayloadSize.QuadPart)); | ||
1292 | progress.qwCacheProgress = qwSuccessfulCachedProgress - liContainerOrPayloadSize.QuadPart; // remove the payload size, since it was marked successful thus included in the successful size already. | ||
1293 | } | ||
1294 | else | ||
1295 | { | ||
1296 | progress.qwCacheProgress = qwSuccessfulCachedProgress; | ||
1297 | } | ||
1298 | |||
1299 | *pfRetry = FALSE; | ||
1300 | |||
1301 | do | ||
1302 | { | ||
1303 | hr = UserExperienceOnCacheVerifyBegin(pUX, wzPackageOrContainerId, wzPayloadId); | ||
1304 | ExitOnRootFailure(hr, "BA aborted cache verify begin."); | ||
1305 | |||
1306 | if (INVALID_HANDLE_VALUE != hPipe) // pass the decision off to the elevated process. | ||
1307 | { | ||
1308 | hr = ElevationCacheOrLayoutContainerOrPayload(hPipe, pContainer, pPackage, pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); | ||
1309 | } | ||
1310 | else if (wzLayoutDirectory) // layout the container or payload. | ||
1311 | { | ||
1312 | if (pContainer) | ||
1313 | { | ||
1314 | hr = CacheLayoutContainer(pContainer, wzLayoutDirectory, wzUnverifiedPath, fMove); | ||
1315 | } | ||
1316 | else | ||
1317 | { | ||
1318 | hr = CacheLayoutPayload(pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); | ||
1319 | } | ||
1320 | } | ||
1321 | else // complete the payload. | ||
1322 | { | ||
1323 | Assert(!pContainer); | ||
1324 | Assert(pPackage); | ||
1325 | |||
1326 | hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); | ||
1327 | } | ||
1328 | |||
1329 | // If succeeded, send 100% complete here. If the payload was already cached this is the first progress the BA | ||
1330 | // will get. | ||
1331 | if (SUCCEEDED(hr)) | ||
1332 | { | ||
1333 | CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress); | ||
1334 | if (progress.fCancel) | ||
1335 | { | ||
1336 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
1337 | } | ||
1338 | else if (progress.fError) | ||
1339 | { | ||
1340 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
1341 | } | ||
1342 | ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pContainer ? "container" : "payload", pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
1343 | } | ||
1344 | |||
1345 | BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; | ||
1346 | UserExperienceOnCacheVerifyComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); | ||
1347 | if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) | ||
1348 | { | ||
1349 | hr = S_FALSE; // retry verify. | ||
1350 | } | ||
1351 | else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) | ||
1352 | { | ||
1353 | *pfRetry = TRUE; // go back and retry acquire. | ||
1354 | } | ||
1355 | } while (S_FALSE == hr); | ||
1356 | |||
1357 | LExit: | ||
1358 | return hr; | ||
1359 | } | ||
1360 | |||
1361 | static HRESULT PromptForSource( | ||
1362 | __in BURN_USER_EXPERIENCE* pUX, | ||
1363 | __in_z LPCWSTR wzPackageOrContainerId, | ||
1364 | __in_z_opt LPCWSTR wzPayloadId, | ||
1365 | __in_z LPCWSTR wzLocalSource, | ||
1366 | __in_z_opt LPCWSTR wzDownloadSource, | ||
1367 | __inout BOOL* pfRetry, | ||
1368 | __inout BOOL* pfDownload | ||
1369 | ) | ||
1370 | { | ||
1371 | HRESULT hr = S_OK; | ||
1372 | BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE; | ||
1373 | |||
1374 | UserExperienceDeactivateEngine(pUX); | ||
1375 | |||
1376 | hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action); | ||
1377 | if (FAILED(hr)) | ||
1378 | { | ||
1379 | ExitFunction(); | ||
1380 | } | ||
1381 | |||
1382 | switch (action) | ||
1383 | { | ||
1384 | case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE: | ||
1385 | hr = E_FILENOTFOUND; | ||
1386 | break; | ||
1387 | |||
1388 | case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY: | ||
1389 | *pfRetry = TRUE; | ||
1390 | break; | ||
1391 | |||
1392 | case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD: | ||
1393 | *pfDownload = TRUE; | ||
1394 | break; | ||
1395 | |||
1396 | default: | ||
1397 | hr = E_INVALIDARG; | ||
1398 | break; | ||
1399 | } | ||
1400 | |||
1401 | LExit: | ||
1402 | UserExperienceActivateEngine(pUX, NULL); | ||
1403 | return hr; | ||
1404 | } | ||
1405 | |||
1406 | static HRESULT CopyPayload( | ||
1407 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
1408 | __in_z LPCWSTR wzSourcePath, | ||
1409 | __in_z LPCWSTR wzDestinationPath | ||
1410 | ) | ||
1411 | { | ||
1412 | HRESULT hr = S_OK; | ||
1413 | DWORD dwFileAttributes = 0; | ||
1414 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; | ||
1415 | LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; | ||
1416 | |||
1417 | DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; | ||
1418 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); | ||
1419 | |||
1420 | // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. | ||
1421 | if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) | ||
1422 | { | ||
1423 | if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) | ||
1424 | { | ||
1425 | dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; | ||
1426 | if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) | ||
1427 | { | ||
1428 | ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); | ||
1429 | } | ||
1430 | } | ||
1431 | } | ||
1432 | |||
1433 | if (!::CopyFileExW(wzSourcePath, wzDestinationPath, CacheProgressRoutine, pProgress, &pProgress->fCancel, 0)) | ||
1434 | { | ||
1435 | if (pProgress->fCancel) | ||
1436 | { | ||
1437 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
1438 | ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1439 | } | ||
1440 | else | ||
1441 | { | ||
1442 | ExitWithLastError(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
1443 | } | ||
1444 | } | ||
1445 | |||
1446 | LExit: | ||
1447 | return hr; | ||
1448 | } | ||
1449 | |||
1450 | static HRESULT DownloadPayload( | ||
1451 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
1452 | __in_z LPCWSTR wzDestinationPath | ||
1453 | ) | ||
1454 | { | ||
1455 | HRESULT hr = S_OK; | ||
1456 | DWORD dwFileAttributes = 0; | ||
1457 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; | ||
1458 | LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; | ||
1459 | DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource; | ||
1460 | DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayload->qwFileSize; | ||
1461 | DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; | ||
1462 | DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; | ||
1463 | APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; | ||
1464 | |||
1465 | DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; | ||
1466 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); | ||
1467 | |||
1468 | // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. | ||
1469 | if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) | ||
1470 | { | ||
1471 | if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) | ||
1472 | { | ||
1473 | dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; | ||
1474 | if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) | ||
1475 | { | ||
1476 | ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); | ||
1477 | } | ||
1478 | } | ||
1479 | } | ||
1480 | |||
1481 | cacheCallback.pfnProgress = CacheProgressRoutine; | ||
1482 | cacheCallback.pfnCancel = NULL; // TODO: set this | ||
1483 | cacheCallback.pv = pProgress; | ||
1484 | |||
1485 | // If the protocol is specially marked, "bits" let's use that. | ||
1486 | if (L'b' == pDownloadSource->sczUrl[0] && | ||
1487 | L'i' == pDownloadSource->sczUrl[1] && | ||
1488 | L't' == pDownloadSource->sczUrl[2] && | ||
1489 | L's' == pDownloadSource->sczUrl[3] && | ||
1490 | (L':' == pDownloadSource->sczUrl[4] || (L's' == pDownloadSource->sczUrl[4] && L':' == pDownloadSource->sczUrl[5])) | ||
1491 | ) | ||
1492 | { | ||
1493 | hr = BitsDownloadUrl(&cacheCallback, pDownloadSource, wzDestinationPath); | ||
1494 | } | ||
1495 | else // wininet handles everything else. | ||
1496 | { | ||
1497 | authenticationData.pUX = pProgress->pUX; | ||
1498 | authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; | ||
1499 | authenticationData.wzPayloadId = wzPayloadId; | ||
1500 | authenticationCallback.pv = static_cast<LPVOID>(&authenticationData); | ||
1501 | authenticationCallback.pfnAuthenticate = &AuthenticationRequired; | ||
1502 | |||
1503 | hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); | ||
1504 | } | ||
1505 | ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); | ||
1506 | |||
1507 | LExit: | ||
1508 | return hr; | ||
1509 | } | ||
1510 | |||
1511 | static HRESULT WINAPI AuthenticationRequired( | ||
1512 | __in LPVOID pData, | ||
1513 | __in HINTERNET hUrl, | ||
1514 | __in long lHttpCode, | ||
1515 | __out BOOL* pfRetrySend, | ||
1516 | __out BOOL* pfRetry | ||
1517 | ) | ||
1518 | { | ||
1519 | Assert(401 == lHttpCode || 407 == lHttpCode); | ||
1520 | |||
1521 | HRESULT hr = S_OK; | ||
1522 | DWORD er = ERROR_SUCCESS; | ||
1523 | BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; | ||
1524 | LPWSTR sczError = NULL; | ||
1525 | int nResult = IDNOACTION; | ||
1526 | |||
1527 | *pfRetrySend = FALSE; | ||
1528 | *pfRetry = FALSE; | ||
1529 | |||
1530 | hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); | ||
1531 | ExitOnFailure(hr, "Failed to allocation error string."); | ||
1532 | |||
1533 | APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData); | ||
1534 | |||
1535 | UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; | ||
1536 | nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); | ||
1537 | if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) | ||
1538 | { | ||
1539 | er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); | ||
1540 | if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) | ||
1541 | { | ||
1542 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
1543 | } | ||
1544 | else if (ERROR_INTERNET_FORCE_RETRY == er) | ||
1545 | { | ||
1546 | *pfRetrySend = TRUE; | ||
1547 | hr = S_OK; | ||
1548 | } | ||
1549 | else | ||
1550 | { | ||
1551 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
1552 | } | ||
1553 | } | ||
1554 | else if (IDRETRY == nResult) | ||
1555 | { | ||
1556 | *pfRetry = TRUE; | ||
1557 | hr = S_OK; | ||
1558 | } | ||
1559 | else | ||
1560 | { | ||
1561 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
1562 | } | ||
1563 | |||
1564 | LExit: | ||
1565 | ReleaseStr(sczError); | ||
1566 | |||
1567 | return hr; | ||
1568 | } | ||
1569 | |||
1570 | static DWORD CALLBACK CacheProgressRoutine( | ||
1571 | __in LARGE_INTEGER TotalFileSize, | ||
1572 | __in LARGE_INTEGER TotalBytesTransferred, | ||
1573 | __in LARGE_INTEGER /*StreamSize*/, | ||
1574 | __in LARGE_INTEGER /*StreamBytesTransferred*/, | ||
1575 | __in DWORD /*dwStreamNumber*/, | ||
1576 | __in DWORD /*dwCallbackReason*/, | ||
1577 | __in HANDLE /*hSourceFile*/, | ||
1578 | __in HANDLE /*hDestinationFile*/, | ||
1579 | __in_opt LPVOID lpData | ||
1580 | ) | ||
1581 | { | ||
1582 | HRESULT hr = S_OK; | ||
1583 | DWORD dwResult = PROGRESS_CONTINUE; | ||
1584 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT*>(lpData); | ||
1585 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; | ||
1586 | LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; | ||
1587 | DWORD64 qwCacheProgress = pProgress->qwCacheProgress + TotalBytesTransferred.QuadPart; | ||
1588 | if (qwCacheProgress > pProgress->qwTotalCacheSize) | ||
1589 | { | ||
1590 | AssertSz(FALSE, "Apply has cached more than Plan envisioned."); | ||
1591 | qwCacheProgress = pProgress->qwTotalCacheSize; | ||
1592 | } | ||
1593 | DWORD dwOverallPercentage = pProgress->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->qwTotalCacheSize) : 0; | ||
1594 | |||
1595 | hr = UserExperienceOnCacheAcquireProgress(pProgress->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); | ||
1596 | if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) | ||
1597 | { | ||
1598 | dwResult = PROGRESS_CANCEL; | ||
1599 | pProgress->fCancel = TRUE; | ||
1600 | } | ||
1601 | else if (FAILED(hr)) | ||
1602 | { | ||
1603 | dwResult = PROGRESS_CANCEL; | ||
1604 | pProgress->fError = TRUE; | ||
1605 | } | ||
1606 | else | ||
1607 | { | ||
1608 | dwResult = PROGRESS_CONTINUE; | ||
1609 | } | ||
1610 | |||
1611 | return dwResult; | ||
1612 | } | ||
1613 | |||
1614 | static void DoRollbackCache( | ||
1615 | __in BURN_USER_EXPERIENCE* /*pUX*/, | ||
1616 | __in BURN_PLAN* pPlan, | ||
1617 | __in HANDLE hPipe, | ||
1618 | __in DWORD dwCheckpoint | ||
1619 | ) | ||
1620 | { | ||
1621 | HRESULT hr = S_OK; | ||
1622 | DWORD iCheckpoint = 0; | ||
1623 | |||
1624 | // Scan to last checkpoint. | ||
1625 | for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) | ||
1626 | { | ||
1627 | BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; | ||
1628 | |||
1629 | if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint) | ||
1630 | { | ||
1631 | iCheckpoint = i; | ||
1632 | break; | ||
1633 | } | ||
1634 | } | ||
1635 | |||
1636 | // Rollback cache actions. | ||
1637 | if (iCheckpoint) | ||
1638 | { | ||
1639 | // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. | ||
1640 | for (int i = iCheckpoint - 1; i >= 0; --i) | ||
1641 | { | ||
1642 | BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; | ||
1643 | |||
1644 | switch (pRollbackCacheAction->type) | ||
1645 | { | ||
1646 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
1647 | break; | ||
1648 | |||
1649 | case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: | ||
1650 | hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage); | ||
1651 | break; | ||
1652 | |||
1653 | default: | ||
1654 | AssertSz(FALSE, "Invalid rollback cache action."); | ||
1655 | break; | ||
1656 | } | ||
1657 | } | ||
1658 | } | ||
1659 | } | ||
1660 | |||
1661 | /* MSI Transactions: | ||
1662 | * All MSI/MSP/MSU packages wrapped in MsiBeginTranasaction-MsiEndTransaction pair are installed or uninstalled together. | ||
1663 | */ | ||
1664 | static HRESULT ExecuteMsiBeginTransaction( | ||
1665 | __in BURN_EXECUTE_CONTEXT* pContext | ||
1666 | , __in BURN_ENGINE_STATE* pEngineState | ||
1667 | ) | ||
1668 | { | ||
1669 | HRESULT hr = S_OK; | ||
1670 | UINT uResult = ERROR_SUCCESS; | ||
1671 | |||
1672 | // Per user/machine context | ||
1673 | if (pEngineState->plan.fPerMachine) | ||
1674 | { | ||
1675 | hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); | ||
1676 | ExitOnFailure(hr, "Failed to begin an MSI transaction."); | ||
1677 | } | ||
1678 | else | ||
1679 | { | ||
1680 | MSIHANDLE hMsiTrns = NULL; | ||
1681 | HANDLE hMsiTrnsEvent = NULL; | ||
1682 | uResult = MsiBeginTransaction(L"WiX", 0, &hMsiTrns, &hMsiTrnsEvent); | ||
1683 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
1684 | } | ||
1685 | |||
1686 | LExit: | ||
1687 | return hr; | ||
1688 | } | ||
1689 | |||
1690 | static HRESULT ExecuteMsiCommitTransaction( | ||
1691 | __in BURN_EXECUTE_CONTEXT* pContext | ||
1692 | , __in BURN_ENGINE_STATE* pEngineState | ||
1693 | ) | ||
1694 | { | ||
1695 | HRESULT hr = S_OK; | ||
1696 | UINT uResult = ERROR_SUCCESS; | ||
1697 | |||
1698 | // Per user/machine context | ||
1699 | if (pEngineState->plan.fPerMachine) | ||
1700 | { | ||
1701 | hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); | ||
1702 | ExitOnFailure(hr, "Failed to commit an MSI transaction."); | ||
1703 | } | ||
1704 | else | ||
1705 | { | ||
1706 | uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); | ||
1707 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
1708 | } | ||
1709 | |||
1710 | LExit: | ||
1711 | return hr; | ||
1712 | } | ||
1713 | |||
1714 | static HRESULT ExecuteMsiRollbackTransaction( | ||
1715 | __in BURN_EXECUTE_CONTEXT* pContext | ||
1716 | , __in BURN_ENGINE_STATE* pEngineState | ||
1717 | ) | ||
1718 | { | ||
1719 | HRESULT hr = S_OK; | ||
1720 | UINT uResult = ERROR_SUCCESS; | ||
1721 | |||
1722 | // Per user/machine context | ||
1723 | if (pEngineState->plan.fPerMachine) | ||
1724 | { | ||
1725 | hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); | ||
1726 | ExitOnFailure(hr, "Failed to rollback an MSI transaction."); | ||
1727 | } | ||
1728 | else | ||
1729 | { | ||
1730 | uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); | ||
1731 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
1732 | } | ||
1733 | |||
1734 | LExit: | ||
1735 | return hr; | ||
1736 | } | ||
1737 | |||
1738 | // Currently, supporting only elevated transactions. | ||
1739 | static HRESULT DoMsiBeginTransaction( | ||
1740 | __in BURN_EXECUTE_CONTEXT *pContext | ||
1741 | , __in BURN_ENGINE_STATE* pEngineState | ||
1742 | ) | ||
1743 | { | ||
1744 | HRESULT hr = S_OK; | ||
1745 | |||
1746 | hr = ExecuteMsiBeginTransaction(pContext, pEngineState); | ||
1747 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
1748 | |||
1749 | LExit: | ||
1750 | return hr; | ||
1751 | } | ||
1752 | |||
1753 | static HRESULT DoMsiCommitTransaction( | ||
1754 | __in BURN_EXECUTE_CONTEXT *pContext | ||
1755 | , __in BURN_ENGINE_STATE* pEngineState | ||
1756 | ) | ||
1757 | { | ||
1758 | HRESULT hr = S_OK; | ||
1759 | |||
1760 | hr = ExecuteMsiCommitTransaction(pContext, pEngineState); | ||
1761 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
1762 | |||
1763 | LExit: | ||
1764 | return hr; | ||
1765 | } | ||
1766 | |||
1767 | static HRESULT DoMsiRollbackTransaction( | ||
1768 | __in BURN_EXECUTE_CONTEXT *pContext | ||
1769 | , __in BURN_ENGINE_STATE* pEngineState | ||
1770 | ) | ||
1771 | { | ||
1772 | HRESULT hr = S_OK; | ||
1773 | |||
1774 | hr = ExecuteMsiRollbackTransaction(pContext, pEngineState); | ||
1775 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
1776 | |||
1777 | LExit: | ||
1778 | return hr; | ||
1779 | } | ||
1780 | |||
1781 | |||
1782 | static HRESULT DoExecuteAction( | ||
1783 | __in BURN_ENGINE_STATE* pEngineState, | ||
1784 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
1785 | __in_opt HANDLE hCacheThread, | ||
1786 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
1787 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
1788 | __out DWORD* pdwCheckpoint, | ||
1789 | __out BOOL* pfKeepRegistration, | ||
1790 | __out BOOL* pfSuspend, | ||
1791 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
1792 | ) | ||
1793 | { | ||
1794 | Assert(!pExecuteAction->fDeleted); | ||
1795 | |||
1796 | HRESULT hr = S_OK; | ||
1797 | HANDLE rghWait[2] = { }; | ||
1798 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
1799 | BOOL fRetry = FALSE; | ||
1800 | BOOL fStopWusaService = FALSE; | ||
1801 | |||
1802 | pContext->fRollback = FALSE; | ||
1803 | |||
1804 | do | ||
1805 | { | ||
1806 | switch (pExecuteAction->type) | ||
1807 | { | ||
1808 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
1809 | *pdwCheckpoint = pExecuteAction->checkpoint.dwId; | ||
1810 | break; | ||
1811 | |||
1812 | case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: | ||
1813 | // wait for cache sync-point | ||
1814 | rghWait[0] = pExecuteAction->syncpoint.hEvent; | ||
1815 | rghWait[1] = hCacheThread; | ||
1816 | switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) | ||
1817 | { | ||
1818 | case WAIT_OBJECT_0: | ||
1819 | break; | ||
1820 | |||
1821 | case WAIT_OBJECT_0 + 1: | ||
1822 | if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) | ||
1823 | { | ||
1824 | ExitWithLastError(hr, "Failed to get cache thread exit code."); | ||
1825 | } | ||
1826 | |||
1827 | if (SUCCEEDED(hr)) | ||
1828 | { | ||
1829 | hr = E_UNEXPECTED; | ||
1830 | } | ||
1831 | ExitOnFailure(hr, "Cache thread exited unexpectedly."); | ||
1832 | |||
1833 | case WAIT_FAILED: __fallthrough; | ||
1834 | default: | ||
1835 | ExitWithLastError(hr, "Failed to wait for cache check-point."); | ||
1836 | } | ||
1837 | break; | ||
1838 | |||
1839 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
1840 | hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
1841 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
1842 | break; | ||
1843 | |||
1844 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
1845 | hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
1846 | ExitOnFailure(hr, "Failed to execute MSI package."); | ||
1847 | break; | ||
1848 | |||
1849 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
1850 | hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
1851 | ExitOnFailure(hr, "Failed to execute MSP package."); | ||
1852 | break; | ||
1853 | |||
1854 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
1855 | hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart); | ||
1856 | fStopWusaService = fRetry; | ||
1857 | ExitOnFailure(hr, "Failed to execute MSU package."); | ||
1858 | break; | ||
1859 | |||
1860 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
1861 | hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext); | ||
1862 | ExitOnFailure(hr, "Failed to execute package provider registration action."); | ||
1863 | break; | ||
1864 | |||
1865 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
1866 | hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext); | ||
1867 | ExitOnFailure(hr, "Failed to execute dependency action."); | ||
1868 | break; | ||
1869 | |||
1870 | case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: | ||
1871 | hr = ExecuteCompatiblePackageAction(pEngineState, pExecuteAction); | ||
1872 | ExitOnFailure(hr, "Failed to execute compatible package action."); | ||
1873 | break; | ||
1874 | |||
1875 | case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: | ||
1876 | *pfKeepRegistration = pExecuteAction->registration.fKeep; | ||
1877 | break; | ||
1878 | |||
1879 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
1880 | *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; | ||
1881 | break; | ||
1882 | |||
1883 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; | ||
1884 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; | ||
1885 | default: | ||
1886 | hr = E_UNEXPECTED; | ||
1887 | ExitOnFailure(hr, "Invalid execute action."); | ||
1888 | } | ||
1889 | |||
1890 | if (*pRestart < restart) | ||
1891 | { | ||
1892 | *pRestart = restart; | ||
1893 | } | ||
1894 | } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED); | ||
1895 | |||
1896 | LExit: | ||
1897 | return hr; | ||
1898 | } | ||
1899 | |||
1900 | static HRESULT DoRollbackActions( | ||
1901 | __in BURN_ENGINE_STATE* pEngineState, | ||
1902 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
1903 | __in DWORD dwCheckpoint, | ||
1904 | __in BOOL fInTransaction, | ||
1905 | __out BOOL* pfKeepRegistration, | ||
1906 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
1907 | ) | ||
1908 | { | ||
1909 | HRESULT hr = S_OK; | ||
1910 | DWORD iCheckpoint = 0; | ||
1911 | BOOL fRetryIgnored = FALSE; | ||
1912 | BOOL fSuspendIgnored = FALSE; | ||
1913 | |||
1914 | pContext->fRollback = TRUE; | ||
1915 | |||
1916 | // Rollback MSI transaction | ||
1917 | if (fInTransaction) | ||
1918 | { | ||
1919 | hr = DoMsiRollbackTransaction(pContext, pEngineState); | ||
1920 | ExitOnFailure(hr, "Failed rolling back transaction"); | ||
1921 | } | ||
1922 | |||
1923 | // scan to last checkpoint | ||
1924 | for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) | ||
1925 | { | ||
1926 | BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; | ||
1927 | if (pRollbackAction->fDeleted) | ||
1928 | { | ||
1929 | continue; | ||
1930 | } | ||
1931 | |||
1932 | if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type) | ||
1933 | { | ||
1934 | if (pRollbackAction->checkpoint.dwId == dwCheckpoint) | ||
1935 | { | ||
1936 | iCheckpoint = i; | ||
1937 | break; | ||
1938 | } | ||
1939 | } | ||
1940 | } | ||
1941 | |||
1942 | // execute rollback actions | ||
1943 | if (iCheckpoint) | ||
1944 | { | ||
1945 | // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. | ||
1946 | for (int i = iCheckpoint - 1; i >= 0; --i) | ||
1947 | { | ||
1948 | BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; | ||
1949 | if (pRollbackAction->fDeleted) | ||
1950 | { | ||
1951 | continue; | ||
1952 | } | ||
1953 | |||
1954 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
1955 | switch (pRollbackAction->type) | ||
1956 | { | ||
1957 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
1958 | break; | ||
1959 | |||
1960 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
1961 | hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
1962 | TraceError(hr, "Failed to rollback EXE package."); | ||
1963 | hr = S_OK; | ||
1964 | break; | ||
1965 | |||
1966 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
1967 | if (fInTransaction) | ||
1968 | { | ||
1969 | LogString(REPORT_STANDARD, "Skipping rolling back an MSI package- already done in transaction rollback\n"); | ||
1970 | break; | ||
1971 | } | ||
1972 | hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
1973 | TraceError(hr, "Failed to rollback MSI package."); | ||
1974 | hr = S_OK; | ||
1975 | break; | ||
1976 | |||
1977 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
1978 | if (fInTransaction) | ||
1979 | { | ||
1980 | LogString(REPORT_STANDARD, "Skipping rolling back an MSP package- already done in transaction rollback\n"); | ||
1981 | break; | ||
1982 | } | ||
1983 | hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
1984 | TraceError(hr, "Failed to rollback MSP package."); | ||
1985 | hr = S_OK; | ||
1986 | break; | ||
1987 | |||
1988 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
1989 | if (fInTransaction) | ||
1990 | { | ||
1991 | LogString(REPORT_STANDARD, "Skipping rolling back an MSU package- already done in transaction rollback\n"); | ||
1992 | break; | ||
1993 | } | ||
1994 | hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
1995 | TraceError(hr, "Failed to rollback MSU package."); | ||
1996 | hr = S_OK; | ||
1997 | break; | ||
1998 | |||
1999 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
2000 | hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); | ||
2001 | TraceError(hr, "Failed to rollback package provider action."); | ||
2002 | hr = S_OK; | ||
2003 | break; | ||
2004 | |||
2005 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
2006 | hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); | ||
2007 | TraceError(hr, "Failed to rollback dependency action."); | ||
2008 | hr = S_OK; | ||
2009 | break; | ||
2010 | |||
2011 | case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: | ||
2012 | *pfKeepRegistration = pRollbackAction->registration.fKeep; | ||
2013 | break; | ||
2014 | |||
2015 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
2016 | ExitFunction1(hr = S_OK); | ||
2017 | |||
2018 | case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: | ||
2019 | hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); | ||
2020 | break; | ||
2021 | |||
2022 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; | ||
2023 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; | ||
2024 | default: | ||
2025 | hr = E_UNEXPECTED; | ||
2026 | ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); | ||
2027 | } | ||
2028 | |||
2029 | if (*pRestart < restart) | ||
2030 | { | ||
2031 | *pRestart = restart; | ||
2032 | } | ||
2033 | } | ||
2034 | } | ||
2035 | |||
2036 | LExit: | ||
2037 | return hr; | ||
2038 | } | ||
2039 | |||
2040 | static HRESULT ExecuteExePackage( | ||
2041 | __in BURN_ENGINE_STATE* pEngineState, | ||
2042 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2043 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2044 | __in BOOL fRollback, | ||
2045 | __out BOOL* pfRetry, | ||
2046 | __out BOOL* pfSuspend, | ||
2047 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2048 | ) | ||
2049 | { | ||
2050 | HRESULT hr = S_OK; | ||
2051 | HRESULT hrExecute = S_OK; | ||
2052 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
2053 | int nResult = 0; | ||
2054 | BOOL fBeginCalled = FALSE; | ||
2055 | |||
2056 | if (FAILED(pExecuteAction->exePackage.pPackage->hrCacheResult)) | ||
2057 | { | ||
2058 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->exePackage.pPackage->sczId, pExecuteAction->exePackage.pPackage->hrCacheResult); | ||
2059 | ExitFunction1(hr = S_OK); | ||
2060 | } | ||
2061 | |||
2062 | Assert(pContext->fRollback == fRollback); | ||
2063 | pContext->pExecutingPackage = pExecuteAction->exePackage.pPackage; | ||
2064 | fBeginCalled = TRUE; | ||
2065 | |||
2066 | // Send package execute begin to BA. | ||
2067 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback); | ||
2068 | ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); | ||
2069 | |||
2070 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2071 | message.dwAllowedResults = MB_OKCANCEL; | ||
2072 | message.progress.dwPercentage = fRollback ? 100 : 0; | ||
2073 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2074 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2075 | ExitOnRootFailure(hr, "UX aborted EXE progress."); | ||
2076 | |||
2077 | // Execute package. | ||
2078 | if (pExecuteAction->exePackage.pPackage->fPerMachine) | ||
2079 | { | ||
2080 | hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | ||
2081 | ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); | ||
2082 | } | ||
2083 | else | ||
2084 | { | ||
2085 | hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | ||
2086 | ExitOnFailure(hrExecute, "Failed to configure per-user EXE package."); | ||
2087 | } | ||
2088 | |||
2089 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2090 | message.dwAllowedResults = MB_OKCANCEL; | ||
2091 | message.progress.dwPercentage = fRollback ? 0 : 100; | ||
2092 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2093 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2094 | ExitOnRootFailure(hr, "UX aborted EXE progress."); | ||
2095 | |||
2096 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2097 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2098 | |||
2099 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2100 | ExitOnRootFailure(hr, "UX aborted EXE package execute progress."); | ||
2101 | |||
2102 | LExit: | ||
2103 | if (fBeginCalled) | ||
2104 | { | ||
2105 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->exePackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2106 | } | ||
2107 | |||
2108 | return hr; | ||
2109 | } | ||
2110 | |||
2111 | static HRESULT ExecuteMsiPackage( | ||
2112 | __in BURN_ENGINE_STATE* pEngineState, | ||
2113 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2114 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2115 | __in BOOL fRollback, | ||
2116 | __out BOOL* pfRetry, | ||
2117 | __out BOOL* pfSuspend, | ||
2118 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2119 | ) | ||
2120 | { | ||
2121 | HRESULT hr = S_OK; | ||
2122 | HRESULT hrExecute = S_OK; | ||
2123 | BOOL fBeginCalled = FALSE; | ||
2124 | |||
2125 | if (FAILED(pExecuteAction->msiPackage.pPackage->hrCacheResult)) | ||
2126 | { | ||
2127 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.pPackage->hrCacheResult); | ||
2128 | ExitFunction1(hr = S_OK); | ||
2129 | } | ||
2130 | |||
2131 | Assert(pContext->fRollback == fRollback); | ||
2132 | pContext->pExecutingPackage = pExecuteAction->msiPackage.pPackage; | ||
2133 | fBeginCalled = TRUE; | ||
2134 | |||
2135 | // Send package execute begin to BA. | ||
2136 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback); | ||
2137 | ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); | ||
2138 | |||
2139 | // execute package | ||
2140 | if (pExecuteAction->msiPackage.pPackage->fPerMachine) | ||
2141 | { | ||
2142 | hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2143 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); | ||
2144 | } | ||
2145 | else | ||
2146 | { | ||
2147 | hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2148 | ExitOnFailure(hrExecute, "Failed to configure per-user MSI package."); | ||
2149 | } | ||
2150 | |||
2151 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2152 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2153 | |||
2154 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2155 | ExitOnRootFailure(hr, "UX aborted MSI package execute progress."); | ||
2156 | |||
2157 | LExit: | ||
2158 | if (fBeginCalled) | ||
2159 | { | ||
2160 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msiPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2161 | } | ||
2162 | |||
2163 | return hr; | ||
2164 | } | ||
2165 | |||
2166 | static HRESULT ExecuteMspPackage( | ||
2167 | __in BURN_ENGINE_STATE* pEngineState, | ||
2168 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2169 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2170 | __in BOOL fRollback, | ||
2171 | __out BOOL* pfRetry, | ||
2172 | __out BOOL* pfSuspend, | ||
2173 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2174 | ) | ||
2175 | { | ||
2176 | HRESULT hr = S_OK; | ||
2177 | HRESULT hrExecute = S_OK; | ||
2178 | BOOL fBeginCalled = FALSE; | ||
2179 | |||
2180 | if (FAILED(pExecuteAction->mspTarget.pPackage->hrCacheResult)) | ||
2181 | { | ||
2182 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.pPackage->hrCacheResult); | ||
2183 | ExitFunction1(hr = S_OK); | ||
2184 | } | ||
2185 | |||
2186 | Assert(pContext->fRollback == fRollback); | ||
2187 | pContext->pExecutingPackage = pExecuteAction->mspTarget.pPackage; | ||
2188 | fBeginCalled = TRUE; | ||
2189 | |||
2190 | // Send package execute begin to BA. | ||
2191 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback); | ||
2192 | ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); | ||
2193 | |||
2194 | // Now send all the patches that target this product code. | ||
2195 | for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) | ||
2196 | { | ||
2197 | BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; | ||
2198 | |||
2199 | hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode); | ||
2200 | ExitOnRootFailure(hr, "BA aborted execute MSP target."); | ||
2201 | } | ||
2202 | |||
2203 | // execute package | ||
2204 | if (pExecuteAction->mspTarget.fPerMachineTarget) | ||
2205 | { | ||
2206 | hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2207 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package."); | ||
2208 | } | ||
2209 | else | ||
2210 | { | ||
2211 | hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
2212 | ExitOnFailure(hrExecute, "Failed to configure per-user MSP package."); | ||
2213 | } | ||
2214 | |||
2215 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2216 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2217 | |||
2218 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2219 | ExitOnRootFailure(hr, "UX aborted MSP package execute progress."); | ||
2220 | |||
2221 | LExit: | ||
2222 | if (fBeginCalled) | ||
2223 | { | ||
2224 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->mspTarget.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2225 | } | ||
2226 | |||
2227 | return hr; | ||
2228 | } | ||
2229 | |||
2230 | static HRESULT ExecuteMsuPackage( | ||
2231 | __in BURN_ENGINE_STATE* pEngineState, | ||
2232 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
2233 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
2234 | __in BOOL fRollback, | ||
2235 | __in BOOL fStopWusaService, | ||
2236 | __out BOOL* pfRetry, | ||
2237 | __out BOOL* pfSuspend, | ||
2238 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
2239 | ) | ||
2240 | { | ||
2241 | HRESULT hr = S_OK; | ||
2242 | HRESULT hrExecute = S_OK; | ||
2243 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
2244 | int nResult = 0; | ||
2245 | BOOL fBeginCalled = FALSE; | ||
2246 | |||
2247 | if (FAILED(pExecuteAction->msuPackage.pPackage->hrCacheResult)) | ||
2248 | { | ||
2249 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msuPackage.pPackage->sczId, pExecuteAction->msuPackage.pPackage->hrCacheResult); | ||
2250 | ExitFunction1(hr = S_OK); | ||
2251 | } | ||
2252 | |||
2253 | Assert(pContext->fRollback == fRollback); | ||
2254 | pContext->pExecutingPackage = pExecuteAction->msuPackage.pPackage; | ||
2255 | fBeginCalled = TRUE; | ||
2256 | |||
2257 | // Send package execute begin to BA. | ||
2258 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback); | ||
2259 | ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); | ||
2260 | |||
2261 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2262 | message.dwAllowedResults = MB_OKCANCEL; | ||
2263 | message.progress.dwPercentage = fRollback ? 100 : 0; | ||
2264 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2265 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2266 | ExitOnRootFailure(hr, "UX aborted MSU progress."); | ||
2267 | |||
2268 | // execute package | ||
2269 | if (pExecuteAction->msuPackage.pPackage->fPerMachine) | ||
2270 | { | ||
2271 | hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); | ||
2272 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); | ||
2273 | } | ||
2274 | else | ||
2275 | { | ||
2276 | hrExecute = E_UNEXPECTED; | ||
2277 | ExitOnFailure(hr, "MSU packages cannot be per-user."); | ||
2278 | } | ||
2279 | |||
2280 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
2281 | message.dwAllowedResults = MB_OKCANCEL; | ||
2282 | message.progress.dwPercentage = fRollback ? 0 : 100; | ||
2283 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
2284 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
2285 | ExitOnRootFailure(hr, "UX aborted MSU progress."); | ||
2286 | |||
2287 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
2288 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
2289 | |||
2290 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
2291 | ExitOnRootFailure(hr, "UX aborted MSU package execute progress."); | ||
2292 | |||
2293 | LExit: | ||
2294 | if (fBeginCalled) | ||
2295 | { | ||
2296 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msuPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
2297 | } | ||
2298 | |||
2299 | return hr; | ||
2300 | } | ||
2301 | |||
2302 | static HRESULT ExecutePackageProviderAction( | ||
2303 | __in BURN_ENGINE_STATE* pEngineState, | ||
2304 | __in BURN_EXECUTE_ACTION* pAction, | ||
2305 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2306 | ) | ||
2307 | { | ||
2308 | HRESULT hr = S_OK; | ||
2309 | |||
2310 | if (pAction->packageProvider.pPackage->fPerMachine) | ||
2311 | { | ||
2312 | hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction); | ||
2313 | ExitOnFailure(hr, "Failed to register the package provider on per-machine package."); | ||
2314 | } | ||
2315 | else | ||
2316 | { | ||
2317 | hr = DependencyExecutePackageProviderAction(pAction); | ||
2318 | ExitOnFailure(hr, "Failed to register the package provider on per-user package."); | ||
2319 | } | ||
2320 | |||
2321 | LExit: | ||
2322 | return hr; | ||
2323 | } | ||
2324 | |||
2325 | static HRESULT ExecuteDependencyAction( | ||
2326 | __in BURN_ENGINE_STATE* pEngineState, | ||
2327 | __in BURN_EXECUTE_ACTION* pAction, | ||
2328 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
2329 | ) | ||
2330 | { | ||
2331 | HRESULT hr = S_OK; | ||
2332 | |||
2333 | if (pAction->packageDependency.pPackage->fPerMachine) | ||
2334 | { | ||
2335 | hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction); | ||
2336 | ExitOnFailure(hr, "Failed to register the dependency on per-machine package."); | ||
2337 | } | ||
2338 | else | ||
2339 | { | ||
2340 | hr = DependencyExecutePackageDependencyAction(FALSE, pAction); | ||
2341 | ExitOnFailure(hr, "Failed to register the dependency on per-user package."); | ||
2342 | } | ||
2343 | |||
2344 | LExit: | ||
2345 | return hr; | ||
2346 | } | ||
2347 | |||
2348 | static HRESULT ExecuteCompatiblePackageAction( | ||
2349 | __in BURN_ENGINE_STATE* pEngineState, | ||
2350 | __in BURN_EXECUTE_ACTION* pAction | ||
2351 | ) | ||
2352 | { | ||
2353 | HRESULT hr = S_OK; | ||
2354 | |||
2355 | if (pAction->compatiblePackage.pReferencePackage->fPerMachine) | ||
2356 | { | ||
2357 | hr = ElevationLoadCompatiblePackageAction(pEngineState->companionConnection.hPipe, pAction); | ||
2358 | ExitOnFailure(hr, "Failed to load compatible package on per-machine package."); | ||
2359 | } | ||
2360 | |||
2361 | // Compatible package already loaded in this process. | ||
2362 | |||
2363 | LExit: | ||
2364 | return hr; | ||
2365 | } | ||
2366 | |||
2367 | static HRESULT CleanPackage( | ||
2368 | __in HANDLE hElevatedPipe, | ||
2369 | __in BURN_PACKAGE* pPackage | ||
2370 | ) | ||
2371 | { | ||
2372 | HRESULT hr = S_OK; | ||
2373 | |||
2374 | if (pPackage->fPerMachine) | ||
2375 | { | ||
2376 | hr = ElevationCleanPackage(hElevatedPipe, pPackage); | ||
2377 | } | ||
2378 | else | ||
2379 | { | ||
2380 | hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); | ||
2381 | } | ||
2382 | |||
2383 | return hr; | ||
2384 | } | ||
2385 | |||
2386 | static int GenericExecuteMessageHandler( | ||
2387 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
2388 | __in LPVOID pvContext | ||
2389 | ) | ||
2390 | { | ||
2391 | BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; | ||
2392 | int nResult = IDNOACTION; | ||
2393 | |||
2394 | switch (pMessage->type) | ||
2395 | { | ||
2396 | case GENERIC_EXECUTE_MESSAGE_PROGRESS: | ||
2397 | { | ||
2398 | DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; | ||
2399 | UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. | ||
2400 | } | ||
2401 | break; | ||
2402 | |||
2403 | case GENERIC_EXECUTE_MESSAGE_ERROR: | ||
2404 | UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value. | ||
2405 | break; | ||
2406 | |||
2407 | case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: | ||
2408 | UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. | ||
2409 | break; | ||
2410 | } | ||
2411 | |||
2412 | nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); | ||
2413 | return nResult; | ||
2414 | } | ||
2415 | |||
2416 | static int MsiExecuteMessageHandler( | ||
2417 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
2418 | __in_opt LPVOID pvContext | ||
2419 | ) | ||
2420 | { | ||
2421 | BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; | ||
2422 | int nResult = IDNOACTION; | ||
2423 | |||
2424 | switch (pMessage->type) | ||
2425 | { | ||
2426 | case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: | ||
2427 | { | ||
2428 | DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; | ||
2429 | UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. | ||
2430 | } | ||
2431 | break; | ||
2432 | |||
2433 | case WIU_MSI_EXECUTE_MESSAGE_ERROR: | ||
2434 | nResult = pMessage->nResultRecommendation; | ||
2435 | UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. | ||
2436 | break; | ||
2437 | |||
2438 | case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: | ||
2439 | nResult = pMessage->nResultRecommendation; | ||
2440 | UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. | ||
2441 | break; | ||
2442 | |||
2443 | case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: | ||
2444 | UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. | ||
2445 | break; | ||
2446 | } | ||
2447 | |||
2448 | nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); | ||
2449 | return nResult; | ||
2450 | } | ||
2451 | |||
2452 | static HRESULT ReportOverallProgressTicks( | ||
2453 | __in BURN_USER_EXPERIENCE* pUX, | ||
2454 | __in BOOL fRollback, | ||
2455 | __in DWORD cOverallProgressTicksTotal, | ||
2456 | __in DWORD cOverallProgressTicks | ||
2457 | ) | ||
2458 | { | ||
2459 | HRESULT hr = S_OK; | ||
2460 | DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0; | ||
2461 | |||
2462 | // TODO: consider sending different progress numbers in the future. | ||
2463 | hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress); | ||
2464 | |||
2465 | return hr; | ||
2466 | } | ||
2467 | |||
2468 | static HRESULT ExecutePackageComplete( | ||
2469 | __in BURN_USER_EXPERIENCE* pUX, | ||
2470 | __in BURN_VARIABLES* pVariables, | ||
2471 | __in BURN_PACKAGE* pPackage, | ||
2472 | __in HRESULT hrOverall, | ||
2473 | __in HRESULT hrExecute, | ||
2474 | __in BOOL fRollback, | ||
2475 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart, | ||
2476 | __out BOOL* pfRetry, | ||
2477 | __out BOOL* pfSuspend | ||
2478 | ) | ||
2479 | { | ||
2480 | HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. | ||
2481 | BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; | ||
2482 | |||
2483 | // Send package execute complete to BA. | ||
2484 | UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); | ||
2485 | if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) | ||
2486 | { | ||
2487 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
2488 | } | ||
2489 | *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures. | ||
2490 | *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction); | ||
2491 | |||
2492 | // Remember this package as the package that initiated the forced restart. | ||
2493 | if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) | ||
2494 | { | ||
2495 | // Best effort to set the forced restart package variable. | ||
2496 | VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE); | ||
2497 | } | ||
2498 | |||
2499 | // If we're retrying, leave a message in the log file and say everything is okay. | ||
2500 | if (*pfRetry) | ||
2501 | { | ||
2502 | LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); | ||
2503 | hr = S_OK; | ||
2504 | } | ||
2505 | else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay. | ||
2506 | { | ||
2507 | LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); | ||
2508 | hr = S_OK; | ||
2509 | } | ||
2510 | else | ||
2511 | { | ||
2512 | LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); | ||
2513 | } | ||
2514 | |||
2515 | return hr; | ||
2516 | } | ||